@indigoai-us/hq-cloud 5.40.0 → 5.42.0
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/bin/sync-runner.d.ts +26 -1
- package/dist/bin/sync-runner.d.ts.map +1 -1
- package/dist/bin/sync-runner.js +105 -0
- package/dist/bin/sync-runner.js.map +1 -1
- package/dist/bin/sync-runner.test.js +168 -1
- package/dist/bin/sync-runner.test.js.map +1 -1
- package/dist/cli/sync-scope.test.d.ts +22 -0
- package/dist/cli/sync-scope.test.d.ts.map +1 -0
- package/dist/cli/sync-scope.test.js +273 -0
- package/dist/cli/sync-scope.test.js.map +1 -0
- package/dist/cli/sync.d.ts +64 -0
- package/dist/cli/sync.d.ts.map +1 -1
- package/dist/cli/sync.js +152 -4
- package/dist/cli/sync.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/prefix-coalesce.d.ts +29 -0
- package/dist/prefix-coalesce.d.ts.map +1 -1
- package/dist/prefix-coalesce.js +48 -0
- package/dist/prefix-coalesce.js.map +1 -1
- package/dist/prefix-coalesce.test.js +51 -1
- package/dist/prefix-coalesce.test.js.map +1 -1
- package/dist/qmd-reindex.d.ts +59 -0
- package/dist/qmd-reindex.d.ts.map +1 -0
- package/dist/qmd-reindex.js +128 -0
- package/dist/qmd-reindex.js.map +1 -0
- package/dist/qmd-reindex.test.d.ts +10 -0
- package/dist/qmd-reindex.test.d.ts.map +1 -0
- package/dist/qmd-reindex.test.js +129 -0
- package/dist/qmd-reindex.test.js.map +1 -0
- package/dist/scope-shrink.d.ts +18 -0
- package/dist/scope-shrink.d.ts.map +1 -1
- package/dist/scope-shrink.js +28 -0
- package/dist/scope-shrink.js.map +1 -1
- package/package.json +1 -1
- package/src/bin/sync-runner.test.ts +222 -0
- package/src/bin/sync-runner.ts +124 -0
- package/src/cli/sync-scope.test.ts +307 -0
- package/src/cli/sync.ts +240 -1
- package/src/index.ts +1 -0
- package/src/prefix-coalesce.test.ts +76 -1
- package/src/prefix-coalesce.ts +45 -0
- package/src/qmd-reindex.test.ts +143 -0
- package/src/qmd-reindex.ts +151 -0
- package/src/scope-shrink.ts +28 -0
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unit tests for reindexAfterSync.
|
|
3
|
+
*
|
|
4
|
+
* The function shells out to the global `qmd` binary, so every test injects a
|
|
5
|
+
* fake `exec` that records the calls and returns canned results — no real qmd
|
|
6
|
+
* is invoked. We assert on the *sequence of qmd commands* the function would
|
|
7
|
+
* run, which is the contract that keeps a teammate's index fresh after sync.
|
|
8
|
+
*/
|
|
9
|
+
import { describe, it, expect } from "vitest";
|
|
10
|
+
import * as fs from "fs";
|
|
11
|
+
import * as os from "os";
|
|
12
|
+
import * as path from "path";
|
|
13
|
+
import { reindexAfterSync } from "./qmd-reindex.js";
|
|
14
|
+
/** Build a fake exec that returns status 0 for everything and logs calls. */
|
|
15
|
+
function fakeExec(opts) {
|
|
16
|
+
const calls = [];
|
|
17
|
+
const exec = (args) => {
|
|
18
|
+
calls.push(args);
|
|
19
|
+
if (opts?.failOn && args[0] === opts.failOn)
|
|
20
|
+
return { status: 1, stdout: "" };
|
|
21
|
+
if (args[0] === "collection" && args[1] === "list") {
|
|
22
|
+
return { status: 0, stdout: opts?.listStdout ?? "" };
|
|
23
|
+
}
|
|
24
|
+
return { status: 0, stdout: "" };
|
|
25
|
+
};
|
|
26
|
+
return { exec, calls };
|
|
27
|
+
}
|
|
28
|
+
describe("reindexAfterSync", () => {
|
|
29
|
+
it("no-ops when the path is not an HQ root", () => {
|
|
30
|
+
const { exec, calls } = fakeExec();
|
|
31
|
+
const r = reindexAfterSync("/tmp/not-hq", {
|
|
32
|
+
exec,
|
|
33
|
+
existsSync: () => false, // core/core.yaml absent
|
|
34
|
+
});
|
|
35
|
+
expect(r.qmdAvailable).toBe(false);
|
|
36
|
+
expect(calls).toHaveLength(0);
|
|
37
|
+
});
|
|
38
|
+
it("no-ops when qmd is unavailable (collection list errors)", () => {
|
|
39
|
+
const { exec, calls } = fakeExec({ failOn: "collection" });
|
|
40
|
+
const r = reindexAfterSync("/hq", {
|
|
41
|
+
exec,
|
|
42
|
+
existsSync: () => true, // core.yaml present
|
|
43
|
+
});
|
|
44
|
+
expect(r.qmdAvailable).toBe(false);
|
|
45
|
+
expect(r.updated).toBe(false);
|
|
46
|
+
// Only the availability probe ran, then bailed.
|
|
47
|
+
expect(calls).toEqual([["collection", "list"]]);
|
|
48
|
+
});
|
|
49
|
+
it("registers a missing company collection then runs an incremental update", () => {
|
|
50
|
+
const { exec, calls } = fakeExec({ listStdout: "Collections (1):\n\nHQ (qmd://HQ/)\n" });
|
|
51
|
+
const r = reindexAfterSync("/hq", {
|
|
52
|
+
exec,
|
|
53
|
+
existsSync: () => true,
|
|
54
|
+
readCompanies: () => ["acme"],
|
|
55
|
+
hasIndexableMarkdown: () => true,
|
|
56
|
+
});
|
|
57
|
+
expect(r.qmdAvailable).toBe(true);
|
|
58
|
+
expect(r.collectionsAdded).toEqual(["acme"]);
|
|
59
|
+
expect(r.updated).toBe(true);
|
|
60
|
+
expect(r.embedded).toBe(false);
|
|
61
|
+
// Expected command sequence: probe, add, context, update.
|
|
62
|
+
expect(calls).toEqual([
|
|
63
|
+
["collection", "list"],
|
|
64
|
+
["collection", "add", path.join("/hq", "companies", "acme", "knowledge"), "--name", "acme", "--mask", "**/*.md"],
|
|
65
|
+
["context", "add", "qmd://acme", "Knowledge base for acme."],
|
|
66
|
+
["update"],
|
|
67
|
+
]);
|
|
68
|
+
});
|
|
69
|
+
it("skips collections that already exist", () => {
|
|
70
|
+
const { exec, calls } = fakeExec({ listStdout: "acme (qmd://acme/)\n" });
|
|
71
|
+
const r = reindexAfterSync("/hq", {
|
|
72
|
+
exec,
|
|
73
|
+
existsSync: () => true,
|
|
74
|
+
readCompanies: () => ["acme"],
|
|
75
|
+
hasIndexableMarkdown: () => true,
|
|
76
|
+
});
|
|
77
|
+
expect(r.collectionsAdded).toEqual([]);
|
|
78
|
+
// No `collection add` — straight to update.
|
|
79
|
+
expect(calls).toEqual([["collection", "list"], ["update"]]);
|
|
80
|
+
});
|
|
81
|
+
it("skips company dirs with no indexable markdown", () => {
|
|
82
|
+
const { exec, calls } = fakeExec({ listStdout: "" });
|
|
83
|
+
const r = reindexAfterSync("/hq", {
|
|
84
|
+
exec,
|
|
85
|
+
existsSync: () => true,
|
|
86
|
+
readCompanies: () => ["empty"],
|
|
87
|
+
hasIndexableMarkdown: () => false,
|
|
88
|
+
});
|
|
89
|
+
expect(r.collectionsAdded).toEqual([]);
|
|
90
|
+
expect(calls).toEqual([["collection", "list"], ["update"]]);
|
|
91
|
+
});
|
|
92
|
+
it("runs embed only when embed:true", () => {
|
|
93
|
+
const { exec, calls } = fakeExec({ listStdout: "" });
|
|
94
|
+
const r = reindexAfterSync("/hq", {
|
|
95
|
+
exec,
|
|
96
|
+
existsSync: () => true,
|
|
97
|
+
readCompanies: () => [],
|
|
98
|
+
embed: true,
|
|
99
|
+
});
|
|
100
|
+
expect(r.embedded).toBe(true);
|
|
101
|
+
expect(calls).toEqual([["collection", "list"], ["update"], ["embed"]]);
|
|
102
|
+
});
|
|
103
|
+
it("never throws even if exec misbehaves", () => {
|
|
104
|
+
const exec = () => {
|
|
105
|
+
throw new Error("boom");
|
|
106
|
+
};
|
|
107
|
+
expect(() => reindexAfterSync("/hq", { exec, existsSync: () => true })).not.toThrow();
|
|
108
|
+
});
|
|
109
|
+
it("hasIndexableMarkdown finds a real .md and ignores INDEX.md", () => {
|
|
110
|
+
const dir = fs.mkdtempSync(path.join(os.tmpdir(), "hq-qmd-test-"));
|
|
111
|
+
try {
|
|
112
|
+
const hq = path.join(dir, "hq");
|
|
113
|
+
const kdir = path.join(hq, "companies", "acme", "knowledge");
|
|
114
|
+
fs.mkdirSync(kdir, { recursive: true });
|
|
115
|
+
fs.mkdirSync(path.join(hq, "core"), { recursive: true });
|
|
116
|
+
fs.writeFileSync(path.join(hq, "core", "core.yaml"), "hqVersion: 1\n");
|
|
117
|
+
fs.writeFileSync(path.join(kdir, "INDEX.md"), "# index\n");
|
|
118
|
+
fs.writeFileSync(path.join(kdir, "note.md"), "# real\n");
|
|
119
|
+
const { exec, calls } = fakeExec({ listStdout: "" });
|
|
120
|
+
const r = reindexAfterSync(hq, { exec }); // real fs walk for md detection
|
|
121
|
+
expect(r.collectionsAdded).toEqual(["acme"]);
|
|
122
|
+
expect(calls.some((c) => c[0] === "collection" && c[1] === "add")).toBe(true);
|
|
123
|
+
}
|
|
124
|
+
finally {
|
|
125
|
+
fs.rmSync(dir, { recursive: true, force: true });
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
});
|
|
129
|
+
//# sourceMappingURL=qmd-reindex.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"qmd-reindex.test.js","sourceRoot":"","sources":["../src/qmd-reindex.test.ts"],"names":[],"mappings":"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"}
|
package/dist/scope-shrink.d.ts
CHANGED
|
@@ -79,6 +79,24 @@ export declare class ScopeShrinkBlockedError extends Error {
|
|
|
79
79
|
readonly code = "SCOPE_SHRINK_BLOCKED";
|
|
80
80
|
constructor(companyUid: string, fromMode: PullRecord["syncMode"] | "unknown", toMode: PullRecord["syncMode"], dirty: OrphanClassification[], clean: OrphanClassification[]);
|
|
81
81
|
}
|
|
82
|
+
/**
|
|
83
|
+
* Structured error thrown when an AUTOMATIC scope shrink would prune more
|
|
84
|
+
* CLEAN local files than the configured safety cap in a single pull. This is
|
|
85
|
+
* the bulk-delete guard: a routine background sync should never silently
|
|
86
|
+
* delete a large local tree — whether from a deliberate-but-abrupt first
|
|
87
|
+
* narrow, a server bug returning an unexpectedly small grant set, or a
|
|
88
|
+
* mis-resolved scope. The operator runs the explicit `hq sync narrow` ritual
|
|
89
|
+
* (which has its own confirmation + dirty gate) or passes `--force-scope-shrink`
|
|
90
|
+
* to proceed. The engine never deletes anything when it throws this.
|
|
91
|
+
*/
|
|
92
|
+
export declare class ScopeShrinkLargePruneError extends Error {
|
|
93
|
+
readonly companyUid: string;
|
|
94
|
+
readonly toMode: PullRecord["syncMode"];
|
|
95
|
+
readonly cleanCount: number;
|
|
96
|
+
readonly cap: number;
|
|
97
|
+
readonly code = "SCOPE_SHRINK_LARGE_PRUNE";
|
|
98
|
+
constructor(companyUid: string, toMode: PullRecord["syncMode"], cleanCount: number, cap: number);
|
|
99
|
+
}
|
|
82
100
|
export interface ApplyScopeShrinkInput {
|
|
83
101
|
journal: SyncJournal;
|
|
84
102
|
plan: ScopeShrinkPlan;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scope-shrink.d.ts","sourceRoot":"","sources":["../src/scope-shrink.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAIH,OAAO,KAAK,EACV,YAAY,EACZ,UAAU,EACV,WAAW,EACZ,MAAM,YAAY,CAAC;AAIpB,MAAM,WAAW,oBAAoB;IACnC,mCAAmC;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,qCAAqC;IACrC,KAAK,EAAE,YAAY,CAAC;IACpB,qEAAqE;IACrE,KAAK,EAAE,OAAO,CAAC;IACf,0EAA0E;IAC1E,WAAW,CAAC,EACR,qBAAqB,GACrB,eAAe,GACf,YAAY,CAAC;CAClB;AAED,MAAM,WAAW,eAAe;IAC9B,6EAA6E;IAC7E,OAAO,EAAE,oBAAoB,EAAE,CAAC;IAChC,iDAAiD;IACjD,KAAK,EAAE,oBAAoB,EAAE,CAAC;IAC9B,kDAAkD;IAClD,KAAK,EAAE,oBAAoB,EAAE,CAAC;IAC9B,8CAA8C;IAC9C,mBAAmB,EAAE,OAAO,CAAC;CAC9B;AAED,MAAM,WAAW,yBAAyB;IACxC,OAAO,EAAE,WAAW,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;IACf,iEAAiE;IACjE,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,oDAAoD;IACpD,gBAAgB,EAAE,MAAM,EAAE,CAAC;CAC5B;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,yBAAyB,GAC/B,eAAe,CAoBjB;AA8ED;;;;GAIG;AACH,qBAAa,uBAAwB,SAAQ,KAAK;aAG9B,UAAU,EAAE,MAAM;aAClB,QAAQ,EAAE,UAAU,CAAC,UAAU,CAAC,GAAG,SAAS;aAC5C,MAAM,EAAE,UAAU,CAAC,UAAU,CAAC;aAC9B,KAAK,EAAE,oBAAoB,EAAE;aAC7B,KAAK,EAAE,oBAAoB,EAAE;IAN/C,QAAQ,CAAC,IAAI,0BAA0B;gBAErB,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,UAAU,CAAC,UAAU,CAAC,GAAG,SAAS,EAC5C,MAAM,EAAE,UAAU,CAAC,UAAU,CAAC,EAC9B,KAAK,EAAE,oBAAoB,EAAE,EAC7B,KAAK,EAAE,oBAAoB,EAAE;CAShD;AAED,MAAM,WAAW,qBAAqB;IACpC,OAAO,EAAE,WAAW,CAAC;IACrB,IAAI,EAAE,eAAe,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;IACf;;;;;;OAMG;IACH,gBAAgB,EAAE,OAAO,CAAC;IAC1B,MAAM,CAAC,EAAE,cAAc,GAAG,cAAc,GAAG,QAAQ,CAAC;CACrD;AAED,MAAM,WAAW,sBAAsB;IACrC,YAAY,EAAE,MAAM,CAAC;IACrB,eAAe,EAAE,MAAM,CAAC;CACzB;AAED;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,qBAAqB,GAC3B,sBAAsB,CA4BxB"}
|
|
1
|
+
{"version":3,"file":"scope-shrink.d.ts","sourceRoot":"","sources":["../src/scope-shrink.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAIH,OAAO,KAAK,EACV,YAAY,EACZ,UAAU,EACV,WAAW,EACZ,MAAM,YAAY,CAAC;AAIpB,MAAM,WAAW,oBAAoB;IACnC,mCAAmC;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,qCAAqC;IACrC,KAAK,EAAE,YAAY,CAAC;IACpB,qEAAqE;IACrE,KAAK,EAAE,OAAO,CAAC;IACf,0EAA0E;IAC1E,WAAW,CAAC,EACR,qBAAqB,GACrB,eAAe,GACf,YAAY,CAAC;CAClB;AAED,MAAM,WAAW,eAAe;IAC9B,6EAA6E;IAC7E,OAAO,EAAE,oBAAoB,EAAE,CAAC;IAChC,iDAAiD;IACjD,KAAK,EAAE,oBAAoB,EAAE,CAAC;IAC9B,kDAAkD;IAClD,KAAK,EAAE,oBAAoB,EAAE,CAAC;IAC9B,8CAA8C;IAC9C,mBAAmB,EAAE,OAAO,CAAC;CAC9B;AAED,MAAM,WAAW,yBAAyB;IACxC,OAAO,EAAE,WAAW,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;IACf,iEAAiE;IACjE,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,oDAAoD;IACpD,gBAAgB,EAAE,MAAM,EAAE,CAAC;CAC5B;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,yBAAyB,GAC/B,eAAe,CAoBjB;AA8ED;;;;GAIG;AACH,qBAAa,uBAAwB,SAAQ,KAAK;aAG9B,UAAU,EAAE,MAAM;aAClB,QAAQ,EAAE,UAAU,CAAC,UAAU,CAAC,GAAG,SAAS;aAC5C,MAAM,EAAE,UAAU,CAAC,UAAU,CAAC;aAC9B,KAAK,EAAE,oBAAoB,EAAE;aAC7B,KAAK,EAAE,oBAAoB,EAAE;IAN/C,QAAQ,CAAC,IAAI,0BAA0B;gBAErB,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,UAAU,CAAC,UAAU,CAAC,GAAG,SAAS,EAC5C,MAAM,EAAE,UAAU,CAAC,UAAU,CAAC,EAC9B,KAAK,EAAE,oBAAoB,EAAE,EAC7B,KAAK,EAAE,oBAAoB,EAAE;CAShD;AAED;;;;;;;;;GASG;AACH,qBAAa,0BAA2B,SAAQ,KAAK;aAGjC,UAAU,EAAE,MAAM;aAClB,MAAM,EAAE,UAAU,CAAC,UAAU,CAAC;aAC9B,UAAU,EAAE,MAAM;aAClB,GAAG,EAAE,MAAM;IAL7B,QAAQ,CAAC,IAAI,8BAA8B;gBAEzB,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,UAAU,CAAC,UAAU,CAAC,EAC9B,UAAU,EAAE,MAAM,EAClB,GAAG,EAAE,MAAM;CAU9B;AAED,MAAM,WAAW,qBAAqB;IACpC,OAAO,EAAE,WAAW,CAAC;IACrB,IAAI,EAAE,eAAe,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;IACf;;;;;;OAMG;IACH,gBAAgB,EAAE,OAAO,CAAC;IAC1B,MAAM,CAAC,EAAE,cAAc,GAAG,cAAc,GAAG,QAAQ,CAAC;CACrD;AAED,MAAM,WAAW,sBAAsB;IACrC,YAAY,EAAE,MAAM,CAAC;IACrB,eAAe,EAAE,MAAM,CAAC;CACzB;AAED;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,qBAAqB,GAC3B,sBAAsB,CA4BxB"}
|
package/dist/scope-shrink.js
CHANGED
|
@@ -157,6 +157,34 @@ export class ScopeShrinkBlockedError extends Error {
|
|
|
157
157
|
this.name = "ScopeShrinkBlockedError";
|
|
158
158
|
}
|
|
159
159
|
}
|
|
160
|
+
/**
|
|
161
|
+
* Structured error thrown when an AUTOMATIC scope shrink would prune more
|
|
162
|
+
* CLEAN local files than the configured safety cap in a single pull. This is
|
|
163
|
+
* the bulk-delete guard: a routine background sync should never silently
|
|
164
|
+
* delete a large local tree — whether from a deliberate-but-abrupt first
|
|
165
|
+
* narrow, a server bug returning an unexpectedly small grant set, or a
|
|
166
|
+
* mis-resolved scope. The operator runs the explicit `hq sync narrow` ritual
|
|
167
|
+
* (which has its own confirmation + dirty gate) or passes `--force-scope-shrink`
|
|
168
|
+
* to proceed. The engine never deletes anything when it throws this.
|
|
169
|
+
*/
|
|
170
|
+
export class ScopeShrinkLargePruneError extends Error {
|
|
171
|
+
companyUid;
|
|
172
|
+
toMode;
|
|
173
|
+
cleanCount;
|
|
174
|
+
cap;
|
|
175
|
+
code = "SCOPE_SHRINK_LARGE_PRUNE";
|
|
176
|
+
constructor(companyUid, toMode, cleanCount, cap) {
|
|
177
|
+
super(`Refusing to auto-prune ${cleanCount} local file(s) for ${companyUid} ` +
|
|
178
|
+
`(${toMode} scope) in one sync — exceeds the safety cap of ${cap}. ` +
|
|
179
|
+
`Run \`hq sync narrow --apply\` to migrate with confirmation, raise ` +
|
|
180
|
+
`HQ_SYNC_MAX_AUTO_PRUNE, or pass --force-scope-shrink.`);
|
|
181
|
+
this.companyUid = companyUid;
|
|
182
|
+
this.toMode = toMode;
|
|
183
|
+
this.cleanCount = cleanCount;
|
|
184
|
+
this.cap = cap;
|
|
185
|
+
this.name = "ScopeShrinkLargePruneError";
|
|
186
|
+
}
|
|
187
|
+
}
|
|
160
188
|
/**
|
|
161
189
|
* Apply a scope-shrink plan: delete clean orphans on disk + tombstone their
|
|
162
190
|
* journal entries. With `forceScopeShrink: true`, dirty orphans are
|
package/dist/scope-shrink.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scope-shrink.js","sourceRoot":"","sources":["../src/scope-shrink.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"scope-shrink.js","sourceRoot":"","sources":["../src/scope-shrink.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -18,6 +18,7 @@ import {
|
|
|
18
18
|
resolveSkipPersonal,
|
|
19
19
|
routeChangeToTarget,
|
|
20
20
|
buildTargetedPushArgv,
|
|
21
|
+
resolvePullScope,
|
|
21
22
|
} from "./sync-runner.js";
|
|
22
23
|
import type {
|
|
23
24
|
RunnerEvent,
|
|
@@ -100,6 +101,9 @@ function defaultSyncResult(overrides: Partial<SyncResult> = {}): SyncResult {
|
|
|
100
101
|
newFilesCount: 0,
|
|
101
102
|
filesExcludedByPolicy: 0,
|
|
102
103
|
filesTombstoned: 0,
|
|
104
|
+
filesOutOfScope: 0,
|
|
105
|
+
scopeOrphansRemoved: 0,
|
|
106
|
+
scopeOrphansBlocked: 0,
|
|
103
107
|
...overrides,
|
|
104
108
|
};
|
|
105
109
|
}
|
|
@@ -1075,6 +1079,9 @@ describe("per-company fanout", () => {
|
|
|
1075
1079
|
filesExcludedByPolicy: 0,
|
|
1076
1080
|
newFiles: result.newFiles,
|
|
1077
1081
|
newFilesCount: result.newFilesCount,
|
|
1082
|
+
filesOutOfScope: result.filesOutOfScope,
|
|
1083
|
+
scopeOrphansRemoved: result.scopeOrphansRemoved,
|
|
1084
|
+
scopeOrphansBlocked: result.scopeOrphansBlocked,
|
|
1078
1085
|
});
|
|
1079
1086
|
});
|
|
1080
1087
|
|
|
@@ -3117,3 +3124,218 @@ describe("resolveSkipPersonal", () => {
|
|
|
3117
3124
|
},
|
|
3118
3125
|
);
|
|
3119
3126
|
});
|
|
3127
|
+
|
|
3128
|
+
// ---------------------------------------------------------------------------
|
|
3129
|
+
// resolvePullScope (US-005) — effective download scope per company leg
|
|
3130
|
+
// ---------------------------------------------------------------------------
|
|
3131
|
+
|
|
3132
|
+
describe("resolvePullScope", () => {
|
|
3133
|
+
function stubClient(
|
|
3134
|
+
overrides: Partial<VaultClientSurface>,
|
|
3135
|
+
): VaultClientSurface {
|
|
3136
|
+
return {
|
|
3137
|
+
listMyMemberships: async () => [],
|
|
3138
|
+
listMyPendingInvitesByEmail: async () => [],
|
|
3139
|
+
claimPendingInvitesByEmail: async () => {},
|
|
3140
|
+
ensureMyPersonEntity: async () => ({ uid: "p", slug: "p" }) as never,
|
|
3141
|
+
entity: {
|
|
3142
|
+
get: async (uid: string) => ({ uid, slug: uid }) as never,
|
|
3143
|
+
listByType: async () => [],
|
|
3144
|
+
},
|
|
3145
|
+
...overrides,
|
|
3146
|
+
};
|
|
3147
|
+
}
|
|
3148
|
+
|
|
3149
|
+
const membership = (companyUid: string, membershipKey: string) =>
|
|
3150
|
+
({
|
|
3151
|
+
membershipKey,
|
|
3152
|
+
personUid: "prs_1",
|
|
3153
|
+
companyUid,
|
|
3154
|
+
role: "member",
|
|
3155
|
+
status: "active",
|
|
3156
|
+
invitedBy: "prs_0",
|
|
3157
|
+
invitedAt: "2026-01-01T00:00:00.000Z",
|
|
3158
|
+
createdAt: "2026-01-01T00:00:00.000Z",
|
|
3159
|
+
updatedAt: "2026-01-01T00:00:00.000Z",
|
|
3160
|
+
}) as never;
|
|
3161
|
+
|
|
3162
|
+
it("returns all when the client has no getMembershipSyncConfig", async () => {
|
|
3163
|
+
const scope = await resolvePullScope(stubClient({}), "cmp_a", "acme");
|
|
3164
|
+
expect(scope).toEqual({ syncMode: "all" });
|
|
3165
|
+
});
|
|
3166
|
+
|
|
3167
|
+
it("returns all for an all-mode membership (no prefixSet)", async () => {
|
|
3168
|
+
const scope = await resolvePullScope(
|
|
3169
|
+
stubClient({
|
|
3170
|
+
listMyMemberships: async () => [membership("cmp_a", "mk_a")],
|
|
3171
|
+
getMembershipSyncConfig: async () => ({
|
|
3172
|
+
membershipId: "mk_a",
|
|
3173
|
+
syncMode: "all",
|
|
3174
|
+
isDefault: true,
|
|
3175
|
+
}),
|
|
3176
|
+
}),
|
|
3177
|
+
"cmp_a",
|
|
3178
|
+
"acme",
|
|
3179
|
+
);
|
|
3180
|
+
expect(scope).toEqual({ syncMode: "all" });
|
|
3181
|
+
});
|
|
3182
|
+
|
|
3183
|
+
it("coalesces explicit grants for a shared-mode membership", async () => {
|
|
3184
|
+
const scope = await resolvePullScope(
|
|
3185
|
+
stubClient({
|
|
3186
|
+
listMyMemberships: async () => [membership("cmp_a", "mk_a")],
|
|
3187
|
+
getMembershipSyncConfig: async () => ({
|
|
3188
|
+
membershipId: "mk_a",
|
|
3189
|
+
syncMode: "shared",
|
|
3190
|
+
isDefault: false,
|
|
3191
|
+
}),
|
|
3192
|
+
listMyExplicitGrants: async () => [
|
|
3193
|
+
{ companyUid: "cmp_a", path: "knowledge/", permission: "read", source: "person" },
|
|
3194
|
+
{ companyUid: "cmp_a", path: "knowledge/sub/", permission: "read", source: "group" },
|
|
3195
|
+
{ companyUid: "cmp_a", path: "shared/", permission: "read", source: "open" },
|
|
3196
|
+
] as never,
|
|
3197
|
+
}),
|
|
3198
|
+
"cmp_a",
|
|
3199
|
+
"acme",
|
|
3200
|
+
);
|
|
3201
|
+
// knowledge/sub/ is covered by knowledge/ → coalesced away.
|
|
3202
|
+
expect(scope.syncMode).toBe("shared");
|
|
3203
|
+
expect(scope.prefixSet).toEqual(["knowledge/", "shared/"]);
|
|
3204
|
+
});
|
|
3205
|
+
|
|
3206
|
+
it("normalizes real-world mixed/glob grant paths into company-relative prefixes", async () => {
|
|
3207
|
+
// The exact shapes observed in the live hq-pro vault for `indigo`:
|
|
3208
|
+
// bare glob, full-anchored + /*, full-anchored exact file, slug-anchored
|
|
3209
|
+
// + /*, company-relative + /*, and a company-relative exact file.
|
|
3210
|
+
const scope = await resolvePullScope(
|
|
3211
|
+
stubClient({
|
|
3212
|
+
listMyMemberships: async () => [membership("cmp_indigo", "mk_i")],
|
|
3213
|
+
getMembershipSyncConfig: async () => ({
|
|
3214
|
+
membershipId: "mk_i",
|
|
3215
|
+
syncMode: "shared",
|
|
3216
|
+
isDefault: false,
|
|
3217
|
+
}),
|
|
3218
|
+
listMyExplicitGrants: async () =>
|
|
3219
|
+
[
|
|
3220
|
+
{ companyUid: "cmp_indigo", path: "companies/indigo/design-pack/*", permission: "write", source: "person" },
|
|
3221
|
+
{ companyUid: "cmp_indigo", path: "companies/indigo/knowledge/README.md", permission: "write", source: "person" },
|
|
3222
|
+
{ companyUid: "cmp_indigo", path: "indigo/data/vyg/old-meetings/*", permission: "write", source: "person" },
|
|
3223
|
+
{ companyUid: "cmp_indigo", path: "data/vyg/*", permission: "write", source: "person" },
|
|
3224
|
+
{ companyUid: "cmp_indigo", path: "company.yaml", permission: "read", source: "open" },
|
|
3225
|
+
] as never,
|
|
3226
|
+
}),
|
|
3227
|
+
"cmp_indigo",
|
|
3228
|
+
"indigo",
|
|
3229
|
+
);
|
|
3230
|
+
expect(scope.syncMode).toBe("shared");
|
|
3231
|
+
// All anchors stripped, globs folded to startsWith-prefixes, sorted —
|
|
3232
|
+
// and `data/vyg/old-meetings/` is subsumed by the broader `data/vyg/`.
|
|
3233
|
+
expect(scope.prefixSet).toEqual([
|
|
3234
|
+
"company.yaml",
|
|
3235
|
+
"data/vyg/",
|
|
3236
|
+
"design-pack/",
|
|
3237
|
+
"knowledge/README.md",
|
|
3238
|
+
]);
|
|
3239
|
+
});
|
|
3240
|
+
|
|
3241
|
+
it("a bare '*' grant resolves to everything (empty-string prefix)", async () => {
|
|
3242
|
+
const scope = await resolvePullScope(
|
|
3243
|
+
stubClient({
|
|
3244
|
+
listMyMemberships: async () => [membership("cmp_a", "mk_a")],
|
|
3245
|
+
getMembershipSyncConfig: async () => ({
|
|
3246
|
+
membershipId: "mk_a",
|
|
3247
|
+
syncMode: "shared",
|
|
3248
|
+
isDefault: false,
|
|
3249
|
+
}),
|
|
3250
|
+
listMyExplicitGrants: async () =>
|
|
3251
|
+
[{ companyUid: "cmp_a", path: "*", permission: "admin", source: "group" }] as never,
|
|
3252
|
+
}),
|
|
3253
|
+
"cmp_a",
|
|
3254
|
+
"acme",
|
|
3255
|
+
);
|
|
3256
|
+
// A `*` grant = everything. Because coalescePrefixes drops the empty
|
|
3257
|
+
// prefix (which would otherwise collapse to "nothing" and prune the
|
|
3258
|
+
// tree), an everything-scope resolves to full-access `all`.
|
|
3259
|
+
expect(scope).toEqual({ syncMode: "all" });
|
|
3260
|
+
});
|
|
3261
|
+
|
|
3262
|
+
it("uses customPaths for a custom-mode membership", async () => {
|
|
3263
|
+
const scope = await resolvePullScope(
|
|
3264
|
+
stubClient({
|
|
3265
|
+
listMyMemberships: async () => [membership("cmp_a", "mk_a")],
|
|
3266
|
+
getMembershipSyncConfig: async () => ({
|
|
3267
|
+
membershipId: "mk_a",
|
|
3268
|
+
syncMode: "custom",
|
|
3269
|
+
customPaths: ["projects/x/", "projects/x/deep/"],
|
|
3270
|
+
isDefault: false,
|
|
3271
|
+
}),
|
|
3272
|
+
}),
|
|
3273
|
+
"cmp_a",
|
|
3274
|
+
"acme",
|
|
3275
|
+
);
|
|
3276
|
+
expect(scope.syncMode).toBe("custom");
|
|
3277
|
+
expect(scope.prefixSet).toEqual(["projects/x/"]);
|
|
3278
|
+
});
|
|
3279
|
+
|
|
3280
|
+
it("degrades to all for shared mode when listMyExplicitGrants is unavailable (never empty-prune)", async () => {
|
|
3281
|
+
const scope = await resolvePullScope(
|
|
3282
|
+
stubClient({
|
|
3283
|
+
listMyMemberships: async () => [membership("cmp_a", "mk_a")],
|
|
3284
|
+
getMembershipSyncConfig: async () => ({
|
|
3285
|
+
membershipId: "mk_a",
|
|
3286
|
+
syncMode: "shared",
|
|
3287
|
+
isDefault: false,
|
|
3288
|
+
}),
|
|
3289
|
+
// listMyExplicitGrants intentionally absent.
|
|
3290
|
+
}),
|
|
3291
|
+
"cmp_a",
|
|
3292
|
+
"acme",
|
|
3293
|
+
);
|
|
3294
|
+
expect(scope).toEqual({ syncMode: "all" });
|
|
3295
|
+
});
|
|
3296
|
+
|
|
3297
|
+
it("allows a genuinely-empty shared scope (grants method present, returns [])", async () => {
|
|
3298
|
+
const scope = await resolvePullScope(
|
|
3299
|
+
stubClient({
|
|
3300
|
+
listMyMemberships: async () => [membership("cmp_a", "mk_a")],
|
|
3301
|
+
getMembershipSyncConfig: async () => ({
|
|
3302
|
+
membershipId: "mk_a",
|
|
3303
|
+
syncMode: "shared",
|
|
3304
|
+
isDefault: false,
|
|
3305
|
+
}),
|
|
3306
|
+
listMyExplicitGrants: async () => [],
|
|
3307
|
+
}),
|
|
3308
|
+
"cmp_a",
|
|
3309
|
+
"acme",
|
|
3310
|
+
);
|
|
3311
|
+
expect(scope).toEqual({ syncMode: "shared", prefixSet: [] });
|
|
3312
|
+
});
|
|
3313
|
+
|
|
3314
|
+
it("degrades to all when the membership is not found", async () => {
|
|
3315
|
+
const scope = await resolvePullScope(
|
|
3316
|
+
stubClient({
|
|
3317
|
+
listMyMemberships: async () => [membership("cmp_other", "mk_o")],
|
|
3318
|
+
getMembershipSyncConfig: async () => {
|
|
3319
|
+
throw new Error("should not be called");
|
|
3320
|
+
},
|
|
3321
|
+
}),
|
|
3322
|
+
"cmp_a",
|
|
3323
|
+
"acme",
|
|
3324
|
+
);
|
|
3325
|
+
expect(scope).toEqual({ syncMode: "all" });
|
|
3326
|
+
});
|
|
3327
|
+
|
|
3328
|
+
it("degrades to all when sync-config resolution throws (never prune on error)", async () => {
|
|
3329
|
+
const scope = await resolvePullScope(
|
|
3330
|
+
stubClient({
|
|
3331
|
+
listMyMemberships: async () => [membership("cmp_a", "mk_a")],
|
|
3332
|
+
getMembershipSyncConfig: async () => {
|
|
3333
|
+
throw new Error("network blip");
|
|
3334
|
+
},
|
|
3335
|
+
}),
|
|
3336
|
+
"cmp_a",
|
|
3337
|
+
"acme",
|
|
3338
|
+
);
|
|
3339
|
+
expect(scope).toEqual({ syncMode: "all" });
|
|
3340
|
+
});
|
|
3341
|
+
});
|