1import {
2 type EnvironmentId,
3 type OrchestrationV2ShellSnapshot,
4 type OrchestrationV2ThreadDetailSnapshot,
5 type ServerConfig,
6 type ThreadId,
7 type VcsListRefsResult,
8} from "@t3tools/contracts";
9import * as Context from "effect/Context";
10import * as Effect from "effect/Effect";
11import * as Option from "effect/Option";
12import * as Schema from "effect/Schema";
13
14import type { ConnectionRegistration } from "../connection/catalog.ts";
15import type { DeferredShellSnapshot } from "../state/shellPullRequests.ts";
16import type { ConnectionTarget, PersistedConnectionTarget } from "../connection/model.ts";
17
18export class ConnectionPersistenceError extends Schema.TaggedError<ConnectionPersistenceError>()(
19 "ConnectionPersistenceError",
20 {
21 operation: Schema.Literals([
22 "list-targets",
23 "list-disabled-targets",
24 "register-connection",
25 "set-connection-routes",
26 "remove-connection",
27 "set-connection-enabled",
28 "load-shell",
29 "save-shell",
30 "load-thread",
31 "save-thread",
32 "remove-thread",
33 "load-server-config",
34 "save-server-config",
35 "load-vcs-refs",
36 "save-vcs-refs",
37 "remove-vcs-refs",
38 "clear-vcs-refs",
39 "clear-environment",
40 ]),
41 message: Schema.String,
42 },
43) {}
44
45export class ConnectionTargetStore extends Context.Service<
46 ConnectionTargetStore,
47 {
48 readonly list: Effect.Effect<ReadonlyArray<ConnectionTarget>, ConnectionPersistenceError>;
49 /** Saved environments the user switched off. See `ConnectionRegistrationStore.setEnabled`. */
50 readonly listDisabled: Effect.Effect<ReadonlyArray<EnvironmentId>, ConnectionPersistenceError>;
51 }
52>()("@t3tools/client-runtime/platform/persistence/ConnectionTargetStore") {}
53
54export class ConnectionRegistrationStore extends Context.Service<
55 ConnectionRegistrationStore,
56 {
57 /**
58 * Saves one route's records and sets the environment's full route list,
59 * preferred first. Records of routes missing from `routes` are dropped.
60 */
61 readonly register: (
62 registration: ConnectionRegistration,
63 routes: ReadonlyArray<PersistedConnectionTarget>,
64 ) => Effect.Effect<void, ConnectionPersistenceError>;
65 /** Reorders or drops routes without adding one. `routes` must not be empty. */
66 readonly setRoutes: (
67 environmentId: EnvironmentId,
68 routes: ReadonlyArray<PersistedConnectionTarget>,
69 ) => Effect.Effect<void, ConnectionPersistenceError>;
70 /** Forgets the environment and every route it had. */
71 readonly remove: (
72 environmentId: EnvironmentId,
73 ) => Effect.Effect<void, ConnectionPersistenceError>;
74 readonly setEnabled: (
75 environmentId: EnvironmentId,
76 enabled: boolean,
77 ) => Effect.Effect<void, ConnectionPersistenceError>;
78 }
79>()("@t3tools/client-runtime/platform/persistence/ConnectionRegistrationStore") {}
80
81export class EnvironmentCacheStore extends Context.Service<
82 EnvironmentCacheStore,
83 {
84 readonly loadShell: (
85 environmentId: EnvironmentId,
86 ) => Effect.Effect<Option.Option<DeferredShellSnapshot>, ConnectionPersistenceError>;
87 readonly saveShell: (
88 environmentId: EnvironmentId,
89 snapshot: OrchestrationV2ShellSnapshot,
90 ) => Effect.Effect<void, ConnectionPersistenceError>;
91 readonly loadThread: (
92 environmentId: EnvironmentId,
93 threadId: ThreadId,
94 ) => Effect.Effect<
95 Option.Option<OrchestrationV2ThreadDetailSnapshot>,
96 ConnectionPersistenceError
97 >;
98 readonly saveThread: (
99 environmentId: EnvironmentId,
100 snapshot: OrchestrationV2ThreadDetailSnapshot,
101 ) => Effect.Effect<void, ConnectionPersistenceError>;
102 readonly removeThread: (
103 environmentId: EnvironmentId,
104 threadId: ThreadId,
105 ) => Effect.Effect<void, ConnectionPersistenceError>;
106 /**
107 * The last complete server configuration. This deliberately includes provider
108 * metadata so offline task creation can still offer the models a user last saw.
109 */
110 readonly loadServerConfig: (
111 environmentId: EnvironmentId,
112 ) => Effect.Effect<Option.Option<ServerConfig>, ConnectionPersistenceError>;
113 readonly saveServerConfig: (
114 environmentId: EnvironmentId,
115 config: ServerConfig,
116 ) => Effect.Effect<void, ConnectionPersistenceError>;
117 /**
118 * The unfiltered branch list for a workspace. Query-specific lists are not
119 * cached because they are incomplete and unsafe to present as a full picker.
120 */
121 readonly loadVcsRefs: (
122 environmentId: EnvironmentId,
123 cwd: string,
124 ) => Effect.Effect<Option.Option<VcsListRefsResult>, ConnectionPersistenceError>;
125 readonly saveVcsRefs: (
126 environmentId: EnvironmentId,
127 cwd: string,
128 refs: VcsListRefsResult,
129 ) => Effect.Effect<void, ConnectionPersistenceError>;
130 readonly removeVcsRefs: (
131 environmentId: EnvironmentId,
132 cwd: string,
133 ) => Effect.Effect<void, ConnectionPersistenceError>;
134 /**
135 * Removes every persisted branch-list snapshot for an environment. Git ref
136 * mutations are repository-wide, and linked worktrees may have cached the
137 * same refs under different working-directory keys.
138 */
139 readonly clearVcsRefs: (
140 environmentId: EnvironmentId,
141 ) => Effect.Effect<void, ConnectionPersistenceError>;
142 readonly clear: (
143 environmentId: EnvironmentId,
144 ) => Effect.Effect<void, ConnectionPersistenceError>;
145 }
146>()("@t3tools/client-runtime/platform/persistence/EnvironmentCacheStore") {}
147
148export class EnvironmentOwnedDataCleanup extends Context.Reference<{
149 readonly clear: (
150 environmentId: EnvironmentId,
151 options?: { readonly retainDrafts?: boolean },
152 ) => Effect.Effect<void>;
153}>("@t3tools/client-runtime/platform/persistence/EnvironmentOwnedDataCleanup", {
154 defaultValue: () => ({
155 clear: () => Effect.void,
156 }),
157}) {}