@indigoai-us/hq-cloud 6.18.18 → 6.18.19
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/cli/sync.d.ts +1 -0
- package/dist/cli/sync.d.ts.map +1 -1
- package/dist/cli/sync.js +50 -0
- package/dist/cli/sync.js.map +1 -1
- package/dist/cli/sync.test.js +89 -1
- package/dist/cli/sync.test.js.map +1 -1
- package/package.json +1 -1
package/dist/cli/sync.test.js
CHANGED
|
@@ -174,7 +174,7 @@ vi.mock("../sync/mass-delete-guards.js", async (importOriginal) => {
|
|
|
174
174
|
return { ...actual, resolveMassDeleteGuards: vi.fn().mockResolvedValue(false) };
|
|
175
175
|
});
|
|
176
176
|
import { resolveMassDeleteGuards } from "../sync/mass-delete-guards.js";
|
|
177
|
-
import { sync, reportNewFilesToNotify, applyRealtimeDeltaPage, isSyncMutationStdinCommand, runSyncMutationStdinJson, SYNC_MUTATION_STDIN_COMMAND, FULL_PULL_COOPERATIVE_CHECKPOINT_BATCH_SIZE, FULL_PULL_LIST_PAGE_CHECKPOINT_SIZE, setListCooperativeYieldMsForTesting, targetedJournalSessionCensusSizes, win32SymlinkFlavorBroken, _testing as syncTesting, } from "./sync.js";
|
|
177
|
+
import { sync, reportNewFilesToNotify, applyRealtimeDeltaPage, isSyncMutationStdinCommand, runSyncMutationStdinJson, SYNC_MUTATION_STDIN_COMMAND, FULL_PULL_COOPERATIVE_CHECKPOINT_BATCH_SIZE, FULL_PULL_LIST_PAGE_CHECKPOINT_SIZE, setListCooperativeYieldMsForTesting, setNewFilesEnrichmentBulkCapForTesting, targetedJournalSessionCensusSizes, win32SymlinkFlavorBroken, _testing as syncTesting, } from "./sync.js";
|
|
178
178
|
import { share } from "./share.js";
|
|
179
179
|
import * as s3Module from "../s3.js";
|
|
180
180
|
import { resolvePersonalConnectorSettingsSync } from "../lib/personal-connector-settings-sync.js";
|
|
@@ -7145,6 +7145,94 @@ describe("sync", () => {
|
|
|
7145
7145
|
const newFilesIdx = eventTypes.indexOf("new-files");
|
|
7146
7146
|
expect(newFilesIdx).toBeGreaterThan(lastProgressIdx);
|
|
7147
7147
|
});
|
|
7148
|
+
// ── Bulk-sync post-pull tail bypass ─────────────────────────────────
|
|
7149
|
+
// Regression for the 2026-09-30 first-sync case
|
|
7150
|
+
// (i-0b8c4677512e81136 · agt_3FZG3TC9GEEMRVWSRF9NJ7Q98C, hq-cloud
|
|
7151
|
+
// 6.18.18): 63,836 new files kept the runner ~20 minutes past
|
|
7152
|
+
// filesDone == filesTotal doing per-file HEAD enrichment
|
|
7153
|
+
// + /v1/notify/file-added POST, while the wizard already showed
|
|
7154
|
+
// "Sync did not finish". Above the bulk cap, the enrichment and the
|
|
7155
|
+
// notify POST are skipped so the pull tail after the last download is
|
|
7156
|
+
// O(1); new-files still emits (with addedBy: null) so downstream
|
|
7157
|
+
// consumers see the full list.
|
|
7158
|
+
describe("bulk-sync post-pull tail bypass (new-files > cap)", () => {
|
|
7159
|
+
const bulkFiles = Array.from({ length: 3 }, (_, i) => ({
|
|
7160
|
+
key: `bulk/file-${i}.md`,
|
|
7161
|
+
size: 10,
|
|
7162
|
+
lastModified: new Date(),
|
|
7163
|
+
etag: `"bulk-${i}"`,
|
|
7164
|
+
}));
|
|
7165
|
+
beforeEach(() => {
|
|
7166
|
+
// Cap at 2 so a 3-file remote trips the bypass in this test suite.
|
|
7167
|
+
setNewFilesEnrichmentBulkCapForTesting(2);
|
|
7168
|
+
});
|
|
7169
|
+
afterEach(() => {
|
|
7170
|
+
setNewFilesEnrichmentBulkCapForTesting(undefined);
|
|
7171
|
+
});
|
|
7172
|
+
it("skips per-file HeadObject enrichment when new files exceed the cap", async () => {
|
|
7173
|
+
vi.mocked(s3Module.listRemoteFiles).mockResolvedValueOnce(bulkFiles);
|
|
7174
|
+
vi.mocked(s3Module.headRemoteFile).mockResolvedValue({
|
|
7175
|
+
lastModified: new Date(),
|
|
7176
|
+
etag: '"any"',
|
|
7177
|
+
size: 10,
|
|
7178
|
+
metadata: { "created-by": "should-not-be-read@example.com" },
|
|
7179
|
+
});
|
|
7180
|
+
const newFilesEvents = [];
|
|
7181
|
+
await sync({
|
|
7182
|
+
company: "acme",
|
|
7183
|
+
vaultConfig: mockConfig,
|
|
7184
|
+
hqRoot: tmpDir,
|
|
7185
|
+
onEvent: (e) => {
|
|
7186
|
+
if (e.type === "new-files")
|
|
7187
|
+
newFilesEvents.push(e);
|
|
7188
|
+
},
|
|
7189
|
+
});
|
|
7190
|
+
// Bypass path: enrichment loop does not run for any of the 3 new
|
|
7191
|
+
// files, and the entire pull tail (HEAD + notify POST) is O(1).
|
|
7192
|
+
expect(vi.mocked(s3Module.headRemoteFile)).not.toHaveBeenCalled();
|
|
7193
|
+
expect(newFilesEvents).toHaveLength(1);
|
|
7194
|
+
expect(newFilesEvents[0].files).toHaveLength(3);
|
|
7195
|
+
for (const f of newFilesEvents[0].files) {
|
|
7196
|
+
expect(f.addedBy).toBeNull();
|
|
7197
|
+
}
|
|
7198
|
+
});
|
|
7199
|
+
it("skips the /v1/notify/file-added POST when new files exceed the cap", async () => {
|
|
7200
|
+
vi.mocked(s3Module.listRemoteFiles).mockResolvedValueOnce(bulkFiles);
|
|
7201
|
+
await sync({
|
|
7202
|
+
company: "acme",
|
|
7203
|
+
vaultConfig: mockConfig,
|
|
7204
|
+
hqRoot: tmpDir,
|
|
7205
|
+
onEvent: () => { },
|
|
7206
|
+
});
|
|
7207
|
+
const calls = vi.mocked(globalThis.fetch).mock.calls;
|
|
7208
|
+
const notifyCalls = calls.filter(([u]) => String(u).includes("/v1/notify/file-added"));
|
|
7209
|
+
expect(notifyCalls).toHaveLength(0);
|
|
7210
|
+
});
|
|
7211
|
+
it("still enriches when new files fit under the cap", async () => {
|
|
7212
|
+
// Two default remote files, cap 2 → NOT over cap → enrichment runs.
|
|
7213
|
+
vi.mocked(s3Module.headRemoteFile).mockResolvedValue({
|
|
7214
|
+
lastModified: new Date(),
|
|
7215
|
+
etag: '"any"',
|
|
7216
|
+
size: 10,
|
|
7217
|
+
metadata: { "created-by": "author@example.com" },
|
|
7218
|
+
});
|
|
7219
|
+
const newFilesEvents = [];
|
|
7220
|
+
await sync({
|
|
7221
|
+
company: "acme",
|
|
7222
|
+
vaultConfig: mockConfig,
|
|
7223
|
+
hqRoot: tmpDir,
|
|
7224
|
+
onEvent: (e) => {
|
|
7225
|
+
if (e.type === "new-files")
|
|
7226
|
+
newFilesEvents.push(e);
|
|
7227
|
+
},
|
|
7228
|
+
});
|
|
7229
|
+
expect(vi.mocked(s3Module.headRemoteFile)).toHaveBeenCalled();
|
|
7230
|
+
expect(newFilesEvents).toHaveLength(1);
|
|
7231
|
+
for (const f of newFilesEvents[0].files) {
|
|
7232
|
+
expect(f.addedBy).toBe("author@example.com");
|
|
7233
|
+
}
|
|
7234
|
+
});
|
|
7235
|
+
});
|
|
7148
7236
|
it("stamps progress.author from the downloaded object's created-by metadata", async () => {
|
|
7149
7237
|
vi.mocked(s3Module.listRemoteFiles).mockResolvedValueOnce([
|
|
7150
7238
|
{ key: "docs/authored.md", size: 10, lastModified: new Date(), etag: '"e1"' },
|