forked from pingdotgg/t3code
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathCheckpointStore.ts
More file actions
170 lines (149 loc) · 5.72 KB
/
Copy pathCheckpointStore.ts
File metadata and controls
170 lines (149 loc) · 5.72 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
/**
* CheckpointStore - Repository interface for filesystem-backed workspace checkpoints.
*
* Owns hidden Git-ref checkpoint capture/restore and diff computation for a
* workspace thread timeline. It does not store user-facing checkpoint metadata
* and does not coordinate provider conversation rollback.
*
* The live adapter resolves the active VCS driver once per checkpoint operation
* and delegates to the driver's optional checkpoint capability.
*
* Uses Effect `Context.Service` for dependency injection and exposes typed
* domain errors for checkpoint storage operations.
*
* @module CheckpointStore
*/
import { VcsUnsupportedOperationError, type CheckpointRef } from "@t3tools/contracts";
import * as Context from "effect/Context";
import * as Effect from "effect/Effect";
import * as Layer from "effect/Layer";
import type { CheckpointStoreError } from "./Errors.ts";
import type { VcsCheckpointOps } from "../vcs/VcsDriver.ts";
import * as VcsDriverRegistry from "../vcs/VcsDriverRegistry.ts";
export interface CaptureCheckpointInput {
readonly cwd: string;
readonly checkpointRef: CheckpointRef;
}
export interface RestoreCheckpointInput {
readonly cwd: string;
readonly checkpointRef: CheckpointRef;
readonly fallbackToHead?: boolean;
}
export interface DiffCheckpointsInput {
readonly cwd: string;
readonly fromCheckpointRef: CheckpointRef;
readonly toCheckpointRef: CheckpointRef;
readonly fallbackFromToHead?: boolean;
readonly ignoreWhitespace: boolean;
}
export interface DeleteCheckpointRefsInput {
readonly cwd: string;
readonly checkpointRefs: ReadonlyArray<CheckpointRef>;
}
/** Service tag for checkpoint persistence and restore operations. */
export class CheckpointStore extends Context.Service<
CheckpointStore,
{
/** Check whether cwd is inside a Git worktree. */
readonly isGitRepository: (cwd: string) => Effect.Effect<boolean, CheckpointStoreError>;
/**
* Capture a checkpoint commit and store it at the provided checkpoint ref.
*
* Uses an isolated temporary Git index and writes a hidden ref.
*/
readonly captureCheckpoint: (
input: CaptureCheckpointInput,
) => Effect.Effect<void, CheckpointStoreError>;
/** Check whether a checkpoint ref exists. */
readonly hasCheckpointRef: (
input: Omit<RestoreCheckpointInput, "fallbackToHead">,
) => Effect.Effect<boolean, CheckpointStoreError>;
/**
* Restore workspace and staging state to a checkpoint.
*
* Optionally falls back to current `HEAD` when the checkpoint ref is missing.
*/
readonly restoreCheckpoint: (
input: RestoreCheckpointInput,
) => Effect.Effect<boolean, CheckpointStoreError>;
/**
* Compute a patch diff between two checkpoint refs.
*
* Can optionally treat a missing "from" ref as `HEAD`.
*/
readonly diffCheckpoints: (
input: DiffCheckpointsInput,
) => Effect.Effect<string, CheckpointStoreError>;
/**
* Delete the provided checkpoint refs.
*
* Best-effort delete: missing refs are tolerated.
*/
readonly deleteCheckpointRefs: (
input: DeleteCheckpointRefsInput,
) => Effect.Effect<void, CheckpointStoreError>;
}
>()("t3/checkpointing/CheckpointStore") {}
export const make = Effect.gen(function* () {
const vcsRegistry = yield* VcsDriverRegistry.VcsDriverRegistry;
const resolveCheckpoints = Effect.fn("CheckpointStore.resolveCheckpoints")(function* (
operation: string,
cwd: string,
) {
const handle = yield* vcsRegistry.resolve({ cwd });
if (!handle.driver.checkpoints) {
return yield* new VcsUnsupportedOperationError({
operation,
kind: handle.kind,
detail: `${handle.kind} driver does not implement checkpoint operations.`,
});
}
return handle.driver.checkpoints satisfies VcsCheckpointOps;
});
const isGitRepository: CheckpointStore["Service"]["isGitRepository"] = (cwd) =>
vcsRegistry
.detect({ cwd, requestedKind: "git" })
.pipe(Effect.map((repository) => repository !== null));
const captureCheckpoint: CheckpointStore["Service"]["captureCheckpoint"] = Effect.fn(
"captureCheckpoint",
)(function* (input) {
const checkpoints = yield* resolveCheckpoints("CheckpointStore.captureCheckpoint", input.cwd);
return yield* checkpoints.captureCheckpoint(input);
});
const hasCheckpointRef: CheckpointStore["Service"]["hasCheckpointRef"] = Effect.fn(
"hasCheckpointRef",
)(function* (input) {
const checkpoints = yield* resolveCheckpoints("CheckpointStore.hasCheckpointRef", input.cwd);
return yield* checkpoints.hasCheckpointRef(input);
});
const restoreCheckpoint: CheckpointStore["Service"]["restoreCheckpoint"] = Effect.fn(
"restoreCheckpoint",
)(function* (input) {
const checkpoints = yield* resolveCheckpoints("CheckpointStore.restoreCheckpoint", input.cwd);
return yield* checkpoints.restoreCheckpoint(input);
});
const diffCheckpoints: CheckpointStore["Service"]["diffCheckpoints"] = Effect.fn(
"diffCheckpoints",
)(function* (input) {
const checkpoints = yield* resolveCheckpoints("CheckpointStore.diffCheckpoints", input.cwd);
return yield* checkpoints.diffCheckpoints(input);
});
const deleteCheckpointRefs: CheckpointStore["Service"]["deleteCheckpointRefs"] = Effect.fn(
"deleteCheckpointRefs",
)(function* (input) {
const checkpoints = yield* resolveCheckpoints(
"CheckpointStore.deleteCheckpointRefs",
input.cwd,
);
return yield* checkpoints.deleteCheckpointRefs(input);
});
return CheckpointStore.of({
isGitRepository,
captureCheckpoint,
hasCheckpointRef,
restoreCheckpoint,
diffCheckpoints,
deleteCheckpointRefs,
});
});
export const layer = Layer.effect(CheckpointStore, make);