@ricsam/r5d-worker 0.0.58 → 0.0.59
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 +1 -1
- package/dist/cjs/main.cjs +683 -227
- package/dist/cjs/package.json +1 -1
- package/dist/cjs/workspace-convergence.cjs +320 -0
- package/dist/cjs/workspace-incident-state.cjs +32 -3
- package/dist/cjs/workspace-manifest-admission.cjs +70 -0
- package/dist/cjs/workspace-sync.cjs +135 -16
- package/dist/mjs/main.mjs +701 -233
- package/dist/mjs/package.json +1 -1
- package/dist/mjs/workspace-convergence.mjs +275 -0
- package/dist/mjs/workspace-incident-state.mjs +31 -3
- package/dist/mjs/workspace-manifest-admission.mjs +35 -0
- package/dist/mjs/workspace-sync.mjs +132 -15
- package/dist/types/workspace-convergence.d.ts +102 -0
- package/dist/types/workspace-incident-state.d.ts +15 -1
- package/dist/types/workspace-manifest-admission.d.ts +27 -0
- package/dist/types/workspace-sync.d.ts +23 -6
- package/package.json +1 -1
- package/dist/cjs/workspace-sync-fast-path.cjs +0 -108
- package/dist/mjs/workspace-sync-fast-path.mjs +0 -81
- package/dist/types/workspace-sync-fast-path.d.ts +0 -69
package/dist/cjs/package.json
CHANGED
|
@@ -0,0 +1,320 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
var workspace_convergence_exports = {};
|
|
30
|
+
__export(workspace_convergence_exports, {
|
|
31
|
+
ExplicitWorkspaceCheckpointQueue: () => ExplicitWorkspaceCheckpointQueue,
|
|
32
|
+
WORKSPACE_CHECKPOINT_QUIET_MS: () => WORKSPACE_CHECKPOINT_QUIET_MS,
|
|
33
|
+
WORKSPACE_IDLE_SAFETY_SCAN_MS: () => WORKSPACE_IDLE_SAFETY_SCAN_MS,
|
|
34
|
+
WorkspaceConvergenceState: () => WorkspaceConvergenceState,
|
|
35
|
+
WorkspaceFilesystemWriterGate: () => WorkspaceFilesystemWriterGate,
|
|
36
|
+
WorkspaceManifestRevisionFence: () => WorkspaceManifestRevisionFence,
|
|
37
|
+
processWorkspaceConvergenceState: () => processWorkspaceConvergenceState,
|
|
38
|
+
processWorkspaceFilesystemWriterGate: () => processWorkspaceFilesystemWriterGate,
|
|
39
|
+
readWorkspaceConvergenceState: () => readWorkspaceConvergenceState,
|
|
40
|
+
waitForWorkspaceCheckpointOnShutdown: () => waitForWorkspaceCheckpointOnShutdown,
|
|
41
|
+
workspaceCheckpointIncidentAction: () => workspaceCheckpointIncidentAction,
|
|
42
|
+
workspaceCheckpointTelemetry: () => workspaceCheckpointTelemetry
|
|
43
|
+
});
|
|
44
|
+
module.exports = __toCommonJS(workspace_convergence_exports);
|
|
45
|
+
var import_node_fs = __toESM(require("node:fs"), 1);
|
|
46
|
+
var import_node_path = __toESM(require("node:path"), 1);
|
|
47
|
+
const WORKSPACE_CHECKPOINT_QUIET_MS = 5e3;
|
|
48
|
+
const WORKSPACE_IDLE_SAFETY_SCAN_MS = 6e4;
|
|
49
|
+
const WORKSPACE_MANIFEST_REVISION_PATTERN = /^[0-9a-f]{64}$/;
|
|
50
|
+
class WorkspaceFilesystemWriterGate {
|
|
51
|
+
activeWriters = 0;
|
|
52
|
+
checkpointActive = false;
|
|
53
|
+
waitingWriters = [];
|
|
54
|
+
waitingCheckpoints = [];
|
|
55
|
+
acquireWriter() {
|
|
56
|
+
if (!this.checkpointActive && this.waitingCheckpoints.length === 0) {
|
|
57
|
+
this.activeWriters += 1;
|
|
58
|
+
return Promise.resolve(this.writerRelease());
|
|
59
|
+
}
|
|
60
|
+
return new Promise((resolve) => this.waitingWriters.push(resolve));
|
|
61
|
+
}
|
|
62
|
+
tryAcquireCheckpoint() {
|
|
63
|
+
if (this.checkpointActive || this.activeWriters > 0 || this.waitingCheckpoints.length > 0) return null;
|
|
64
|
+
this.checkpointActive = true;
|
|
65
|
+
return this.checkpointRelease();
|
|
66
|
+
}
|
|
67
|
+
acquireCheckpoint() {
|
|
68
|
+
const immediate = this.tryAcquireCheckpoint();
|
|
69
|
+
if (immediate) return Promise.resolve(immediate);
|
|
70
|
+
return new Promise((resolve) => this.waitingCheckpoints.push(resolve));
|
|
71
|
+
}
|
|
72
|
+
checkpointRelease() {
|
|
73
|
+
let released = false;
|
|
74
|
+
return () => {
|
|
75
|
+
if (released) return;
|
|
76
|
+
released = true;
|
|
77
|
+
this.checkpointActive = false;
|
|
78
|
+
this.drain();
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
writerRelease() {
|
|
82
|
+
let released = false;
|
|
83
|
+
return () => {
|
|
84
|
+
if (released) return;
|
|
85
|
+
released = true;
|
|
86
|
+
this.activeWriters -= 1;
|
|
87
|
+
this.drain();
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
drain() {
|
|
91
|
+
if (this.checkpointActive || this.activeWriters > 0) return;
|
|
92
|
+
const checkpoint = this.waitingCheckpoints.shift();
|
|
93
|
+
if (checkpoint) {
|
|
94
|
+
this.checkpointActive = true;
|
|
95
|
+
checkpoint(this.checkpointRelease());
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
while (this.waitingWriters.length > 0) {
|
|
99
|
+
const resolve = this.waitingWriters.shift();
|
|
100
|
+
if (!resolve) continue;
|
|
101
|
+
this.activeWriters += 1;
|
|
102
|
+
resolve(this.writerRelease());
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
const PROCESS_WORKSPACE_FILESYSTEM_WRITERS = new WorkspaceFilesystemWriterGate();
|
|
107
|
+
function processWorkspaceFilesystemWriterGate() {
|
|
108
|
+
return PROCESS_WORKSPACE_FILESYSTEM_WRITERS;
|
|
109
|
+
}
|
|
110
|
+
class WorkspaceManifestRevisionFence {
|
|
111
|
+
sequence = 0;
|
|
112
|
+
requestedRevision = null;
|
|
113
|
+
completedRevision = null;
|
|
114
|
+
begin(revision) {
|
|
115
|
+
if (!WORKSPACE_MANIFEST_REVISION_PATTERN.test(revision)) {
|
|
116
|
+
throw new Error("Workspace manifest revision must be a lowercase SHA-256 hash");
|
|
117
|
+
}
|
|
118
|
+
this.sequence += 1;
|
|
119
|
+
this.requestedRevision = revision;
|
|
120
|
+
this.completedRevision = null;
|
|
121
|
+
return { sequence: this.sequence, revision };
|
|
122
|
+
}
|
|
123
|
+
isCurrent(token) {
|
|
124
|
+
return token.sequence === this.sequence && token.revision === this.requestedRevision;
|
|
125
|
+
}
|
|
126
|
+
complete(token) {
|
|
127
|
+
if (!this.isCurrent(token)) return false;
|
|
128
|
+
this.completedRevision = token.revision;
|
|
129
|
+
return true;
|
|
130
|
+
}
|
|
131
|
+
readyRevision() {
|
|
132
|
+
return this.completedRevision === this.requestedRevision ? this.completedRevision : null;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
function workspaceCheckpointTelemetry(input) {
|
|
136
|
+
return {
|
|
137
|
+
totalMs: Math.max(0, input.finishedAt - input.requestedAt),
|
|
138
|
+
queueMs: Math.max(0, input.prepareStartedAt - input.requestedAt),
|
|
139
|
+
prepareMs: Math.max(0, input.synchronizeStartedAt - input.prepareStartedAt),
|
|
140
|
+
synchronizeMs: Math.max(0, input.finishedAt - input.synchronizeStartedAt)
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
function workspaceCheckpointIncidentAction(requestId) {
|
|
144
|
+
return requestId ? "request_terminal_authority" : "defer_automatic";
|
|
145
|
+
}
|
|
146
|
+
class ExplicitWorkspaceCheckpointQueue {
|
|
147
|
+
scheduled;
|
|
148
|
+
queued = [];
|
|
149
|
+
schedule(checkpoint, pendingRequestId) {
|
|
150
|
+
if (pendingRequestId === checkpoint.requestId || this.scheduled?.requestId === checkpoint.requestId || this.queued.some((queued) => queued.requestId === checkpoint.requestId)) {
|
|
151
|
+
return false;
|
|
152
|
+
}
|
|
153
|
+
if (pendingRequestId || this.scheduled) {
|
|
154
|
+
this.queued.push(checkpoint);
|
|
155
|
+
return false;
|
|
156
|
+
}
|
|
157
|
+
this.scheduled = checkpoint;
|
|
158
|
+
return true;
|
|
159
|
+
}
|
|
160
|
+
hasScheduled() {
|
|
161
|
+
return this.scheduled !== void 0;
|
|
162
|
+
}
|
|
163
|
+
consumeScheduled(requestId) {
|
|
164
|
+
if (this.scheduled?.requestId === requestId) this.scheduled = void 0;
|
|
165
|
+
}
|
|
166
|
+
enqueue(checkpoint, pendingRequestId) {
|
|
167
|
+
if (pendingRequestId === checkpoint.requestId || this.scheduled?.requestId === checkpoint.requestId || this.queued.some((queued) => queued.requestId === checkpoint.requestId)) {
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
this.queued.push(checkpoint);
|
|
171
|
+
}
|
|
172
|
+
next() {
|
|
173
|
+
return this.queued.shift();
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
const EMPTY_STATE = {
|
|
177
|
+
localHead: null,
|
|
178
|
+
desiredCanonicalHead: null,
|
|
179
|
+
dirtyGeneration: 0,
|
|
180
|
+
publishedGeneration: 0
|
|
181
|
+
};
|
|
182
|
+
function validHead(value) {
|
|
183
|
+
return value === null || typeof value === "string" && /^[0-9a-f]{40,64}$/.test(value);
|
|
184
|
+
}
|
|
185
|
+
function validGeneration(value) {
|
|
186
|
+
return typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
|
|
187
|
+
}
|
|
188
|
+
function readWorkspaceConvergenceState(statePath) {
|
|
189
|
+
try {
|
|
190
|
+
const parsed = JSON.parse(import_node_fs.default.readFileSync(statePath, "utf8"));
|
|
191
|
+
if (!validHead(parsed.localHead) || !validHead(parsed.desiredCanonicalHead) || !validGeneration(parsed.dirtyGeneration) || !validGeneration(parsed.publishedGeneration) || parsed.publishedGeneration > parsed.dirtyGeneration) {
|
|
192
|
+
return { ...EMPTY_STATE };
|
|
193
|
+
}
|
|
194
|
+
return {
|
|
195
|
+
localHead: parsed.localHead,
|
|
196
|
+
desiredCanonicalHead: parsed.desiredCanonicalHead,
|
|
197
|
+
dirtyGeneration: parsed.dirtyGeneration,
|
|
198
|
+
publishedGeneration: parsed.publishedGeneration
|
|
199
|
+
};
|
|
200
|
+
} catch {
|
|
201
|
+
return { ...EMPTY_STATE };
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
class WorkspaceConvergenceState {
|
|
205
|
+
constructor(statePath) {
|
|
206
|
+
this.statePath = statePath;
|
|
207
|
+
this.state = readWorkspaceConvergenceState(statePath);
|
|
208
|
+
this.checkpointState = this.state.dirtyGeneration > this.state.publishedGeneration ? "scheduled" : "idle";
|
|
209
|
+
}
|
|
210
|
+
statePath;
|
|
211
|
+
state;
|
|
212
|
+
checkpointState;
|
|
213
|
+
snapshot() {
|
|
214
|
+
return { ...this.state, checkpointState: this.checkpointState };
|
|
215
|
+
}
|
|
216
|
+
setLocalHead(localHead) {
|
|
217
|
+
if (!validHead(localHead)) throw new Error("Invalid local workspace head");
|
|
218
|
+
this.state.localHead = localHead;
|
|
219
|
+
this.persist();
|
|
220
|
+
}
|
|
221
|
+
observeCanonicalHead(canonicalHead) {
|
|
222
|
+
if (!validHead(canonicalHead)) throw new Error("Invalid canonical workspace head");
|
|
223
|
+
if (this.state.desiredCanonicalHead === canonicalHead) return false;
|
|
224
|
+
this.state.desiredCanonicalHead = canonicalHead;
|
|
225
|
+
this.persist();
|
|
226
|
+
return true;
|
|
227
|
+
}
|
|
228
|
+
markDirty() {
|
|
229
|
+
this.state.dirtyGeneration += 1;
|
|
230
|
+
if (this.checkpointState === "idle") this.checkpointState = "scheduled";
|
|
231
|
+
this.persist();
|
|
232
|
+
return this.state.dirtyGeneration;
|
|
233
|
+
}
|
|
234
|
+
schedule() {
|
|
235
|
+
if (this.checkpointState === "idle") this.checkpointState = "scheduled";
|
|
236
|
+
}
|
|
237
|
+
beginPreparing() {
|
|
238
|
+
if (this.checkpointState === "preparing" || this.checkpointState === "submitting") {
|
|
239
|
+
throw new Error("A workspace checkpoint is already in flight");
|
|
240
|
+
}
|
|
241
|
+
this.checkpointState = "preparing";
|
|
242
|
+
}
|
|
243
|
+
beginSubmitting() {
|
|
244
|
+
if (this.checkpointState !== "preparing") throw new Error("Workspace checkpoint authority was not prepared");
|
|
245
|
+
this.checkpointState = "submitting";
|
|
246
|
+
}
|
|
247
|
+
defer() {
|
|
248
|
+
this.checkpointState = "scheduled";
|
|
249
|
+
}
|
|
250
|
+
block() {
|
|
251
|
+
this.checkpointState = "blocked";
|
|
252
|
+
}
|
|
253
|
+
releaseBlock() {
|
|
254
|
+
this.checkpointState = this.state.dirtyGeneration > this.state.publishedGeneration ? "scheduled" : "idle";
|
|
255
|
+
}
|
|
256
|
+
observeIncidentState(blocked) {
|
|
257
|
+
if (this.checkpointState === "preparing" || this.checkpointState === "submitting") return;
|
|
258
|
+
if (blocked) this.block();
|
|
259
|
+
else this.releaseBlock();
|
|
260
|
+
}
|
|
261
|
+
resetTransientCheckpointState() {
|
|
262
|
+
this.checkpointState = this.state.dirtyGeneration > this.state.publishedGeneration ? "scheduled" : "idle";
|
|
263
|
+
}
|
|
264
|
+
complete(input) {
|
|
265
|
+
if (!validGeneration(input.generation) || input.generation > this.state.dirtyGeneration) {
|
|
266
|
+
throw new Error("Invalid completed workspace generation");
|
|
267
|
+
}
|
|
268
|
+
if (!validHead(input.canonicalHead)) throw new Error("Invalid completed canonical workspace head");
|
|
269
|
+
if (input.published) {
|
|
270
|
+
this.state.publishedGeneration = Math.max(this.state.publishedGeneration, input.generation);
|
|
271
|
+
this.state.localHead = input.canonicalHead;
|
|
272
|
+
this.state.desiredCanonicalHead = input.canonicalHead;
|
|
273
|
+
}
|
|
274
|
+
this.checkpointState = this.state.dirtyGeneration > this.state.publishedGeneration ? "scheduled" : "idle";
|
|
275
|
+
this.persist();
|
|
276
|
+
}
|
|
277
|
+
persist() {
|
|
278
|
+
import_node_fs.default.mkdirSync(import_node_path.default.dirname(this.statePath), { recursive: true });
|
|
279
|
+
const temporaryPath = `${this.statePath}.${process.pid}.${crypto.randomUUID()}.tmp`;
|
|
280
|
+
import_node_fs.default.writeFileSync(temporaryPath, `${JSON.stringify(this.state)}
|
|
281
|
+
`, { mode: 384 });
|
|
282
|
+
import_node_fs.default.renameSync(temporaryPath, this.statePath);
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
const PROCESS_WORKSPACE_CONVERGENCE_STATES = /* @__PURE__ */ new Map();
|
|
286
|
+
function processWorkspaceConvergenceState(statePath) {
|
|
287
|
+
const key = import_node_path.default.resolve(statePath);
|
|
288
|
+
const existing = PROCESS_WORKSPACE_CONVERGENCE_STATES.get(key);
|
|
289
|
+
if (existing) return existing;
|
|
290
|
+
const created = new WorkspaceConvergenceState(key);
|
|
291
|
+
PROCESS_WORKSPACE_CONVERGENCE_STATES.set(key, created);
|
|
292
|
+
return created;
|
|
293
|
+
}
|
|
294
|
+
async function waitForWorkspaceCheckpointOnShutdown(input) {
|
|
295
|
+
input.forceCheckpoint();
|
|
296
|
+
const deadline = Date.now() + (input.timeoutMs ?? 1e4);
|
|
297
|
+
const pollMs = input.pollMs ?? 50;
|
|
298
|
+
while (Date.now() < deadline) {
|
|
299
|
+
const snapshot = input.snapshot();
|
|
300
|
+
if (snapshot.checkpointState === "blocked") return "blocked";
|
|
301
|
+
if (snapshot.checkpointState === "idle" && snapshot.publishedGeneration >= snapshot.dirtyGeneration) return "settled";
|
|
302
|
+
await new Promise((resolve) => setTimeout(resolve, pollMs));
|
|
303
|
+
}
|
|
304
|
+
return "timed_out";
|
|
305
|
+
}
|
|
306
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
307
|
+
0 && (module.exports = {
|
|
308
|
+
ExplicitWorkspaceCheckpointQueue,
|
|
309
|
+
WORKSPACE_CHECKPOINT_QUIET_MS,
|
|
310
|
+
WORKSPACE_IDLE_SAFETY_SCAN_MS,
|
|
311
|
+
WorkspaceConvergenceState,
|
|
312
|
+
WorkspaceFilesystemWriterGate,
|
|
313
|
+
WorkspaceManifestRevisionFence,
|
|
314
|
+
processWorkspaceConvergenceState,
|
|
315
|
+
processWorkspaceFilesystemWriterGate,
|
|
316
|
+
readWorkspaceConvergenceState,
|
|
317
|
+
waitForWorkspaceCheckpointOnShutdown,
|
|
318
|
+
workspaceCheckpointIncidentAction,
|
|
319
|
+
workspaceCheckpointTelemetry
|
|
320
|
+
});
|
|
@@ -18,22 +18,51 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
18
18
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
19
|
var workspace_incident_state_exports = {};
|
|
20
20
|
__export(workspace_incident_state_exports, {
|
|
21
|
+
WorkspaceIncidentOrderingFence: () => WorkspaceIncidentOrderingFence,
|
|
21
22
|
applyWorkspaceIncidentUpdate: () => applyWorkspaceIncidentUpdate,
|
|
22
23
|
releasePendingWorkspaceHead: () => releasePendingWorkspaceHead
|
|
23
24
|
});
|
|
24
25
|
module.exports = __toCommonJS(workspace_incident_state_exports);
|
|
26
|
+
const DEFAULT_TERMINAL_INCIDENT_HISTORY_LIMIT = 32;
|
|
27
|
+
function isTerminalWorkspaceIncidentUpdate(update) {
|
|
28
|
+
return update.incidentId !== null && (update.status === "resolved" || update.status === "confirmed" || update.status === "reset");
|
|
29
|
+
}
|
|
30
|
+
class WorkspaceIncidentOrderingFence {
|
|
31
|
+
constructor(historyLimit = DEFAULT_TERMINAL_INCIDENT_HISTORY_LIMIT) {
|
|
32
|
+
this.historyLimit = historyLimit;
|
|
33
|
+
if (!Number.isSafeInteger(historyLimit) || historyLimit < 1) {
|
|
34
|
+
throw new Error("Workspace incident history limit must be a positive integer");
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
historyLimit;
|
|
38
|
+
terminalIncidentIds = /* @__PURE__ */ new Set();
|
|
39
|
+
terminalIncidentOrder = [];
|
|
40
|
+
observe(update) {
|
|
41
|
+
if (!isTerminalWorkspaceIncidentUpdate(update) || this.terminalIncidentIds.has(update.incidentId)) return;
|
|
42
|
+
this.terminalIncidentIds.add(update.incidentId);
|
|
43
|
+
this.terminalIncidentOrder.push(update.incidentId);
|
|
44
|
+
while (this.terminalIncidentOrder.length > this.historyLimit) {
|
|
45
|
+
const expired = this.terminalIncidentOrder.shift();
|
|
46
|
+
if (expired) this.terminalIncidentIds.delete(expired);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
permitsBlockedResponse(incidentId) {
|
|
50
|
+
if (!incidentId) return false;
|
|
51
|
+
return !this.terminalIncidentIds.has(incidentId);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
25
54
|
function applyWorkspaceIncidentUpdate(currentIncidentId, update) {
|
|
26
55
|
if (update.status === "remediating" || update.status === "waiting_for_worker") return update.incidentId;
|
|
27
56
|
if (currentIncidentId && update.incidentId && update.incidentId !== currentIncidentId) return currentIncidentId;
|
|
28
57
|
return null;
|
|
29
58
|
}
|
|
30
59
|
function releasePendingWorkspaceHead(previousIncidentId, currentIncidentId, terminalIncidentId) {
|
|
31
|
-
if (!previousIncidentId || currentIncidentId) return {
|
|
32
|
-
|
|
33
|
-
return { syncHead: null };
|
|
60
|
+
if (!previousIncidentId || currentIncidentId) return { fetchAuthoritativeHead: false };
|
|
61
|
+
return { fetchAuthoritativeHead: terminalIncidentId === previousIncidentId };
|
|
34
62
|
}
|
|
35
63
|
// Annotate the CommonJS export names for ESM import in node:
|
|
36
64
|
0 && (module.exports = {
|
|
65
|
+
WorkspaceIncidentOrderingFence,
|
|
37
66
|
applyWorkspaceIncidentUpdate,
|
|
38
67
|
releasePendingWorkspaceHead
|
|
39
68
|
});
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
var workspace_manifest_admission_exports = {};
|
|
30
|
+
__export(workspace_manifest_admission_exports, {
|
|
31
|
+
acquireWorkspaceManifestVisibleLease: () => acquireWorkspaceManifestVisibleLease,
|
|
32
|
+
inspectWorkspaceManifestAdmission: () => inspectWorkspaceManifestAdmission
|
|
33
|
+
});
|
|
34
|
+
module.exports = __toCommonJS(workspace_manifest_admission_exports);
|
|
35
|
+
var import_node_fs = __toESM(require("node:fs"), 1);
|
|
36
|
+
var import_node_path = __toESM(require("node:path"), 1);
|
|
37
|
+
var import_managed_paths = require("./managed-paths.cjs");
|
|
38
|
+
function hasNormalGitDirectory(checkoutPath) {
|
|
39
|
+
const gitPath = import_node_path.default.join(checkoutPath, ".git");
|
|
40
|
+
return import_node_fs.default.existsSync(gitPath) && import_node_fs.default.statSync(gitPath).isDirectory();
|
|
41
|
+
}
|
|
42
|
+
function inspectWorkspaceManifestAdmission(input) {
|
|
43
|
+
const firstBootstrap = !hasNormalGitDirectory(input.workspaceShadowRoot);
|
|
44
|
+
const missingCheckouts = [];
|
|
45
|
+
const migratedProjectIds = [];
|
|
46
|
+
for (const project of input.projects) {
|
|
47
|
+
const projectRoot = (0, import_managed_paths.managedProjectRoot)(input.projectsRoot, project.checkoutPathSegments);
|
|
48
|
+
const legacyRoot = import_node_path.default.join(input.projectsRoot, (0, import_managed_paths.legacyProjectSlug)(project.projectPath, project.projectId));
|
|
49
|
+
if (import_node_fs.default.existsSync(legacyRoot)) migratedProjectIds.push(project.projectId);
|
|
50
|
+
for (const branchName of project.branches) {
|
|
51
|
+
if (!hasNormalGitDirectory(import_node_path.default.join(projectRoot, branchName))) {
|
|
52
|
+
missingCheckouts.push({ projectId: project.projectId, branchName });
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return {
|
|
57
|
+
firstBootstrap,
|
|
58
|
+
missingCheckouts,
|
|
59
|
+
migratedProjectIds,
|
|
60
|
+
requiresVisibleLease: firstBootstrap || migratedProjectIds.length > 0
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
async function acquireWorkspaceManifestVisibleLease(gate, admission) {
|
|
64
|
+
return admission.requiresVisibleLease ? await gate.acquireCheckpoint() : void 0;
|
|
65
|
+
}
|
|
66
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
67
|
+
0 && (module.exports = {
|
|
68
|
+
acquireWorkspaceManifestVisibleLease,
|
|
69
|
+
inspectWorkspaceManifestAdmission
|
|
70
|
+
});
|
|
@@ -32,11 +32,13 @@ __export(workspace_sync_exports, {
|
|
|
32
32
|
WORKSPACE_BRANCH: () => WORKSPACE_BRANCH,
|
|
33
33
|
WORKSPACE_INTENT_REF_PREFIX: () => WORKSPACE_INTENT_REF_PREFIX,
|
|
34
34
|
WORKSPACE_NATIVE_PLANS_RELATIVE_PATH: () => WORKSPACE_NATIVE_PLANS_RELATIVE_PATH,
|
|
35
|
-
|
|
35
|
+
WORKSPACE_PUBLICATION_REF_PREFIX: () => WORKSPACE_PUBLICATION_REF_PREFIX,
|
|
36
36
|
WorkspaceSyncSingleFlight: () => WorkspaceSyncSingleFlight,
|
|
37
37
|
assertCanonicalCheckoutIndexMatchesWorktree: () => assertCanonicalCheckoutIndexMatchesWorktree,
|
|
38
38
|
assertWorkspaceQuarantineRef: () => assertWorkspaceQuarantineRef,
|
|
39
39
|
calculateWorkspaceDiffFingerprint: () => calculateWorkspaceDiffFingerprint,
|
|
40
|
+
calculateWorkspaceLocalDiffFingerprint: () => calculateWorkspaceLocalDiffFingerprint,
|
|
41
|
+
convergeWorkspaceHead: () => convergeWorkspaceHead,
|
|
40
42
|
encodeWorkspaceBranch: () => encodeWorkspaceBranch,
|
|
41
43
|
mirrorShadowWorkspaceToVisible: () => mirrorShadowWorkspaceToVisible,
|
|
42
44
|
mirrorVisibleWorkspaceToShadow: () => mirrorVisibleWorkspaceToShadow,
|
|
@@ -54,8 +56,8 @@ var import_managed_paths = require("./managed-paths.cjs");
|
|
|
54
56
|
var import_workspace_mutation_gate = require("./workspace-mutation-gate.cjs");
|
|
55
57
|
const WORKSPACE_BRANCH = "main";
|
|
56
58
|
const WORKSPACE_INTENT_REF_PREFIX = "refs/r5d/workspace-intents/";
|
|
59
|
+
const WORKSPACE_PUBLICATION_REF_PREFIX = "refs/r5d/workspace-publications/";
|
|
57
60
|
const MAX_WORKSPACE_SYNC_DIFF_BYTES = 5 * 1024 * 1024;
|
|
58
|
-
const WORKSPACE_PERIODIC_SCAN_INTERVAL_MS = 5e3;
|
|
59
61
|
const MAX_REPORTED_WORKSPACE_PATHS = 2e3;
|
|
60
62
|
const MAX_REPORTED_WORKSPACE_STATUS_BYTES = 256 * 1024;
|
|
61
63
|
const MAX_MIRROR_COMPARISON_CACHE_ENTRIES = 2e5;
|
|
@@ -1754,9 +1756,12 @@ function commitMessage(input) {
|
|
|
1754
1756
|
});
|
|
1755
1757
|
}
|
|
1756
1758
|
function assertWorkspaceQuarantineRef(quarantineRef) {
|
|
1757
|
-
const
|
|
1758
|
-
|
|
1759
|
-
|
|
1759
|
+
const prefix = quarantineRef.startsWith(WORKSPACE_PUBLICATION_REF_PREFIX) ? WORKSPACE_PUBLICATION_REF_PREFIX : WORKSPACE_INTENT_REF_PREFIX;
|
|
1760
|
+
const suffix = quarantineRef.slice(prefix.length);
|
|
1761
|
+
if (!quarantineRef.startsWith(prefix) || !/^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/.test(suffix)) {
|
|
1762
|
+
throw new Error(
|
|
1763
|
+
`Workspace candidate ref must be below ${WORKSPACE_PUBLICATION_REF_PREFIX} or ${WORKSPACE_INTENT_REF_PREFIX}`
|
|
1764
|
+
);
|
|
1760
1765
|
}
|
|
1761
1766
|
const result = Bun.spawnSync(["git", "check-ref-format", quarantineRef], {
|
|
1762
1767
|
stdout: "pipe",
|
|
@@ -1876,6 +1881,36 @@ function preserveWorkspaceConflictCandidate(input, observed, options) {
|
|
|
1876
1881
|
};
|
|
1877
1882
|
}
|
|
1878
1883
|
}
|
|
1884
|
+
function preserveLargeDiffCandidate(input, observed, error) {
|
|
1885
|
+
try {
|
|
1886
|
+
if (stagedPaths(input).length > 0) {
|
|
1887
|
+
runGit(input, input.shadowRoot, ["commit", "-m", commitMessage(input)], "commit blocked workspace candidate");
|
|
1888
|
+
}
|
|
1889
|
+
const candidateHead = revParse(input, "HEAD");
|
|
1890
|
+
if (!candidateHead) throw new Error("the blocked workspace candidate does not have a commit");
|
|
1891
|
+
assertUnscopedCanonicalCheckoutSubtreesMatchRemote(input, candidateHead);
|
|
1892
|
+
const upload = uploadWorkspaceCandidate(input, candidateHead);
|
|
1893
|
+
if (upload.exitCode !== 0) {
|
|
1894
|
+
throw new Error(upload.stderr || upload.stdout || "blocked workspace candidate upload failed");
|
|
1895
|
+
}
|
|
1896
|
+
return {
|
|
1897
|
+
...observed,
|
|
1898
|
+
outcome: "large_diff_blocked",
|
|
1899
|
+
expectedHead: revParse(input, `origin/${WORKSPACE_BRANCH}`),
|
|
1900
|
+
candidateHead,
|
|
1901
|
+
quarantineRef: input.quarantineRef,
|
|
1902
|
+
error,
|
|
1903
|
+
gitStatus: gitStatus(input)
|
|
1904
|
+
};
|
|
1905
|
+
} catch (candidateError) {
|
|
1906
|
+
return {
|
|
1907
|
+
...observed,
|
|
1908
|
+
outcome: "failed",
|
|
1909
|
+
error: `Workspace safety guard blocked publication but could not preserve a durable candidate: ${candidateError instanceof Error ? candidateError.message : String(candidateError)}`,
|
|
1910
|
+
gitStatus: gitStatus(input)
|
|
1911
|
+
};
|
|
1912
|
+
}
|
|
1913
|
+
}
|
|
1879
1914
|
async function synchronizeWorkspace(rawInput) {
|
|
1880
1915
|
let input = { ...rawInput, attemptId: rawInput.attemptId ?? crypto.randomUUID() };
|
|
1881
1916
|
try {
|
|
@@ -1887,6 +1922,15 @@ async function synchronizeWorkspace(rawInput) {
|
|
|
1887
1922
|
let hydrationPreimages = input.skipVisibleMirror ? hydrationPreimagesFromShadowRevision(input, shadowWasCreated ? null : revParse(input, "HEAD")) : /* @__PURE__ */ new Map();
|
|
1888
1923
|
const remoteHeadAtStart = revParse(input, `origin/${WORKSPACE_BRANCH}`);
|
|
1889
1924
|
const result = baseResult(input, remoteHeadAtStart);
|
|
1925
|
+
if (input.expectedCanonicalHead !== void 0 && remoteHeadAtStart !== input.expectedCanonicalHead) {
|
|
1926
|
+
return {
|
|
1927
|
+
...result,
|
|
1928
|
+
outcome: "failed",
|
|
1929
|
+
expectedHead: input.expectedCanonicalHead,
|
|
1930
|
+
publishedHead: remoteHeadAtStart ?? void 0,
|
|
1931
|
+
error: `canonical_head_advanced: expected ${input.expectedCanonicalHead ?? "unborn"}, fetched ${remoteHeadAtStart ?? "unborn"}`
|
|
1932
|
+
};
|
|
1933
|
+
}
|
|
1890
1934
|
const inboundConflictsAtStart = input.skipVisibleMirror && !isCanonicalRemediationSync && remoteHeadAtStart ? visibleHydrationConflictPaths(input, hydrationPreimages, `origin/${WORKSPACE_BRANCH}`) : [];
|
|
1891
1935
|
if (input.resetToCanonical) {
|
|
1892
1936
|
if (input.trigger.canonicalCheckoutOnly) {
|
|
@@ -1935,18 +1979,18 @@ async function synchronizeWorkspace(rawInput) {
|
|
|
1935
1979
|
const destructiveReductions = destructiveCheckoutReductions(input, baseRevision);
|
|
1936
1980
|
if (destructiveReductions.length > 0 && !input.confirmedLargeDiff) {
|
|
1937
1981
|
const summary = destructiveReductions.map(({ root, trackedBefore, trackedAfter }) => `${root} (${trackedBefore} -> ${trackedAfter} tracked files)`).join(", ");
|
|
1938
|
-
return
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
1982
|
+
return preserveLargeDiffCandidate(
|
|
1983
|
+
input,
|
|
1984
|
+
observed,
|
|
1985
|
+
`Workspace safety guard blocked a destructive checkout reduction: ${summary}`
|
|
1986
|
+
);
|
|
1943
1987
|
}
|
|
1944
1988
|
if (paths.length > 0 && diffSizeBytes > MAX_WORKSPACE_SYNC_DIFF_BYTES && !input.confirmedLargeDiff && !input.trigger.canonicalCheckoutOnly) {
|
|
1945
|
-
return
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
|
|
1989
|
+
return preserveLargeDiffCandidate(
|
|
1990
|
+
input,
|
|
1991
|
+
observed,
|
|
1992
|
+
`Workspace safety guard blocked a ${diffSizeBytes}-byte diff above the ${MAX_WORKSPACE_SYNC_DIFF_BYTES}-byte automatic publication limit.`
|
|
1993
|
+
);
|
|
1950
1994
|
}
|
|
1951
1995
|
if (stagedWorkingPaths.length > 0) {
|
|
1952
1996
|
runGit(input, input.shadowRoot, ["commit", "-m", commitMessage(input)], "commit workspace changes");
|
|
@@ -2111,6 +2155,79 @@ async function synchronizeWorkspace(rawInput) {
|
|
|
2111
2155
|
};
|
|
2112
2156
|
}
|
|
2113
2157
|
}
|
|
2158
|
+
async function convergeWorkspaceHead(rawInput, options) {
|
|
2159
|
+
const input = {
|
|
2160
|
+
...rawInput,
|
|
2161
|
+
projects: workspaceProjectsForSync(rawInput.projects, rawInput.trigger)
|
|
2162
|
+
};
|
|
2163
|
+
if (input.trigger.canonicalCheckoutOnly) {
|
|
2164
|
+
throw new Error("Ordinary inbound convergence cannot target a canonical resolver checkout");
|
|
2165
|
+
}
|
|
2166
|
+
ensureShadowWorkspace(input);
|
|
2167
|
+
const fetchedLocalHead = revParse(input, "HEAD");
|
|
2168
|
+
const fetchedCanonicalHead = revParse(input, `origin/${WORKSPACE_BRANCH}`);
|
|
2169
|
+
if (!options.hydrateVisible) {
|
|
2170
|
+
return {
|
|
2171
|
+
localHead: fetchedLocalHead,
|
|
2172
|
+
canonicalHead: fetchedCanonicalHead,
|
|
2173
|
+
localChanges: false,
|
|
2174
|
+
hydrated: false,
|
|
2175
|
+
hydrationConflicts: []
|
|
2176
|
+
};
|
|
2177
|
+
}
|
|
2178
|
+
resetUncommittedShadowSnapshot(input);
|
|
2179
|
+
restoreUnscopedCanonicalCheckoutSubtrees(input);
|
|
2180
|
+
const localHead = fetchedLocalHead;
|
|
2181
|
+
const canonicalHead = fetchedCanonicalHead;
|
|
2182
|
+
const projection = mirrorVisibleWorkspaceToShadow(input);
|
|
2183
|
+
runGit(
|
|
2184
|
+
input,
|
|
2185
|
+
input.shadowRoot,
|
|
2186
|
+
["add", "-A", "--", ".", ...projection.opaqueRoots.map((root) => `:(exclude,literal)${root}`)],
|
|
2187
|
+
"stage inbound workspace observation"
|
|
2188
|
+
);
|
|
2189
|
+
forceStageCanonicalCheckoutFiles(input, projection.entries, projection.opaqueRoots);
|
|
2190
|
+
const stagedTree = runGit(input, input.shadowRoot, ["write-tree"], "write inbound workspace observation tree");
|
|
2191
|
+
const headTree = localHead ? revParse(input, `${localHead}^{tree}`) : null;
|
|
2192
|
+
const localChanges = stagedTree !== headTree;
|
|
2193
|
+
if (!options.hydrateVisible || localChanges || !canonicalHead || localHead === canonicalHead) {
|
|
2194
|
+
resetUncommittedShadowSnapshot(input);
|
|
2195
|
+
return { localHead, canonicalHead, localChanges, hydrated: false, hydrationConflicts: [] };
|
|
2196
|
+
}
|
|
2197
|
+
const preimages = projection.visiblePreimages ?? hydrationPreimagesFromShadowRevision(input, localHead);
|
|
2198
|
+
const hydrationConflicts = localHead ? hydrationConflictPaths(input, preimages, localHead, canonicalHead) : [];
|
|
2199
|
+
if (hydrationConflicts.length > 0) {
|
|
2200
|
+
resetUncommittedShadowSnapshot(input);
|
|
2201
|
+
return { localHead, canonicalHead, localChanges: true, hydrated: false, hydrationConflicts };
|
|
2202
|
+
}
|
|
2203
|
+
runGit(input, input.shadowRoot, ["reset", "--hard", canonicalHead], "fast-forward workspace to available canonical head");
|
|
2204
|
+
runGit(input, input.shadowRoot, ["clean", "-fd"], "clean fast-forwarded workspace");
|
|
2205
|
+
mirrorShadowWorkspaceToVisible(input, projection.opaqueRoots, preimages);
|
|
2206
|
+
return { localHead: canonicalHead, canonicalHead, localChanges: false, hydrated: true, hydrationConflicts: [] };
|
|
2207
|
+
}
|
|
2208
|
+
function calculateWorkspaceLocalDiffFingerprint(rawInput) {
|
|
2209
|
+
const input = {
|
|
2210
|
+
...rawInput,
|
|
2211
|
+
projects: workspaceProjectsForSync(rawInput.projects, rawInput.trigger)
|
|
2212
|
+
};
|
|
2213
|
+
if (!import_node_fs.default.existsSync(import_node_path.default.join(input.shadowRoot, ".git"))) {
|
|
2214
|
+
throw new Error("Cannot inspect local workspace changes before first bootstrap");
|
|
2215
|
+
}
|
|
2216
|
+
resetUncommittedShadowSnapshot(input);
|
|
2217
|
+
restoreUnscopedCanonicalCheckoutSubtrees(input);
|
|
2218
|
+
const projection = mirrorVisibleWorkspaceToShadow(input);
|
|
2219
|
+
runGit(
|
|
2220
|
+
input,
|
|
2221
|
+
input.shadowRoot,
|
|
2222
|
+
["add", "-A", "--", ".", ...projection.opaqueRoots.map((root) => `:(exclude,literal)${root}`)],
|
|
2223
|
+
"stage local workspace fingerprint"
|
|
2224
|
+
);
|
|
2225
|
+
forceStageCanonicalCheckoutFiles(input, projection.entries, projection.opaqueRoots);
|
|
2226
|
+
const stagedTree = runGit(input, input.shadowRoot, ["write-tree"], "write local workspace fingerprint tree");
|
|
2227
|
+
const headTree = revParse(input, "HEAD^{tree}");
|
|
2228
|
+
resetUncommittedShadowSnapshot(input);
|
|
2229
|
+
return stagedTree === headTree ? (0, import_node_crypto.createHash)("sha256").update("").digest("hex") : (0, import_node_crypto.createHash)("sha256").update(stagedTree).digest("hex");
|
|
2230
|
+
}
|
|
2114
2231
|
function calculateWorkspaceDiffFingerprint(rawInput) {
|
|
2115
2232
|
const input = {
|
|
2116
2233
|
...rawInput,
|
|
@@ -2183,11 +2300,13 @@ class WorkspaceSyncSingleFlight {
|
|
|
2183
2300
|
WORKSPACE_BRANCH,
|
|
2184
2301
|
WORKSPACE_INTENT_REF_PREFIX,
|
|
2185
2302
|
WORKSPACE_NATIVE_PLANS_RELATIVE_PATH,
|
|
2186
|
-
|
|
2303
|
+
WORKSPACE_PUBLICATION_REF_PREFIX,
|
|
2187
2304
|
WorkspaceSyncSingleFlight,
|
|
2188
2305
|
assertCanonicalCheckoutIndexMatchesWorktree,
|
|
2189
2306
|
assertWorkspaceQuarantineRef,
|
|
2190
2307
|
calculateWorkspaceDiffFingerprint,
|
|
2308
|
+
calculateWorkspaceLocalDiffFingerprint,
|
|
2309
|
+
convergeWorkspaceHead,
|
|
2191
2310
|
encodeWorkspaceBranch,
|
|
2192
2311
|
mirrorShadowWorkspaceToVisible,
|
|
2193
2312
|
mirrorVisibleWorkspaceToShadow,
|