Repository navigation
Expand file tree
/
Copy pathCheckpointStore.ts
More file actions
158 lines (134 loc) · 5.35 KB
/
Copy pathCheckpointStore.ts
File metadata and controls
158 lines (134 loc) · 5.35 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
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;
readonly format?: "patch" | "numstat";
/** Limits the diff to these exact paths. An empty list yields an empty diff. */
readonly filePaths?: ReadonlyArray<string>;
}
export interface ListAuthoredPathsInput {
readonly cwd: string;
readonly fromCheckpointRef: CheckpointRef;
readonly toCheckpointRef: CheckpointRef;
}
export interface DeleteCheckpointRefsInput {
readonly cwd: string;
readonly checkpointRefs: ReadonlyArray<CheckpointRef>;
}
export class CheckpointStore extends Context.Service<
CheckpointStore,
{
readonly isGitRepository: (cwd: string) => Effect.Effect<boolean, CheckpointStoreError>;
readonly captureCheckpoint: (
input: CaptureCheckpointInput,
) => Effect.Effect<void, CheckpointStoreError>;
readonly hasCheckpointRef: (
input: Omit<RestoreCheckpointInput, "fallbackToHead">,
) => Effect.Effect<boolean, CheckpointStoreError>;
readonly restoreCheckpoint: (
input: RestoreCheckpointInput,
) => Effect.Effect<boolean, CheckpointStoreError>;
readonly diffCheckpoints: (
input: DiffCheckpointsInput,
) => Effect.Effect<string, CheckpointStoreError>;
/**
* Returns null when HEAD did not move or a checkpoint does not record HEAD.
* Then every changed path belongs to the turn.
*/
readonly listAuthoredPaths: (
input: ListAuthoredPathsInput,
) => Effect.Effect<ReadonlySet<string> | null, CheckpointStoreError>;
readonly deleteCheckpointRefs: (
input: DeleteCheckpointRefsInput,
) => Effect.Effect<void, CheckpointStoreError>;
}
>()("t3/checkpointing/CheckpointStore") {}
/** @public Service construction is part of the canonical Effect module API. */
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 listAuthoredPaths: CheckpointStore["Service"]["listAuthoredPaths"] = Effect.fn(
"listAuthoredPaths",
)(function* (input) {
const checkpoints = yield* resolveCheckpoints("CheckpointStore.listAuthoredPaths", input.cwd);
return yield* checkpoints.listAuthoredPaths(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,
listAuthoredPaths,
deleteCheckpointRefs,
});
});
export const layer = Layer.effect(CheckpointStore, make);