@indigoai-us/hq-cloud 6.15.55 → 6.15.57
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-watch-loop.d.ts.map +1 -1
- package/dist/bin/sync-runner-watch-loop.js +21 -15
- package/dist/bin/sync-runner-watch-loop.js.map +1 -1
- package/dist/bin/sync-runner.d.ts +4 -4
- package/dist/bin/sync-runner.d.ts.map +1 -1
- package/dist/bin/sync-runner.test.js +45 -13
- package/dist/bin/sync-runner.test.js.map +1 -1
- package/dist/cli/sync.d.ts.map +1 -1
- package/dist/cli/sync.js +113 -13
- package/dist/cli/sync.js.map +1 -1
- package/dist/cli/sync.test.js +85 -0
- package/dist/cli/sync.test.js.map +1 -1
- package/dist/journal.d.ts +2 -0
- package/dist/journal.d.ts.map +1 -1
- package/dist/journal.js +12 -1
- package/dist/journal.js.map +1 -1
- package/dist/sync/event-sync.d.ts +6 -5
- package/dist/sync/event-sync.d.ts.map +1 -1
- package/dist/sync/event-sync.js +13 -8
- package/dist/sync/event-sync.js.map +1 -1
- package/dist/sync/event-sync.test.js +8 -4
- package/dist/sync/event-sync.test.js.map +1 -1
- package/dist/sync/index.d.ts +2 -2
- package/dist/sync/index.d.ts.map +1 -1
- package/dist/sync/index.js +1 -1
- package/dist/sync/index.js.map +1 -1
- package/dist/sync/push-receiver.d.ts +43 -11
- package/dist/sync/push-receiver.d.ts.map +1 -1
- package/dist/sync/push-receiver.js +148 -92
- package/dist/sync/push-receiver.js.map +1 -1
- package/dist/sync/push-receiver.test.js +132 -4
- package/dist/sync/push-receiver.test.js.map +1 -1
- package/dist/watcher.d.ts +24 -1
- package/dist/watcher.d.ts.map +1 -1
- package/dist/watcher.js +54 -10
- package/dist/watcher.js.map +1 -1
- package/dist/watcher.test.js +176 -0
- package/dist/watcher.test.js.map +1 -1
- package/package.json +1 -1
package/dist/cli/sync.test.js
CHANGED
|
@@ -120,13 +120,20 @@ vi.mock("../s3.js", async (importOriginal) => {
|
|
|
120
120
|
vi.mock("./reindex.js", () => ({
|
|
121
121
|
reindex: vi.fn(() => ({ status: 0 })),
|
|
122
122
|
}));
|
|
123
|
+
vi.mock("../remote-pull.js", async (importOriginal) => {
|
|
124
|
+
const actual = await importOriginal();
|
|
125
|
+
const { vi: innerVi } = await import("vitest");
|
|
126
|
+
return { ...actual, listRemoteForScope: innerVi.fn(actual.listRemoteForScope) };
|
|
127
|
+
});
|
|
123
128
|
import { sync, reportNewFilesToNotify, applyRealtimeDeltaPage, isSyncMutationStdinCommand, runSyncMutationStdinJson, SYNC_MUTATION_STDIN_COMMAND, win32SymlinkFlavorBroken, } from "./sync.js";
|
|
124
129
|
import { share } from "./share.js";
|
|
125
130
|
import * as s3Module from "../s3.js";
|
|
126
131
|
import * as conflictModule from "./conflict.js";
|
|
132
|
+
import * as remotePullModule from "../remote-pull.js";
|
|
127
133
|
import { reindex } from "./reindex.js";
|
|
128
134
|
import { readSyncProgress } from "../sync-progress.js";
|
|
129
135
|
import { VaultAuthError } from "../vault-client.js";
|
|
136
|
+
import * as journalModule from "../journal.js";
|
|
130
137
|
import { readJournal, writeJournal, getEntry, listJournals } from "../journal.js";
|
|
131
138
|
/**
|
|
132
139
|
* Read a journal the way production does.
|
|
@@ -238,6 +245,7 @@ describe("sync", () => {
|
|
|
238
245
|
});
|
|
239
246
|
afterEach(() => {
|
|
240
247
|
vi.unstubAllGlobals();
|
|
248
|
+
vi.mocked(remotePullModule.listRemoteForScope).mockRestore();
|
|
241
249
|
vi.clearAllMocks();
|
|
242
250
|
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
243
251
|
fs.rmSync(stateDir, { recursive: true, force: true });
|
|
@@ -293,6 +301,83 @@ describe("sync", () => {
|
|
|
293
301
|
expect(readJournal("acme").files["deleted-during-list.md"]?.removedReason)
|
|
294
302
|
.toBe("local-delete");
|
|
295
303
|
});
|
|
304
|
+
it("targeted pulls do not rematerialize the v3 journal after their cold keyed-session open", async () => {
|
|
305
|
+
writeJournal("acme", {
|
|
306
|
+
version: "2",
|
|
307
|
+
lastSync: "",
|
|
308
|
+
pulls: [],
|
|
309
|
+
files: {
|
|
310
|
+
"docs/known.md": {
|
|
311
|
+
hash: "a".repeat(64), size: 1, syncedAt: "2026-08-25T00:00:00.000Z", direction: "down",
|
|
312
|
+
},
|
|
313
|
+
},
|
|
314
|
+
});
|
|
315
|
+
const materialisations = vi.spyOn(journalModule, "readJournal");
|
|
316
|
+
materialisations.mockClear();
|
|
317
|
+
vi.mocked(remotePullModule.listRemoteForScope).mockResolvedValue([
|
|
318
|
+
{ key: "docs/known.md", size: 1, lastModified: new Date(), etag: '"known"' },
|
|
319
|
+
]);
|
|
320
|
+
await sync({ company: "acme", vaultConfig: mockConfig, hqRoot: tmpDir, pullPrefixes: ["docs"], skipReindex: true });
|
|
321
|
+
await sync({ company: "acme", vaultConfig: mockConfig, hqRoot: tmpDir, pullPrefixes: ["docs"], skipReindex: true });
|
|
322
|
+
// Unfixed origin/main fails here with:
|
|
323
|
+
// expected "readJournal" to be called +0 times, but got 2 times
|
|
324
|
+
expect(materialisations).not.toHaveBeenCalled();
|
|
325
|
+
});
|
|
326
|
+
it("keeps warm targeted journal work in milliseconds with a 100k-row journal", async () => {
|
|
327
|
+
const files = {
|
|
328
|
+
"docs/known.md": { hash: "a".repeat(64), size: 1, syncedAt: "2026-08-25T00:00:00.000Z", direction: "down" },
|
|
329
|
+
};
|
|
330
|
+
for (let index = 0; index < 100_000; index++) {
|
|
331
|
+
files[`unrelated/${index}.md`] = {
|
|
332
|
+
hash: "b".repeat(64), size: index, syncedAt: "2026-08-25T00:00:00.000Z", direction: "down",
|
|
333
|
+
};
|
|
334
|
+
}
|
|
335
|
+
writeJournal("acme", { version: "2", lastSync: "", pulls: [], files });
|
|
336
|
+
vi.mocked(remotePullModule.listRemoteForScope).mockResolvedValue([
|
|
337
|
+
{ key: "docs/known.md", size: 1, lastModified: new Date(), etag: '"known"' },
|
|
338
|
+
]);
|
|
339
|
+
const options = { company: "acme", vaultConfig: mockConfig, hqRoot: tmpDir, pullPrefixes: ["docs"], skipReindex: true };
|
|
340
|
+
// Cold open is intentionally excluded: the contract is no *per-pass*
|
|
341
|
+
// materialisation after the long-lived keyed session has opened.
|
|
342
|
+
await sync(options);
|
|
343
|
+
const started = performance.now();
|
|
344
|
+
await sync(options);
|
|
345
|
+
expect(performance.now() - started).toBeLessThan(500);
|
|
346
|
+
});
|
|
347
|
+
it("only tombstones remote deletions inside the targeted prefix", async () => {
|
|
348
|
+
writeJournal("acme", {
|
|
349
|
+
version: "2", lastSync: "", pulls: [], files: {
|
|
350
|
+
"docs/deleted.md": { hash: crypto.createHash("sha256").update("gone").digest("hex"), size: 4, syncedAt: "2026-08-25T00:00:00.000Z", direction: "down" },
|
|
351
|
+
"outside/kept.md": { hash: "b".repeat(64), size: 1, syncedAt: "2026-08-25T00:00:00.000Z", direction: "down" },
|
|
352
|
+
},
|
|
353
|
+
});
|
|
354
|
+
const companyRoot = path.join(tmpDir, "companies", "acme");
|
|
355
|
+
fs.mkdirSync(path.join(companyRoot, "docs"), { recursive: true });
|
|
356
|
+
fs.mkdirSync(path.join(companyRoot, "outside"), { recursive: true });
|
|
357
|
+
fs.writeFileSync(path.join(companyRoot, "docs", "deleted.md"), "gone");
|
|
358
|
+
fs.writeFileSync(path.join(companyRoot, "outside", "kept.md"), "keep");
|
|
359
|
+
vi.mocked(remotePullModule.listRemoteForScope).mockResolvedValue([]);
|
|
360
|
+
vi.mocked(s3Module.headRemoteFile).mockResolvedValue(null);
|
|
361
|
+
await sync({ company: "acme", vaultConfig: mockConfig, hqRoot: tmpDir, pullPrefixes: ["docs"], skipReindex: true });
|
|
362
|
+
expect(readJournal("acme").files["docs/deleted.md"]).toBeUndefined();
|
|
363
|
+
expect(readJournal("acme").files["outside/kept.md"]).toBeDefined();
|
|
364
|
+
expect(fs.readFileSync(path.join(companyRoot, "outside", "kept.md"), "utf8")).toBe("keep");
|
|
365
|
+
});
|
|
366
|
+
it("refreshes a concurrent writer after LIST and explicit keyed delta leaves it intact", async () => {
|
|
367
|
+
writeJournal("acme", { version: "2", lastSync: "", pulls: [], files: {} });
|
|
368
|
+
vi.mocked(remotePullModule.listRemoteForScope).mockImplementationOnce(async () => {
|
|
369
|
+
const concurrent = readJournal("acme");
|
|
370
|
+
concurrent.files["outside/push.md"] = {
|
|
371
|
+
hash: "c".repeat(64), size: 7, syncedAt: "2026-08-25T00:00:00.000Z", direction: "up",
|
|
372
|
+
};
|
|
373
|
+
writeJournal("acme", concurrent);
|
|
374
|
+
return [{ key: "docs/pull.md", size: 4, lastModified: new Date(), etag: '"pull"' }];
|
|
375
|
+
});
|
|
376
|
+
await sync({ company: "acme", vaultConfig: mockConfig, hqRoot: tmpDir, pullPrefixes: ["docs"], skipReindex: true });
|
|
377
|
+
const finalJournal = readJournal("acme");
|
|
378
|
+
expect(finalJournal.files["outside/push.md"]?.direction).toBe("up");
|
|
379
|
+
expect(finalJournal.files["docs/pull.md"]).toBeDefined();
|
|
380
|
+
});
|
|
296
381
|
it("pulses liveness from the synchronous pull planner", async () => {
|
|
297
382
|
let heartbeatCalls = 0;
|
|
298
383
|
await sync({
|