@ricsam/r5d-worker 0.0.74 → 0.0.76
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -4
- package/dist/cjs/main.cjs +1031 -976
- package/dist/cjs/package.json +1 -1
- package/dist/cjs/project-workspace-state.cjs +777 -0
- package/dist/cjs/project-worktrees.cjs +559 -0
- package/dist/cjs/working-tree-mirror.cjs +225 -0
- package/dist/cjs/workspace-git-sync.cjs +565 -0
- package/dist/cjs/workspace-incident-state.cjs +2 -38
- package/dist/mjs/main.mjs +1048 -987
- package/dist/mjs/package.json +1 -1
- package/dist/mjs/project-workspace-state.mjs +745 -0
- package/dist/mjs/project-worktrees.mjs +513 -0
- package/dist/mjs/working-tree-mirror.mjs +190 -0
- package/dist/mjs/workspace-git-sync.mjs +524 -0
- package/dist/mjs/workspace-incident-state.mjs +1 -35
- package/dist/types/main.d.ts +35 -55
- package/dist/types/project-workspace-state.d.ts +144 -0
- package/dist/types/project-worktrees.d.ts +112 -0
- package/dist/types/working-tree-mirror.d.ts +24 -0
- package/dist/types/workspace-git-sync.d.ts +97 -0
- package/dist/types/workspace-incident-state.d.ts +0 -17
- package/dist/types/workspace-mutation-gate.d.ts +3 -3
- package/package.json +1 -1
- package/dist/cjs/workspace-convergence.cjs +0 -280
- package/dist/cjs/workspace-manifest-admission.cjs +0 -60
- package/dist/cjs/workspace-sync.cjs +0 -2469
- package/dist/mjs/workspace-convergence.mjs +0 -236
- package/dist/mjs/workspace-manifest-admission.mjs +0 -26
- package/dist/mjs/workspace-sync.mjs +0 -2417
- package/dist/types/workspace-convergence.d.ts +0 -93
- package/dist/types/workspace-manifest-admission.d.ts +0 -22
- package/dist/types/workspace-sync.d.ts +0 -167
|
@@ -1,236 +0,0 @@
|
|
|
1
|
-
import fs from "node:fs";
|
|
2
|
-
import path from "node:path";
|
|
3
|
-
const WORKSPACE_PUBLICATION_QUIET_MS = 5e3;
|
|
4
|
-
const WORKSPACE_IDLE_SAFETY_SCAN_MS = 6e4;
|
|
5
|
-
const WORKSPACE_MANIFEST_REVISION_PATTERN = /^[0-9a-f]{64}$/;
|
|
6
|
-
class WorkspaceManifestRevisionFence {
|
|
7
|
-
sequence = 0;
|
|
8
|
-
requestedRevision = null;
|
|
9
|
-
completedRevision = null;
|
|
10
|
-
begin(revision) {
|
|
11
|
-
if (!WORKSPACE_MANIFEST_REVISION_PATTERN.test(revision)) {
|
|
12
|
-
throw new Error("Workspace manifest revision must be a lowercase SHA-256 hash");
|
|
13
|
-
}
|
|
14
|
-
this.sequence += 1;
|
|
15
|
-
this.requestedRevision = revision;
|
|
16
|
-
this.completedRevision = null;
|
|
17
|
-
return { sequence: this.sequence, revision };
|
|
18
|
-
}
|
|
19
|
-
isCurrent(token) {
|
|
20
|
-
return token.sequence === this.sequence && token.revision === this.requestedRevision;
|
|
21
|
-
}
|
|
22
|
-
complete(token) {
|
|
23
|
-
if (!this.isCurrent(token)) return false;
|
|
24
|
-
this.completedRevision = token.revision;
|
|
25
|
-
return true;
|
|
26
|
-
}
|
|
27
|
-
readyRevision() {
|
|
28
|
-
return this.completedRevision === this.requestedRevision ? this.completedRevision : null;
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
function workspacePublicationTelemetry(input) {
|
|
32
|
-
return {
|
|
33
|
-
totalMs: Math.max(0, input.finishedAt - input.requestedAt),
|
|
34
|
-
queueMs: Math.max(0, input.prepareStartedAt - input.requestedAt),
|
|
35
|
-
prepareMs: Math.max(0, input.synchronizeStartedAt - input.prepareStartedAt),
|
|
36
|
-
synchronizeMs: Math.max(0, input.finishedAt - input.synchronizeStartedAt)
|
|
37
|
-
};
|
|
38
|
-
}
|
|
39
|
-
function workspacePublicationIncidentAction(requestId) {
|
|
40
|
-
return requestId ? "request_terminal_authority" : "defer_automatic";
|
|
41
|
-
}
|
|
42
|
-
class ExplicitWorkspacePublicationQueue {
|
|
43
|
-
scheduled;
|
|
44
|
-
queued = [];
|
|
45
|
-
schedule(publication, pendingRequestId) {
|
|
46
|
-
if (pendingRequestId === publication.requestId || this.scheduled?.requestId === publication.requestId || this.queued.some((queued) => queued.requestId === publication.requestId)) {
|
|
47
|
-
return false;
|
|
48
|
-
}
|
|
49
|
-
if (pendingRequestId || this.scheduled) {
|
|
50
|
-
this.queued.push(publication);
|
|
51
|
-
return false;
|
|
52
|
-
}
|
|
53
|
-
this.scheduled = publication;
|
|
54
|
-
return true;
|
|
55
|
-
}
|
|
56
|
-
hasScheduled() {
|
|
57
|
-
return this.scheduled !== void 0;
|
|
58
|
-
}
|
|
59
|
-
consumeScheduled(requestId) {
|
|
60
|
-
if (this.scheduled?.requestId === requestId) this.scheduled = void 0;
|
|
61
|
-
}
|
|
62
|
-
enqueue(publication, pendingRequestId) {
|
|
63
|
-
if (pendingRequestId === publication.requestId || this.scheduled?.requestId === publication.requestId || this.queued.some((queued) => queued.requestId === publication.requestId)) {
|
|
64
|
-
return;
|
|
65
|
-
}
|
|
66
|
-
this.queued.push(publication);
|
|
67
|
-
}
|
|
68
|
-
next() {
|
|
69
|
-
return this.queued.shift();
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
const EMPTY_STATE = {
|
|
73
|
-
localHead: null,
|
|
74
|
-
visibleBaselineHead: null,
|
|
75
|
-
desiredCanonicalHead: null,
|
|
76
|
-
dirtyGeneration: 0,
|
|
77
|
-
publishedGeneration: 0
|
|
78
|
-
};
|
|
79
|
-
function validHead(value) {
|
|
80
|
-
return value === null || typeof value === "string" && /^[0-9a-f]{40,64}$/.test(value);
|
|
81
|
-
}
|
|
82
|
-
function validGeneration(value) {
|
|
83
|
-
return typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
|
|
84
|
-
}
|
|
85
|
-
function readWorkspaceConvergenceState(statePath) {
|
|
86
|
-
try {
|
|
87
|
-
const parsed = JSON.parse(fs.readFileSync(statePath, "utf8"));
|
|
88
|
-
if (!validHead(parsed.localHead) || !(parsed.visibleBaselineHead === void 0 || validHead(parsed.visibleBaselineHead)) || !validHead(parsed.desiredCanonicalHead) || !validGeneration(parsed.dirtyGeneration) || !validGeneration(parsed.publishedGeneration) || parsed.publishedGeneration > parsed.dirtyGeneration) {
|
|
89
|
-
return { ...EMPTY_STATE };
|
|
90
|
-
}
|
|
91
|
-
return {
|
|
92
|
-
localHead: parsed.localHead,
|
|
93
|
-
visibleBaselineHead: parsed.visibleBaselineHead === void 0 ? parsed.localHead : parsed.visibleBaselineHead,
|
|
94
|
-
desiredCanonicalHead: parsed.desiredCanonicalHead,
|
|
95
|
-
dirtyGeneration: parsed.dirtyGeneration,
|
|
96
|
-
publishedGeneration: parsed.publishedGeneration
|
|
97
|
-
};
|
|
98
|
-
} catch {
|
|
99
|
-
return { ...EMPTY_STATE };
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
class WorkspaceConvergenceState {
|
|
103
|
-
constructor(statePath) {
|
|
104
|
-
this.statePath = statePath;
|
|
105
|
-
this.state = readWorkspaceConvergenceState(statePath);
|
|
106
|
-
this.publicationState = this.state.dirtyGeneration > this.state.publishedGeneration ? "scheduled" : "idle";
|
|
107
|
-
}
|
|
108
|
-
statePath;
|
|
109
|
-
state;
|
|
110
|
-
publicationState;
|
|
111
|
-
snapshot() {
|
|
112
|
-
return {
|
|
113
|
-
localHead: this.state.localHead,
|
|
114
|
-
desiredCanonicalHead: this.state.desiredCanonicalHead,
|
|
115
|
-
dirtyGeneration: this.state.dirtyGeneration,
|
|
116
|
-
publishedGeneration: this.state.publishedGeneration,
|
|
117
|
-
publicationState: this.publicationState
|
|
118
|
-
};
|
|
119
|
-
}
|
|
120
|
-
visibleBaselineHead() {
|
|
121
|
-
return this.state.visibleBaselineHead;
|
|
122
|
-
}
|
|
123
|
-
setLocalHead(localHead) {
|
|
124
|
-
if (!validHead(localHead)) throw new Error("Invalid local workspace head");
|
|
125
|
-
this.state.localHead = localHead;
|
|
126
|
-
this.state.visibleBaselineHead = localHead;
|
|
127
|
-
this.persist();
|
|
128
|
-
}
|
|
129
|
-
setVisibleBaselineHead(visibleBaselineHead) {
|
|
130
|
-
if (!validHead(visibleBaselineHead)) throw new Error("Invalid visible workspace baseline head");
|
|
131
|
-
this.state.visibleBaselineHead = visibleBaselineHead;
|
|
132
|
-
this.persist();
|
|
133
|
-
}
|
|
134
|
-
observeCanonicalHead(canonicalHead) {
|
|
135
|
-
if (!validHead(canonicalHead)) throw new Error("Invalid canonical workspace head");
|
|
136
|
-
if (this.state.desiredCanonicalHead === canonicalHead) return false;
|
|
137
|
-
this.state.desiredCanonicalHead = canonicalHead;
|
|
138
|
-
this.persist();
|
|
139
|
-
return true;
|
|
140
|
-
}
|
|
141
|
-
markDirty() {
|
|
142
|
-
this.state.dirtyGeneration += 1;
|
|
143
|
-
if (this.publicationState === "idle") this.publicationState = "scheduled";
|
|
144
|
-
this.persist();
|
|
145
|
-
return this.state.dirtyGeneration;
|
|
146
|
-
}
|
|
147
|
-
schedule() {
|
|
148
|
-
if (this.publicationState === "idle") this.publicationState = "scheduled";
|
|
149
|
-
}
|
|
150
|
-
beginPreparing() {
|
|
151
|
-
if (this.publicationState === "preparing" || this.publicationState === "submitting") {
|
|
152
|
-
throw new Error("A workspace publication is already in flight");
|
|
153
|
-
}
|
|
154
|
-
this.publicationState = "preparing";
|
|
155
|
-
}
|
|
156
|
-
beginSubmitting() {
|
|
157
|
-
if (this.publicationState !== "preparing") throw new Error("Workspace publication authority was not prepared");
|
|
158
|
-
this.publicationState = "submitting";
|
|
159
|
-
}
|
|
160
|
-
defer() {
|
|
161
|
-
this.publicationState = "scheduled";
|
|
162
|
-
}
|
|
163
|
-
block() {
|
|
164
|
-
this.publicationState = "blocked";
|
|
165
|
-
}
|
|
166
|
-
releaseBlock() {
|
|
167
|
-
this.publicationState = this.state.dirtyGeneration > this.state.publishedGeneration ? "scheduled" : "idle";
|
|
168
|
-
}
|
|
169
|
-
observeIncidentState(blocked) {
|
|
170
|
-
if (this.publicationState === "preparing" || this.publicationState === "submitting") return;
|
|
171
|
-
if (blocked) this.block();
|
|
172
|
-
else this.releaseBlock();
|
|
173
|
-
}
|
|
174
|
-
resetTransientPublicationState() {
|
|
175
|
-
this.publicationState = this.state.dirtyGeneration > this.state.publishedGeneration ? "scheduled" : "idle";
|
|
176
|
-
}
|
|
177
|
-
complete(input) {
|
|
178
|
-
if (!validGeneration(input.generation) || input.generation > this.state.dirtyGeneration) {
|
|
179
|
-
throw new Error("Invalid completed workspace generation");
|
|
180
|
-
}
|
|
181
|
-
if (!validHead(input.canonicalHead)) throw new Error("Invalid completed canonical workspace head");
|
|
182
|
-
if (input.published) {
|
|
183
|
-
this.state.publishedGeneration = Math.max(this.state.publishedGeneration, input.generation);
|
|
184
|
-
this.state.localHead = input.canonicalHead;
|
|
185
|
-
this.state.visibleBaselineHead = input.canonicalHead;
|
|
186
|
-
this.state.desiredCanonicalHead = input.canonicalHead;
|
|
187
|
-
}
|
|
188
|
-
this.publicationState = this.state.dirtyGeneration > this.state.publishedGeneration ? "scheduled" : "idle";
|
|
189
|
-
this.persist();
|
|
190
|
-
}
|
|
191
|
-
persist() {
|
|
192
|
-
fs.mkdirSync(path.dirname(this.statePath), { recursive: true });
|
|
193
|
-
const temporaryPath = `${this.statePath}.${process.pid}.${crypto.randomUUID()}.tmp`;
|
|
194
|
-
fs.writeFileSync(temporaryPath, `${JSON.stringify(this.state)}
|
|
195
|
-
`, { mode: 384 });
|
|
196
|
-
fs.renameSync(temporaryPath, this.statePath);
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
const PROCESS_WORKSPACE_CONVERGENCE_STATES = /* @__PURE__ */ new Map();
|
|
200
|
-
function processWorkspaceConvergenceState(statePath) {
|
|
201
|
-
const key = path.resolve(statePath);
|
|
202
|
-
const existing = PROCESS_WORKSPACE_CONVERGENCE_STATES.get(key);
|
|
203
|
-
if (existing) return existing;
|
|
204
|
-
const created = new WorkspaceConvergenceState(key);
|
|
205
|
-
PROCESS_WORKSPACE_CONVERGENCE_STATES.set(key, created);
|
|
206
|
-
return created;
|
|
207
|
-
}
|
|
208
|
-
function workspaceBootstrapEstablishesVisibleBaseline(input) {
|
|
209
|
-
if (input.hydrated) return true;
|
|
210
|
-
return input.firstBootstrap && !input.incidentActive && input.currentLocalHead === null && input.currentVisibleBaselineHead === null;
|
|
211
|
-
}
|
|
212
|
-
async function waitForWorkspacePublicationOnShutdown(input) {
|
|
213
|
-
input.forcePublication();
|
|
214
|
-
const deadline = Date.now() + (input.timeoutMs ?? 1e4);
|
|
215
|
-
const pollMs = input.pollMs ?? 50;
|
|
216
|
-
while (Date.now() < deadline) {
|
|
217
|
-
const snapshot = input.snapshot();
|
|
218
|
-
if (snapshot.publicationState === "blocked") return "blocked";
|
|
219
|
-
if (snapshot.publicationState === "idle" && snapshot.publishedGeneration >= snapshot.dirtyGeneration) return "settled";
|
|
220
|
-
await new Promise((resolve) => setTimeout(resolve, pollMs));
|
|
221
|
-
}
|
|
222
|
-
return "timed_out";
|
|
223
|
-
}
|
|
224
|
-
export {
|
|
225
|
-
ExplicitWorkspacePublicationQueue,
|
|
226
|
-
WORKSPACE_IDLE_SAFETY_SCAN_MS,
|
|
227
|
-
WORKSPACE_PUBLICATION_QUIET_MS,
|
|
228
|
-
WorkspaceConvergenceState,
|
|
229
|
-
WorkspaceManifestRevisionFence,
|
|
230
|
-
processWorkspaceConvergenceState,
|
|
231
|
-
readWorkspaceConvergenceState,
|
|
232
|
-
waitForWorkspacePublicationOnShutdown,
|
|
233
|
-
workspaceBootstrapEstablishesVisibleBaseline,
|
|
234
|
-
workspacePublicationIncidentAction,
|
|
235
|
-
workspacePublicationTelemetry
|
|
236
|
-
};
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
import fs from "node:fs";
|
|
2
|
-
import path from "node:path";
|
|
3
|
-
import { managedProjectRoot } from "./managed-paths.mjs";
|
|
4
|
-
function hasNormalGitDirectory(checkoutPath) {
|
|
5
|
-
const gitPath = path.join(checkoutPath, ".git");
|
|
6
|
-
return fs.existsSync(gitPath) && fs.statSync(gitPath).isDirectory();
|
|
7
|
-
}
|
|
8
|
-
function inspectWorkspaceManifestFilesystem(input) {
|
|
9
|
-
const firstBootstrap = !hasNormalGitDirectory(input.workspaceShadowRoot);
|
|
10
|
-
const missingCheckouts = [];
|
|
11
|
-
for (const project of input.projects) {
|
|
12
|
-
const projectRoot = managedProjectRoot(input.projectsRoot, project.checkoutPathSegments);
|
|
13
|
-
for (const branchName of project.branches) {
|
|
14
|
-
if (!hasNormalGitDirectory(path.join(projectRoot, branchName))) {
|
|
15
|
-
missingCheckouts.push({ projectId: project.projectId, branchName });
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
return {
|
|
20
|
-
firstBootstrap,
|
|
21
|
-
missingCheckouts
|
|
22
|
-
};
|
|
23
|
-
}
|
|
24
|
-
export {
|
|
25
|
-
inspectWorkspaceManifestFilesystem
|
|
26
|
-
};
|