@ricsam/r5d-worker 0.0.51 → 0.0.54
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/dist/cjs/heartbeat.cjs +15 -2
- package/dist/cjs/main.cjs +186 -145
- package/dist/cjs/package.json +1 -1
- package/dist/cjs/workspace-incident-state.cjs +39 -0
- package/dist/cjs/workspace-mutation-gate.cjs +92 -0
- package/dist/cjs/workspace-sync.cjs +101 -41
- package/dist/mjs/heartbeat.mjs +12 -1
- package/dist/mjs/main.mjs +191 -146
- package/dist/mjs/package.json +1 -1
- package/dist/mjs/workspace-incident-state.mjs +14 -0
- package/dist/mjs/workspace-mutation-gate.mjs +68 -0
- package/dist/mjs/workspace-sync.mjs +99 -41
- package/dist/types/heartbeat.d.ts +7 -0
- package/dist/types/workspace-incident-state.d.ts +8 -0
- package/dist/types/workspace-mutation-gate.d.ts +19 -0
- package/dist/types/workspace-sync.d.ts +14 -1
- package/package.json +1 -1
|
@@ -2,7 +2,9 @@ import { createHash } from "node:crypto";
|
|
|
2
2
|
import fs from "node:fs";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { managedBranchPath } from "./managed-paths.mjs";
|
|
5
|
+
import { WorkspaceMutationGate } from "./workspace-mutation-gate.mjs";
|
|
5
6
|
const WORKSPACE_BRANCH = "main";
|
|
7
|
+
const WORKSPACE_INTENT_REF_PREFIX = "refs/r5d/workspace-intents/";
|
|
6
8
|
const MAX_WORKSPACE_SYNC_DIFF_BYTES = 5 * 1024 * 1024;
|
|
7
9
|
const WORKSPACE_PERIODIC_SCAN_INTERVAL_MS = 5e3;
|
|
8
10
|
const MAX_REPORTED_WORKSPACE_PATHS = 2e3;
|
|
@@ -1072,10 +1074,27 @@ function commitMessage(input) {
|
|
|
1072
1074
|
...input.confirmedLargeDiff ? { confirmedLargeDiff: true, confirmationReason: input.confirmationReason } : {}
|
|
1073
1075
|
});
|
|
1074
1076
|
}
|
|
1075
|
-
function
|
|
1076
|
-
const
|
|
1077
|
-
|
|
1078
|
-
|
|
1077
|
+
function assertWorkspaceQuarantineRef(quarantineRef) {
|
|
1078
|
+
const suffix = quarantineRef.slice(WORKSPACE_INTENT_REF_PREFIX.length);
|
|
1079
|
+
if (!quarantineRef.startsWith(WORKSPACE_INTENT_REF_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)) {
|
|
1080
|
+
throw new Error(`Workspace candidate ref must be below ${WORKSPACE_INTENT_REF_PREFIX}`);
|
|
1081
|
+
}
|
|
1082
|
+
const result = Bun.spawnSync(["git", "check-ref-format", quarantineRef], {
|
|
1083
|
+
stdout: "pipe",
|
|
1084
|
+
stderr: "pipe",
|
|
1085
|
+
env: { ...process.env, GIT_TERMINAL_PROMPT: "0" }
|
|
1086
|
+
});
|
|
1087
|
+
if (result.exitCode !== 0) {
|
|
1088
|
+
throw new Error(`Invalid workspace candidate ref ${JSON.stringify(quarantineRef)}`);
|
|
1089
|
+
}
|
|
1090
|
+
}
|
|
1091
|
+
function uploadWorkspaceCandidate(input, candidateHead) {
|
|
1092
|
+
const quarantineRef = input.quarantineRef;
|
|
1093
|
+
if (!quarantineRef) {
|
|
1094
|
+
throw new Error("A server-issued workspace candidate ref is required before uploading a canonical workspace candidate");
|
|
1095
|
+
}
|
|
1096
|
+
assertWorkspaceQuarantineRef(quarantineRef);
|
|
1097
|
+
return runGitResult(input, input.shadowRoot, ["push", "origin", `HEAD:${quarantineRef}`]);
|
|
1079
1098
|
}
|
|
1080
1099
|
function resetShadowToRemote(input, opaqueWorkspaceRoots = []) {
|
|
1081
1100
|
runGit(input, input.shadowRoot, ["fetch", "origin", "--prune"], "fetch canonical workspace before reset");
|
|
@@ -1094,6 +1113,7 @@ function baseResult(input, startingHead) {
|
|
|
1094
1113
|
workerLabel: input.workerLabel,
|
|
1095
1114
|
trigger: input.trigger,
|
|
1096
1115
|
startingHead,
|
|
1116
|
+
expectedHead: startingHead,
|
|
1097
1117
|
rebaseCount: 0,
|
|
1098
1118
|
diffSizeBytes: 0,
|
|
1099
1119
|
gitStatus: "",
|
|
@@ -1103,6 +1123,32 @@ function baseResult(input, startingHead) {
|
|
|
1103
1123
|
localChangesDiscarded: false
|
|
1104
1124
|
};
|
|
1105
1125
|
}
|
|
1126
|
+
function convergeRedundantInboundCandidate(input, result) {
|
|
1127
|
+
if (input.trigger.type !== "inbound_head" || !input.skipVisibleMirror || input.trigger.canonicalCheckoutOnly) return null;
|
|
1128
|
+
const remoteHead = revParse(input, `origin/${WORKSPACE_BRANCH}`);
|
|
1129
|
+
if (!remoteHead) return null;
|
|
1130
|
+
const stagedTree = runGit(input, input.shadowRoot, ["write-tree"], "write inbound workspace candidate tree");
|
|
1131
|
+
const remoteTree = runGit(
|
|
1132
|
+
input,
|
|
1133
|
+
input.shadowRoot,
|
|
1134
|
+
["rev-parse", "--verify", `${remoteHead}^{tree}`],
|
|
1135
|
+
"resolve authoritative workspace tree"
|
|
1136
|
+
);
|
|
1137
|
+
if (stagedTree !== remoteTree) return null;
|
|
1138
|
+
const localHead = revParse(input, "HEAD");
|
|
1139
|
+
runGit(input, input.shadowRoot, ["reset", "--hard", remoteHead], "converge redundant inbound workspace candidate");
|
|
1140
|
+
runGit(input, input.shadowRoot, ["clean", "-fd"], "clean redundant inbound workspace candidate");
|
|
1141
|
+
mirrorShadowWorkspaceToVisible(input);
|
|
1142
|
+
return {
|
|
1143
|
+
...result,
|
|
1144
|
+
outcome: localHead === remoteHead ? "no_change" : "updated",
|
|
1145
|
+
publishedHead: remoteHead,
|
|
1146
|
+
diffSizeBytes: 0,
|
|
1147
|
+
gitStatus: gitStatus(input),
|
|
1148
|
+
affectedProjects: [],
|
|
1149
|
+
affectedPaths: []
|
|
1150
|
+
};
|
|
1151
|
+
}
|
|
1106
1152
|
async function synchronizeWorkspace(rawInput) {
|
|
1107
1153
|
let input = { ...rawInput, attemptId: rawInput.attemptId ?? crypto.randomUUID() };
|
|
1108
1154
|
try {
|
|
@@ -1136,6 +1182,8 @@ async function synchronizeWorkspace(rawInput) {
|
|
|
1136
1182
|
const stagePathspecs = mirroredGitlinkProjection.opaqueRoots.flatMap((root) => [`:(exclude,literal)${root}`]);
|
|
1137
1183
|
runGit(input, input.shadowRoot, ["add", "-A", "--", ".", ...stagePathspecs], "stage workspace changes");
|
|
1138
1184
|
forceStageCanonicalCheckoutFiles(input, mirroredGitlinkProjection.entries, mirroredGitlinkProjection.opaqueRoots);
|
|
1185
|
+
const redundantInboundCandidate = convergeRedundantInboundCandidate(input, result);
|
|
1186
|
+
if (redundantInboundCandidate) return redundantInboundCandidate;
|
|
1139
1187
|
const sampledCanonicalCheckoutTreeHash = sampleCanonicalCheckoutTree(input);
|
|
1140
1188
|
const stagedWorkingPaths = stagedPaths(input);
|
|
1141
1189
|
const baseRevision = candidateBaseRevision(input);
|
|
@@ -1169,7 +1217,7 @@ async function synchronizeWorkspace(rawInput) {
|
|
|
1169
1217
|
if (stagedWorkingPaths.length > 0) {
|
|
1170
1218
|
runGit(input, input.shadowRoot, ["commit", "-m", commitMessage(input)], "commit workspace changes");
|
|
1171
1219
|
}
|
|
1172
|
-
|
|
1220
|
+
let candidateHead = revParse(input, "HEAD");
|
|
1173
1221
|
if (candidateHead) assertUnscopedCanonicalCheckoutSubtreesMatchRemote(input, candidateHead);
|
|
1174
1222
|
const hasRemoteMain = Boolean(revParse(input, `origin/${WORKSPACE_BRANCH}`));
|
|
1175
1223
|
const hasUnpushedCommit = candidateHead ? !hasRemoteMain || !tryGit(input, input.shadowRoot, ["merge-base", "--is-ancestor", candidateHead, `origin/${WORKSPACE_BRANCH}`]) : false;
|
|
@@ -1205,58 +1253,59 @@ async function synchronizeWorkspace(rawInput) {
|
|
|
1205
1253
|
};
|
|
1206
1254
|
}
|
|
1207
1255
|
let rebaseCount = 0;
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
if (push.exitCode === 0) {
|
|
1211
|
-
const publishedHead = revParse(input, "HEAD") ?? candidateHead;
|
|
1212
|
-
if (publishedHead) assertPublishedCanonicalCheckoutTree(input, publishedHead, sampledCanonicalCheckoutTreeHash);
|
|
1213
|
-
mirrorShadowWorkspaceToVisible(input, mirroredGitlinkProjection.opaqueRoots);
|
|
1214
|
-
return {
|
|
1215
|
-
...observed,
|
|
1216
|
-
outcome: "published",
|
|
1217
|
-
candidateHead: candidateHead ?? void 0,
|
|
1218
|
-
publishedHead: publishedHead ?? void 0,
|
|
1219
|
-
rebaseCount,
|
|
1220
|
-
gitStatus: gitStatus(input)
|
|
1221
|
-
};
|
|
1222
|
-
}
|
|
1223
|
-
if (!isNonFastForward(push)) {
|
|
1224
|
-
return {
|
|
1225
|
-
...observed,
|
|
1226
|
-
outcome: "failed",
|
|
1227
|
-
candidateHead: candidateHead ?? void 0,
|
|
1228
|
-
rebaseCount,
|
|
1229
|
-
error: push.stderr || push.stdout || "Workspace push failed",
|
|
1230
|
-
gitStatus: gitStatus(input)
|
|
1231
|
-
};
|
|
1232
|
-
}
|
|
1233
|
-
runGit(input, input.shadowRoot, ["fetch", "origin", "--prune"], "fetch workspace after push race");
|
|
1256
|
+
const expectedHead = revParse(input, `origin/${WORKSPACE_BRANCH}`);
|
|
1257
|
+
if (candidateHead && expectedHead && !tryGit(input, input.shadowRoot, ["merge-base", "--is-ancestor", expectedHead, candidateHead])) {
|
|
1234
1258
|
const rebase = runGitResult(input, input.shadowRoot, ["rebase", `origin/${WORKSPACE_BRANCH}`]);
|
|
1235
1259
|
rebaseCount += 1;
|
|
1236
1260
|
if (rebase.exitCode !== 0) {
|
|
1237
1261
|
const conflicted = runGitResult(input, input.shadowRoot, ["diff", "--name-only", "--diff-filter=U", "-z"]);
|
|
1238
|
-
const
|
|
1239
|
-
|
|
1262
|
+
const conflictPaths = reportedPaths([.../* @__PURE__ */ new Set([...paths, ...conflicted.stdout.split("\0").filter(Boolean)])].sort());
|
|
1263
|
+
runGit(input, input.shadowRoot, ["rebase", "--abort"], "preserve workspace candidate after rebase conflict");
|
|
1240
1264
|
return {
|
|
1241
1265
|
...observed,
|
|
1242
|
-
outcome: "
|
|
1266
|
+
outcome: "failed",
|
|
1267
|
+
expectedHead,
|
|
1243
1268
|
candidateHead: candidateHead ?? void 0,
|
|
1244
|
-
publishedHead: publishedHead ?? void 0,
|
|
1245
1269
|
rebaseCount,
|
|
1246
|
-
|
|
1247
|
-
|
|
1270
|
+
affectedPaths: conflictPaths,
|
|
1271
|
+
error: `Workspace candidate conflicts with canonical head ${expectedHead}; local changes were preserved for reconciliation.`,
|
|
1248
1272
|
gitStatus: gitStatus(input)
|
|
1249
1273
|
};
|
|
1250
1274
|
}
|
|
1275
|
+
candidateHead = revParse(input, "HEAD");
|
|
1251
1276
|
assertPublishedCanonicalCheckoutTree(input, "HEAD", sampledCanonicalCheckoutTreeHash);
|
|
1252
1277
|
assertUnscopedCanonicalCheckoutSubtreesMatchRemote(input, "HEAD");
|
|
1253
1278
|
}
|
|
1279
|
+
if (!candidateHead) {
|
|
1280
|
+
return {
|
|
1281
|
+
...observed,
|
|
1282
|
+
outcome: "failed",
|
|
1283
|
+
rebaseCount,
|
|
1284
|
+
error: "Workspace candidate does not have a commit",
|
|
1285
|
+
gitStatus: gitStatus(input)
|
|
1286
|
+
};
|
|
1287
|
+
}
|
|
1288
|
+
const upload = uploadWorkspaceCandidate(input, candidateHead);
|
|
1289
|
+
if (upload.exitCode !== 0) {
|
|
1290
|
+
return {
|
|
1291
|
+
...observed,
|
|
1292
|
+
outcome: "failed",
|
|
1293
|
+
candidateHead,
|
|
1294
|
+
quarantineRef: input.quarantineRef,
|
|
1295
|
+
rebaseCount,
|
|
1296
|
+
error: upload.stderr || upload.stdout || "Workspace candidate upload failed",
|
|
1297
|
+
gitStatus: gitStatus(input)
|
|
1298
|
+
};
|
|
1299
|
+
}
|
|
1300
|
+
assertPublishedCanonicalCheckoutTree(input, candidateHead, sampledCanonicalCheckoutTreeHash);
|
|
1301
|
+
mirrorShadowWorkspaceToVisible(input, mirroredGitlinkProjection.opaqueRoots);
|
|
1254
1302
|
return {
|
|
1255
1303
|
...observed,
|
|
1256
|
-
outcome: "
|
|
1257
|
-
|
|
1304
|
+
outcome: "candidate_ready",
|
|
1305
|
+
expectedHead,
|
|
1306
|
+
candidateHead,
|
|
1307
|
+
quarantineRef: input.quarantineRef,
|
|
1258
1308
|
rebaseCount,
|
|
1259
|
-
error: "Workspace push did not converge after three attempts",
|
|
1260
1309
|
gitStatus: gitStatus(input)
|
|
1261
1310
|
};
|
|
1262
1311
|
} catch (error) {
|
|
@@ -1299,9 +1348,10 @@ function calculateWorkspaceDiffFingerprint(rawInput) {
|
|
|
1299
1348
|
return createHash("sha256").update(JSON.stringify({ stagedTree, localHead, remoteHead })).digest("hex");
|
|
1300
1349
|
}
|
|
1301
1350
|
class WorkspaceSyncSingleFlight {
|
|
1351
|
+
mutationGate = new WorkspaceMutationGate();
|
|
1302
1352
|
queue = Promise.resolve();
|
|
1303
1353
|
enqueue(task) {
|
|
1304
|
-
const queued = this.
|
|
1354
|
+
const queued = this.mutationGate.runSync(task);
|
|
1305
1355
|
this.queue = queued.then(
|
|
1306
1356
|
() => void 0,
|
|
1307
1357
|
() => void 0
|
|
@@ -1320,6 +1370,12 @@ class WorkspaceSyncSingleFlight {
|
|
|
1320
1370
|
fingerprintPrepared(prepare) {
|
|
1321
1371
|
return this.enqueue(() => calculateWorkspaceDiffFingerprint(prepare()));
|
|
1322
1372
|
}
|
|
1373
|
+
runMutation(operation) {
|
|
1374
|
+
return this.mutationGate.runMutation(operation);
|
|
1375
|
+
}
|
|
1376
|
+
acquireMutation() {
|
|
1377
|
+
return this.mutationGate.acquireMutation();
|
|
1378
|
+
}
|
|
1323
1379
|
afterCurrent() {
|
|
1324
1380
|
return this.queue;
|
|
1325
1381
|
}
|
|
@@ -1327,10 +1383,12 @@ class WorkspaceSyncSingleFlight {
|
|
|
1327
1383
|
export {
|
|
1328
1384
|
MAX_WORKSPACE_SYNC_DIFF_BYTES,
|
|
1329
1385
|
WORKSPACE_BRANCH,
|
|
1386
|
+
WORKSPACE_INTENT_REF_PREFIX,
|
|
1330
1387
|
WORKSPACE_NATIVE_PLANS_RELATIVE_PATH,
|
|
1331
1388
|
WORKSPACE_PERIODIC_SCAN_INTERVAL_MS,
|
|
1332
1389
|
WorkspaceSyncSingleFlight,
|
|
1333
1390
|
assertCanonicalCheckoutIndexMatchesWorktree,
|
|
1391
|
+
assertWorkspaceQuarantineRef,
|
|
1334
1392
|
calculateWorkspaceDiffFingerprint,
|
|
1335
1393
|
encodeWorkspaceBranch,
|
|
1336
1394
|
mirrorShadowWorkspaceToVisible,
|
|
@@ -1,3 +1,10 @@
|
|
|
1
1
|
export declare const WORKER_HEARTBEAT_INTERVAL_MS = 15000;
|
|
2
2
|
export declare const WORKER_HEARTBEAT_TIMEOUT_MS = 45000;
|
|
3
3
|
export declare function hasWorkerHeartbeatTimedOut(lastHeartbeatAt: number | null, now?: number, timeoutMs?: number): boolean;
|
|
4
|
+
export type WorkerHeartbeatBusyGrace = {
|
|
5
|
+
heartbeatAt: number;
|
|
6
|
+
expiresAt: number;
|
|
7
|
+
};
|
|
8
|
+
/** Grant one bounded grace period for one actual server-heartbeat generation. */
|
|
9
|
+
export declare function grantWorkerHeartbeatBusyGrace(lastHeartbeatAt: number | null, currentGrace: WorkerHeartbeatBusyGrace | null, now?: number, timeoutMs?: number, graceMs?: number): WorkerHeartbeatBusyGrace | null;
|
|
10
|
+
export declare function hasWorkerHeartbeatTimedOutWithBusyGrace(lastHeartbeatAt: number | null, busyGrace: WorkerHeartbeatBusyGrace | null, now?: number, timeoutMs?: number): boolean;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export type WorkspaceIncidentUpdate = {
|
|
2
|
+
incidentId: string | null;
|
|
3
|
+
status: "remediating" | "waiting_for_worker" | "resolved" | "confirmed" | "reset" | null;
|
|
4
|
+
};
|
|
5
|
+
export declare function applyWorkspaceIncidentUpdate(currentIncidentId: string | null, update: WorkspaceIncidentUpdate): string | null;
|
|
6
|
+
export declare function releasePendingWorkspaceHead(previousIncidentId: string | null, currentIncidentId: string | null, terminalIncidentId?: string | null): {
|
|
7
|
+
syncHead: string | null;
|
|
8
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export type WorkspaceMutationLease = () => void;
|
|
2
|
+
/**
|
|
3
|
+
* Coordinates the visible workspace with its disposable sync shadow.
|
|
4
|
+
*
|
|
5
|
+
* Ordinary worktree activity may overlap other ordinary activity, but a sync
|
|
6
|
+
* gets exclusive access. Once a sync is queued, later mutations wait behind it
|
|
7
|
+
* so a busy shell cannot starve canonical publication indefinitely.
|
|
8
|
+
*/
|
|
9
|
+
export declare class WorkspaceMutationGate {
|
|
10
|
+
private activeMutations;
|
|
11
|
+
private syncActive;
|
|
12
|
+
private readonly waiters;
|
|
13
|
+
acquireMutation(): Promise<WorkspaceMutationLease>;
|
|
14
|
+
runMutation<T>(operation: () => Promise<T> | T): Promise<T>;
|
|
15
|
+
runSync<T>(operation: () => Promise<T> | T): Promise<T>;
|
|
16
|
+
private drain;
|
|
17
|
+
private releaseMutation;
|
|
18
|
+
private releaseSync;
|
|
19
|
+
}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { type CheckoutPathSegments } from "./managed-paths";
|
|
2
|
+
import { type WorkspaceMutationLease } from "./workspace-mutation-gate";
|
|
2
3
|
export declare const WORKSPACE_BRANCH = "main";
|
|
4
|
+
export declare const WORKSPACE_INTENT_REF_PREFIX = "refs/r5d/workspace-intents/";
|
|
3
5
|
export declare const MAX_WORKSPACE_SYNC_DIFF_BYTES: number;
|
|
4
6
|
export declare const WORKSPACE_PERIODIC_SCAN_INTERVAL_MS = 5000;
|
|
5
7
|
export type WorkspaceProjectManifestEntry = {
|
|
@@ -30,7 +32,7 @@ export type WorkspaceSyncTrigger = {
|
|
|
30
32
|
canonicalCheckoutOnly?: boolean;
|
|
31
33
|
detail?: string;
|
|
32
34
|
};
|
|
33
|
-
export type WorkspaceSyncOutcome = "no_change" | "published" | "updated" | "conflict_reset" | "large_diff_blocked" | "reset" | "failed";
|
|
35
|
+
export type WorkspaceSyncOutcome = "no_change" | "candidate_ready" | "published" | "updated" | "conflict_reset" | "large_diff_blocked" | "reset" | "failed";
|
|
34
36
|
export type WorkspaceSyncResult = {
|
|
35
37
|
type: "workspace_sync";
|
|
36
38
|
attemptId: string;
|
|
@@ -38,7 +40,9 @@ export type WorkspaceSyncResult = {
|
|
|
38
40
|
trigger: WorkspaceSyncTrigger;
|
|
39
41
|
outcome: WorkspaceSyncOutcome;
|
|
40
42
|
startingHead: string | null;
|
|
43
|
+
expectedHead?: string | null;
|
|
41
44
|
candidateHead?: string;
|
|
45
|
+
quarantineRef?: string;
|
|
42
46
|
publishedHead?: string;
|
|
43
47
|
rebaseCount: number;
|
|
44
48
|
diffSizeBytes: number;
|
|
@@ -60,6 +64,11 @@ export type WorkspaceSyncInput = {
|
|
|
60
64
|
shadowRoot: string;
|
|
61
65
|
projects: WorkspaceProjectManifestEntry[];
|
|
62
66
|
trigger: WorkspaceSyncTrigger;
|
|
67
|
+
/**
|
|
68
|
+
* Server-issued, attempt-scoped destination for candidate object uploads.
|
|
69
|
+
* Workers never publish the canonical workspace branch directly.
|
|
70
|
+
*/
|
|
71
|
+
quarantineRef?: string;
|
|
63
72
|
confirmedLargeDiff?: boolean;
|
|
64
73
|
confirmationReason?: string;
|
|
65
74
|
resetToCanonical?: boolean;
|
|
@@ -91,15 +100,19 @@ type GitlinkProjection = {
|
|
|
91
100
|
export declare function assertCanonicalCheckoutIndexMatchesWorktree(checkoutPath: string): void;
|
|
92
101
|
export declare function mirrorVisibleWorkspaceToShadow(input: WorkspaceSyncInput, excludedCheckouts?: ReadonlySet<string>): GitlinkProjection;
|
|
93
102
|
export declare function mirrorShadowWorkspaceToVisible(input: WorkspaceSyncInput, opaqueWorkspaceRoots?: readonly string[]): void;
|
|
103
|
+
export declare function assertWorkspaceQuarantineRef(quarantineRef: string): void;
|
|
94
104
|
export declare function synchronizeWorkspace(rawInput: WorkspaceSyncInput): Promise<WorkspaceSyncResult>;
|
|
95
105
|
export declare function calculateWorkspaceDiffFingerprint(rawInput: WorkspaceSyncInput): string;
|
|
96
106
|
export declare class WorkspaceSyncSingleFlight {
|
|
107
|
+
private readonly mutationGate;
|
|
97
108
|
private queue;
|
|
98
109
|
private enqueue;
|
|
99
110
|
run(input: WorkspaceSyncInput): Promise<WorkspaceSyncResult>;
|
|
100
111
|
runPrepared(prepare: () => WorkspaceSyncInput): Promise<WorkspaceSyncResult>;
|
|
101
112
|
fingerprint(input: WorkspaceSyncInput): Promise<string>;
|
|
102
113
|
fingerprintPrepared(prepare: () => WorkspaceSyncInput): Promise<string>;
|
|
114
|
+
runMutation<T>(operation: () => Promise<T> | T): Promise<T>;
|
|
115
|
+
acquireMutation(): Promise<WorkspaceMutationLease>;
|
|
103
116
|
afterCurrent(): Promise<void>;
|
|
104
117
|
}
|
|
105
118
|
export {};
|