1import { WS_METHODS } from "@t3tools/contracts";
2import { Atom } from "effect/reactivity";
3
4import {
5 createAtomCommandScheduler,
6 createEnvironmentRpcCommand,
7 createEnvironmentRpcQueryAtomFamily,
8 createEnvironmentRpcSubscriptionAtomFamily,
9} from "./runtime.ts";
10import type { EnvironmentRegistry } from "../connection/registry.ts";
11import * as Persistence from "../platform/persistence.ts";
12import { vcsCommandConcurrency, vcsCommandScheduler } from "./vcsCommandScheduler.ts";
13import { invalidateCachedVcsRefs } from "./vcsRefInvalidation.ts";
14
15export function createSourceControlEnvironmentAtoms<R, E>(
16 runtime: Atom.AtomRuntime<EnvironmentRegistry | Persistence.EnvironmentCacheStore | R, E>,
17) {
18 const commandScheduler = createAtomCommandScheduler();
19 return {
20 discovery: createEnvironmentRpcQueryAtomFamily(runtime, {
21 label: "environment-data:server:source-control-discovery",
22 tag: WS_METHODS.serverDiscoverSourceControl,
23 }),
24 repository: createEnvironmentRpcQueryAtomFamily(runtime, {
25 label: "environment-data:source-control:repository",
26 tag: WS_METHODS.sourceControlLookupRepository,
27 }),
28 cloneRepository: createEnvironmentRpcCommand(runtime, {
29 label: "environment-data:source-control:clone-repository",
30 tag: WS_METHODS.sourceControlCloneRepository,
31 scheduler: commandScheduler,
32 concurrency: {
33 mode: "serial",
34 key: ({ environmentId }) => environmentId,
35 },
36 }),
37 // Clone-backed project creation. The RPC returns once the project exists
38 // and the clone runs in the background; `projectClones` carries progress.
39 startProjectClone: createEnvironmentRpcCommand(runtime, {
40 label: "environment-data:source-control:project-clone-start",
41 tag: WS_METHODS.projectCloneStart,
42 scheduler: commandScheduler,
43 concurrency: {
44 mode: "serial",
45 key: ({ environmentId }) => environmentId,
46 },
47 }),
48 // Cancel and retry share the start queue so a double click cannot race
49 // two actions against the same clone.
50 cancelProjectClone: createEnvironmentRpcCommand(runtime, {
51 label: "environment-data:source-control:project-clone-cancel",
52 tag: WS_METHODS.projectCloneCancel,
53 scheduler: commandScheduler,
54 concurrency: {
55 mode: "serial",
56 key: ({ environmentId }) => environmentId,
57 },
58 }),
59 retryProjectClone: createEnvironmentRpcCommand(runtime, {
60 label: "environment-data:source-control:project-clone-retry",
61 tag: WS_METHODS.projectCloneRetry,
62 scheduler: commandScheduler,
63 concurrency: {
64 mode: "serial",
65 key: ({ environmentId }) => environmentId,
66 },
67 }),
68 // Every clone the environment tracks. Empty until a clone starts; a
69 // finished clone drops out after a grace period, a failed one stays.
70 projectClones: createEnvironmentRpcSubscriptionAtomFamily(runtime, {
71 label: "environment-data:source-control:project-clones",
72 tag: WS_METHODS.subscribeProjectClones,
73 }),
74 publishRepository: createEnvironmentRpcCommand(runtime, {
75 label: "environment-data:source-control:publish-repository",
76 tag: WS_METHODS.sourceControlPublishRepository,
77 scheduler: vcsCommandScheduler,
78 concurrency: vcsCommandConcurrency,
79 onSettled: (target, registry) =>
80 invalidateCachedVcsRefs(registry, {
81 environmentId: target.environmentId,
82 cwd: target.input.cwd,
83 }),
84 }),
85 };
86}