@indigoai-us/hq-cloud 6.16.65 → 6.16.66

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.
Files changed (126) hide show
  1. package/dist/bin/sync-runner-company.d.ts +14 -0
  2. package/dist/bin/sync-runner-company.d.ts.map +1 -1
  3. package/dist/bin/sync-runner-company.js +42 -8
  4. package/dist/bin/sync-runner-company.js.map +1 -1
  5. package/dist/bin/sync-runner-company.test.js +62 -7
  6. package/dist/bin/sync-runner-company.test.js.map +1 -1
  7. package/dist/bin/sync-runner-planning.d.ts +12 -3
  8. package/dist/bin/sync-runner-planning.d.ts.map +1 -1
  9. package/dist/bin/sync-runner-planning.js +65 -3
  10. package/dist/bin/sync-runner-planning.js.map +1 -1
  11. package/dist/bin/sync-runner-planning.test.js +172 -1
  12. package/dist/bin/sync-runner-planning.test.js.map +1 -1
  13. package/dist/bin/sync-runner-watch-loop.d.ts +51 -2
  14. package/dist/bin/sync-runner-watch-loop.d.ts.map +1 -1
  15. package/dist/bin/sync-runner-watch-loop.js +409 -165
  16. package/dist/bin/sync-runner-watch-loop.js.map +1 -1
  17. package/dist/bin/sync-runner-watch-routes.d.ts +19 -2
  18. package/dist/bin/sync-runner-watch-routes.d.ts.map +1 -1
  19. package/dist/bin/sync-runner-watch-routes.js +40 -5
  20. package/dist/bin/sync-runner-watch-routes.js.map +1 -1
  21. package/dist/bin/sync-runner-watch-routes.test.js +47 -0
  22. package/dist/bin/sync-runner-watch-routes.test.js.map +1 -1
  23. package/dist/bin/sync-runner.d.ts +59 -5
  24. package/dist/bin/sync-runner.d.ts.map +1 -1
  25. package/dist/bin/sync-runner.js +48 -4
  26. package/dist/bin/sync-runner.js.map +1 -1
  27. package/dist/bin/sync-runner.test.js +1553 -192
  28. package/dist/bin/sync-runner.test.js.map +1 -1
  29. package/dist/cli/onepass-pull-planner-regressions.test.d.ts +2 -0
  30. package/dist/cli/onepass-pull-planner-regressions.test.d.ts.map +1 -0
  31. package/dist/cli/onepass-pull-planner-regressions.test.js +300 -0
  32. package/dist/cli/onepass-pull-planner-regressions.test.js.map +1 -0
  33. package/dist/cli/onepass-pull-planner.test.d.ts +2 -0
  34. package/dist/cli/onepass-pull-planner.test.d.ts.map +1 -0
  35. package/dist/cli/onepass-pull-planner.test.js +266 -0
  36. package/dist/cli/onepass-pull-planner.test.js.map +1 -0
  37. package/dist/cli/push-scan-exclusions.test.js +4 -4
  38. package/dist/cli/push-scan-exclusions.test.js.map +1 -1
  39. package/dist/cli/share.d.ts +31 -1
  40. package/dist/cli/share.d.ts.map +1 -1
  41. package/dist/cli/share.js +136 -6
  42. package/dist/cli/share.js.map +1 -1
  43. package/dist/cli/share.test.js +295 -5
  44. package/dist/cli/share.test.js.map +1 -1
  45. package/dist/cli/streaming-pull-planner.bench.test.d.ts +15 -0
  46. package/dist/cli/streaming-pull-planner.bench.test.d.ts.map +1 -0
  47. package/dist/cli/streaming-pull-planner.bench.test.js +273 -0
  48. package/dist/cli/streaming-pull-planner.bench.test.js.map +1 -0
  49. package/dist/cli/sync-scope.test.js +8 -0
  50. package/dist/cli/sync-scope.test.js.map +1 -1
  51. package/dist/cli/sync.d.ts +12 -0
  52. package/dist/cli/sync.d.ts.map +1 -1
  53. package/dist/cli/sync.js +297 -125
  54. package/dist/cli/sync.js.map +1 -1
  55. package/dist/cli/sync.test.js +255 -2
  56. package/dist/cli/sync.test.js.map +1 -1
  57. package/dist/journal-row-store.d.ts +40 -5
  58. package/dist/journal-row-store.d.ts.map +1 -1
  59. package/dist/journal-row-store.js +240 -21
  60. package/dist/journal-row-store.js.map +1 -1
  61. package/dist/journal-row-store.test.js +54 -0
  62. package/dist/journal-row-store.test.js.map +1 -1
  63. package/dist/journal-snapshot-v4.d.ts.map +1 -1
  64. package/dist/journal-snapshot-v4.js +11 -6
  65. package/dist/journal-snapshot-v4.js.map +1 -1
  66. package/dist/journal-store.test.js +324 -7
  67. package/dist/journal-store.test.js.map +1 -1
  68. package/dist/journal.d.ts +51 -16
  69. package/dist/journal.d.ts.map +1 -1
  70. package/dist/journal.js +303 -79
  71. package/dist/journal.js.map +1 -1
  72. package/dist/lib/exit-codes.d.ts +15 -0
  73. package/dist/lib/exit-codes.d.ts.map +1 -1
  74. package/dist/lib/exit-codes.js +15 -0
  75. package/dist/lib/exit-codes.js.map +1 -1
  76. package/dist/personal-vault.d.ts.map +1 -1
  77. package/dist/personal-vault.js +10 -3
  78. package/dist/personal-vault.js.map +1 -1
  79. package/dist/personal-vault.test.js +35 -0
  80. package/dist/personal-vault.test.js.map +1 -1
  81. package/dist/remote-pull.d.ts.map +1 -1
  82. package/dist/remote-pull.js +9 -2
  83. package/dist/remote-pull.js.map +1 -1
  84. package/dist/remote-pull.test.js +59 -2
  85. package/dist/remote-pull.test.js.map +1 -1
  86. package/dist/s3.d.ts +1 -0
  87. package/dist/s3.d.ts.map +1 -1
  88. package/dist/s3.js +20 -2
  89. package/dist/s3.js.map +1 -1
  90. package/dist/s3.list-cost.test.d.ts +2 -0
  91. package/dist/s3.list-cost.test.d.ts.map +1 -0
  92. package/dist/s3.list-cost.test.js +51 -0
  93. package/dist/s3.list-cost.test.js.map +1 -0
  94. package/dist/sync/area-ledger.d.ts +27 -3
  95. package/dist/sync/area-ledger.d.ts.map +1 -1
  96. package/dist/sync/area-ledger.js +149 -39
  97. package/dist/sync/area-ledger.js.map +1 -1
  98. package/dist/sync/area-ledger.test.js +192 -5
  99. package/dist/sync/area-ledger.test.js.map +1 -1
  100. package/dist/sync/parallel-sync-coordinator.test.js +29 -0
  101. package/dist/sync/parallel-sync-coordinator.test.js.map +1 -1
  102. package/dist/sync/realtime-telemetry.d.ts +1 -1
  103. package/dist/sync/realtime-telemetry.d.ts.map +1 -1
  104. package/dist/sync/realtime-telemetry.js +14 -6
  105. package/dist/sync/realtime-telemetry.js.map +1 -1
  106. package/dist/sync/realtime-telemetry.test.js +136 -16
  107. package/dist/sync/realtime-telemetry.test.js.map +1 -1
  108. package/dist/sync/remote-file-spool.d.ts +16 -0
  109. package/dist/sync/remote-file-spool.d.ts.map +1 -0
  110. package/dist/sync/remote-file-spool.js +91 -0
  111. package/dist/sync/remote-file-spool.js.map +1 -0
  112. package/dist/sync/remote-file-spool.test.d.ts +2 -0
  113. package/dist/sync/remote-file-spool.test.d.ts.map +1 -0
  114. package/dist/sync/remote-file-spool.test.js +77 -0
  115. package/dist/sync/remote-file-spool.test.js.map +1 -0
  116. package/dist/sync/state-store.d.ts +6 -0
  117. package/dist/sync/state-store.d.ts.map +1 -1
  118. package/dist/sync/state-store.js +14 -2
  119. package/dist/sync/state-store.js.map +1 -1
  120. package/dist/watcher.d.ts +12 -0
  121. package/dist/watcher.d.ts.map +1 -1
  122. package/dist/watcher.js +131 -0
  123. package/dist/watcher.js.map +1 -1
  124. package/dist/watcher.test.js +63 -0
  125. package/dist/watcher.test.js.map +1 -1
  126. package/package.json +2 -1
@@ -64,7 +64,7 @@ vi.mock("readline", () => ({
64
64
  })),
65
65
  }));
66
66
  import * as readline from "readline";
67
- import { share, ServerOwnedPushPathsError, UnreachablePushPathsError, UnregisteredCompanySkillError, currentUploadBytes, parseRegisteredCompanySkillUid, _testing as shareTesting, } from "./share.js";
67
+ import { share, ServerOwnedPushPathsError, UnreachablePushPathsError, UnregisteredCompanySkillError, currentUploadBytes, FULL_PUSH_COOPERATIVE_CHECKPOINT_BATCH_SIZE, parseRegisteredCompanySkillUid, _testing as shareTesting, } from "./share.js";
68
68
  import { deleteRemoteFile, downloadFile, headRemoteFile, primeObjectTransport, primeUploads, uploadFile, uploadSymlink, WindowsSymlinkPrivilegeError, } from "../s3.js";
69
69
  import { VaultAuthError, VaultClientError, VaultConflictError, VaultPermissionDeniedError, } from "../vault-client.js";
70
70
  import { CognitoRefreshError } from "../cognito-auth.js";
@@ -72,6 +72,7 @@ import { RateLimitedError } from "../object-io.js";
72
72
  import { AreaLedgerMigration, closeCachedAreaJournalMigration, hashFile, openJournalStateStoreForMigration, readJournal, readJournalScoped, setAreaLedgerMigrationTestHooksForTest, writeJournal, } from "../journal.js";
73
73
  import { ReconcileCursorStore } from "../sync/reconcile-cursor.js";
74
74
  import { AreaResolver } from "../sync/area-resolver.js";
75
+ import { CooperativePassScheduler } from "../bin/sync-runner-watch-loop.js";
75
76
  /**
76
77
  * Read a journal the way production does.
77
78
  *
@@ -1658,6 +1659,7 @@ describe("share", () => {
1658
1659
  fs.rmSync(tmpDir, { recursive: true, force: true });
1659
1660
  fs.rmSync(stateDir, { recursive: true, force: true });
1660
1661
  delete process.env.HQ_STATE_DIR;
1662
+ shareTesting.disableCooperativeRowVisits();
1661
1663
  });
1662
1664
  it("shares a single file keyed relative to the company root", async () => {
1663
1665
  const companyRoot = path.join(tmpDir, "companies", "acme");
@@ -1675,6 +1677,285 @@ describe("share", () => {
1675
1677
  // Remote key must be company-relative, not hqRoot-relative
1676
1678
  expect(uploadFile).toHaveBeenCalledWith(expect.anything(), testFile, "test.md", undefined, expect.anything());
1677
1679
  });
1680
+ it("hands queued realtime work a settled full-push boundary without losing journal rows", async () => {
1681
+ const companyRoot = path.join(tmpDir, "companies", "acme");
1682
+ const fileCount = FULL_PUSH_COOPERATIVE_CHECKPOINT_BATCH_SIZE + 1;
1683
+ for (let index = 0; index < fileCount; index++) {
1684
+ const file = path.join(companyRoot, "docs", "full-push", `${index}.md`);
1685
+ fs.mkdirSync(path.dirname(file), { recursive: true });
1686
+ fs.writeFileSync(file, "full-push body");
1687
+ }
1688
+ const scheduler = new CooperativePassScheduler(() => 0);
1689
+ let uploadsCompleted = 0;
1690
+ let uploadsInFlight = 0;
1691
+ let maxUploadsInFlight = 0;
1692
+ let realtimeQueued = false;
1693
+ let realtimeDispatchedAt = -1;
1694
+ let checkpointCalls = 0;
1695
+ let realtimePass;
1696
+ const upload = vi.mocked(uploadFile);
1697
+ const originalUpload = upload.getMockImplementation();
1698
+ upload.mockImplementation(async () => {
1699
+ uploadsInFlight++;
1700
+ maxUploadsInFlight = Math.max(maxUploadsInFlight, uploadsInFlight);
1701
+ await Promise.resolve();
1702
+ uploadsCompleted++;
1703
+ if (uploadsCompleted === 1) {
1704
+ realtimeQueued = true;
1705
+ realtimePass = scheduler.submit(async () => {
1706
+ // `executeUploads` drains the full unit before the scheduler grants
1707
+ // realtime the root lock: no two durable writers overlap.
1708
+ expect(uploadsInFlight).toBe(0);
1709
+ realtimeDispatchedAt = uploadsCompleted;
1710
+ const realtimePath = path.join(companyRoot, "docs", "realtime.md");
1711
+ fs.writeFileSync(realtimePath, "realtime body");
1712
+ const journal = readJournal("acme");
1713
+ journal.files["docs/realtime.md"] = {
1714
+ hash: hashFile(realtimePath),
1715
+ size: fs.statSync(realtimePath).size,
1716
+ syncedAt: "2026-09-17T12:00:00.000Z",
1717
+ direction: "up",
1718
+ remoteEtag: "realtime",
1719
+ mtimeMs: fs.statSync(realtimePath).mtimeMs,
1720
+ ctimeMs: fs.statSync(realtimePath).ctimeMs,
1721
+ };
1722
+ writeJournal("acme", journal);
1723
+ }, "realtime");
1724
+ }
1725
+ uploadsInFlight--;
1726
+ return { etag: '"full-push-etag"' };
1727
+ });
1728
+ try {
1729
+ await scheduler.submit(async ({ checkpoint }) => {
1730
+ await share({
1731
+ paths: [companyRoot],
1732
+ company: "acme",
1733
+ vaultConfig: mockConfig,
1734
+ hqRoot: tmpDir,
1735
+ skipUnchanged: true,
1736
+ propagateDeletes: true,
1737
+ cooperativeCheckpoint: async () => {
1738
+ checkpointCalls++;
1739
+ return checkpoint();
1740
+ },
1741
+ });
1742
+ }, "chore");
1743
+ await realtimePass;
1744
+ }
1745
+ finally {
1746
+ if (originalUpload)
1747
+ upload.mockImplementation(originalUpload);
1748
+ else
1749
+ upload.mockReset();
1750
+ }
1751
+ expect(maxUploadsInFlight).toBeGreaterThan(1);
1752
+ expect(realtimeQueued).toBe(true);
1753
+ expect(checkpointCalls).toBeGreaterThan(0);
1754
+ expect(realtimeDispatchedAt).toBeGreaterThan(0);
1755
+ expect(realtimeDispatchedAt).toBeLessThanOrEqual(FULL_PUSH_COOPERATIVE_CHECKPOINT_BATCH_SIZE);
1756
+ // Reopen the durable journal, rather than inspecting the stale first-pass
1757
+ // object: every pre-handoff key and the realtime key must survive.
1758
+ const durable = readJournal("acme");
1759
+ expect(Object.keys(durable.files)).toHaveLength(fileCount + 1);
1760
+ expect(durable.files["docs/realtime.md"]).toBeDefined();
1761
+ for (let index = 0; index < fileCount; index++) {
1762
+ expect(durable.files[`docs/full-push/${index}.md`]).toBeDefined();
1763
+ }
1764
+ }, 120_000);
1765
+ it("hands an all-skipped full personal push to queued realtime work", async () => {
1766
+ const companyRoot = path.join(tmpDir, "companies", "acme");
1767
+ const fileCount = FULL_PUSH_COOPERATIVE_CHECKPOINT_BATCH_SIZE + 1;
1768
+ const files = {};
1769
+ for (let index = 0; index < fileCount; index++) {
1770
+ const relativePath = `docs/unchanged/${index}.md`;
1771
+ const file = path.join(companyRoot, relativePath);
1772
+ fs.mkdirSync(path.dirname(file), { recursive: true });
1773
+ fs.writeFileSync(file, "unchanged body");
1774
+ const stat = fs.statSync(file);
1775
+ files[relativePath] = {
1776
+ hash: hashFile(file),
1777
+ size: stat.size,
1778
+ mtimeMs: stat.mtimeMs,
1779
+ ctimeMs: stat.ctimeMs,
1780
+ syncedAt: "2026-09-17T12:00:00.000Z",
1781
+ direction: "up",
1782
+ remoteEtag: "unchanged",
1783
+ };
1784
+ }
1785
+ seedJournalAt(path.join(stateDir, "sync-journal.acme.json"), {
1786
+ version: "2",
1787
+ lastSync: "2026-09-17T12:00:00.000Z",
1788
+ files,
1789
+ pulls: [],
1790
+ });
1791
+ const scheduler = new CooperativePassScheduler(() => 0);
1792
+ let fullPassFinished = false;
1793
+ let eventLoopTicked = false;
1794
+ let realtimeQueued = false;
1795
+ let realtimeRanBeforeFullPassFinished = false;
1796
+ let checkpointCalls = 0;
1797
+ let realtimePass;
1798
+ let resolveRealtimeScheduled;
1799
+ const realtimeScheduled = new Promise((resolve) => {
1800
+ resolveRealtimeScheduled = resolve;
1801
+ });
1802
+ const queueRealtime = () => {
1803
+ if (realtimeQueued)
1804
+ return;
1805
+ realtimeQueued = true;
1806
+ setImmediate(() => {
1807
+ eventLoopTicked = true;
1808
+ realtimePass = scheduler.submit(async () => {
1809
+ realtimeRanBeforeFullPassFinished = !fullPassFinished;
1810
+ }, "realtime");
1811
+ resolveRealtimeScheduled?.();
1812
+ });
1813
+ };
1814
+ await scheduler.submit(async ({ checkpoint }) => {
1815
+ try {
1816
+ const result = await share({
1817
+ paths: [companyRoot],
1818
+ company: "acme",
1819
+ vaultConfig: mockConfig,
1820
+ hqRoot: tmpDir,
1821
+ skipUnchanged: true,
1822
+ onEvent: (event) => {
1823
+ if (event.type === "plan")
1824
+ queueRealtime();
1825
+ },
1826
+ cooperativeCheckpoint: async () => {
1827
+ checkpointCalls++;
1828
+ return checkpoint();
1829
+ },
1830
+ });
1831
+ expect(result.filesSkipped).toBe(fileCount);
1832
+ expect(result.filesUploaded).toBe(0);
1833
+ }
1834
+ finally {
1835
+ fullPassFinished = true;
1836
+ }
1837
+ }, "chore");
1838
+ await realtimeScheduled;
1839
+ await realtimePass;
1840
+ expect(eventLoopTicked).toBe(true);
1841
+ expect(realtimeQueued).toBe(true);
1842
+ expect(realtimeRanBeforeFullPassFinished).toBe(true);
1843
+ expect(checkpointCalls).toBeGreaterThan(0);
1844
+ expect(uploadFile).not.toHaveBeenCalled();
1845
+ expect(Object.keys(readJournal("acme").files)).toHaveLength(fileCount);
1846
+ }, 120_000);
1847
+ it("resumes a skip-only full push at the next unit after realtime hand-off", async () => {
1848
+ const companyRoot = path.join(tmpDir, "companies", "acme");
1849
+ const fileCount = FULL_PUSH_COOPERATIVE_CHECKPOINT_BATCH_SIZE * 3;
1850
+ const files = {};
1851
+ for (let index = 0; index < fileCount; index++) {
1852
+ const relativePath = `docs/resume/${index}.md`;
1853
+ const file = path.join(companyRoot, relativePath);
1854
+ fs.mkdirSync(path.dirname(file), { recursive: true });
1855
+ fs.writeFileSync(file, "resume body");
1856
+ const stat = fs.statSync(file);
1857
+ files[relativePath] = {
1858
+ hash: hashFile(file),
1859
+ size: stat.size,
1860
+ mtimeMs: stat.mtimeMs,
1861
+ ctimeMs: stat.ctimeMs,
1862
+ syncedAt: "2026-09-17T12:00:00.000Z",
1863
+ direction: "up",
1864
+ remoteEtag: "unchanged",
1865
+ };
1866
+ }
1867
+ seedJournalAt(path.join(stateDir, "sync-journal.acme.json"), {
1868
+ version: "2",
1869
+ lastSync: "2026-09-17T12:00:00.000Z",
1870
+ files,
1871
+ pulls: [],
1872
+ });
1873
+ const scheduler = new CooperativePassScheduler(() => 0);
1874
+ let fullPassFinished = false;
1875
+ let realtimeRanBeforeFullPassFinished = false;
1876
+ const planEvents = [];
1877
+ const realtimePhaseRuns = [];
1878
+ const realtimePasses = [];
1879
+ const queuedForPhase = new Set();
1880
+ let currentPhase = "collection";
1881
+ const queueRealtimeForCurrentPhase = () => {
1882
+ const phase = currentPhase;
1883
+ if (queuedForPhase.has(phase))
1884
+ return;
1885
+ queuedForPhase.add(phase);
1886
+ setImmediate(() => {
1887
+ realtimePasses.push(scheduler.submit(async () => {
1888
+ realtimePhaseRuns.push(phase);
1889
+ if (!fullPassFinished)
1890
+ realtimeRanBeforeFullPassFinished = true;
1891
+ }, "realtime"));
1892
+ });
1893
+ };
1894
+ shareTesting.enableCooperativeRowVisits();
1895
+ let visits = {
1896
+ collect: new Map(),
1897
+ plan: new Map(),
1898
+ };
1899
+ try {
1900
+ await scheduler.submit(async ({ checkpoint }) => {
1901
+ queueRealtimeForCurrentPhase();
1902
+ try {
1903
+ const result = await share({
1904
+ paths: [companyRoot],
1905
+ company: "acme",
1906
+ vaultConfig: mockConfig,
1907
+ hqRoot: tmpDir,
1908
+ skipUnchanged: true,
1909
+ propagateDeletes: true,
1910
+ onEvent: (event) => {
1911
+ if (event.type === "plan") {
1912
+ planEvents.push(event);
1913
+ currentPhase = "execution";
1914
+ queueRealtimeForCurrentPhase();
1915
+ }
1916
+ },
1917
+ cooperativeCheckpoint: async () => {
1918
+ if (currentPhase === "collection" &&
1919
+ shareTesting.cooperativeRowVisits().plan.size > 0) {
1920
+ currentPhase = "planning";
1921
+ }
1922
+ if (currentPhase === "planning")
1923
+ queueRealtimeForCurrentPhase();
1924
+ return checkpoint();
1925
+ },
1926
+ });
1927
+ expect(result.filesSkipped).toBe(fileCount);
1928
+ expect(result.filesUploaded).toBe(0);
1929
+ }
1930
+ finally {
1931
+ fullPassFinished = true;
1932
+ }
1933
+ }, "chore");
1934
+ await Promise.all(realtimePasses);
1935
+ visits = shareTesting.cooperativeRowVisits();
1936
+ }
1937
+ finally {
1938
+ shareTesting.disableCooperativeRowVisits();
1939
+ }
1940
+ expect(realtimeRanBeforeFullPassFinished).toBe(true);
1941
+ expect(realtimePhaseRuns).toEqual(["collection", "planning", "execution"]);
1942
+ expect(planEvents).toHaveLength(1);
1943
+ expect(planEvents[0]).toMatchObject({
1944
+ type: "plan",
1945
+ filesToUpload: 0,
1946
+ filesToSkip: fileCount,
1947
+ filesToDelete: 0,
1948
+ });
1949
+ expect(visits.collect.size).toBe(fileCount);
1950
+ expect(visits.plan.size).toBe(fileCount);
1951
+ for (let index = 0; index < fileCount; index++) {
1952
+ const relativePath = `docs/resume/${index}.md`;
1953
+ expect(visits.collect.get(relativePath)).toBe(1);
1954
+ expect(visits.plan.get(relativePath)).toBe(1);
1955
+ }
1956
+ expect(uploadFile).not.toHaveBeenCalled();
1957
+ expect(Object.keys(readJournal("acme").files)).toHaveLength(fileCount);
1958
+ }, 120_000);
1678
1959
  it("defers a rate-limited upload as a diagnostic error and leaves the journal row unchanged", async () => {
1679
1960
  // Production (2026-09-18): vault 100/hr throttle on a per-file PUT was
1680
1961
  // emitted as a hard type:"error", so the company landed in errors[] and
@@ -2085,7 +2366,7 @@ describe("share", () => {
2085
2366
  });
2086
2367
  expect(result.filesUploaded).toBe(2);
2087
2368
  });
2088
- it("walks a very large directory without using its entries as call arguments", () => {
2369
+ it("walks a very large directory without using its entries as call arguments", async () => {
2089
2370
  const companyRoot = path.join(tmpDir, "companies", "acme");
2090
2371
  fs.mkdirSync(companyRoot, { recursive: true });
2091
2372
  const nestedDir = path.join(companyRoot, "nested");
@@ -2112,8 +2393,15 @@ describe("share", () => {
2112
2393
  .spyOn(fs, "readdirSync")
2113
2394
  .mockImplementation(((dir) => path.resolve(String(dir)) === nestedDir ? nestedEntries : rootEntries));
2114
2395
  let heartbeatCalls = 0;
2396
+ let eventLoopTicked = false;
2397
+ const eventLoopTick = new Promise((resolve) => {
2398
+ setImmediate(() => {
2399
+ eventLoopTicked = true;
2400
+ resolve();
2401
+ });
2402
+ });
2115
2403
  try {
2116
- const collected = shareTesting.collectFiles([companyRoot], tmpDir, companyRoot, () => true, { onHeartbeat: () => heartbeatCalls++ });
2404
+ const collected = await shareTesting.collectFiles([companyRoot], tmpDir, companyRoot, () => true, { onHeartbeat: () => heartbeatCalls++ });
2117
2405
  expect(collected).toHaveLength(entryCount);
2118
2406
  expect(collected[0]).toMatchObject({ relativePath: "before.md" });
2119
2407
  expect(collected[1]).toMatchObject({ relativePath: "nested/child.md" });
@@ -2122,6 +2410,8 @@ describe("share", () => {
2122
2410
  relativePath: `entry-${entryCount - 3}.md`,
2123
2411
  });
2124
2412
  expect(heartbeatCalls).toBeGreaterThan(entryCount);
2413
+ expect(eventLoopTicked).toBe(true);
2414
+ await eventLoopTick;
2125
2415
  }
2126
2416
  finally {
2127
2417
  readdirSpy.mockRestore();
@@ -7851,7 +8141,7 @@ describe("share", () => {
7851
8141
  expect(uploadFile).toHaveBeenCalledWith(expect.anything(), realPolicy, "policies/real.md", undefined, expect.anything());
7852
8142
  expect(uploadSymlink).toHaveBeenCalledWith(expect.anything(), "real.md", "policies/link.md", undefined, expect.anything());
7853
8143
  });
7854
- it("skips and reports a top-level link whose win32 readlink raises EINVAL", () => {
8144
+ it("skips and reports a top-level link whose win32 readlink raises EINVAL", async () => {
7855
8145
  const companyRoot = path.join(tmpDir, "companies", "acme");
7856
8146
  const targetDir = path.join(tmpDir, "outside", "target");
7857
8147
  fs.mkdirSync(targetDir, { recursive: true });
@@ -7870,7 +8160,7 @@ describe("share", () => {
7870
8160
  const warnings = vi.spyOn(console, "error").mockImplementation(() => { });
7871
8161
  const unreachable = [];
7872
8162
  try {
7873
- const collected = shareTesting.collectFiles([unreadableLink], tmpDir, companyRoot, () => true, {
8163
+ const collected = await shareTesting.collectFiles([unreadableLink], tmpDir, companyRoot, () => true, {
7874
8164
  onUnreachablePath: (namedPath, reason) => unreachable.push([namedPath, reason]),
7875
8165
  });
7876
8166
  expect(collected).toEqual([]);