@ricsam/r5d-worker 0.0.100 → 0.0.103
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 +16 -3
- package/dist/cjs/main.cjs +390 -322
- package/dist/cjs/package.json +1 -1
- package/dist/cjs/project-mirror-fetch-policy.cjs +48 -7
- package/dist/cjs/project-mirror-refs-token.cjs +36 -2
- package/dist/cjs/project-worktrees.cjs +49 -32
- package/dist/cjs/working-tree-mirror.cjs +347 -99
- package/dist/cjs/workspace-git-sync.cjs +189 -28
- package/dist/cjs/workspace-merge-projection.cjs +5 -19
- package/dist/cjs/workspace-mutation-gate.cjs +35 -0
- package/dist/cjs/workspace-projection-ledger.cjs +81 -0
- package/dist/cjs/workspace-sync-coalescer.cjs +58 -0
- package/dist/mjs/main.mjs +395 -324
- package/dist/mjs/package.json +1 -1
- package/dist/mjs/project-mirror-fetch-policy.mjs +46 -6
- package/dist/mjs/project-mirror-refs-token.mjs +30 -1
- package/dist/mjs/project-worktrees.mjs +52 -33
- package/dist/mjs/working-tree-mirror.mjs +347 -99
- package/dist/mjs/workspace-git-sync.mjs +189 -28
- package/dist/mjs/workspace-merge-projection.mjs +5 -19
- package/dist/mjs/workspace-mutation-gate.mjs +35 -0
- package/dist/mjs/workspace-projection-ledger.mjs +57 -0
- package/dist/mjs/workspace-sync-coalescer.mjs +34 -0
- package/dist/types/main.d.ts +29 -17
- package/dist/types/project-mirror-fetch-policy.d.ts +70 -15
- package/dist/types/project-mirror-refs-token.d.ts +34 -0
- package/dist/types/project-worktrees.d.ts +26 -8
- package/dist/types/working-tree-mirror.d.ts +56 -11
- package/dist/types/workspace-git-sync.d.ts +41 -4
- package/dist/types/workspace-mutation-gate.d.ts +24 -3
- package/dist/types/workspace-projection-ledger.d.ts +59 -0
- package/dist/types/workspace-sync-coalescer.d.ts +28 -0
- package/package.json +1 -1
package/dist/cjs/package.json
CHANGED
|
@@ -18,24 +18,65 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
18
18
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
19
|
var project_mirror_fetch_policy_exports = {};
|
|
20
20
|
__export(project_mirror_fetch_policy_exports, {
|
|
21
|
-
|
|
21
|
+
parseProjectMirrorRefsTokensMessage: () => parseProjectMirrorRefsTokensMessage,
|
|
22
|
+
planProjectMirrorObservationFetch: () => planProjectMirrorObservationFetch,
|
|
22
23
|
projectMirrorFetchDecision: () => projectMirrorFetchDecision
|
|
23
24
|
});
|
|
24
25
|
module.exports = __toCommonJS(project_mirror_fetch_policy_exports);
|
|
25
|
-
const PROJECT_MIRROR_FETCH_BACKSTOP_MS = 10 * 6e4;
|
|
26
26
|
function projectMirrorFetchDecision(input) {
|
|
27
27
|
if (!input.skippingEnabled) return { fetch: true, reason: "skipping_disabled" };
|
|
28
28
|
if (input.advertisedToken === void 0) return { fetch: true, reason: "no_advertised_token" };
|
|
29
29
|
if (input.fetchedToken === void 0) return { fetch: true, reason: "never_fetched" };
|
|
30
30
|
if (input.advertisedToken !== input.fetchedToken) return { fetch: true, reason: "token_changed" };
|
|
31
|
-
const backstopMs = input.backstopMs ?? PROJECT_MIRROR_FETCH_BACKSTOP_MS;
|
|
32
|
-
if (input.lastFetchAtMs === void 0 || input.nowMs - input.lastFetchAtMs >= backstopMs) {
|
|
33
|
-
return { fetch: true, reason: "backstop_elapsed" };
|
|
34
|
-
}
|
|
35
31
|
return { fetch: false, reason: "token_match" };
|
|
36
32
|
}
|
|
33
|
+
function planProjectMirrorObservationFetch(input) {
|
|
34
|
+
if (!input.skippingEnabled) return { scope: "project", reason: "skipping_disabled" };
|
|
35
|
+
if (input.advertisedMountTokens === void 0) {
|
|
36
|
+
const decision = projectMirrorFetchDecision({
|
|
37
|
+
advertisedToken: input.advertisedProjectToken,
|
|
38
|
+
fetchedToken: input.fetchedProjectToken,
|
|
39
|
+
skippingEnabled: true
|
|
40
|
+
});
|
|
41
|
+
return decision.fetch ? { scope: "project", reason: decision.reason } : { scope: "none" };
|
|
42
|
+
}
|
|
43
|
+
const mounts = [];
|
|
44
|
+
for (const branchName of [...input.branchNames].sort()) {
|
|
45
|
+
const decision = projectMirrorFetchDecision({
|
|
46
|
+
advertisedToken: input.advertisedMountTokens.get(branchName),
|
|
47
|
+
fetchedToken: input.fetchedMountTokens.get(branchName),
|
|
48
|
+
skippingEnabled: true
|
|
49
|
+
});
|
|
50
|
+
if (decision.fetch) mounts.push({ branchName, reason: decision.reason });
|
|
51
|
+
}
|
|
52
|
+
return mounts.length === 0 ? { scope: "none" } : { scope: "mounts", mounts };
|
|
53
|
+
}
|
|
54
|
+
const TOKEN_RE = /^[0-9a-f]{64}$/;
|
|
55
|
+
const GIT_OBJECT_NAME_RE = /^(?:[0-9a-f]{40}|[0-9a-f]{64})$/;
|
|
56
|
+
function parseProjectMirrorRefsTokensMessage(message) {
|
|
57
|
+
const projectTokens = /* @__PURE__ */ new Map();
|
|
58
|
+
if (message.tokens && typeof message.tokens === "object") {
|
|
59
|
+
for (const [projectId, token] of Object.entries(message.tokens)) {
|
|
60
|
+
if (typeof token === "string" && TOKEN_RE.test(token)) projectTokens.set(projectId, token);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
const mountTokens = /* @__PURE__ */ new Map();
|
|
64
|
+
if (message.mountTokens && typeof message.mountTokens === "object") {
|
|
65
|
+
for (const [projectId, tokensByBranch] of Object.entries(message.mountTokens)) {
|
|
66
|
+
if (!tokensByBranch || typeof tokensByBranch !== "object") continue;
|
|
67
|
+
const perMount = /* @__PURE__ */ new Map();
|
|
68
|
+
for (const [branchName, token] of Object.entries(tokensByBranch)) {
|
|
69
|
+
if (typeof token === "string" && TOKEN_RE.test(token)) perMount.set(branchName, token);
|
|
70
|
+
}
|
|
71
|
+
mountTokens.set(projectId, perMount);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
const workspaceHead = typeof message.workspaceHead === "string" && GIT_OBJECT_NAME_RE.test(message.workspaceHead) ? message.workspaceHead : void 0;
|
|
75
|
+
return { projectTokens, mountTokens, workspaceHead };
|
|
76
|
+
}
|
|
37
77
|
// Annotate the CommonJS export names for ESM import in node:
|
|
38
78
|
0 && (module.exports = {
|
|
39
|
-
|
|
79
|
+
parseProjectMirrorRefsTokensMessage,
|
|
80
|
+
planProjectMirrorObservationFetch,
|
|
40
81
|
projectMirrorFetchDecision
|
|
41
82
|
});
|
|
@@ -18,19 +18,38 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
18
18
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
19
|
var project_mirror_refs_token_exports = {};
|
|
20
20
|
__export(project_mirror_refs_token_exports, {
|
|
21
|
+
PROJECT_MIRROR_REFS_TOKEN_NAMESPACES: () => PROJECT_MIRROR_REFS_TOKEN_NAMESPACES,
|
|
22
|
+
PROJECT_MIRROR_REF_LISTING_FORMAT: () => PROJECT_MIRROR_REF_LISTING_FORMAT,
|
|
21
23
|
PROJECT_MIRROR_REF_PREFIX: () => PROJECT_MIRROR_REF_PREFIX,
|
|
22
24
|
PROJECT_SERVER_SYNCED_REF_PREFIX: () => PROJECT_SERVER_SYNCED_REF_PREFIX,
|
|
23
25
|
canonicalizeLocalProjectMirrorRefs: () => canonicalizeLocalProjectMirrorRefs,
|
|
24
|
-
hashProjectMirrorRefs: () => hashProjectMirrorRefs
|
|
26
|
+
hashProjectMirrorRefs: () => hashProjectMirrorRefs,
|
|
27
|
+
parseProjectMirrorRefListing: () => parseProjectMirrorRefListing,
|
|
28
|
+
projectMirrorMountRefs: () => projectMirrorMountRefs,
|
|
29
|
+
projectMirrorRefsTokens: () => projectMirrorRefsTokens
|
|
25
30
|
});
|
|
26
31
|
module.exports = __toCommonJS(project_mirror_refs_token_exports);
|
|
27
32
|
var import_node_crypto = require("node:crypto");
|
|
33
|
+
const PROJECT_MIRROR_REFS_TOKEN_NAMESPACES = ["refs/heads/", "refs/r5d/server-synced/"];
|
|
28
34
|
const PROJECT_MIRROR_REF_PREFIX = "refs/r5d/mirror/";
|
|
29
35
|
const PROJECT_SERVER_SYNCED_REF_PREFIX = "refs/r5d/server-synced/";
|
|
36
|
+
const PROJECT_MIRROR_REF_LISTING_FORMAT = "--format=%(objectname) %(refname)";
|
|
37
|
+
const GIT_OBJECT_NAME_RE = /^(?:[0-9a-f]{40}|[0-9a-f]{64})$/;
|
|
30
38
|
function hashProjectMirrorRefs(refs) {
|
|
31
39
|
const lines = refs.map(({ objectName, refname }) => `${objectName} ${refname}`).sort((left, right) => left < right ? -1 : left > right ? 1 : 0);
|
|
32
40
|
return (0, import_node_crypto.createHash)("sha256").update(lines.join("\n")).digest("hex");
|
|
33
41
|
}
|
|
42
|
+
function parseProjectMirrorRefListing(output, options = {}) {
|
|
43
|
+
return output.split(/\r?\n/).filter(Boolean).map((row) => {
|
|
44
|
+
const separator = row.indexOf(" ");
|
|
45
|
+
const objectName = separator < 0 ? "" : row.slice(0, separator);
|
|
46
|
+
const refname = separator < 0 ? "" : row.slice(separator + 1);
|
|
47
|
+
if (!GIT_OBJECT_NAME_RE.test(objectName) || refname.length === 0 || options.namespaces !== void 0 && !options.namespaces.some((prefix) => refname.startsWith(prefix))) {
|
|
48
|
+
throw new Error(`Invalid ref listing row: ${row}`);
|
|
49
|
+
}
|
|
50
|
+
return { refname, objectName };
|
|
51
|
+
});
|
|
52
|
+
}
|
|
34
53
|
function canonicalizeLocalProjectMirrorRefs(refs) {
|
|
35
54
|
const canonical = [];
|
|
36
55
|
for (const { refname, objectName } of refs) {
|
|
@@ -42,10 +61,25 @@ function canonicalizeLocalProjectMirrorRefs(refs) {
|
|
|
42
61
|
}
|
|
43
62
|
return canonical;
|
|
44
63
|
}
|
|
64
|
+
function projectMirrorMountRefs(refs, mount) {
|
|
65
|
+
const headRef = `refs/heads/${mount.branchName}`;
|
|
66
|
+
const handoffPrefix = `${PROJECT_SERVER_SYNCED_REF_PREFIX}${mount.branchId}/`;
|
|
67
|
+
return refs.filter(({ refname }) => refname === headRef || refname.startsWith(handoffPrefix));
|
|
68
|
+
}
|
|
69
|
+
function projectMirrorRefsTokens(refs, mounts) {
|
|
70
|
+
const mountTokens = /* @__PURE__ */ new Map();
|
|
71
|
+
for (const mount of mounts) mountTokens.set(mount.branchName, hashProjectMirrorRefs(projectMirrorMountRefs(refs, mount)));
|
|
72
|
+
return { project: hashProjectMirrorRefs(refs), mounts: mountTokens };
|
|
73
|
+
}
|
|
45
74
|
// Annotate the CommonJS export names for ESM import in node:
|
|
46
75
|
0 && (module.exports = {
|
|
76
|
+
PROJECT_MIRROR_REFS_TOKEN_NAMESPACES,
|
|
77
|
+
PROJECT_MIRROR_REF_LISTING_FORMAT,
|
|
47
78
|
PROJECT_MIRROR_REF_PREFIX,
|
|
48
79
|
PROJECT_SERVER_SYNCED_REF_PREFIX,
|
|
49
80
|
canonicalizeLocalProjectMirrorRefs,
|
|
50
|
-
hashProjectMirrorRefs
|
|
81
|
+
hashProjectMirrorRefs,
|
|
82
|
+
parseProjectMirrorRefListing,
|
|
83
|
+
projectMirrorMountRefs,
|
|
84
|
+
projectMirrorRefsTokens
|
|
51
85
|
});
|
|
@@ -32,7 +32,7 @@ __export(project_worktrees_exports, {
|
|
|
32
32
|
ProjectBranchCreationRollbackIncompleteError: () => ProjectBranchCreationRollbackIncompleteError,
|
|
33
33
|
applyObservedProjectMirrorHeads: () => applyObservedProjectMirrorHeads,
|
|
34
34
|
assertProjectMirrorHeadsPushed: () => assertProjectMirrorHeadsPushed,
|
|
35
|
-
|
|
35
|
+
computeLocalProjectMirrorRefsTokens: () => computeLocalProjectMirrorRefsTokens,
|
|
36
36
|
createLinkedProjectBranch: () => createLinkedProjectBranch,
|
|
37
37
|
createOrRetryLinkedProjectBranch: () => createOrRetryLinkedProjectBranch,
|
|
38
38
|
deleteLinkedProjectBranch: () => deleteLinkedProjectBranch,
|
|
@@ -1174,8 +1174,12 @@ function createLinkedProjectBranch(input) {
|
|
|
1174
1174
|
);
|
|
1175
1175
|
configureProjectBranchTracking(targetPath, input.branchName, input.branchName);
|
|
1176
1176
|
assertNoSymlinkBranchPathComponents(input.projectRoot, targetPath);
|
|
1177
|
-
|
|
1178
|
-
|
|
1177
|
+
if (input.workingTree === "carry") {
|
|
1178
|
+
git(targetPath, ["read-tree", sourceHead], `initialize linked worktree index ${input.branchName}`);
|
|
1179
|
+
(0, import_working_tree_mirror.mirrorWorkingTree)({ sourceRoot: sourcePath, targetRoot: targetPath, sourceMode: "git", deletionMode: "git" });
|
|
1180
|
+
} else {
|
|
1181
|
+
git(targetPath, ["reset", "--hard", sourceHead], `check out linked worktree ${input.branchName}`);
|
|
1182
|
+
}
|
|
1179
1183
|
return { branchPath: targetPath, baseCommitHash: sourceHead };
|
|
1180
1184
|
} catch (error) {
|
|
1181
1185
|
if (branchCreated) {
|
|
@@ -1365,24 +1369,46 @@ function assertProjectMirrorHeadsPushed(results) {
|
|
|
1365
1369
|
throw new Error(`Hidden project mirror publication skipped ${skipped.branchName}${skipped.reason ? ` (${skipped.reason})` : ""}`);
|
|
1366
1370
|
}
|
|
1367
1371
|
}
|
|
1372
|
+
const PROJECT_MIRROR_FETCH_REFSPECS = ["+refs/heads/*:refs/r5d/mirror/*", "+refs/r5d/server-synced/*:refs/r5d/server-synced/*"];
|
|
1373
|
+
function projectMirrorMountFetchRefspecs(branchName, branchId) {
|
|
1374
|
+
(0, import_managed_paths.validateManagedBranchName)(branchName);
|
|
1375
|
+
if (branchId !== void 0 && !/^[A-Za-z0-9][A-Za-z0-9_-]{0,127}$/.test(branchId)) throw new Error(`Invalid branch id for ${branchName}`);
|
|
1376
|
+
return [
|
|
1377
|
+
`+refs/heads/${branchName}:refs/r5d/mirror/${branchName}`,
|
|
1378
|
+
branchId === void 0 ? "+refs/r5d/server-synced/*:refs/r5d/server-synced/*" : `+refs/r5d/server-synced/${branchId}/*:refs/r5d/server-synced/${branchId}/*`
|
|
1379
|
+
];
|
|
1380
|
+
}
|
|
1381
|
+
async function fetchProjectMirrorRefs(input) {
|
|
1382
|
+
const fetch = (refspecs) => gitResultAsync(
|
|
1383
|
+
input.primaryPath,
|
|
1384
|
+
[
|
|
1385
|
+
...(0, import_git_process_environment.gitTransportSecurityArgs)(input.mirrorUrl, input.credentialHelper, input.credentialUsername),
|
|
1386
|
+
"fetch",
|
|
1387
|
+
"--no-recurse-submodules",
|
|
1388
|
+
"--prune",
|
|
1389
|
+
input.mirrorUrl,
|
|
1390
|
+
...refspecs
|
|
1391
|
+
],
|
|
1392
|
+
PROJECT_GIT_TRANSPORT_TIMEOUT_MS
|
|
1393
|
+
);
|
|
1394
|
+
if (input.fetchOnlyBranches && input.fetchOnlyBranches.size > 0) {
|
|
1395
|
+
const refspecs = [...input.fetchOnlyBranches].sort().flatMap((branchName) => projectMirrorMountFetchRefspecs(branchName, input.branchIds?.get(branchName)));
|
|
1396
|
+
const scoped = await fetch(refspecs);
|
|
1397
|
+
if (scoped.exitCode === 0) return;
|
|
1398
|
+
if (!/(couldn't find remote ref|no such ref)/i.test(`${scoped.stderr}
|
|
1399
|
+
${scoped.stdout}`)) {
|
|
1400
|
+
throw new Error(`Refresh project head mirror: ${scoped.stderr || scoped.stdout || `git exited ${scoped.exitCode}`}`);
|
|
1401
|
+
}
|
|
1402
|
+
}
|
|
1403
|
+
const whole = await fetch(PROJECT_MIRROR_FETCH_REFSPECS);
|
|
1404
|
+
if (whole.exitCode !== 0) {
|
|
1405
|
+
throw new Error(`Refresh project head mirror: ${whole.stderr || whole.stdout || `git exited ${whole.exitCode}`}`);
|
|
1406
|
+
}
|
|
1407
|
+
}
|
|
1368
1408
|
async function observeProjectMirrorHeads(input) {
|
|
1369
1409
|
const primaryPath = branchPath(input.projectRoot, input.primaryBranchName);
|
|
1370
|
-
if (input.refreshRemote !== false) {
|
|
1371
|
-
|
|
1372
|
-
primaryPath,
|
|
1373
|
-
[
|
|
1374
|
-
...(0, import_git_process_environment.gitTransportSecurityArgs)(input.mirrorUrl, input.credentialHelper, input.credentialUsername),
|
|
1375
|
-
"fetch",
|
|
1376
|
-
"--no-recurse-submodules",
|
|
1377
|
-
"--prune",
|
|
1378
|
-
input.mirrorUrl,
|
|
1379
|
-
"+refs/heads/*:refs/r5d/mirror/*",
|
|
1380
|
-
"+refs/r5d/server-synced/*:refs/r5d/server-synced/*"
|
|
1381
|
-
],
|
|
1382
|
-
PROJECT_GIT_TRANSPORT_TIMEOUT_MS
|
|
1383
|
-
);
|
|
1384
|
-
if (fetch.exitCode !== 0)
|
|
1385
|
-
throw new Error(`Refresh project head mirror: ${fetch.stderr || fetch.stdout || `git exited ${fetch.exitCode}`}`);
|
|
1410
|
+
if (input.refreshRemote !== false && !(input.fetchOnlyBranches && input.fetchOnlyBranches.size === 0)) {
|
|
1411
|
+
await fetchProjectMirrorRefs({ ...input, primaryPath });
|
|
1386
1412
|
}
|
|
1387
1413
|
const observations = [];
|
|
1388
1414
|
for (const branchName of [...input.branchNames].sort()) {
|
|
@@ -1429,23 +1455,14 @@ async function observeProjectMirrorHeads(input) {
|
|
|
1429
1455
|
}
|
|
1430
1456
|
return observations;
|
|
1431
1457
|
}
|
|
1432
|
-
async function
|
|
1458
|
+
async function computeLocalProjectMirrorRefsTokens(input) {
|
|
1433
1459
|
const primaryPath = branchPath(input.projectRoot, input.primaryBranchName);
|
|
1434
1460
|
const rows = await gitAsync(
|
|
1435
1461
|
primaryPath,
|
|
1436
|
-
["for-each-ref",
|
|
1462
|
+
["for-each-ref", import_project_mirror_refs_token.PROJECT_MIRROR_REF_LISTING_FORMAT, import_project_mirror_refs_token.PROJECT_MIRROR_REF_PREFIX, import_project_mirror_refs_token.PROJECT_SERVER_SYNCED_REF_PREFIX],
|
|
1437
1463
|
"list cached project mirror refs"
|
|
1438
1464
|
);
|
|
1439
|
-
|
|
1440
|
-
const separator = row.indexOf(" ");
|
|
1441
|
-
const objectName = separator < 0 ? "" : row.slice(0, separator);
|
|
1442
|
-
const refname = separator < 0 ? "" : row.slice(separator + 1);
|
|
1443
|
-
if (!/^[0-9a-f]{40,64}$/.test(objectName) || refname.length === 0) {
|
|
1444
|
-
throw new Error(`Invalid cached mirror ref listing row: ${row}`);
|
|
1445
|
-
}
|
|
1446
|
-
return { refname, objectName };
|
|
1447
|
-
});
|
|
1448
|
-
return (0, import_project_mirror_refs_token.hashProjectMirrorRefs)((0, import_project_mirror_refs_token.canonicalizeLocalProjectMirrorRefs)(refs));
|
|
1465
|
+
return (0, import_project_mirror_refs_token.projectMirrorRefsTokens)((0, import_project_mirror_refs_token.canonicalizeLocalProjectMirrorRefs)((0, import_project_mirror_refs_token.parseProjectMirrorRefListing)(rows)), input.mounts);
|
|
1449
1466
|
}
|
|
1450
1467
|
async function applyObservedProjectMirrorHeads(input) {
|
|
1451
1468
|
const results = [];
|
|
@@ -1529,7 +1546,7 @@ const projectWorktreesTestHarness = {
|
|
|
1529
1546
|
ProjectBranchCreationRollbackIncompleteError,
|
|
1530
1547
|
applyObservedProjectMirrorHeads,
|
|
1531
1548
|
assertProjectMirrorHeadsPushed,
|
|
1532
|
-
|
|
1549
|
+
computeLocalProjectMirrorRefsTokens,
|
|
1533
1550
|
createLinkedProjectBranch,
|
|
1534
1551
|
createOrRetryLinkedProjectBranch,
|
|
1535
1552
|
deleteLinkedProjectBranch,
|