@indigoai-us/hq-cloud 6.18.44-alpha.9 → 6.18.44
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-planning.d.ts +12 -0
- package/dist/bin/sync-runner-planning.d.ts.map +1 -1
- package/dist/bin/sync-runner-planning.js +18 -0
- package/dist/bin/sync-runner-planning.js.map +1 -1
- package/dist/bin/sync-runner-watch-loop.d.ts +18 -0
- package/dist/bin/sync-runner-watch-loop.d.ts.map +1 -1
- package/dist/bin/sync-runner-watch-loop.js +75 -6
- package/dist/bin/sync-runner-watch-loop.js.map +1 -1
- package/dist/bin/sync-runner.d.ts +22 -1
- package/dist/bin/sync-runner.d.ts.map +1 -1
- package/dist/bin/sync-runner.js +33 -4
- package/dist/bin/sync-runner.js.map +1 -1
- package/dist/cli/conflict.d.ts.map +1 -1
- package/dist/cli/conflict.js +2 -1
- package/dist/cli/conflict.js.map +1 -1
- package/dist/cli/rescue-core.d.ts +6 -0
- package/dist/cli/rescue-core.d.ts.map +1 -1
- package/dist/cli/rescue-core.js +10 -1
- package/dist/cli/rescue-core.js.map +1 -1
- package/dist/cli/rescue-windows-test-harness.d.ts +19 -0
- package/dist/cli/rescue-windows-test-harness.d.ts.map +1 -0
- package/dist/cli/rescue-windows-test-harness.js +443 -0
- package/dist/cli/rescue-windows-test-harness.js.map +1 -0
- package/dist/company-resolver.d.ts +22 -0
- package/dist/company-resolver.d.ts.map +1 -1
- package/dist/company-resolver.js +1 -1
- package/dist/company-resolver.js.map +1 -1
- package/dist/ignore.d.ts +4 -1
- package/dist/ignore.d.ts.map +1 -1
- package/dist/ignore.js +10 -12
- package/dist/ignore.js.map +1 -1
- package/dist/journal-snapshot-decoder.js +15 -2
- package/dist/journal-snapshot-decoder.js.map +1 -1
- package/dist/sync/durable-apply.d.ts +2 -0
- package/dist/sync/durable-apply.d.ts.map +1 -1
- package/dist/sync/durable-apply.js +9 -12
- package/dist/sync/durable-apply.js.map +1 -1
- package/dist/sync/fsync-compat.d.ts +13 -0
- package/dist/sync/fsync-compat.d.ts.map +1 -0
- package/dist/sync/fsync-compat.js +33 -0
- package/dist/sync/fsync-compat.js.map +1 -0
- package/dist/sync/multipart-uploader.d.ts.map +1 -1
- package/dist/sync/multipart-uploader.js +2 -1
- package/dist/sync/multipart-uploader.js.map +1 -1
- package/dist/sync/scope-inventory-client.d.ts.map +1 -1
- package/dist/sync/scope-inventory-client.js +7 -2
- package/dist/sync/scope-inventory-client.js.map +1 -1
- package/dist/telemetry.d.ts +31 -0
- package/dist/telemetry.d.ts.map +1 -1
- package/dist/telemetry.js +177 -17
- package/dist/telemetry.js.map +1 -1
- package/dist/usage-company-fallback.d.ts +104 -0
- package/dist/usage-company-fallback.d.ts.map +1 -0
- package/dist/usage-company-fallback.js +599 -0
- package/dist/usage-company-fallback.js.map +1 -0
- package/dist/watch-owner-lease.d.ts +3 -0
- package/dist/watch-owner-lease.d.ts.map +1 -1
- package/dist/watch-owner-lease.js +14 -3
- package/dist/watch-owner-lease.js.map +1 -1
- package/package.json +2 -2
- package/dist/active-company.test.d.ts +0 -2
- package/dist/active-company.test.d.ts.map +0 -1
- package/dist/active-company.test.js +0 -149
- package/dist/active-company.test.js.map +0 -1
- package/dist/agent-codex-instructions.test.d.ts +0 -2
- package/dist/agent-codex-instructions.test.d.ts.map +0 -1
- package/dist/agent-codex-instructions.test.js +0 -271
- package/dist/agent-codex-instructions.test.js.map +0 -1
- package/dist/backup-prune.test.d.ts +0 -2
- package/dist/backup-prune.test.d.ts.map +0 -1
- package/dist/backup-prune.test.js +0 -154
- package/dist/backup-prune.test.js.map +0 -1
- package/dist/bandwidth.test.d.ts +0 -2
- package/dist/bandwidth.test.d.ts.map +0 -1
- package/dist/bandwidth.test.js +0 -342
- package/dist/bandwidth.test.js.map +0 -1
- package/dist/best-effort-error.test.d.ts +0 -2
- package/dist/best-effort-error.test.d.ts.map +0 -1
- package/dist/best-effort-error.test.js +0 -54
- package/dist/best-effort-error.test.js.map +0 -1
- package/dist/bin/area-ledger-migration.test.d.ts +0 -2
- package/dist/bin/area-ledger-migration.test.d.ts.map +0 -1
- package/dist/bin/area-ledger-migration.test.js +0 -410
- package/dist/bin/area-ledger-migration.test.js.map +0 -1
- package/dist/bin/backup-prune-runner.test.d.ts +0 -2
- package/dist/bin/backup-prune-runner.test.d.ts.map +0 -1
- package/dist/bin/backup-prune-runner.test.js +0 -22
- package/dist/bin/backup-prune-runner.test.js.map +0 -1
- package/dist/bin/journal-repair.test.d.ts +0 -2
- package/dist/bin/journal-repair.test.d.ts.map +0 -1
- package/dist/bin/journal-repair.test.js +0 -53
- package/dist/bin/journal-repair.test.js.map +0 -1
- package/dist/bin/layout-compatibility.test.d.ts +0 -2
- package/dist/bin/layout-compatibility.test.d.ts.map +0 -1
- package/dist/bin/layout-compatibility.test.js +0 -87
- package/dist/bin/layout-compatibility.test.js.map +0 -1
- package/dist/bin/outpost-heartbeat.test.d.ts +0 -7
- package/dist/bin/outpost-heartbeat.test.d.ts.map +0 -1
- package/dist/bin/outpost-heartbeat.test.js +0 -84
- package/dist/bin/outpost-heartbeat.test.js.map +0 -1
- package/dist/bin/outpost-session-host.test.d.ts +0 -7
- package/dist/bin/outpost-session-host.test.d.ts.map +0 -1
- package/dist/bin/outpost-session-host.test.js +0 -52
- package/dist/bin/outpost-session-host.test.js.map +0 -1
- package/dist/bin/sync-mutation.test.d.ts +0 -2
- package/dist/bin/sync-mutation.test.d.ts.map +0 -1
- package/dist/bin/sync-mutation.test.js +0 -64
- package/dist/bin/sync-mutation.test.js.map +0 -1
- package/dist/bin/sync-runner-args.test.d.ts +0 -2
- package/dist/bin/sync-runner-args.test.d.ts.map +0 -1
- package/dist/bin/sync-runner-args.test.js +0 -82
- package/dist/bin/sync-runner-args.test.js.map +0 -1
- package/dist/bin/sync-runner-auth-wait.test.d.ts +0 -2
- package/dist/bin/sync-runner-auth-wait.test.d.ts.map +0 -1
- package/dist/bin/sync-runner-auth-wait.test.js +0 -406
- package/dist/bin/sync-runner-auth-wait.test.js.map +0 -1
- package/dist/bin/sync-runner-backoff.regression.test.d.ts +0 -29
- package/dist/bin/sync-runner-backoff.regression.test.d.ts.map +0 -1
- package/dist/bin/sync-runner-backoff.regression.test.js +0 -224
- package/dist/bin/sync-runner-backoff.regression.test.js.map +0 -1
- package/dist/bin/sync-runner-company.test.d.ts +0 -2
- package/dist/bin/sync-runner-company.test.d.ts.map +0 -1
- package/dist/bin/sync-runner-company.test.js +0 -2439
- package/dist/bin/sync-runner-company.test.js.map +0 -1
- package/dist/bin/sync-runner-delete-snapshot-mixed.bench.test.d.ts +0 -2
- package/dist/bin/sync-runner-delete-snapshot-mixed.bench.test.d.ts.map +0 -1
- package/dist/bin/sync-runner-delete-snapshot-mixed.bench.test.js +0 -648
- package/dist/bin/sync-runner-delete-snapshot-mixed.bench.test.js.map +0 -1
- package/dist/bin/sync-runner-delete-snapshot.bench.test.d.ts +0 -2
- package/dist/bin/sync-runner-delete-snapshot.bench.test.d.ts.map +0 -1
- package/dist/bin/sync-runner-delete-snapshot.bench.test.js +0 -329
- package/dist/bin/sync-runner-delete-snapshot.bench.test.js.map +0 -1
- package/dist/bin/sync-runner-events.test.d.ts +0 -2
- package/dist/bin/sync-runner-events.test.d.ts.map +0 -1
- package/dist/bin/sync-runner-events.test.js +0 -364
- package/dist/bin/sync-runner-events.test.js.map +0 -1
- package/dist/bin/sync-runner-heap-telemetry.test.d.ts +0 -2
- package/dist/bin/sync-runner-heap-telemetry.test.d.ts.map +0 -1
- package/dist/bin/sync-runner-heap-telemetry.test.js +0 -1966
- package/dist/bin/sync-runner-heap-telemetry.test.js.map +0 -1
- package/dist/bin/sync-runner-manifest.test.d.ts +0 -12
- package/dist/bin/sync-runner-manifest.test.d.ts.map +0 -1
- package/dist/bin/sync-runner-manifest.test.js +0 -921
- package/dist/bin/sync-runner-manifest.test.js.map +0 -1
- package/dist/bin/sync-runner-planning.test.d.ts +0 -2
- package/dist/bin/sync-runner-planning.test.d.ts.map +0 -1
- package/dist/bin/sync-runner-planning.test.js +0 -448
- package/dist/bin/sync-runner-planning.test.js.map +0 -1
- package/dist/bin/sync-runner-post-sync-flags.test.d.ts +0 -2
- package/dist/bin/sync-runner-post-sync-flags.test.d.ts.map +0 -1
- package/dist/bin/sync-runner-post-sync-flags.test.js +0 -64
- package/dist/bin/sync-runner-post-sync-flags.test.js.map +0 -1
- package/dist/bin/sync-runner-publish-expansion.test.d.ts +0 -2
- package/dist/bin/sync-runner-publish-expansion.test.d.ts.map +0 -1
- package/dist/bin/sync-runner-publish-expansion.test.js +0 -54
- package/dist/bin/sync-runner-publish-expansion.test.js.map +0 -1
- package/dist/bin/sync-runner-receiver-unresolved-company.test.d.ts +0 -2
- package/dist/bin/sync-runner-receiver-unresolved-company.test.d.ts.map +0 -1
- package/dist/bin/sync-runner-receiver-unresolved-company.test.js +0 -379
- package/dist/bin/sync-runner-receiver-unresolved-company.test.js.map +0 -1
- package/dist/bin/sync-runner-rollup.test.d.ts +0 -2
- package/dist/bin/sync-runner-rollup.test.d.ts.map +0 -1
- package/dist/bin/sync-runner-rollup.test.js +0 -24
- package/dist/bin/sync-runner-rollup.test.js.map +0 -1
- package/dist/bin/sync-runner-watch-loop.test.d.ts +0 -2
- package/dist/bin/sync-runner-watch-loop.test.d.ts.map +0 -1
- package/dist/bin/sync-runner-watch-loop.test.js +0 -734
- package/dist/bin/sync-runner-watch-loop.test.js.map +0 -1
- package/dist/bin/sync-runner-watch-routes.test.d.ts +0 -2
- package/dist/bin/sync-runner-watch-routes.test.d.ts.map +0 -1
- package/dist/bin/sync-runner-watch-routes.test.js +0 -181
- package/dist/bin/sync-runner-watch-routes.test.js.map +0 -1
- package/dist/bin/sync-runner.test.d.ts +0 -10
- package/dist/bin/sync-runner.test.d.ts.map +0 -1
- package/dist/bin/sync-runner.test.js +0 -18984
- package/dist/bin/sync-runner.test.js.map +0 -1
- package/dist/cli/case-alias.test.d.ts +0 -2
- package/dist/cli/case-alias.test.d.ts.map +0 -1
- package/dist/cli/case-alias.test.js +0 -96
- package/dist/cli/case-alias.test.js.map +0 -1
- package/dist/cli/conflict-recovery.test.d.ts +0 -2
- package/dist/cli/conflict-recovery.test.d.ts.map +0 -1
- package/dist/cli/conflict-recovery.test.js +0 -201
- package/dist/cli/conflict-recovery.test.js.map +0 -1
- package/dist/cli/conflict-winner.test.d.ts +0 -7
- package/dist/cli/conflict-winner.test.d.ts.map +0 -1
- package/dist/cli/conflict-winner.test.js +0 -98
- package/dist/cli/conflict-winner.test.js.map +0 -1
- package/dist/cli/doctor.test.d.ts +0 -13
- package/dist/cli/doctor.test.d.ts.map +0 -1
- package/dist/cli/doctor.test.js +0 -607
- package/dist/cli/doctor.test.js.map +0 -1
- package/dist/cli/invite.test.d.ts +0 -5
- package/dist/cli/invite.test.d.ts.map +0 -1
- package/dist/cli/invite.test.js +0 -178
- package/dist/cli/invite.test.js.map +0 -1
- package/dist/cli/onepass-pull-planner-regressions.test.d.ts +0 -2
- package/dist/cli/onepass-pull-planner-regressions.test.d.ts.map +0 -1
- package/dist/cli/onepass-pull-planner-regressions.test.js +0 -300
- package/dist/cli/onepass-pull-planner-regressions.test.js.map +0 -1
- package/dist/cli/onepass-pull-planner.test.d.ts +0 -2
- package/dist/cli/onepass-pull-planner.test.d.ts.map +0 -1
- package/dist/cli/onepass-pull-planner.test.js +0 -266
- package/dist/cli/onepass-pull-planner.test.js.map +0 -1
- package/dist/cli/push-scan-exclusions.test.d.ts +0 -2
- package/dist/cli/push-scan-exclusions.test.d.ts.map +0 -1
- package/dist/cli/push-scan-exclusions.test.js +0 -99
- package/dist/cli/push-scan-exclusions.test.js.map +0 -1
- package/dist/cli/reindex-knowledge.test.d.ts +0 -12
- package/dist/cli/reindex-knowledge.test.d.ts.map +0 -1
- package/dist/cli/reindex-knowledge.test.js +0 -260
- package/dist/cli/reindex-knowledge.test.js.map +0 -1
- package/dist/cli/reindex.test.d.ts +0 -11
- package/dist/cli/reindex.test.d.ts.map +0 -1
- package/dist/cli/reindex.test.js +0 -811
- package/dist/cli/reindex.test.js.map +0 -1
- package/dist/cli/rescue-breaker.test.d.ts +0 -2
- package/dist/cli/rescue-breaker.test.d.ts.map +0 -1
- package/dist/cli/rescue-breaker.test.js +0 -812
- package/dist/cli/rescue-breaker.test.js.map +0 -1
- package/dist/cli/rescue-classify-ordering.test.d.ts +0 -2
- package/dist/cli/rescue-classify-ordering.test.d.ts.map +0 -1
- package/dist/cli/rescue-classify-ordering.test.js +0 -500
- package/dist/cli/rescue-classify-ordering.test.js.map +0 -1
- package/dist/cli/rescue-clone-diagnostics.test.d.ts +0 -2
- package/dist/cli/rescue-clone-diagnostics.test.d.ts.map +0 -1
- package/dist/cli/rescue-clone-diagnostics.test.js +0 -101
- package/dist/cli/rescue-clone-diagnostics.test.js.map +0 -1
- package/dist/cli/rescue-clone-network-retry.test.d.ts +0 -2
- package/dist/cli/rescue-clone-network-retry.test.d.ts.map +0 -1
- package/dist/cli/rescue-clone-network-retry.test.js +0 -415
- package/dist/cli/rescue-clone-network-retry.test.js.map +0 -1
- package/dist/cli/rescue-core-stamp.test.d.ts +0 -2
- package/dist/cli/rescue-core-stamp.test.d.ts.map +0 -1
- package/dist/cli/rescue-core-stamp.test.js +0 -364
- package/dist/cli/rescue-core-stamp.test.js.map +0 -1
- package/dist/cli/rescue-drift-reconcile.test.d.ts +0 -2
- package/dist/cli/rescue-drift-reconcile.test.d.ts.map +0 -1
- package/dist/cli/rescue-drift-reconcile.test.js +0 -162
- package/dist/cli/rescue-drift-reconcile.test.js.map +0 -1
- package/dist/cli/rescue-drop-dir-symlink.test.d.ts +0 -2
- package/dist/cli/rescue-drop-dir-symlink.test.d.ts.map +0 -1
- package/dist/cli/rescue-drop-dir-symlink.test.js +0 -255
- package/dist/cli/rescue-drop-dir-symlink.test.js.map +0 -1
- package/dist/cli/rescue-exec-bit-preserve.test.d.ts +0 -2
- package/dist/cli/rescue-exec-bit-preserve.test.d.ts.map +0 -1
- package/dist/cli/rescue-exec-bit-preserve.test.js +0 -177
- package/dist/cli/rescue-exec-bit-preserve.test.js.map +0 -1
- package/dist/cli/rescue-hq-root-guard.test.d.ts +0 -2
- package/dist/cli/rescue-hq-root-guard.test.d.ts.map +0 -1
- package/dist/cli/rescue-hq-root-guard.test.js +0 -210
- package/dist/cli/rescue-hq-root-guard.test.js.map +0 -1
- package/dist/cli/rescue-journal-reconcile.test.d.ts +0 -2
- package/dist/cli/rescue-journal-reconcile.test.d.ts.map +0 -1
- package/dist/cli/rescue-journal-reconcile.test.js +0 -186
- package/dist/cli/rescue-journal-reconcile.test.js.map +0 -1
- package/dist/cli/rescue-keep-local-ignore.test.d.ts +0 -2
- package/dist/cli/rescue-keep-local-ignore.test.d.ts.map +0 -1
- package/dist/cli/rescue-keep-local-ignore.test.js +0 -315
- package/dist/cli/rescue-keep-local-ignore.test.js.map +0 -1
- package/dist/cli/rescue-move.test.d.ts +0 -2
- package/dist/cli/rescue-move.test.d.ts.map +0 -1
- package/dist/cli/rescue-move.test.js +0 -166
- package/dist/cli/rescue-move.test.js.map +0 -1
- package/dist/cli/rescue-mtime-preserve.test.d.ts +0 -2
- package/dist/cli/rescue-mtime-preserve.test.d.ts.map +0 -1
- package/dist/cli/rescue-mtime-preserve.test.js +0 -186
- package/dist/cli/rescue-mtime-preserve.test.js.map +0 -1
- package/dist/cli/rescue-preserve-copy-symlink.test.d.ts +0 -2
- package/dist/cli/rescue-preserve-copy-symlink.test.d.ts.map +0 -1
- package/dist/cli/rescue-preserve-copy-symlink.test.js +0 -483
- package/dist/cli/rescue-preserve-copy-symlink.test.js.map +0 -1
- package/dist/cli/rescue-realpath.test.d.ts +0 -2
- package/dist/cli/rescue-realpath.test.d.ts.map +0 -1
- package/dist/cli/rescue-realpath.test.js +0 -109
- package/dist/cli/rescue-realpath.test.js.map +0 -1
- package/dist/cli/rescue-restore-flag.test.d.ts +0 -2
- package/dist/cli/rescue-restore-flag.test.d.ts.map +0 -1
- package/dist/cli/rescue-restore-flag.test.js +0 -82
- package/dist/cli/rescue-restore-flag.test.js.map +0 -1
- package/dist/cli/rescue-rsync-preflight.test.d.ts +0 -2
- package/dist/cli/rescue-rsync-preflight.test.d.ts.map +0 -1
- package/dist/cli/rescue-rsync-preflight.test.js +0 -579
- package/dist/cli/rescue-rsync-preflight.test.js.map +0 -1
- package/dist/cli/rescue-rsync-status.test.d.ts +0 -2
- package/dist/cli/rescue-rsync-status.test.d.ts.map +0 -1
- package/dist/cli/rescue-rsync-status.test.js +0 -406
- package/dist/cli/rescue-rsync-status.test.js.map +0 -1
- package/dist/cli/rescue-settings-reconcile.test.d.ts +0 -2
- package/dist/cli/rescue-settings-reconcile.test.d.ts.map +0 -1
- package/dist/cli/rescue-settings-reconcile.test.js +0 -610
- package/dist/cli/rescue-settings-reconcile.test.js.map +0 -1
- package/dist/cli/rescue-snapshot-copy.test.d.ts +0 -2
- package/dist/cli/rescue-snapshot-copy.test.d.ts.map +0 -1
- package/dist/cli/rescue-snapshot-copy.test.js +0 -790
- package/dist/cli/rescue-snapshot-copy.test.js.map +0 -1
- package/dist/cli/rescue-snapshot-recovery.test.d.ts +0 -2
- package/dist/cli/rescue-snapshot-recovery.test.d.ts.map +0 -1
- package/dist/cli/rescue-snapshot-recovery.test.js +0 -265
- package/dist/cli/rescue-snapshot-recovery.test.js.map +0 -1
- package/dist/cli/rescue-snapshot.test.d.ts +0 -2
- package/dist/cli/rescue-snapshot.test.d.ts.map +0 -1
- package/dist/cli/rescue-snapshot.test.js +0 -46
- package/dist/cli/rescue-snapshot.test.js.map +0 -1
- package/dist/cli/rescue-source-switch-floor.test.d.ts +0 -2
- package/dist/cli/rescue-source-switch-floor.test.d.ts.map +0 -1
- package/dist/cli/rescue-source-switch-floor.test.js +0 -247
- package/dist/cli/rescue-source-switch-floor.test.js.map +0 -1
- package/dist/cli/rescue-unchanged-delete.test.d.ts +0 -2
- package/dist/cli/rescue-unchanged-delete.test.d.ts.map +0 -1
- package/dist/cli/rescue-unchanged-delete.test.js +0 -305
- package/dist/cli/rescue-unchanged-delete.test.js.map +0 -1
- package/dist/cli/rescue.reindex.test.d.ts +0 -2
- package/dist/cli/rescue.reindex.test.d.ts.map +0 -1
- package/dist/cli/rescue.reindex.test.js +0 -72
- package/dist/cli/rescue.reindex.test.js.map +0 -1
- package/dist/cli/rescue.test.d.ts +0 -2
- package/dist/cli/rescue.test.d.ts.map +0 -1
- package/dist/cli/rescue.test.js +0 -127
- package/dist/cli/rescue.test.js.map +0 -1
- package/dist/cli/share-plan-limit-presign.test.d.ts +0 -2
- package/dist/cli/share-plan-limit-presign.test.d.ts.map +0 -1
- package/dist/cli/share-plan-limit-presign.test.js +0 -271
- package/dist/cli/share-plan-limit-presign.test.js.map +0 -1
- package/dist/cli/share-presign-transport.test.d.ts +0 -22
- package/dist/cli/share-presign-transport.test.d.ts.map +0 -1
- package/dist/cli/share-presign-transport.test.js +0 -388
- package/dist/cli/share-presign-transport.test.js.map +0 -1
- package/dist/cli/share.test.d.ts +0 -5
- package/dist/cli/share.test.d.ts.map +0 -1
- package/dist/cli/share.test.js +0 -12335
- package/dist/cli/share.test.js.map +0 -1
- package/dist/cli/streaming-pull-planner.bench.test.d.ts +0 -15
- package/dist/cli/streaming-pull-planner.bench.test.d.ts.map +0 -1
- package/dist/cli/streaming-pull-planner.bench.test.js +0 -273
- package/dist/cli/streaming-pull-planner.bench.test.js.map +0 -1
- package/dist/cli/sync-scope.test.d.ts +0 -22
- package/dist/cli/sync-scope.test.d.ts.map +0 -1
- package/dist/cli/sync-scope.test.js +0 -903
- package/dist/cli/sync-scope.test.js.map +0 -1
- package/dist/cli/sync.test.d.ts +0 -5
- package/dist/cli/sync.test.d.ts.map +0 -1
- package/dist/cli/sync.test.js +0 -9028
- package/dist/cli/sync.test.js.map +0 -1
- package/dist/cli/tombstones.test.d.ts +0 -2
- package/dist/cli/tombstones.test.d.ts.map +0 -1
- package/dist/cli/tombstones.test.js +0 -120
- package/dist/cli/tombstones.test.js.map +0 -1
- package/dist/cli/watch-event-push-conflict.test.d.ts +0 -25
- package/dist/cli/watch-event-push-conflict.test.d.ts.map +0 -1
- package/dist/cli/watch-event-push-conflict.test.js +0 -230
- package/dist/cli/watch-event-push-conflict.test.js.map +0 -1
- package/dist/client-info.test.d.ts +0 -11
- package/dist/client-info.test.d.ts.map +0 -1
- package/dist/client-info.test.js +0 -168
- package/dist/client-info.test.js.map +0 -1
- package/dist/cognito-auth-lock-no-hard-links.test.d.ts +0 -11
- package/dist/cognito-auth-lock-no-hard-links.test.d.ts.map +0 -1
- package/dist/cognito-auth-lock-no-hard-links.test.js +0 -76
- package/dist/cognito-auth-lock-no-hard-links.test.js.map +0 -1
- package/dist/cognito-auth.test.d.ts +0 -9
- package/dist/cognito-auth.test.d.ts.map +0 -1
- package/dist/cognito-auth.test.js +0 -745
- package/dist/cognito-auth.test.js.map +0 -1
- package/dist/company-resolver.test.d.ts +0 -7
- package/dist/company-resolver.test.d.ts.map +0 -1
- package/dist/company-resolver.test.js +0 -533
- package/dist/company-resolver.test.js.map +0 -1
- package/dist/company-slug.test.d.ts +0 -7
- package/dist/company-slug.test.d.ts.map +0 -1
- package/dist/company-slug.test.js +0 -43
- package/dist/company-slug.test.js.map +0 -1
- package/dist/company-vault-transport.test.d.ts +0 -12
- package/dist/company-vault-transport.test.d.ts.map +0 -1
- package/dist/company-vault-transport.test.js +0 -97
- package/dist/company-vault-transport.test.js.map +0 -1
- package/dist/context.test.d.ts +0 -7
- package/dist/context.test.d.ts.map +0 -1
- package/dist/context.test.js +0 -646
- package/dist/context.test.js.map +0 -1
- package/dist/entity-resolver.test.d.ts +0 -10
- package/dist/entity-resolver.test.d.ts.map +0 -1
- package/dist/entity-resolver.test.js +0 -244
- package/dist/entity-resolver.test.js.map +0 -1
- package/dist/getindigo-dead-refs-contract.test.d.ts +0 -2
- package/dist/getindigo-dead-refs-contract.test.d.ts.map +0 -1
- package/dist/getindigo-dead-refs-contract.test.js +0 -58
- package/dist/getindigo-dead-refs-contract.test.js.map +0 -1
- package/dist/hermetic-test-env.test.d.ts +0 -2
- package/dist/hermetic-test-env.test.d.ts.map +0 -1
- package/dist/hermetic-test-env.test.js +0 -61
- package/dist/hermetic-test-env.test.js.map +0 -1
- package/dist/hq-root-recovery.test.d.ts +0 -2
- package/dist/hq-root-recovery.test.d.ts.map +0 -1
- package/dist/hq-root-recovery.test.js +0 -65
- package/dist/hq-root-recovery.test.js.map +0 -1
- package/dist/ignore.test.d.ts +0 -10
- package/dist/ignore.test.d.ts.map +0 -1
- package/dist/ignore.test.js +0 -926
- package/dist/ignore.test.js.map +0 -1
- package/dist/index.test.d.ts +0 -2
- package/dist/index.test.d.ts.map +0 -1
- package/dist/index.test.js +0 -26
- package/dist/index.test.js.map +0 -1
- package/dist/journal-area-ledger-read-boundary.test.d.ts +0 -2
- package/dist/journal-area-ledger-read-boundary.test.d.ts.map +0 -1
- package/dist/journal-area-ledger-read-boundary.test.js +0 -420
- package/dist/journal-area-ledger-read-boundary.test.js.map +0 -1
- package/dist/journal-baseline.test.d.ts +0 -2
- package/dist/journal-baseline.test.d.ts.map +0 -1
- package/dist/journal-baseline.test.js +0 -218
- package/dist/journal-baseline.test.js.map +0 -1
- package/dist/journal-decode-window.test.d.ts +0 -2
- package/dist/journal-decode-window.test.d.ts.map +0 -1
- package/dist/journal-decode-window.test.js +0 -99
- package/dist/journal-decode-window.test.js.map +0 -1
- package/dist/journal-delta-writes.test.d.ts +0 -2
- package/dist/journal-delta-writes.test.d.ts.map +0 -1
- package/dist/journal-delta-writes.test.js +0 -159
- package/dist/journal-delta-writes.test.js.map +0 -1
- package/dist/journal-ledger-index.test.d.ts +0 -15
- package/dist/journal-ledger-index.test.d.ts.map +0 -1
- package/dist/journal-ledger-index.test.js +0 -269
- package/dist/journal-ledger-index.test.js.map +0 -1
- package/dist/journal-public-boundary.test.d.ts +0 -2
- package/dist/journal-public-boundary.test.d.ts.map +0 -1
- package/dist/journal-public-boundary.test.js +0 -216
- package/dist/journal-public-boundary.test.js.map +0 -1
- package/dist/journal-row-store.test.d.ts +0 -2
- package/dist/journal-row-store.test.d.ts.map +0 -1
- package/dist/journal-row-store.test.js +0 -729
- package/dist/journal-row-store.test.js.map +0 -1
- package/dist/journal-snapshot-memory-helpers.d.ts +0 -33
- package/dist/journal-snapshot-memory-helpers.d.ts.map +0 -1
- package/dist/journal-snapshot-memory-helpers.js +0 -212
- package/dist/journal-snapshot-memory-helpers.js.map +0 -1
- package/dist/journal-snapshot-memory.test.d.ts +0 -2
- package/dist/journal-snapshot-memory.test.d.ts.map +0 -1
- package/dist/journal-snapshot-memory.test.js +0 -47
- package/dist/journal-snapshot-memory.test.js.map +0 -1
- package/dist/journal-snapshot-v4-memory.test.d.ts +0 -2
- package/dist/journal-snapshot-v4-memory.test.d.ts.map +0 -1
- package/dist/journal-snapshot-v4-memory.test.js +0 -24
- package/dist/journal-snapshot-v4-memory.test.js.map +0 -1
- package/dist/journal-snapshot-v4-writer-memory.test.d.ts +0 -2
- package/dist/journal-snapshot-v4-writer-memory.test.d.ts.map +0 -1
- package/dist/journal-snapshot-v4-writer-memory.test.js +0 -76
- package/dist/journal-snapshot-v4-writer-memory.test.js.map +0 -1
- package/dist/journal-snapshot-v4-writer.test.d.ts +0 -2
- package/dist/journal-snapshot-v4-writer.test.d.ts.map +0 -1
- package/dist/journal-snapshot-v4-writer.test.js +0 -282
- package/dist/journal-snapshot-v4-writer.test.js.map +0 -1
- package/dist/journal-snapshot-v4.test.d.ts +0 -2
- package/dist/journal-snapshot-v4.test.d.ts.map +0 -1
- package/dist/journal-snapshot-v4.test.js +0 -281
- package/dist/journal-snapshot-v4.test.js.map +0 -1
- package/dist/journal-store.test.d.ts +0 -2
- package/dist/journal-store.test.d.ts.map +0 -1
- package/dist/journal-store.test.js +0 -1227
- package/dist/journal-store.test.js.map +0 -1
- package/dist/journal-streaming.heap.test.d.ts +0 -2
- package/dist/journal-streaming.heap.test.d.ts.map +0 -1
- package/dist/journal-streaming.heap.test.js +0 -190
- package/dist/journal-streaming.heap.test.js.map +0 -1
- package/dist/journal.test.d.ts +0 -9
- package/dist/journal.test.d.ts.map +0 -1
- package/dist/journal.test.js +0 -1074
- package/dist/journal.test.js.map +0 -1
- package/dist/kms10-etag-safe-pull.test.d.ts +0 -2
- package/dist/kms10-etag-safe-pull.test.d.ts.map +0 -1
- package/dist/kms10-etag-safe-pull.test.js +0 -53
- package/dist/kms10-etag-safe-pull.test.js.map +0 -1
- package/dist/lib/cloud-authoritative.test.d.ts +0 -2
- package/dist/lib/cloud-authoritative.test.d.ts.map +0 -1
- package/dist/lib/cloud-authoritative.test.js +0 -42
- package/dist/lib/cloud-authoritative.test.js.map +0 -1
- package/dist/lib/conflict-reconcile.test.d.ts +0 -7
- package/dist/lib/conflict-reconcile.test.d.ts.map +0 -1
- package/dist/lib/conflict-reconcile.test.js +0 -246
- package/dist/lib/conflict-reconcile.test.js.map +0 -1
- package/dist/lib/conflict.test.d.ts +0 -9
- package/dist/lib/conflict.test.d.ts.map +0 -1
- package/dist/lib/conflict.test.js +0 -340
- package/dist/lib/conflict.test.js.map +0 -1
- package/dist/lib/describe-error.test.d.ts +0 -2
- package/dist/lib/describe-error.test.d.ts.map +0 -1
- package/dist/lib/describe-error.test.js +0 -148
- package/dist/lib/describe-error.test.js.map +0 -1
- package/dist/lib/machine-id.test.d.ts +0 -8
- package/dist/lib/machine-id.test.d.ts.map +0 -1
- package/dist/lib/machine-id.test.js +0 -228
- package/dist/lib/machine-id.test.js.map +0 -1
- package/dist/lib/net-errors.test.d.ts +0 -2
- package/dist/lib/net-errors.test.d.ts.map +0 -1
- package/dist/lib/net-errors.test.js +0 -111
- package/dist/lib/net-errors.test.js.map +0 -1
- package/dist/lib/ontology-entity-meta.test.d.ts +0 -2
- package/dist/lib/ontology-entity-meta.test.d.ts.map +0 -1
- package/dist/lib/ontology-entity-meta.test.js +0 -79
- package/dist/lib/ontology-entity-meta.test.js.map +0 -1
- package/dist/lib/ontology-entity-push.test.d.ts +0 -2
- package/dist/lib/ontology-entity-push.test.d.ts.map +0 -1
- package/dist/lib/ontology-entity-push.test.js +0 -156
- package/dist/lib/ontology-entity-push.test.js.map +0 -1
- package/dist/lib/personal-connector-settings-sync.test.d.ts +0 -2
- package/dist/lib/personal-connector-settings-sync.test.d.ts.map +0 -1
- package/dist/lib/personal-connector-settings-sync.test.js +0 -34
- package/dist/lib/personal-connector-settings-sync.test.js.map +0 -1
- package/dist/lib/readlink-safe.test.d.ts +0 -2
- package/dist/lib/readlink-safe.test.d.ts.map +0 -1
- package/dist/lib/readlink-safe.test.js +0 -34
- package/dist/lib/readlink-safe.test.js.map +0 -1
- package/dist/lib/s3-content-checksum.test.d.ts +0 -2
- package/dist/lib/s3-content-checksum.test.d.ts.map +0 -1
- package/dist/lib/s3-content-checksum.test.js +0 -26
- package/dist/lib/s3-content-checksum.test.js.map +0 -1
- package/dist/lib/skill-register-landed-bytes.test.d.ts +0 -2
- package/dist/lib/skill-register-landed-bytes.test.d.ts.map +0 -1
- package/dist/lib/skill-register-landed-bytes.test.js +0 -83
- package/dist/lib/skill-register-landed-bytes.test.js.map +0 -1
- package/dist/local-path-codec.test.d.ts +0 -14
- package/dist/local-path-codec.test.d.ts.map +0 -1
- package/dist/local-path-codec.test.js +0 -122
- package/dist/local-path-codec.test.js.map +0 -1
- package/dist/machine-auth.test.d.ts +0 -14
- package/dist/machine-auth.test.d.ts.map +0 -1
- package/dist/machine-auth.test.js +0 -1492
- package/dist/machine-auth.test.js.map +0 -1
- package/dist/machine-token-cache-isolation.test.d.ts +0 -24
- package/dist/machine-token-cache-isolation.test.d.ts.map +0 -1
- package/dist/machine-token-cache-isolation.test.js +0 -284
- package/dist/machine-token-cache-isolation.test.js.map +0 -1
- package/dist/manifest/US-002.acceptance.test.d.ts +0 -11
- package/dist/manifest/US-002.acceptance.test.d.ts.map +0 -1
- package/dist/manifest/US-002.acceptance.test.js +0 -160
- package/dist/manifest/US-002.acceptance.test.js.map +0 -1
- package/dist/manifest/US-004.acceptance.test.d.ts +0 -12
- package/dist/manifest/US-004.acceptance.test.d.ts.map +0 -1
- package/dist/manifest/US-004.acceptance.test.js +0 -173
- package/dist/manifest/US-004.acceptance.test.js.map +0 -1
- package/dist/manifest/build-manifest-resident-parity.test.d.ts +0 -2
- package/dist/manifest/build-manifest-resident-parity.test.d.ts.map +0 -1
- package/dist/manifest/build-manifest-resident-parity.test.js +0 -69
- package/dist/manifest/build-manifest-resident-parity.test.js.map +0 -1
- package/dist/manifest/build-manifest.bench.test.d.ts +0 -22
- package/dist/manifest/build-manifest.bench.test.d.ts.map +0 -1
- package/dist/manifest/build-manifest.bench.test.js +0 -930
- package/dist/manifest/build-manifest.bench.test.js.map +0 -1
- package/dist/manifest/build-manifest.test.d.ts +0 -10
- package/dist/manifest/build-manifest.test.d.ts.map +0 -1
- package/dist/manifest/build-manifest.test.js +0 -1217
- package/dist/manifest/build-manifest.test.js.map +0 -1
- package/dist/manifest/contract.test.d.ts +0 -17
- package/dist/manifest/contract.test.d.ts.map +0 -1
- package/dist/manifest/contract.test.js +0 -346
- package/dist/manifest/contract.test.js.map +0 -1
- package/dist/manifest/import-graph.test.d.ts +0 -25
- package/dist/manifest/import-graph.test.d.ts.map +0 -1
- package/dist/manifest/import-graph.test.js +0 -120
- package/dist/manifest/import-graph.test.js.map +0 -1
- package/dist/manifest/ledger-shard-isolation.test.d.ts +0 -18
- package/dist/manifest/ledger-shard-isolation.test.d.ts.map +0 -1
- package/dist/manifest/ledger-shard-isolation.test.js +0 -153
- package/dist/manifest/ledger-shard-isolation.test.js.map +0 -1
- package/dist/manifest/snapshot-store.test.d.ts +0 -11
- package/dist/manifest/snapshot-store.test.d.ts.map +0 -1
- package/dist/manifest/snapshot-store.test.js +0 -206
- package/dist/manifest/snapshot-store.test.js.map +0 -1
- package/dist/manifest/upload-manifest.test.d.ts +0 -10
- package/dist/manifest/upload-manifest.test.d.ts.map +0 -1
- package/dist/manifest/upload-manifest.test.js +0 -1335
- package/dist/manifest/upload-manifest.test.js.map +0 -1
- package/dist/manifest-reconcile.test.d.ts +0 -2
- package/dist/manifest-reconcile.test.d.ts.map +0 -1
- package/dist/manifest-reconcile.test.js +0 -1029
- package/dist/manifest-reconcile.test.js.map +0 -1
- package/dist/object-io.test.d.ts +0 -11
- package/dist/object-io.test.d.ts.map +0 -1
- package/dist/object-io.test.js +0 -2376
- package/dist/object-io.test.js.map +0 -1
- package/dist/operation-lock.test.d.ts +0 -5
- package/dist/operation-lock.test.d.ts.map +0 -1
- package/dist/operation-lock.test.js +0 -796
- package/dist/operation-lock.test.js.map +0 -1
- package/dist/outcome-telemetry.test.d.ts +0 -9
- package/dist/outcome-telemetry.test.d.ts.map +0 -1
- package/dist/outcome-telemetry.test.js +0 -663
- package/dist/outcome-telemetry.test.js.map +0 -1
- package/dist/outposts/client.test.d.ts +0 -9
- package/dist/outposts/client.test.d.ts.map +0 -1
- package/dist/outposts/client.test.js +0 -415
- package/dist/outposts/client.test.js.map +0 -1
- package/dist/outposts/command.test.d.ts +0 -18
- package/dist/outposts/command.test.d.ts.map +0 -1
- package/dist/outposts/command.test.js +0 -1230
- package/dist/outposts/command.test.js.map +0 -1
- package/dist/outposts/replica-sync.test.d.ts +0 -2
- package/dist/outposts/replica-sync.test.d.ts.map +0 -1
- package/dist/outposts/replica-sync.test.js +0 -224
- package/dist/outposts/replica-sync.test.js.map +0 -1
- package/dist/outposts/self-deploy.test.d.ts +0 -2
- package/dist/outposts/self-deploy.test.d.ts.map +0 -1
- package/dist/outposts/self-deploy.test.js +0 -218
- package/dist/outposts/self-deploy.test.js.map +0 -1
- package/dist/outposts/session-heartbeat-bounds.test.d.ts +0 -25
- package/dist/outposts/session-heartbeat-bounds.test.d.ts.map +0 -1
- package/dist/outposts/session-heartbeat-bounds.test.js +0 -156
- package/dist/outposts/session-heartbeat-bounds.test.js.map +0 -1
- package/dist/outposts/session-heartbeat-guard.test.d.ts +0 -16
- package/dist/outposts/session-heartbeat-guard.test.d.ts.map +0 -1
- package/dist/outposts/session-heartbeat-guard.test.js +0 -88
- package/dist/outposts/session-heartbeat-guard.test.js.map +0 -1
- package/dist/outposts/session-heartbeat-publisher.test.d.ts +0 -10
- package/dist/outposts/session-heartbeat-publisher.test.d.ts.map +0 -1
- package/dist/outposts/session-heartbeat-publisher.test.js +0 -153
- package/dist/outposts/session-heartbeat-publisher.test.js.map +0 -1
- package/dist/outposts/session-heartbeat-retain-guard.test.d.ts +0 -18
- package/dist/outposts/session-heartbeat-retain-guard.test.d.ts.map +0 -1
- package/dist/outposts/session-heartbeat-retain-guard.test.js +0 -106
- package/dist/outposts/session-heartbeat-retain-guard.test.js.map +0 -1
- package/dist/outposts/session-heartbeat-scan.test.d.ts +0 -20
- package/dist/outposts/session-heartbeat-scan.test.d.ts.map +0 -1
- package/dist/outposts/session-heartbeat-scan.test.js +0 -241
- package/dist/outposts/session-heartbeat-scan.test.js.map +0 -1
- package/dist/outposts/session-heartbeat.test.d.ts +0 -11
- package/dist/outposts/session-heartbeat.test.d.ts.map +0 -1
- package/dist/outposts/session-heartbeat.test.js +0 -370
- package/dist/outposts/session-heartbeat.test.js.map +0 -1
- package/dist/outposts/session-host-auth.test.d.ts +0 -16
- package/dist/outposts/session-host-auth.test.d.ts.map +0 -1
- package/dist/outposts/session-host-auth.test.js +0 -284
- package/dist/outposts/session-host-auth.test.js.map +0 -1
- package/dist/outposts/session-host-command.test.d.ts +0 -10
- package/dist/outposts/session-host-command.test.d.ts.map +0 -1
- package/dist/outposts/session-host-command.test.js +0 -180
- package/dist/outposts/session-host-command.test.js.map +0 -1
- package/dist/outposts/session-host-connection.test.d.ts +0 -18
- package/dist/outposts/session-host-connection.test.d.ts.map +0 -1
- package/dist/outposts/session-host-connection.test.js +0 -392
- package/dist/outposts/session-host-connection.test.js.map +0 -1
- package/dist/outposts/session-host-fakes.d.ts +0 -61
- package/dist/outposts/session-host-fakes.d.ts.map +0 -1
- package/dist/outposts/session-host-fakes.js +0 -115
- package/dist/outposts/session-host-fakes.js.map +0 -1
- package/dist/outposts/session-host-ndjson.test.d.ts +0 -11
- package/dist/outposts/session-host-ndjson.test.d.ts.map +0 -1
- package/dist/outposts/session-host-ndjson.test.js +0 -73
- package/dist/outposts/session-host-ndjson.test.js.map +0 -1
- package/dist/outposts/session-host-protocol.test.d.ts +0 -15
- package/dist/outposts/session-host-protocol.test.d.ts.map +0 -1
- package/dist/outposts/session-host-protocol.test.js +0 -0
- package/dist/outposts/session-host-protocol.test.js.map +0 -1
- package/dist/outposts/session-host-server.test.d.ts +0 -10
- package/dist/outposts/session-host-server.test.d.ts.map +0 -1
- package/dist/outposts/session-host-server.test.js +0 -118
- package/dist/outposts/session-host-server.test.js.map +0 -1
- package/dist/outposts/session-host-sessions.test.d.ts +0 -18
- package/dist/outposts/session-host-sessions.test.d.ts.map +0 -1
- package/dist/outposts/session-host-sessions.test.js +0 -439
- package/dist/outposts/session-host-sessions.test.js.map +0 -1
- package/dist/outposts/terminal.test.d.ts +0 -2
- package/dist/outposts/terminal.test.d.ts.map +0 -1
- package/dist/outposts/terminal.test.js +0 -146
- package/dist/outposts/terminal.test.js.map +0 -1
- package/dist/outposts/transport.test.d.ts +0 -10
- package/dist/outposts/transport.test.d.ts.map +0 -1
- package/dist/outposts/transport.test.js +0 -205
- package/dist/outposts/transport.test.js.map +0 -1
- package/dist/packed-journal-phase2.test.d.ts +0 -2
- package/dist/packed-journal-phase2.test.d.ts.map +0 -1
- package/dist/packed-journal-phase2.test.js +0 -273
- package/dist/packed-journal-phase2.test.js.map +0 -1
- package/dist/path-ancestors.test.d.ts +0 -2
- package/dist/path-ancestors.test.d.ts.map +0 -1
- package/dist/path-ancestors.test.js +0 -21
- package/dist/path-ancestors.test.js.map +0 -1
- package/dist/personal-vault-exclusions.test.d.ts +0 -22
- package/dist/personal-vault-exclusions.test.d.ts.map +0 -1
- package/dist/personal-vault-exclusions.test.js +0 -294
- package/dist/personal-vault-exclusions.test.js.map +0 -1
- package/dist/personal-vault.test.d.ts +0 -18
- package/dist/personal-vault.test.d.ts.map +0 -1
- package/dist/personal-vault.test.js +0 -1099
- package/dist/personal-vault.test.js.map +0 -1
- package/dist/plan-limit-pause.test.d.ts +0 -2
- package/dist/plan-limit-pause.test.d.ts.map +0 -1
- package/dist/plan-limit-pause.test.js +0 -150
- package/dist/plan-limit-pause.test.js.map +0 -1
- package/dist/plan-limit.fixtures.d.ts +0 -34
- package/dist/plan-limit.fixtures.d.ts.map +0 -1
- package/dist/plan-limit.fixtures.js +0 -96
- package/dist/plan-limit.fixtures.js.map +0 -1
- package/dist/plan-limit.test.d.ts +0 -2
- package/dist/plan-limit.test.d.ts.map +0 -1
- package/dist/plan-limit.test.js +0 -132
- package/dist/plan-limit.test.js.map +0 -1
- package/dist/prefix-coalesce.test.d.ts +0 -2
- package/dist/prefix-coalesce.test.d.ts.map +0 -1
- package/dist/prefix-coalesce.test.js +0 -260
- package/dist/prefix-coalesce.test.js.map +0 -1
- package/dist/private-folder-transport.test.d.ts +0 -2
- package/dist/private-folder-transport.test.d.ts.map +0 -1
- package/dist/private-folder-transport.test.js +0 -33
- package/dist/private-folder-transport.test.js.map +0 -1
- package/dist/public-surface.test.d.ts +0 -15
- package/dist/public-surface.test.d.ts.map +0 -1
- package/dist/public-surface.test.js +0 -110
- package/dist/public-surface.test.js.map +0 -1
- package/dist/qmd-reindex.test.d.ts +0 -14
- package/dist/qmd-reindex.test.d.ts.map +0 -1
- package/dist/qmd-reindex.test.js +0 -899
- package/dist/qmd-reindex.test.js.map +0 -1
- package/dist/read-only-state-dir.test.d.ts +0 -27
- package/dist/read-only-state-dir.test.d.ts.map +0 -1
- package/dist/read-only-state-dir.test.js +0 -167
- package/dist/read-only-state-dir.test.js.map +0 -1
- package/dist/remote-pull.test.d.ts +0 -2
- package/dist/remote-pull.test.d.ts.map +0 -1
- package/dist/remote-pull.test.js +0 -1147
- package/dist/remote-pull.test.js.map +0 -1
- package/dist/s3-download-stream-hash.test.d.ts +0 -2
- package/dist/s3-download-stream-hash.test.d.ts.map +0 -1
- package/dist/s3-download-stream-hash.test.js +0 -70
- package/dist/s3-download-stream-hash.test.js.map +0 -1
- package/dist/s3.list-cost.test.d.ts +0 -2
- package/dist/s3.list-cost.test.d.ts.map +0 -1
- package/dist/s3.list-cost.test.js +0 -51
- package/dist/s3.list-cost.test.js.map +0 -1
- package/dist/s3.rename-retry.test.d.ts +0 -2
- package/dist/s3.rename-retry.test.d.ts.map +0 -1
- package/dist/s3.rename-retry.test.js +0 -217
- package/dist/s3.rename-retry.test.js.map +0 -1
- package/dist/s3.symlink-materialize.test.d.ts +0 -18
- package/dist/s3.symlink-materialize.test.d.ts.map +0 -1
- package/dist/s3.symlink-materialize.test.js +0 -579
- package/dist/s3.symlink-materialize.test.js.map +0 -1
- package/dist/s3.test.d.ts +0 -9
- package/dist/s3.test.d.ts.map +0 -1
- package/dist/s3.test.js +0 -2047
- package/dist/s3.test.js.map +0 -1
- package/dist/s3.windows-portability.test.d.ts +0 -12
- package/dist/s3.windows-portability.test.d.ts.map +0 -1
- package/dist/s3.windows-portability.test.js +0 -223
- package/dist/s3.windows-portability.test.js.map +0 -1
- package/dist/schemas/signal-types.test.d.ts +0 -2
- package/dist/schemas/signal-types.test.d.ts.map +0 -1
- package/dist/schemas/signal-types.test.js +0 -65
- package/dist/schemas/signal-types.test.js.map +0 -1
- package/dist/schemas/source-channels.test.d.ts +0 -2
- package/dist/schemas/source-channels.test.d.ts.map +0 -1
- package/dist/schemas/source-channels.test.js +0 -65
- package/dist/schemas/source-channels.test.js.map +0 -1
- package/dist/scope-shrink.test.d.ts +0 -13
- package/dist/scope-shrink.test.d.ts.map +0 -1
- package/dist/scope-shrink.test.js +0 -558
- package/dist/scope-shrink.test.js.map +0 -1
- package/dist/session-log-capture.test.d.ts +0 -2
- package/dist/session-log-capture.test.d.ts.map +0 -1
- package/dist/session-log-capture.test.js +0 -533
- package/dist/session-log-capture.test.js.map +0 -1
- package/dist/session-log-retention.test.d.ts +0 -2
- package/dist/session-log-retention.test.d.ts.map +0 -1
- package/dist/session-log-retention.test.js +0 -383
- package/dist/session-log-retention.test.js.map +0 -1
- package/dist/signals/get.test.d.ts +0 -5
- package/dist/signals/get.test.d.ts.map +0 -1
- package/dist/signals/get.test.js +0 -254
- package/dist/signals/get.test.js.map +0 -1
- package/dist/signals/list.test.d.ts +0 -9
- package/dist/signals/list.test.d.ts.map +0 -1
- package/dist/signals/list.test.js +0 -330
- package/dist/signals/list.test.js.map +0 -1
- package/dist/skill-telemetry.test.d.ts +0 -2
- package/dist/skill-telemetry.test.d.ts.map +0 -1
- package/dist/skill-telemetry.test.js +0 -1484
- package/dist/skill-telemetry.test.js.map +0 -1
- package/dist/sources/get.test.d.ts +0 -5
- package/dist/sources/get.test.d.ts.map +0 -1
- package/dist/sources/get.test.js +0 -236
- package/dist/sources/get.test.js.map +0 -1
- package/dist/sources/list.test.d.ts +0 -8
- package/dist/sources/list.test.d.ts.map +0 -1
- package/dist/sources/list.test.js +0 -318
- package/dist/sources/list.test.js.map +0 -1
- package/dist/sts-credential-refresh.test.d.ts +0 -2
- package/dist/sts-credential-refresh.test.d.ts.map +0 -1
- package/dist/sts-credential-refresh.test.js +0 -181
- package/dist/sts-credential-refresh.test.js.map +0 -1
- package/dist/swallowed-error-sites.test.d.ts +0 -2
- package/dist/swallowed-error-sites.test.d.ts.map +0 -1
- package/dist/swallowed-error-sites.test.js +0 -199
- package/dist/swallowed-error-sites.test.js.map +0 -1
- package/dist/swallowed-error-w32.test.d.ts +0 -2
- package/dist/swallowed-error-w32.test.d.ts.map +0 -1
- package/dist/swallowed-error-w32.test.js +0 -171
- package/dist/swallowed-error-w32.test.js.map +0 -1
- package/dist/swallowed-error-w33.test.d.ts +0 -2
- package/dist/swallowed-error-w33.test.d.ts.map +0 -1
- package/dist/swallowed-error-w33.test.js +0 -125
- package/dist/swallowed-error-w33.test.js.map +0 -1
- package/dist/swallowed-error-w34.test.d.ts +0 -2
- package/dist/swallowed-error-w34.test.d.ts.map +0 -1
- package/dist/swallowed-error-w34.test.js +0 -110
- package/dist/swallowed-error-w34.test.js.map +0 -1
- package/dist/swallowed-error-w35.test.d.ts +0 -2
- package/dist/swallowed-error-w35.test.d.ts.map +0 -1
- package/dist/swallowed-error-w35.test.js +0 -223
- package/dist/swallowed-error-w35.test.js.map +0 -1
- package/dist/sync/area-ledger-auto-migration.test.d.ts +0 -2
- package/dist/sync/area-ledger-auto-migration.test.d.ts.map +0 -1
- package/dist/sync/area-ledger-auto-migration.test.js +0 -291
- package/dist/sync/area-ledger-auto-migration.test.js.map +0 -1
- package/dist/sync/area-ledger-migration.test.d.ts +0 -2
- package/dist/sync/area-ledger-migration.test.d.ts.map +0 -1
- package/dist/sync/area-ledger-migration.test.js +0 -554
- package/dist/sync/area-ledger-migration.test.js.map +0 -1
- package/dist/sync/area-ledger-recovery.bench.test.d.ts +0 -2
- package/dist/sync/area-ledger-recovery.bench.test.d.ts.map +0 -1
- package/dist/sync/area-ledger-recovery.bench.test.js +0 -60
- package/dist/sync/area-ledger-recovery.bench.test.js.map +0 -1
- package/dist/sync/area-ledger-writer-lock.bench.test.d.ts +0 -2
- package/dist/sync/area-ledger-writer-lock.bench.test.d.ts.map +0 -1
- package/dist/sync/area-ledger-writer-lock.bench.test.js +0 -57
- package/dist/sync/area-ledger-writer-lock.bench.test.js.map +0 -1
- package/dist/sync/area-ledger.test.d.ts +0 -2
- package/dist/sync/area-ledger.test.d.ts.map +0 -1
- package/dist/sync/area-ledger.test.js +0 -1401
- package/dist/sync/area-ledger.test.js.map +0 -1
- package/dist/sync/area-resolver.test.d.ts +0 -2
- package/dist/sync/area-resolver.test.d.ts.map +0 -1
- package/dist/sync/area-resolver.test.js +0 -234
- package/dist/sync/area-resolver.test.js.map +0 -1
- package/dist/sync/base-version-resolver.test.d.ts +0 -2
- package/dist/sync/base-version-resolver.test.d.ts.map +0 -1
- package/dist/sync/base-version-resolver.test.js +0 -24
- package/dist/sync/base-version-resolver.test.js.map +0 -1
- package/dist/sync/candidate-uploader.test.d.ts +0 -2
- package/dist/sync/candidate-uploader.test.d.ts.map +0 -1
- package/dist/sync/candidate-uploader.test.js +0 -132
- package/dist/sync/candidate-uploader.test.js.map +0 -1
- package/dist/sync/child-process-sync-worker-env.test.d.ts +0 -2
- package/dist/sync/child-process-sync-worker-env.test.d.ts.map +0 -1
- package/dist/sync/child-process-sync-worker-env.test.js +0 -38
- package/dist/sync/child-process-sync-worker-env.test.js.map +0 -1
- package/dist/sync/child-process-sync-worker-errors.test.d.ts +0 -2
- package/dist/sync/child-process-sync-worker-errors.test.d.ts.map +0 -1
- package/dist/sync/child-process-sync-worker-errors.test.js +0 -121
- package/dist/sync/child-process-sync-worker-errors.test.js.map +0 -1
- package/dist/sync/child-process-sync-worker.test.d.ts +0 -2
- package/dist/sync/child-process-sync-worker.test.d.ts.map +0 -1
- package/dist/sync/child-process-sync-worker.test.js +0 -262
- package/dist/sync/child-process-sync-worker.test.js.map +0 -1
- package/dist/sync/content-hash.test.d.ts +0 -2
- package/dist/sync/content-hash.test.d.ts.map +0 -1
- package/dist/sync/content-hash.test.js +0 -16
- package/dist/sync/content-hash.test.js.map +0 -1
- package/dist/sync/delta-client.test.d.ts +0 -2
- package/dist/sync/delta-client.test.d.ts.map +0 -1
- package/dist/sync/delta-client.test.js +0 -97
- package/dist/sync/delta-client.test.js.map +0 -1
- package/dist/sync/durable-apply.test.d.ts +0 -2
- package/dist/sync/durable-apply.test.d.ts.map +0 -1
- package/dist/sync/durable-apply.test.js +0 -180
- package/dist/sync/durable-apply.test.js.map +0 -1
- package/dist/sync/event-sync.test.d.ts +0 -14
- package/dist/sync/event-sync.test.d.ts.map +0 -1
- package/dist/sync/event-sync.test.js +0 -1714
- package/dist/sync/event-sync.test.js.map +0 -1
- package/dist/sync/feature-flags.test.d.ts +0 -24
- package/dist/sync/feature-flags.test.d.ts.map +0 -1
- package/dist/sync/feature-flags.test.js +0 -594
- package/dist/sync/feature-flags.test.js.map +0 -1
- package/dist/sync/full-pass-cap-governor.bench.test.d.ts +0 -2
- package/dist/sync/full-pass-cap-governor.bench.test.d.ts.map +0 -1
- package/dist/sync/full-pass-cap-governor.bench.test.js +0 -179
- package/dist/sync/full-pass-cap-governor.bench.test.js.map +0 -1
- package/dist/sync/full-pass-caps.test.d.ts +0 -2
- package/dist/sync/full-pass-caps.test.d.ts.map +0 -1
- package/dist/sync/full-pass-caps.test.js +0 -550
- package/dist/sync/full-pass-caps.test.js.map +0 -1
- package/dist/sync/idle-full-reconcile.test.d.ts +0 -2
- package/dist/sync/idle-full-reconcile.test.d.ts.map +0 -1
- package/dist/sync/idle-full-reconcile.test.js +0 -62
- package/dist/sync/idle-full-reconcile.test.js.map +0 -1
- package/dist/sync/inbound-event-admission.test.d.ts +0 -2
- package/dist/sync/inbound-event-admission.test.d.ts.map +0 -1
- package/dist/sync/inbound-event-admission.test.js +0 -125
- package/dist/sync/inbound-event-admission.test.js.map +0 -1
- package/dist/sync/journal-ledger-stream.test.d.ts +0 -12
- package/dist/sync/journal-ledger-stream.test.d.ts.map +0 -1
- package/dist/sync/journal-ledger-stream.test.js +0 -134
- package/dist/sync/journal-ledger-stream.test.js.map +0 -1
- package/dist/sync/lease-client.test.d.ts +0 -2
- package/dist/sync/lease-client.test.d.ts.map +0 -1
- package/dist/sync/lease-client.test.js +0 -119
- package/dist/sync/lease-client.test.js.map +0 -1
- package/dist/sync/logger.test.d.ts +0 -19
- package/dist/sync/logger.test.d.ts.map +0 -1
- package/dist/sync/logger.test.js +0 -204
- package/dist/sync/logger.test.js.map +0 -1
- package/dist/sync/metrics.test.d.ts +0 -19
- package/dist/sync/metrics.test.d.ts.map +0 -1
- package/dist/sync/metrics.test.js +0 -357
- package/dist/sync/metrics.test.js.map +0 -1
- package/dist/sync/multipart-uploader.test.d.ts +0 -2
- package/dist/sync/multipart-uploader.test.d.ts.map +0 -1
- package/dist/sync/multipart-uploader.test.js +0 -135
- package/dist/sync/multipart-uploader.test.js.map +0 -1
- package/dist/sync/mutation-client.test.d.ts +0 -2
- package/dist/sync/mutation-client.test.d.ts.map +0 -1
- package/dist/sync/mutation-client.test.js +0 -100
- package/dist/sync/mutation-client.test.js.map +0 -1
- package/dist/sync/mutation-session.test.d.ts +0 -2
- package/dist/sync/mutation-session.test.d.ts.map +0 -1
- package/dist/sync/mutation-session.test.js +0 -190
- package/dist/sync/mutation-session.test.js.map +0 -1
- package/dist/sync/parallel-sync-coordinator.test.d.ts +0 -2
- package/dist/sync/parallel-sync-coordinator.test.d.ts.map +0 -1
- package/dist/sync/parallel-sync-coordinator.test.js +0 -564
- package/dist/sync/parallel-sync-coordinator.test.js.map +0 -1
- package/dist/sync/path-leases.test.d.ts +0 -2
- package/dist/sync/path-leases.test.d.ts.map +0 -1
- package/dist/sync/path-leases.test.js +0 -51
- package/dist/sync/path-leases.test.js.map +0 -1
- package/dist/sync/per-area-maintenance.test.d.ts +0 -2
- package/dist/sync/per-area-maintenance.test.d.ts.map +0 -1
- package/dist/sync/per-area-maintenance.test.js +0 -100
- package/dist/sync/per-area-maintenance.test.js.map +0 -1
- package/dist/sync/push-event.test.d.ts +0 -15
- package/dist/sync/push-event.test.d.ts.map +0 -1
- package/dist/sync/push-event.test.js +0 -282
- package/dist/sync/push-event.test.js.map +0 -1
- package/dist/sync/push-receiver.test.d.ts +0 -25
- package/dist/sync/push-receiver.test.d.ts.map +0 -1
- package/dist/sync/push-receiver.test.js +0 -2851
- package/dist/sync/push-receiver.test.js.map +0 -1
- package/dist/sync/push-transport.test.d.ts +0 -2
- package/dist/sync/push-transport.test.d.ts.map +0 -1
- package/dist/sync/push-transport.test.js +0 -70
- package/dist/sync/push-transport.test.js.map +0 -1
- package/dist/sync/push-walk-matchers.bench.test.d.ts +0 -2
- package/dist/sync/push-walk-matchers.bench.test.d.ts.map +0 -1
- package/dist/sync/push-walk-matchers.bench.test.js +0 -230
- package/dist/sync/push-walk-matchers.bench.test.js.map +0 -1
- package/dist/sync/realtime-network-integration.test.d.ts +0 -2
- package/dist/sync/realtime-network-integration.test.d.ts.map +0 -1
- package/dist/sync/realtime-network-integration.test.js +0 -83
- package/dist/sync/realtime-network-integration.test.js.map +0 -1
- package/dist/sync/realtime-rollout.test.d.ts +0 -2
- package/dist/sync/realtime-rollout.test.d.ts.map +0 -1
- package/dist/sync/realtime-rollout.test.js +0 -94
- package/dist/sync/realtime-rollout.test.js.map +0 -1
- package/dist/sync/realtime-telemetry.test.d.ts +0 -2
- package/dist/sync/realtime-telemetry.test.d.ts.map +0 -1
- package/dist/sync/realtime-telemetry.test.js +0 -300
- package/dist/sync/realtime-telemetry.test.js.map +0 -1
- package/dist/sync/receiver-origin-config.test.d.ts +0 -2
- package/dist/sync/receiver-origin-config.test.d.ts.map +0 -1
- package/dist/sync/receiver-origin-config.test.js +0 -39
- package/dist/sync/receiver-origin-config.test.js.map +0 -1
- package/dist/sync/reconcile-cursor.test.d.ts +0 -2
- package/dist/sync/reconcile-cursor.test.d.ts.map +0 -1
- package/dist/sync/reconcile-cursor.test.js +0 -139
- package/dist/sync/reconcile-cursor.test.js.map +0 -1
- package/dist/sync/remote-file-spool.test.d.ts +0 -2
- package/dist/sync/remote-file-spool.test.d.ts.map +0 -1
- package/dist/sync/remote-file-spool.test.js +0 -77
- package/dist/sync/remote-file-spool.test.js.map +0 -1
- package/dist/sync/scope-inventory-client.test.d.ts +0 -2
- package/dist/sync/scope-inventory-client.test.d.ts.map +0 -1
- package/dist/sync/scope-inventory-client.test.js +0 -103
- package/dist/sync/scope-inventory-client.test.js.map +0 -1
- package/dist/sync/secret-path-policy.test.d.ts +0 -2
- package/dist/sync/secret-path-policy.test.d.ts.map +0 -1
- package/dist/sync/secret-path-policy.test.js +0 -183
- package/dist/sync/secret-path-policy.test.js.map +0 -1
- package/dist/sync/snapshot-client.test.d.ts +0 -2
- package/dist/sync/snapshot-client.test.d.ts.map +0 -1
- package/dist/sync/snapshot-client.test.js +0 -169
- package/dist/sync/snapshot-client.test.js.map +0 -1
- package/dist/sync/state-store.test.d.ts +0 -2
- package/dist/sync/state-store.test.d.ts.map +0 -1
- package/dist/sync/state-store.test.js +0 -1556
- package/dist/sync/state-store.test.js.map +0 -1
- package/dist/sync/sync-contract.test.d.ts +0 -2
- package/dist/sync/sync-contract.test.d.ts.map +0 -1
- package/dist/sync/sync-contract.test.js +0 -141
- package/dist/sync/sync-contract.test.js.map +0 -1
- package/dist/sync/targeted-pull-prefixes.test.d.ts +0 -2
- package/dist/sync/targeted-pull-prefixes.test.d.ts.map +0 -1
- package/dist/sync/targeted-pull-prefixes.test.js +0 -33
- package/dist/sync/targeted-pull-prefixes.test.js.map +0 -1
- package/dist/sync/targeted-upsert-exact-list.test.d.ts +0 -2
- package/dist/sync/targeted-upsert-exact-list.test.d.ts.map +0 -1
- package/dist/sync/targeted-upsert-exact-list.test.js +0 -247
- package/dist/sync/targeted-upsert-exact-list.test.js.map +0 -1
- package/dist/sync/unchanged-push-events.test.d.ts +0 -2
- package/dist/sync/unchanged-push-events.test.d.ts.map +0 -1
- package/dist/sync/unchanged-push-events.test.js +0 -76
- package/dist/sync/unchanged-push-events.test.js.map +0 -1
- package/dist/sync/uploader-finalization.test.d.ts +0 -2
- package/dist/sync/uploader-finalization.test.d.ts.map +0 -1
- package/dist/sync/uploader-finalization.test.js +0 -107
- package/dist/sync/uploader-finalization.test.js.map +0 -1
- package/dist/sync-core.test.d.ts +0 -2
- package/dist/sync-core.test.d.ts.map +0 -1
- package/dist/sync-core.test.js +0 -189
- package/dist/sync-core.test.js.map +0 -1
- package/dist/sync-progress.test.d.ts +0 -2
- package/dist/sync-progress.test.d.ts.map +0 -1
- package/dist/sync-progress.test.js +0 -277
- package/dist/sync-progress.test.js.map +0 -1
- package/dist/telemetry-events.test.d.ts +0 -2
- package/dist/telemetry-events.test.d.ts.map +0 -1
- package/dist/telemetry-events.test.js +0 -113
- package/dist/telemetry-events.test.js.map +0 -1
- package/dist/telemetry.test.d.ts +0 -11
- package/dist/telemetry.test.d.ts.map +0 -1
- package/dist/telemetry.test.js +0 -1603
- package/dist/telemetry.test.js.map +0 -1
- package/dist/vault-client.test.d.ts +0 -7
- package/dist/vault-client.test.d.ts.map +0 -1
- package/dist/vault-client.test.js +0 -1334
- package/dist/vault-client.test.js.map +0 -1
- package/dist/watch-coverage.test.d.ts +0 -2
- package/dist/watch-coverage.test.d.ts.map +0 -1
- package/dist/watch-coverage.test.js +0 -56
- package/dist/watch-coverage.test.js.map +0 -1
- package/dist/watch-owner-lease.test.d.ts +0 -2
- package/dist/watch-owner-lease.test.d.ts.map +0 -1
- package/dist/watch-owner-lease.test.js +0 -353
- package/dist/watch-owner-lease.test.js.map +0 -1
- package/dist/watch-parent.test.d.ts +0 -2
- package/dist/watch-parent.test.d.ts.map +0 -1
- package/dist/watch-parent.test.js +0 -131
- package/dist/watch-parent.test.js.map +0 -1
- package/dist/watch-roots.test.d.ts +0 -18
- package/dist/watch-roots.test.d.ts.map +0 -1
- package/dist/watch-roots.test.js +0 -395
- package/dist/watch-roots.test.js.map +0 -1
- package/dist/watcher-continuity-hot-path.test.d.ts +0 -2
- package/dist/watcher-continuity-hot-path.test.d.ts.map +0 -1
- package/dist/watcher-continuity-hot-path.test.js +0 -40
- package/dist/watcher-continuity-hot-path.test.js.map +0 -1
- package/dist/watcher-event-gate.test.d.ts +0 -16
- package/dist/watcher-event-gate.test.d.ts.map +0 -1
- package/dist/watcher-event-gate.test.js +0 -191
- package/dist/watcher-event-gate.test.js.map +0 -1
- package/dist/watcher-replan-sweep.test.d.ts +0 -2
- package/dist/watcher-replan-sweep.test.d.ts.map +0 -1
- package/dist/watcher-replan-sweep.test.js +0 -245
- package/dist/watcher-replan-sweep.test.js.map +0 -1
- package/dist/watcher-stdout.test.d.ts +0 -2
- package/dist/watcher-stdout.test.d.ts.map +0 -1
- package/dist/watcher-stdout.test.js +0 -86
- package/dist/watcher-stdout.test.js.map +0 -1
- package/dist/watcher.test.d.ts +0 -2
- package/dist/watcher.test.d.ts.map +0 -1
- package/dist/watcher.test.js +0 -4301
- package/dist/watcher.test.js.map +0 -1
- package/dist/watcher.warmup-cancel.test.d.ts +0 -2
- package/dist/watcher.warmup-cancel.test.d.ts.map +0 -1
- package/dist/watcher.warmup-cancel.test.js +0 -59
- package/dist/watcher.warmup-cancel.test.js.map +0 -1
- package/dist/watcher.warmup-responsive.test.d.ts +0 -2
- package/dist/watcher.warmup-responsive.test.d.ts.map +0 -1
- package/dist/watcher.warmup-responsive.test.js +0 -62
- package/dist/watcher.warmup-responsive.test.js.map +0 -1
|
@@ -1,1217 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Unit tests for the client manifest generator (US-002).
|
|
3
|
-
*
|
|
4
|
-
* The suite is organised around the four things that make a manifest useful:
|
|
5
|
-
* the ledger/disk UNION (and its three `source` verdicts), the COST controls
|
|
6
|
-
* (stat-only walk, abort, wall clock), the DELTA machinery, and the wire
|
|
7
|
-
* safety rails (contract validity, NFC paths, tenant isolation).
|
|
8
|
-
*/
|
|
9
|
-
import * as fs from "node:fs";
|
|
10
|
-
import * as os from "node:os";
|
|
11
|
-
import * as path from "node:path";
|
|
12
|
-
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
13
|
-
/**
|
|
14
|
-
* `node:fs` is an ESM namespace, so `vi.spyOn(fs, …)` cannot redefine it. The
|
|
15
|
-
* stat/hash race needs exactly one observable behaviour change — the SECOND
|
|
16
|
-
* `lstatSync` of a given path reporting a moved mtime — so the module is
|
|
17
|
-
* mocked once, delegating to the real implementation unless a test installs a
|
|
18
|
-
* hook. Every other test in this file therefore still runs against real files.
|
|
19
|
-
*/
|
|
20
|
-
const fsHooks = vi.hoisted(() => ({
|
|
21
|
-
onLstat: null,
|
|
22
|
-
/**
|
|
23
|
-
* Same seam, for `readdirSync`. It exists for exactly one thing the
|
|
24
|
-
* filesystem will not do for us: hand the walk TWO directory entries whose
|
|
25
|
-
* names differ on disk but collapse onto one NFC key. macOS is
|
|
26
|
-
* normalization-insensitive, so the NFC and NFD spellings of `café.md`
|
|
27
|
-
* cannot both exist as real files there — the listing has to be forged.
|
|
28
|
-
*/
|
|
29
|
-
onReaddir: null,
|
|
30
|
-
}));
|
|
31
|
-
vi.mock("node:fs", async (importOriginal) => {
|
|
32
|
-
const actual = await importOriginal();
|
|
33
|
-
return {
|
|
34
|
-
...actual,
|
|
35
|
-
default: actual,
|
|
36
|
-
lstatSync: ((target, options) => {
|
|
37
|
-
const stats = actual.lstatSync(target, options);
|
|
38
|
-
return fsHooks.onLstat ? fsHooks.onLstat(String(target), stats) : stats;
|
|
39
|
-
}),
|
|
40
|
-
readdirSync: ((target, options) => {
|
|
41
|
-
const result = actual.readdirSync(target, options);
|
|
42
|
-
if (!fsHooks.onReaddir || !Array.isArray(result))
|
|
43
|
-
return result;
|
|
44
|
-
return fsHooks.onReaddir(String(target), result);
|
|
45
|
-
}),
|
|
46
|
-
};
|
|
47
|
-
});
|
|
48
|
-
import { PERSONAL_VAULT_JOURNAL_SLUG, hashFile, hashSymlinkTarget, openJournalStoreSession, } from "../journal.js";
|
|
49
|
-
import { buildSyncManifest, chunkManifest, classifyManifestUploadResponse, planManifestChunks, } from "./build-manifest.js";
|
|
50
|
-
import { SYNC_MANIFEST_DEFAULT_CHUNK_BYTE_BUDGET, SYNC_MANIFEST_MAX_CHUNK_BYTES, SYNC_MANIFEST_MAX_CHUNK_COUNT, SYNC_MANIFEST_MAX_ENTRIES_PER_CHUNK, validateSyncManifestUpload, } from "./contract.js";
|
|
51
|
-
import { manifestSnapshotPath, readManifestSnapshot, writeManifestSnapshot, } from "./snapshot-store.js";
|
|
52
|
-
const NOW_MS = Date.parse("2026-09-04T12:00:00.000Z");
|
|
53
|
-
let hqRoot;
|
|
54
|
-
let stateDir;
|
|
55
|
-
beforeEach(() => {
|
|
56
|
-
const base = fs.mkdtempSync(path.join(os.tmpdir(), "hq-manifest-"));
|
|
57
|
-
hqRoot = path.join(base, "hq");
|
|
58
|
-
stateDir = path.join(base, "state");
|
|
59
|
-
fs.mkdirSync(hqRoot, { recursive: true });
|
|
60
|
-
fs.mkdirSync(stateDir, { recursive: true });
|
|
61
|
-
});
|
|
62
|
-
afterEach(() => {
|
|
63
|
-
fsHooks.onLstat = null;
|
|
64
|
-
fsHooks.onReaddir = null;
|
|
65
|
-
vi.restoreAllMocks();
|
|
66
|
-
});
|
|
67
|
-
function writeFileAt(relPath, contents) {
|
|
68
|
-
const abs = path.join(hqRoot, relPath);
|
|
69
|
-
fs.mkdirSync(path.dirname(abs), { recursive: true });
|
|
70
|
-
fs.writeFileSync(abs, contents, "utf8");
|
|
71
|
-
return abs;
|
|
72
|
-
}
|
|
73
|
-
function journalEntryFor(absPath, overrides = {}) {
|
|
74
|
-
const stats = fs.lstatSync(absPath);
|
|
75
|
-
return {
|
|
76
|
-
hash: stats.isSymbolicLink() ? hashSymlinkTarget(fs.readlinkSync(absPath)) : hashFile(absPath),
|
|
77
|
-
size: stats.size,
|
|
78
|
-
syncedAt: "2026-09-01T00:00:00.000Z",
|
|
79
|
-
direction: "up",
|
|
80
|
-
mtimeMs: Math.floor(stats.mtimeMs),
|
|
81
|
-
...overrides,
|
|
82
|
-
};
|
|
83
|
-
}
|
|
84
|
-
function journalWith(files) {
|
|
85
|
-
return { version: "2", lastSync: "2026-09-01T00:00:00.000Z", files, pulls: [] };
|
|
86
|
-
}
|
|
87
|
-
function summaries(slug, files) {
|
|
88
|
-
return [{ slug, path: `/state/sync-journal.${slug}.json`, journal: journalWith(files) }];
|
|
89
|
-
}
|
|
90
|
-
function baseOptions(overrides = {}) {
|
|
91
|
-
return {
|
|
92
|
-
scope: { kind: "personal" },
|
|
93
|
-
hqRoot,
|
|
94
|
-
stateDir,
|
|
95
|
-
installationId: "install-abc123",
|
|
96
|
-
machineId: "machine-abc123",
|
|
97
|
-
source: "cli",
|
|
98
|
-
sequence: 7,
|
|
99
|
-
now: NOW_MS,
|
|
100
|
-
listJournalsImpl: () => [],
|
|
101
|
-
// These suites predate the stat-only default and assert on computed
|
|
102
|
-
// hashes throughout. `full` preserves exactly what they were written to
|
|
103
|
-
// exercise; the stat-only DEFAULT gets its own dedicated coverage below.
|
|
104
|
-
hashPolicy: "full",
|
|
105
|
-
...overrides,
|
|
106
|
-
};
|
|
107
|
-
}
|
|
108
|
-
describe("buildSyncManifest — ledger/disk union", () => {
|
|
109
|
-
it("reports a file the journal never tracked as disk-only", async () => {
|
|
110
|
-
writeFileAt("notes/untracked.md", "hello");
|
|
111
|
-
const result = await buildSyncManifest(baseOptions());
|
|
112
|
-
const entry = result.chunks[0].entries.find((candidate) => candidate.path === "notes/untracked.md");
|
|
113
|
-
expect(entry?.source).toBe("disk");
|
|
114
|
-
expect(entry?.hash).toBe(hashFile(path.join(hqRoot, "notes/untracked.md")));
|
|
115
|
-
expect(result.walkStats.hashedFiles).toBe(1);
|
|
116
|
-
});
|
|
117
|
-
it("reports an agreeing ledger row as `both` and never opens the file", async () => {
|
|
118
|
-
const abs = writeFileAt("notes/tracked.md", "hello");
|
|
119
|
-
const ledgerEntry = journalEntryFor(abs, { remoteEtag: "etag-1" });
|
|
120
|
-
const result = await buildSyncManifest(baseOptions({
|
|
121
|
-
listJournalsImpl: () => summaries("__hq_personal_vault__", { "notes/tracked.md": ledgerEntry }),
|
|
122
|
-
}));
|
|
123
|
-
const entry = result.chunks[0].entries.find((candidate) => candidate.path === "notes/tracked.md");
|
|
124
|
-
expect(entry?.source).toBe("both");
|
|
125
|
-
expect(entry?.ledgerRemoteEtag).toBe("etag-1");
|
|
126
|
-
expect(entry?.ledgerDirection).toBe("up");
|
|
127
|
-
expect(entry?.pendingPush).toBeUndefined();
|
|
128
|
-
// The whole point of the stat pair: an agreeing ledger row costs zero reads.
|
|
129
|
-
expect(result.walkStats.hashedFiles).toBe(0);
|
|
130
|
-
});
|
|
131
|
-
it("re-hashes and flags pendingPush when the stat pair disagrees", async () => {
|
|
132
|
-
const abs = writeFileAt("notes/edited.md", "new contents");
|
|
133
|
-
const stale = journalEntryFor(abs, { hash: "0".repeat(64), size: 1 });
|
|
134
|
-
const result = await buildSyncManifest(baseOptions({
|
|
135
|
-
listJournalsImpl: () => summaries("__hq_personal_vault__", { "notes/edited.md": stale }),
|
|
136
|
-
}));
|
|
137
|
-
const entry = result.chunks[0].entries.find((candidate) => candidate.path === "notes/edited.md");
|
|
138
|
-
expect(entry?.source).toBe("both");
|
|
139
|
-
expect(entry?.hash).toBe(hashFile(abs));
|
|
140
|
-
expect(entry?.pendingPush).toBe(true);
|
|
141
|
-
expect(result.walkStats.hashedFiles).toBe(1);
|
|
142
|
-
});
|
|
143
|
-
it("still emits a ledger row whose local file has vanished", async () => {
|
|
144
|
-
writeFileAt("notes/present.md", "here");
|
|
145
|
-
const gone = {
|
|
146
|
-
hash: "a".repeat(64),
|
|
147
|
-
size: 12,
|
|
148
|
-
syncedAt: "2026-09-01T00:00:00.000Z",
|
|
149
|
-
direction: "down",
|
|
150
|
-
mtimeMs: NOW_MS - 5_000,
|
|
151
|
-
};
|
|
152
|
-
const result = await buildSyncManifest(baseOptions({
|
|
153
|
-
listJournalsImpl: () => summaries("__hq_personal_vault__", { "notes/vanished.md": gone }),
|
|
154
|
-
}));
|
|
155
|
-
const entry = result.chunks[0].entries.find((candidate) => candidate.path === "notes/vanished.md");
|
|
156
|
-
expect(entry?.source).toBe("ledger");
|
|
157
|
-
expect(entry?.hash).toBe("a".repeat(64));
|
|
158
|
-
expect(entry?.ledgerDirection).toBe("down");
|
|
159
|
-
});
|
|
160
|
-
it("does not resurrect tombstoned ledger rows as vanished files", async () => {
|
|
161
|
-
const tombstoned = {
|
|
162
|
-
hash: "b".repeat(64),
|
|
163
|
-
size: 3,
|
|
164
|
-
syncedAt: "2026-09-01T00:00:00.000Z",
|
|
165
|
-
direction: "down",
|
|
166
|
-
removedAt: "2026-09-02T00:00:00.000Z",
|
|
167
|
-
removedReason: "scope_shrink",
|
|
168
|
-
};
|
|
169
|
-
const result = await buildSyncManifest(baseOptions({
|
|
170
|
-
listJournalsImpl: () => summaries("__hq_personal_vault__", { "notes/pruned.md": tombstoned }),
|
|
171
|
-
}));
|
|
172
|
-
expect(result.chunks[0].entries).toHaveLength(0);
|
|
173
|
-
});
|
|
174
|
-
it("emits a disk-only manifest with ledgerUnavailable instead of throwing", async () => {
|
|
175
|
-
writeFileAt("notes/a.md", "a");
|
|
176
|
-
const abs = writeFileAt("notes/b.md", "b");
|
|
177
|
-
const ledgerEntry = journalEntryFor(abs);
|
|
178
|
-
const result = await buildSyncManifest(baseOptions({
|
|
179
|
-
listJournalsImpl: () => {
|
|
180
|
-
throw new TypeError("journal state store is locked");
|
|
181
|
-
},
|
|
182
|
-
}));
|
|
183
|
-
expect(result.chunks[0].ledgerUnavailable).toEqual({ errorClass: "TypeError" });
|
|
184
|
-
expect(result.chunks[0].entries.every((entry) => entry.source === "disk")).toBe(true);
|
|
185
|
-
expect(ledgerEntry.hash).toBeTruthy();
|
|
186
|
-
});
|
|
187
|
-
it("treats a missing shard as an empty ledger, not an unavailable one", async () => {
|
|
188
|
-
writeFileAt("notes/a.md", "a");
|
|
189
|
-
const result = await buildSyncManifest(baseOptions({ listJournalsImpl: () => summaries("other-co", {}) }));
|
|
190
|
-
expect(result.chunks[0].ledgerUnavailable).toBeUndefined();
|
|
191
|
-
expect(result.chunks[0].entries[0].source).toBe("disk");
|
|
192
|
-
});
|
|
193
|
-
it("hashes a symlink by its target, not by following it", async () => {
|
|
194
|
-
writeFileAt("notes/real.md", "real");
|
|
195
|
-
const link = path.join(hqRoot, "notes", "link.md");
|
|
196
|
-
fs.symlinkSync("real.md", link);
|
|
197
|
-
const result = await buildSyncManifest(baseOptions());
|
|
198
|
-
const entry = result.chunks[0].entries.find((candidate) => candidate.path === "notes/link.md");
|
|
199
|
-
expect(entry?.hash).toBe(hashSymlinkTarget("real.md"));
|
|
200
|
-
});
|
|
201
|
-
it("marks a symlink entry `symlink: true`, and a regular file not at all", async () => {
|
|
202
|
-
// The reconciler cannot compare a symlink against its vault object: the
|
|
203
|
-
// local `size` is the length of the TARGET STRING, the vault object is the
|
|
204
|
-
// resolved file. Without this flag it read 1,480 rows of the first real
|
|
205
|
-
// audit run as `hash_drift` (`.codex/claude`, `.agents/skills`, `AGENTS.md`).
|
|
206
|
-
writeFileAt("notes/real.md", "real");
|
|
207
|
-
fs.symlinkSync("real.md", path.join(hqRoot, "notes", "link.md"));
|
|
208
|
-
const result = await buildSyncManifest(baseOptions());
|
|
209
|
-
const byPath = new Map(result.chunks[0].entries.map((e) => [e.path, e]));
|
|
210
|
-
expect(byPath.get("notes/link.md")?.symlink).toBe(true);
|
|
211
|
-
// Absent, not `false`: a regular file's entry stays byte-identical to what
|
|
212
|
-
// an older client shipped.
|
|
213
|
-
expect(byPath.get("notes/real.md")).not.toHaveProperty("symlink");
|
|
214
|
-
// And the flag must survive the shared wire contract.
|
|
215
|
-
for (const chunk of result.chunks) {
|
|
216
|
-
expect(() => validateSyncManifestUpload(chunk)).not.toThrow();
|
|
217
|
-
}
|
|
218
|
-
});
|
|
219
|
-
});
|
|
220
|
-
describe("buildSyncManifest — wire safety", () => {
|
|
221
|
-
it("produces chunks the shared contract validator accepts, extra walkStats field and all", async () => {
|
|
222
|
-
writeFileAt("notes/a.md", "a");
|
|
223
|
-
const result = await buildSyncManifest(baseOptions());
|
|
224
|
-
expect(result.walkStats.truncated).toBe(false);
|
|
225
|
-
for (const chunk of result.chunks) {
|
|
226
|
-
expect(() => validateSyncManifestUpload(chunk)).not.toThrow();
|
|
227
|
-
}
|
|
228
|
-
expect(result.chunks[0].chunkIndex).toBe(0);
|
|
229
|
-
expect(result.chunks[0].chunkCount).toBe(1);
|
|
230
|
-
});
|
|
231
|
-
it("always emits at least one chunk for an empty scope", async () => {
|
|
232
|
-
const result = await buildSyncManifest(baseOptions());
|
|
233
|
-
expect(result.chunks).toHaveLength(1);
|
|
234
|
-
expect(result.chunks[0].entries).toEqual([]);
|
|
235
|
-
expect(() => validateSyncManifestUpload(result.chunks[0])).not.toThrow();
|
|
236
|
-
});
|
|
237
|
-
it("normalizes NFD filenames to NFC on the wire", async () => {
|
|
238
|
-
const nfd = "notes/café.md";
|
|
239
|
-
writeFileAt(nfd, "coffee");
|
|
240
|
-
const result = await buildSyncManifest(baseOptions());
|
|
241
|
-
const paths = result.chunks[0].entries.map((entry) => entry.path);
|
|
242
|
-
expect(paths).toContain("notes/café.md".normalize("NFC"));
|
|
243
|
-
expect(paths.every((candidate) => candidate.normalize("NFC") === candidate)).toBe(true);
|
|
244
|
-
});
|
|
245
|
-
it("skips and counts a path the contract cannot represent", async () => {
|
|
246
|
-
writeFileAt("notes/ok.md", "ok");
|
|
247
|
-
// A right-to-left override renders `evil.fdp.md` as `evil.md.pdf`; the
|
|
248
|
-
// contract rejects it outright, and so must the walk.
|
|
249
|
-
writeFileAt("notes/evil\u202Edm.dp.md", "spoofed");
|
|
250
|
-
const result = await buildSyncManifest(baseOptions());
|
|
251
|
-
expect(result.unrepresentablePaths).toBe(1);
|
|
252
|
-
expect(result.chunks[0].entries.map((entry) => entry.path)).toEqual(["notes/ok.md"]);
|
|
253
|
-
});
|
|
254
|
-
it("keeps a personal manifest free of any company identifier", async () => {
|
|
255
|
-
writeFileAt("notes/a.md", "a");
|
|
256
|
-
const result = await buildSyncManifest(
|
|
257
|
-
// A caller that wrongly stuffs a slug into a personal scope must not have
|
|
258
|
-
// it leak onto the wire.
|
|
259
|
-
baseOptions({ scope: { kind: "personal", slug: "acme" } }));
|
|
260
|
-
expect(result.chunks[0].scope).toEqual({ kind: "personal" });
|
|
261
|
-
expect(JSON.stringify(result.chunks[0].scope)).not.toContain("acme");
|
|
262
|
-
});
|
|
263
|
-
it("rejects a personal scope carrying a companyUid", async () => {
|
|
264
|
-
await expect(buildSyncManifest(baseOptions({ scope: { kind: "personal", companyUid: "co-1" } }))).rejects.toThrow(/must not carry a companyUid/);
|
|
265
|
-
});
|
|
266
|
-
it("walks only the company root and reports the uid, never the slug", async () => {
|
|
267
|
-
writeFileAt("companies/acme/deal.md", "acme");
|
|
268
|
-
writeFileAt("companies/other/secret.md", "other");
|
|
269
|
-
const result = await buildSyncManifest(baseOptions({ scope: { kind: "company", companyUid: "co-acme", slug: "acme" } }));
|
|
270
|
-
expect(result.chunks[0].scope).toEqual({
|
|
271
|
-
kind: "company",
|
|
272
|
-
companyUid: "co-acme",
|
|
273
|
-
companySlug: "acme",
|
|
274
|
-
scopeRoot: "companies/acme",
|
|
275
|
-
});
|
|
276
|
-
expect(result.chunks[0].entries.map((entry) => entry.path)).toEqual(["deal.md"]);
|
|
277
|
-
});
|
|
278
|
-
});
|
|
279
|
-
describe("buildSyncManifest — ignore rules", () => {
|
|
280
|
-
it("applies the layered ignore filter and reports the rules it used", async () => {
|
|
281
|
-
writeFileAt("notes/keep.md", "keep");
|
|
282
|
-
writeFileAt("node_modules/pkg/index.js", "junk");
|
|
283
|
-
writeFileAt(".env", "SECRET=1");
|
|
284
|
-
fs.writeFileSync(path.join(hqRoot, ".hqignore"), "scratch/\n# a comment\n", "utf8");
|
|
285
|
-
writeFileAt("scratch/draft.md", "draft");
|
|
286
|
-
const result = await buildSyncManifest(baseOptions());
|
|
287
|
-
const paths = result.chunks[0].entries.map((entry) => entry.path);
|
|
288
|
-
expect(paths).toContain("notes/keep.md");
|
|
289
|
-
expect(paths).not.toContain("node_modules/pkg/index.js");
|
|
290
|
-
expect(paths).not.toContain(".env");
|
|
291
|
-
expect(paths).not.toContain("scratch/draft.md");
|
|
292
|
-
expect(result.chunks[0].ignoreRules).toContain("node_modules/");
|
|
293
|
-
expect(result.chunks[0].ignoreRules).toContain("scratch/");
|
|
294
|
-
expect(result.chunks[0].ignoreRules).not.toContain("# a comment");
|
|
295
|
-
expect(result.walkStats.ignoredFiles).toBeGreaterThan(0);
|
|
296
|
-
});
|
|
297
|
-
it("skips the push-only sessions prefix exactly as the pull scope does", async () => {
|
|
298
|
-
writeFileAt("companies/acme/deal.md", "acme");
|
|
299
|
-
writeFileAt("companies/acme/sessions/p-1/log.jsonl", "{}");
|
|
300
|
-
const result = await buildSyncManifest(baseOptions({ scope: { kind: "company", companyUid: "co-acme", slug: "acme" } }));
|
|
301
|
-
expect(result.chunks[0].entries.map((entry) => entry.path)).toEqual(["deal.md"]);
|
|
302
|
-
expect(result.chunks[0].ignoreRules).toContain("sessions/");
|
|
303
|
-
});
|
|
304
|
-
});
|
|
305
|
-
describe("buildSyncManifest — cost controls", () => {
|
|
306
|
-
it("marks the manifest truncated and skips the snapshot when aborted", async () => {
|
|
307
|
-
for (let index = 0; index < 20; index++)
|
|
308
|
-
writeFileAt(`notes/f${index}.md`, `${index}`);
|
|
309
|
-
const controller = new AbortController();
|
|
310
|
-
controller.abort();
|
|
311
|
-
const result = await buildSyncManifest(baseOptions({ abortSignal: controller.signal }));
|
|
312
|
-
expect(result.walkStats.truncated).toBe(true);
|
|
313
|
-
expect(fs.existsSync(manifestSnapshotPath(stateDir, "personal"))).toBe(false);
|
|
314
|
-
expect(() => validateSyncManifestUpload(result.chunks[0])).not.toThrow();
|
|
315
|
-
});
|
|
316
|
-
it("stops at the wall-clock cap rather than finishing the tree", async () => {
|
|
317
|
-
for (let index = 0; index < 40; index++)
|
|
318
|
-
writeFileAt(`notes/f${index}.md`, `${index}`);
|
|
319
|
-
const result = await buildSyncManifest(baseOptions({ maxWallMs: -1 }));
|
|
320
|
-
expect(result.walkStats.truncated).toBe(true);
|
|
321
|
-
expect(result.walkStats.filesWalked).toBe(0);
|
|
322
|
-
});
|
|
323
|
-
it("yields to the event loop while walking", async () => {
|
|
324
|
-
for (let index = 0; index < 12; index++)
|
|
325
|
-
writeFileAt(`notes/f${index}.md`, `${index}`);
|
|
326
|
-
let ticks = 0;
|
|
327
|
-
const timer = setInterval(() => {
|
|
328
|
-
ticks += 1;
|
|
329
|
-
}, 1);
|
|
330
|
-
try {
|
|
331
|
-
await buildSyncManifest(baseOptions({ yieldEvery: 1 }));
|
|
332
|
-
}
|
|
333
|
-
finally {
|
|
334
|
-
clearInterval(timer);
|
|
335
|
-
}
|
|
336
|
-
expect(ticks).toBeGreaterThan(0);
|
|
337
|
-
});
|
|
338
|
-
it("drops an entry whose file changed while it was being hashed", async () => {
|
|
339
|
-
const abs = writeFileAt("notes/racing.md", "before");
|
|
340
|
-
let seen = 0;
|
|
341
|
-
fsHooks.onLstat = (target, stats) => {
|
|
342
|
-
if (target !== abs)
|
|
343
|
-
return stats;
|
|
344
|
-
seen += 1;
|
|
345
|
-
// The second observation IS the post-hash re-stat.
|
|
346
|
-
if (seen === 1)
|
|
347
|
-
return stats;
|
|
348
|
-
return { ...stats, mtimeMs: stats.mtimeMs + 5_000 };
|
|
349
|
-
};
|
|
350
|
-
const result = await buildSyncManifest(baseOptions());
|
|
351
|
-
expect(result.droppedStatRaces).toBe(1);
|
|
352
|
-
expect(result.chunks[0].entries).toHaveLength(0);
|
|
353
|
-
});
|
|
354
|
-
it("yields to the event loop during the HASH phase, not just the walk", async () => {
|
|
355
|
-
// Regression: the hash phase opens files and is at least as expensive as
|
|
356
|
-
// the walk, but used to run as one uninterrupted synchronous block —
|
|
357
|
-
// freezing the daemon for the whole of a first-ever sync.
|
|
358
|
-
//
|
|
359
|
-
// The walk yields too, so ticks alone prove nothing. The walk lstats each
|
|
360
|
-
// path ONCE and the hash phase lstats it a SECOND time (the post-hash
|
|
361
|
-
// re-stat), so a second observation of any path marks the hash phase; the
|
|
362
|
-
// assertion is that the tick count GROWS after that point.
|
|
363
|
-
const total = 24;
|
|
364
|
-
for (let index = 0; index < total; index++)
|
|
365
|
-
writeFileAt(`notes/f${index}.md`, `${index}`);
|
|
366
|
-
let ticks = 0;
|
|
367
|
-
let ticksAtHashStart = null;
|
|
368
|
-
const seenOnce = new Set();
|
|
369
|
-
fsHooks.onLstat = (target, stats) => {
|
|
370
|
-
if (target.endsWith(".md")) {
|
|
371
|
-
if (seenOnce.has(target)) {
|
|
372
|
-
if (ticksAtHashStart === null)
|
|
373
|
-
ticksAtHashStart = ticks;
|
|
374
|
-
}
|
|
375
|
-
else {
|
|
376
|
-
seenOnce.add(target);
|
|
377
|
-
}
|
|
378
|
-
}
|
|
379
|
-
return stats;
|
|
380
|
-
};
|
|
381
|
-
const timer = setInterval(() => {
|
|
382
|
-
ticks += 1;
|
|
383
|
-
}, 1);
|
|
384
|
-
try {
|
|
385
|
-
await buildSyncManifest(baseOptions({ yieldEvery: 2 }));
|
|
386
|
-
}
|
|
387
|
-
finally {
|
|
388
|
-
clearInterval(timer);
|
|
389
|
-
}
|
|
390
|
-
const hashStart = ticksAtHashStart;
|
|
391
|
-
expect(hashStart).not.toBeNull();
|
|
392
|
-
expect(ticks).toBeGreaterThan(hashStart ?? Number.POSITIVE_INFINITY);
|
|
393
|
-
});
|
|
394
|
-
it("stops hashing when the signal aborts and marks the manifest truncated", async () => {
|
|
395
|
-
// The abort must fire in the HASH phase, not the walk — otherwise the
|
|
396
|
-
// pre-existing walk-abort path would satisfy this test on a broken
|
|
397
|
-
// implementation. A second lstat of a path only happens post-hash.
|
|
398
|
-
const total = 40;
|
|
399
|
-
for (let index = 0; index < total; index++)
|
|
400
|
-
writeFileAt(`notes/f${index}.md`, `${index}`);
|
|
401
|
-
const controller = new AbortController();
|
|
402
|
-
const seenOnce = new Set();
|
|
403
|
-
let rehashed = 0;
|
|
404
|
-
fsHooks.onLstat = (target, stats) => {
|
|
405
|
-
if (target.endsWith(".md")) {
|
|
406
|
-
if (seenOnce.has(target)) {
|
|
407
|
-
if (++rehashed >= 4)
|
|
408
|
-
controller.abort();
|
|
409
|
-
}
|
|
410
|
-
else {
|
|
411
|
-
seenOnce.add(target);
|
|
412
|
-
}
|
|
413
|
-
}
|
|
414
|
-
return stats;
|
|
415
|
-
};
|
|
416
|
-
const result = await buildSyncManifest(baseOptions({ abortSignal: controller.signal, yieldEvery: 2 }));
|
|
417
|
-
// The walk completed (that is what makes this a hash-phase abort), but the
|
|
418
|
-
// hash phase stopped early, so the manifest is partial and unsafe to
|
|
419
|
-
// persist as a delta base.
|
|
420
|
-
expect(result.walkStats.filesWalked).toBe(total);
|
|
421
|
-
expect(result.walkStats.truncated).toBe(true);
|
|
422
|
-
expect(result.walkStats.hashedFiles).toBeLessThan(total);
|
|
423
|
-
expect(fs.existsSync(manifestSnapshotPath(stateDir, "personal"))).toBe(false);
|
|
424
|
-
expect(() => validateSyncManifestUpload(result.chunks[0])).not.toThrow();
|
|
425
|
-
});
|
|
426
|
-
it("computes no hashes at all under the ledger-only hash policy", async () => {
|
|
427
|
-
writeFileAt("notes/a.md", "a");
|
|
428
|
-
const result = await buildSyncManifest(baseOptions({ hashPolicy: "ledger-only" }));
|
|
429
|
-
expect(result.walkStats.filesWalked).toBe(1);
|
|
430
|
-
expect(result.walkStats.hashedFiles).toBe(0);
|
|
431
|
-
expect(result.skippedUnhashed).toBe(1);
|
|
432
|
-
expect(result.chunks[0].entries).toEqual([]);
|
|
433
|
-
});
|
|
434
|
-
});
|
|
435
|
-
describe("buildSyncManifest — delta mode", () => {
|
|
436
|
-
it("emits a full manifest when no snapshot exists, then a delta of the change", async () => {
|
|
437
|
-
writeFileAt("notes/a.md", "a");
|
|
438
|
-
writeFileAt("notes/b.md", "b");
|
|
439
|
-
const first = await buildSyncManifest(baseOptions());
|
|
440
|
-
expect(first.mode).toBe("full");
|
|
441
|
-
expect(first.chunks[0].baseSnapshotId).toBeUndefined();
|
|
442
|
-
writeFileAt("notes/b.md", "b-changed");
|
|
443
|
-
writeFileAt("notes/c.md", "c");
|
|
444
|
-
fs.rmSync(path.join(hqRoot, "notes/a.md"));
|
|
445
|
-
const second = await buildSyncManifest(baseOptions({ sequence: 8 }));
|
|
446
|
-
expect(second.mode).toBe("delta");
|
|
447
|
-
expect(second.chunks[0].baseSnapshotId).toBe(first.snapshot.snapshotId);
|
|
448
|
-
expect(second.chunks[0].entries.map((entry) => entry.path).sort()).toEqual([
|
|
449
|
-
"notes/b.md",
|
|
450
|
-
"notes/c.md",
|
|
451
|
-
]);
|
|
452
|
-
expect(second.chunks[0].removedPaths).toEqual(["notes/a.md"]);
|
|
453
|
-
expect(() => validateSyncManifestUpload(second.chunks[0])).not.toThrow();
|
|
454
|
-
});
|
|
455
|
-
it("forces a full manifest when the caller asks (server resend_full)", async () => {
|
|
456
|
-
writeFileAt("notes/a.md", "a");
|
|
457
|
-
await buildSyncManifest(baseOptions());
|
|
458
|
-
const forced = await buildSyncManifest(baseOptions({ forceFull: true, sequence: 9 }));
|
|
459
|
-
expect(forced.mode).toBe("full");
|
|
460
|
-
expect(forced.chunks[0].entries.map((entry) => entry.path)).toEqual(["notes/a.md"]);
|
|
461
|
-
expect(forced.chunks[0].removedPaths).toEqual([]);
|
|
462
|
-
});
|
|
463
|
-
it("re-baselines when the snapshot is older than seven days", async () => {
|
|
464
|
-
writeFileAt("notes/a.md", "a");
|
|
465
|
-
await buildSyncManifest(baseOptions());
|
|
466
|
-
const later = await buildSyncManifest(baseOptions({ now: NOW_MS + 8 * 24 * 60 * 60 * 1000, sequence: 10 }));
|
|
467
|
-
expect(later.mode).toBe("full");
|
|
468
|
-
});
|
|
469
|
-
it("does not report a file as removed just because it raced during hashing", async () => {
|
|
470
|
-
// Regression: a file the user is actively SAVING fails its post-hash
|
|
471
|
-
// re-stat and is dropped from the manifest. It is still on disk, so
|
|
472
|
-
// reporting it in `removedPaths` would tell the audit the local copy
|
|
473
|
-
// vanished — a finding raised against every file being edited.
|
|
474
|
-
const abs = writeFileAt("notes/racing.md", "before");
|
|
475
|
-
writeFileAt("notes/stable.md", "stable");
|
|
476
|
-
await buildSyncManifest(baseOptions());
|
|
477
|
-
writeFileAt("notes/racing.md", "after");
|
|
478
|
-
let seen = 0;
|
|
479
|
-
fsHooks.onLstat = (target, stats) => {
|
|
480
|
-
if (target !== abs)
|
|
481
|
-
return stats;
|
|
482
|
-
seen += 1;
|
|
483
|
-
if (seen === 1)
|
|
484
|
-
return stats;
|
|
485
|
-
return { ...stats, mtimeMs: stats.mtimeMs + 5_000 };
|
|
486
|
-
};
|
|
487
|
-
const second = await buildSyncManifest(baseOptions({ sequence: 8 }));
|
|
488
|
-
expect(second.mode).toBe("delta");
|
|
489
|
-
expect(second.droppedStatRaces).toBe(1);
|
|
490
|
-
expect(second.chunks[0].removedPaths).toEqual([]);
|
|
491
|
-
});
|
|
492
|
-
it("asserts no removals at all while the ledger is unreadable", async () => {
|
|
493
|
-
// With the ledger unreadable the `ledger`-source loop is skipped, so every
|
|
494
|
-
// ledger-only path in the base would otherwise surface as a deletion.
|
|
495
|
-
const abs = writeFileAt("notes/vanishing.md", "v");
|
|
496
|
-
const journal = { "notes/vanishing.md": journalEntryFor(abs) };
|
|
497
|
-
const readable = () => summaries(PERSONAL_VAULT_JOURNAL_SLUG, journal);
|
|
498
|
-
await buildSyncManifest(baseOptions({ listJournalsImpl: readable }));
|
|
499
|
-
fs.rmSync(abs);
|
|
500
|
-
const second = await buildSyncManifest(baseOptions({
|
|
501
|
-
sequence: 8,
|
|
502
|
-
listJournalsImpl: () => {
|
|
503
|
-
throw new Error("journal locked");
|
|
504
|
-
},
|
|
505
|
-
}));
|
|
506
|
-
expect(second.chunks[0].ledgerUnavailable?.errorClass).toBe("Error");
|
|
507
|
-
expect(second.chunks[0].removedPaths).toEqual([]);
|
|
508
|
-
});
|
|
509
|
-
it("never persists a truncated walk as the delta base", async () => {
|
|
510
|
-
writeFileAt("notes/a.md", "a");
|
|
511
|
-
await buildSyncManifest(baseOptions());
|
|
512
|
-
const before = readManifestSnapshot(stateDir, "personal");
|
|
513
|
-
await buildSyncManifest(baseOptions({ maxWallMs: -1, sequence: 11 }));
|
|
514
|
-
expect(readManifestSnapshot(stateDir, "personal")?.snapshotId).toBe(before?.snapshotId);
|
|
515
|
-
});
|
|
516
|
-
});
|
|
517
|
-
describe("snapshot store", () => {
|
|
518
|
-
it("round-trips through an atomic write and leaves no temp files", async () => {
|
|
519
|
-
const snapshot = {
|
|
520
|
-
snapshotId: "snap-deadbeef",
|
|
521
|
-
generatedAt: "2026-09-04T12:00:00.000Z",
|
|
522
|
-
sequence: 3,
|
|
523
|
-
entries: { "a.md": { hash: "c".repeat(64), size: 1, mtimeMs: 5 } },
|
|
524
|
-
};
|
|
525
|
-
expect(writeManifestSnapshot(stateDir, "company:co-1", snapshot)).toBe(true);
|
|
526
|
-
expect(readManifestSnapshot(stateDir, "company:co-1")).toEqual(snapshot);
|
|
527
|
-
const dir = path.dirname(manifestSnapshotPath(stateDir, "company:co-1"));
|
|
528
|
-
expect(fs.readdirSync(dir).filter((name) => name.endsWith(".tmp"))).toEqual([]);
|
|
529
|
-
// The colon is sanitized out of the filename rather than reaching the FS,
|
|
530
|
-
// and a digest of the RAW scope key keeps two tenants whose uids sanitize
|
|
531
|
-
// alike (`acme.eu` vs `acme:eu`) off the same file.
|
|
532
|
-
expect(fs.readdirSync(dir)).toEqual([path.basename(manifestSnapshotPath(stateDir, "company:co-1"))]);
|
|
533
|
-
expect(fs.readdirSync(dir)[0]).toMatch(/^company_co-1-[0-9a-f]{16}\.json$/);
|
|
534
|
-
});
|
|
535
|
-
it("returns null for missing and corrupt snapshots instead of throwing", () => {
|
|
536
|
-
expect(readManifestSnapshot(stateDir, "personal")).toBeNull();
|
|
537
|
-
const target = manifestSnapshotPath(stateDir, "personal");
|
|
538
|
-
fs.mkdirSync(path.dirname(target), { recursive: true });
|
|
539
|
-
fs.writeFileSync(target, "{ not json", "utf8");
|
|
540
|
-
expect(readManifestSnapshot(stateDir, "personal")).toBeNull();
|
|
541
|
-
fs.writeFileSync(target, JSON.stringify({ snapshotId: 4 }), "utf8");
|
|
542
|
-
expect(readManifestSnapshot(stateDir, "personal")).toBeNull();
|
|
543
|
-
});
|
|
544
|
-
it("drops individual malformed rows without discarding the snapshot", () => {
|
|
545
|
-
const target = manifestSnapshotPath(stateDir, "personal");
|
|
546
|
-
fs.mkdirSync(path.dirname(target), { recursive: true });
|
|
547
|
-
fs.writeFileSync(target, JSON.stringify({
|
|
548
|
-
snapshotId: "snap-1",
|
|
549
|
-
generatedAt: "2026-09-04T12:00:00.000Z",
|
|
550
|
-
sequence: 1,
|
|
551
|
-
entries: { good: { hash: "d".repeat(64), size: 1, mtimeMs: 2 }, bad: { hash: 5 } },
|
|
552
|
-
}), "utf8");
|
|
553
|
-
const snapshot = readManifestSnapshot(stateDir, "personal");
|
|
554
|
-
expect(Object.keys(snapshot?.entries ?? {})).toEqual(["good"]);
|
|
555
|
-
});
|
|
556
|
-
});
|
|
557
|
-
describe("classifyManifestUploadResponse", () => {
|
|
558
|
-
it("treats 404 as a soft skip so an older server is never an error", () => {
|
|
559
|
-
expect(classifyManifestUploadResponse(404)).toEqual({ outcome: "soft_skip" });
|
|
560
|
-
});
|
|
561
|
-
it("classifies success, resend_full, and genuine failures", () => {
|
|
562
|
-
expect(classifyManifestUploadResponse(200)).toEqual({ outcome: "ok" });
|
|
563
|
-
expect(classifyManifestUploadResponse(202)).toEqual({ outcome: "ok" });
|
|
564
|
-
expect(classifyManifestUploadResponse(409)).toEqual({ outcome: "resend_full" });
|
|
565
|
-
expect(classifyManifestUploadResponse(200, { action: "resend_full" })).toEqual({
|
|
566
|
-
outcome: "resend_full",
|
|
567
|
-
});
|
|
568
|
-
expect(classifyManifestUploadResponse(400, { resendFull: true })).toEqual({
|
|
569
|
-
outcome: "resend_full",
|
|
570
|
-
});
|
|
571
|
-
expect(classifyManifestUploadResponse(500)).toEqual({ outcome: "error" });
|
|
572
|
-
expect(classifyManifestUploadResponse(403)).toEqual({ outcome: "error" });
|
|
573
|
-
});
|
|
574
|
-
});
|
|
575
|
-
// ─── Codex review regressions ────────────────────────────────────────────────
|
|
576
|
-
/**
|
|
577
|
-
* These cover the five ways the builder could previously state something FALSE
|
|
578
|
-
* about a scope — a wrong tree, an invisible subtree, a newly-ignored file, a
|
|
579
|
-
* corrupt snapshot key, and a normalization collision — plus the chunk-boundary
|
|
580
|
-
* arithmetic that had no coverage at all.
|
|
581
|
-
*/
|
|
582
|
-
describe("buildSyncManifest — company slug is a tenant boundary", () => {
|
|
583
|
-
it("refuses a slug that escapes companies/", async () => {
|
|
584
|
-
fs.mkdirSync(path.join(hqRoot, "companies", "acme"), { recursive: true });
|
|
585
|
-
writeFileAt("personal/secret.md", "not acme's");
|
|
586
|
-
await expect(buildSyncManifest(baseOptions({ scope: { kind: "company", companyUid: "co_acme", slug: "../personal" } }))).rejects.toThrow(/direct child of companies/);
|
|
587
|
-
});
|
|
588
|
-
it.each(["..", ".", "nested/slug", "sub\\slug", "__proto__"])("refuses the unsafe slug %j", async (slug) => {
|
|
589
|
-
await expect(buildSyncManifest(baseOptions({ scope: { kind: "company", companyUid: "co_acme", slug } }))).rejects.toThrow(/direct child of companies/);
|
|
590
|
-
});
|
|
591
|
-
it("still accepts an ordinary slug", async () => {
|
|
592
|
-
writeFileAt("companies/acme/deal.md", "hello");
|
|
593
|
-
const result = await buildSyncManifest(baseOptions({ scope: { kind: "company", companyUid: "co_acme", slug: "acme" } }));
|
|
594
|
-
expect(result.chunks[0].entries.map((entry) => entry.path)).toEqual(["deal.md"]);
|
|
595
|
-
});
|
|
596
|
-
});
|
|
597
|
-
describe("buildSyncManifest — a directory it cannot read is invisible, not empty", () => {
|
|
598
|
-
const chmodded = [];
|
|
599
|
-
function makeUnreadable(relDir) {
|
|
600
|
-
const abs = path.join(hqRoot, relDir);
|
|
601
|
-
fs.chmodSync(abs, 0o000);
|
|
602
|
-
chmodded.push(abs);
|
|
603
|
-
return abs;
|
|
604
|
-
}
|
|
605
|
-
afterEach(() => {
|
|
606
|
-
for (const abs of chmodded.splice(0)) {
|
|
607
|
-
try {
|
|
608
|
-
fs.chmodSync(abs, 0o700);
|
|
609
|
-
}
|
|
610
|
-
catch {
|
|
611
|
-
// Already gone; the tmpdir cleanup does not care either way.
|
|
612
|
-
}
|
|
613
|
-
}
|
|
614
|
-
});
|
|
615
|
-
it("does not report a file under an unreadable directory as vanished", async () => {
|
|
616
|
-
const abs = writeFileAt("locked/tracked.md", "hello");
|
|
617
|
-
const ledgerEntry = journalEntryFor(abs);
|
|
618
|
-
writeFileAt("open/visible.md", "hi");
|
|
619
|
-
makeUnreadable("locked");
|
|
620
|
-
const result = await buildSyncManifest(baseOptions({
|
|
621
|
-
listJournalsImpl: () => summaries(PERSONAL_VAULT_JOURNAL_SLUG, { "locked/tracked.md": ledgerEntry }),
|
|
622
|
-
}));
|
|
623
|
-
const paths = result.chunks[0].entries.map((entry) => entry.path);
|
|
624
|
-
// The whole point: it must NOT appear as `source: "ledger"` (= "the local
|
|
625
|
-
// copy is gone") merely because we were not allowed to look.
|
|
626
|
-
expect(paths).not.toContain("locked/tracked.md");
|
|
627
|
-
// ...while the readable half of the tree is still fully reported.
|
|
628
|
-
expect(paths).toContain("open/visible.md");
|
|
629
|
-
expect(result.walkStats.unreadableDirs).toBe(1);
|
|
630
|
-
// Scoped suppression, not a blanket bail-out: the rest of the manifest is
|
|
631
|
-
// still trustworthy enough to persist as a delta base.
|
|
632
|
-
expect(result.walkStats.truncated).toBe(false);
|
|
633
|
-
});
|
|
634
|
-
it("does not report a file under an unreadable directory as removed", async () => {
|
|
635
|
-
const abs = writeFileAt("locked/tracked.md", "hello");
|
|
636
|
-
writeFileAt("open/visible.md", "hi");
|
|
637
|
-
const base = await buildSyncManifest(baseOptions());
|
|
638
|
-
expect(base.snapshot.entries["locked/tracked.md"]).toBeDefined();
|
|
639
|
-
expect(fs.existsSync(abs)).toBe(true);
|
|
640
|
-
makeUnreadable("locked");
|
|
641
|
-
const result = await buildSyncManifest(baseOptions({ sequence: 8 }));
|
|
642
|
-
expect(result.mode).toBe("delta");
|
|
643
|
-
expect(result.chunks[0].removedPaths).toEqual([]);
|
|
644
|
-
});
|
|
645
|
-
});
|
|
646
|
-
describe("buildSyncManifest — a newly ignored path is not our business", () => {
|
|
647
|
-
it("does not synthesize a ledger-only entry for a path that is now ignored", async () => {
|
|
648
|
-
const abs = writeFileAt("drafts/artifact.md", "hello");
|
|
649
|
-
const ledgerEntry = journalEntryFor(abs);
|
|
650
|
-
fs.writeFileSync(path.join(hqRoot, ".hqignore"), "drafts/\n", "utf8");
|
|
651
|
-
const result = await buildSyncManifest(baseOptions({
|
|
652
|
-
listJournalsImpl: () => summaries(PERSONAL_VAULT_JOURNAL_SLUG, { "drafts/artifact.md": ledgerEntry }),
|
|
653
|
-
}));
|
|
654
|
-
// The file is still right there on disk. Reporting it as `ledger` would
|
|
655
|
-
// claim it vanished, which is the opposite of true.
|
|
656
|
-
expect(fs.existsSync(abs)).toBe(true);
|
|
657
|
-
expect(result.chunks[0].entries.map((entry) => entry.path)).not.toContain("drafts/artifact.md");
|
|
658
|
-
});
|
|
659
|
-
it("does not report a newly ignored path as removed", async () => {
|
|
660
|
-
writeFileAt("drafts/artifact.md", "hello");
|
|
661
|
-
writeFileAt("notes/keep.md", "hi");
|
|
662
|
-
const base = await buildSyncManifest(baseOptions());
|
|
663
|
-
expect(base.snapshot.entries["drafts/artifact.md"]).toBeDefined();
|
|
664
|
-
fs.writeFileSync(path.join(hqRoot, ".hqignore"), "drafts/\n", "utf8");
|
|
665
|
-
const result = await buildSyncManifest(baseOptions({ sequence: 8 }));
|
|
666
|
-
expect(result.mode).toBe("delta");
|
|
667
|
-
expect(result.chunks[0].removedPaths).toEqual([]);
|
|
668
|
-
});
|
|
669
|
-
});
|
|
670
|
-
describe("buildSyncManifest — NFC is a wire key, not a file locator", () => {
|
|
671
|
-
const NFD_NAME = "café.md"; // e + combining acute
|
|
672
|
-
const NFC_NAME = "café.md";
|
|
673
|
-
it("reports an NFD filename under its NFC path and hashes the real file", async () => {
|
|
674
|
-
const abs = writeFileAt(`notes/${NFD_NAME}`, "hello");
|
|
675
|
-
expect(fs.existsSync(abs)).toBe(true);
|
|
676
|
-
const result = await buildSyncManifest(baseOptions());
|
|
677
|
-
const entry = result.chunks[0].entries.find((candidate) => candidate.path.startsWith("notes/"));
|
|
678
|
-
expect(entry?.path).toBe(`notes/${NFC_NAME}`);
|
|
679
|
-
expect(entry?.hash).toBe(hashFile(abs));
|
|
680
|
-
expect(result.unrepresentablePaths).toBe(0);
|
|
681
|
-
});
|
|
682
|
-
it("drops both spellings when two raw names collide on one NFC key", async () => {
|
|
683
|
-
writeFileAt(`notes/${NFC_NAME}`, "hello");
|
|
684
|
-
writeFileAt("notes/plain.md", "hi");
|
|
685
|
-
// Forge the second spelling into the listing (see fsHooks.onReaddir). On
|
|
686
|
-
// macOS both spellings lstat to the same real file, which is exactly the
|
|
687
|
-
// ambiguity the builder must refuse to guess at.
|
|
688
|
-
fsHooks.onReaddir = (absDir, dirents) => {
|
|
689
|
-
if (path.basename(absDir) !== "notes")
|
|
690
|
-
return dirents;
|
|
691
|
-
const real = dirents.find((dirent) => dirent.name === NFC_NAME);
|
|
692
|
-
if (!real)
|
|
693
|
-
return dirents;
|
|
694
|
-
const alias = Object.create(Object.getPrototypeOf(real));
|
|
695
|
-
Object.assign(alias, real);
|
|
696
|
-
alias.name = NFD_NAME;
|
|
697
|
-
return [...dirents, alias];
|
|
698
|
-
};
|
|
699
|
-
const result = await buildSyncManifest(baseOptions());
|
|
700
|
-
const paths = result.chunks[0].entries.map((entry) => entry.path);
|
|
701
|
-
expect(paths).not.toContain(`notes/${NFC_NAME}`);
|
|
702
|
-
// Unaffected siblings still report normally — one ambiguous name must not
|
|
703
|
-
// cost the directory.
|
|
704
|
-
expect(paths).toContain("notes/plain.md");
|
|
705
|
-
expect(result.unrepresentablePaths).toBe(2);
|
|
706
|
-
expect(result.walkStats.filesWalked).toBe(1);
|
|
707
|
-
});
|
|
708
|
-
it("counts the collision even when the second spelling does not resolve", async () => {
|
|
709
|
-
// The platform-portability half, and a real CI failure: the test above
|
|
710
|
-
// passes on macOS for the wrong reason. There, both spellings `lstat` to
|
|
711
|
-
// the same file, so a collision check placed AFTER the stat still sees the
|
|
712
|
-
// duplicate. On Linux the NFD spelling of a file stored as NFC resolves to
|
|
713
|
-
// nothing, the stat throws, and the ambiguity vanishes silently — on
|
|
714
|
-
// exactly the platform where the two listing entries really are two
|
|
715
|
-
// different files, and where publishing one's content under the other's
|
|
716
|
-
// name is therefore a live risk.
|
|
717
|
-
//
|
|
718
|
-
// Forcing the alias `lstat` to throw reproduces the byte-exact filesystem
|
|
719
|
-
// on any host, so this pins the rule rather than the platform.
|
|
720
|
-
writeFileAt(`notes/${NFC_NAME}`, "hello");
|
|
721
|
-
writeFileAt("notes/plain.md", "hi");
|
|
722
|
-
fsHooks.onReaddir = (absDir, dirents) => {
|
|
723
|
-
if (path.basename(absDir) !== "notes")
|
|
724
|
-
return dirents;
|
|
725
|
-
const real = dirents.find((dirent) => dirent.name === NFC_NAME);
|
|
726
|
-
if (!real)
|
|
727
|
-
return dirents;
|
|
728
|
-
const alias = Object.create(Object.getPrototypeOf(real));
|
|
729
|
-
Object.assign(alias, real);
|
|
730
|
-
alias.name = NFD_NAME;
|
|
731
|
-
return [...dirents, alias];
|
|
732
|
-
};
|
|
733
|
-
fsHooks.onLstat = (absPath, stats) => {
|
|
734
|
-
if (absPath.endsWith(NFD_NAME) && !absPath.endsWith(NFC_NAME)) {
|
|
735
|
-
throw Object.assign(new Error("ENOENT"), { code: "ENOENT" });
|
|
736
|
-
}
|
|
737
|
-
return stats;
|
|
738
|
-
};
|
|
739
|
-
const result = await buildSyncManifest(baseOptions());
|
|
740
|
-
const paths = result.chunks[0].entries.map((entry) => entry.path);
|
|
741
|
-
expect(paths).not.toContain(`notes/${NFC_NAME}`);
|
|
742
|
-
expect(paths).toContain("notes/plain.md");
|
|
743
|
-
expect(result.unrepresentablePaths).toBe(2);
|
|
744
|
-
expect(result.walkStats.filesWalked).toBe(1);
|
|
745
|
-
});
|
|
746
|
-
});
|
|
747
|
-
describe("chunkManifest — boundary arithmetic", () => {
|
|
748
|
-
const walkStats = {
|
|
749
|
-
filesWalked: 0,
|
|
750
|
-
walkMs: 1,
|
|
751
|
-
hashedFiles: 0,
|
|
752
|
-
ignoredFiles: 0,
|
|
753
|
-
truncated: false,
|
|
754
|
-
unreadableDirs: 0,
|
|
755
|
-
};
|
|
756
|
-
/**
|
|
757
|
-
* Synthetic entries, built once and shared. The per-chunk maximum is 50k, so
|
|
758
|
-
* every test here is inherently large; rebuilding the array per test is a
|
|
759
|
-
* second of CPU that this file does not need to spend (and that the walk
|
|
760
|
-
* benchmark, running in a sibling worker, would rather have). `chunkManifest`
|
|
761
|
-
* only ever slices, so sharing the objects is safe.
|
|
762
|
-
*/
|
|
763
|
-
const entryCache = new Map();
|
|
764
|
-
function syntheticEntries(count) {
|
|
765
|
-
const cached = entryCache.get(count);
|
|
766
|
-
if (cached)
|
|
767
|
-
return cached;
|
|
768
|
-
const entries = new Array(count);
|
|
769
|
-
for (let index = 0; index < count; index++) {
|
|
770
|
-
entries[index] = {
|
|
771
|
-
path: `notes/file-${index}.md`,
|
|
772
|
-
hash: "a".repeat(64),
|
|
773
|
-
size: 1,
|
|
774
|
-
mtimeMs: NOW_MS,
|
|
775
|
-
source: "disk",
|
|
776
|
-
};
|
|
777
|
-
}
|
|
778
|
-
entryCache.set(count, entries);
|
|
779
|
-
return entries;
|
|
780
|
-
}
|
|
781
|
-
/**
|
|
782
|
-
* `perChunk` defaults WELL below the contract ceiling because these cases
|
|
783
|
-
* are about the ENTRY bound in isolation, and 50,000 of these entries is
|
|
784
|
-
* ~6.5 MB of JSON — past the byte budget, which would now (correctly) cut
|
|
785
|
-
* first and make an entry-boundary assertion measure the wrong bound. The
|
|
786
|
-
* byte bound gets its own cases below.
|
|
787
|
-
*/
|
|
788
|
-
const ENTRY_BOUNDARY_PER_CHUNK = 1_000;
|
|
789
|
-
function chunk(entryCount, removedPaths = [], overrides = {}) {
|
|
790
|
-
return chunkManifest({
|
|
791
|
-
entries: syntheticEntries(entryCount),
|
|
792
|
-
removedPaths,
|
|
793
|
-
walkStats,
|
|
794
|
-
ignoreRules: [],
|
|
795
|
-
mode: removedPaths.length > 0 ? "delta" : "full",
|
|
796
|
-
generatedAt: new Date(NOW_MS).toISOString(),
|
|
797
|
-
installationId: "install-abc123",
|
|
798
|
-
machineId: "machine-abc123",
|
|
799
|
-
scope: { kind: "personal" },
|
|
800
|
-
source: "cli",
|
|
801
|
-
sequence: 7,
|
|
802
|
-
maxEntriesPerChunk: overrides.maxEntriesPerChunk ?? ENTRY_BOUNDARY_PER_CHUNK,
|
|
803
|
-
chunkByteBudget: overrides.chunkByteBudget ?? SYNC_MANIFEST_MAX_CHUNK_BYTES,
|
|
804
|
-
});
|
|
805
|
-
}
|
|
806
|
-
it("emits one chunk at exactly the per-chunk maximum", () => {
|
|
807
|
-
const uploads = chunk(ENTRY_BOUNDARY_PER_CHUNK);
|
|
808
|
-
expect(uploads).toHaveLength(1);
|
|
809
|
-
expect(uploads[0].chunkIndex).toBe(0);
|
|
810
|
-
expect(uploads[0].chunkCount).toBe(1);
|
|
811
|
-
expect(uploads[0].entries).toHaveLength(ENTRY_BOUNDARY_PER_CHUNK);
|
|
812
|
-
});
|
|
813
|
-
it("spills a single extra entry into a second chunk", () => {
|
|
814
|
-
const uploads = chunk(ENTRY_BOUNDARY_PER_CHUNK + 1);
|
|
815
|
-
expect(uploads).toHaveLength(2);
|
|
816
|
-
expect(uploads.map((upload) => upload.chunkIndex)).toEqual([0, 1]);
|
|
817
|
-
expect(uploads.every((upload) => upload.chunkCount === 2)).toBe(true);
|
|
818
|
-
expect(uploads[0].entries).toHaveLength(ENTRY_BOUNDARY_PER_CHUNK);
|
|
819
|
-
expect(uploads[1].entries).toHaveLength(1);
|
|
820
|
-
// No entry is duplicated or lost across the seam.
|
|
821
|
-
expect(uploads[1].entries[0].path).toBe(`notes/file-${ENTRY_BOUNDARY_PER_CHUNK}.md`);
|
|
822
|
-
});
|
|
823
|
-
/**
|
|
824
|
-
* THE CANARY, AS A TEST.
|
|
825
|
-
*
|
|
826
|
-
* @indigoai-us/hq-cloud 6.16.21 planned the operator's 199,394-entry
|
|
827
|
-
* personal scope as four COUNT-sized chunks of 15.9-17.9 MiB and API Gateway
|
|
828
|
-
* refused chunk 0 with a 413 before the handler ever ran. The shape that
|
|
829
|
-
* produced it is the only thing that matters here — realistic entry counts
|
|
830
|
-
* and realistic path lengths — so this builds the same order of magnitude
|
|
831
|
-
* and asserts on SERIALISED bytes rather than on entry counts.
|
|
832
|
-
*/
|
|
833
|
-
it("cuts 200k realistic entries on bytes, with no chunk over the budget", () => {
|
|
834
|
-
const ENTRY_COUNT = 200_000;
|
|
835
|
-
const TARGET_PATH_BYTES = 120;
|
|
836
|
-
const entries = new Array(ENTRY_COUNT);
|
|
837
|
-
for (let index = 0; index < ENTRY_COUNT; index++) {
|
|
838
|
-
// ~120 bytes of path, the way a real vault produces them: nested
|
|
839
|
-
// directories and long human-authored file names, not `f-1.md`.
|
|
840
|
-
const suffix = `${index}`.padStart(7, "0");
|
|
841
|
-
const stem = `companies/acme/knowledge/quarterly-planning/${suffix}/meeting-notes-and-action-items`;
|
|
842
|
-
const path = `${stem.padEnd(TARGET_PATH_BYTES - 8, "x")}-${suffix}.md`;
|
|
843
|
-
entries[index] = {
|
|
844
|
-
path,
|
|
845
|
-
hash: "a".repeat(64),
|
|
846
|
-
size: 4096 + index,
|
|
847
|
-
mtimeMs: NOW_MS,
|
|
848
|
-
source: "both",
|
|
849
|
-
};
|
|
850
|
-
}
|
|
851
|
-
const plan = planManifestChunks({
|
|
852
|
-
entries,
|
|
853
|
-
removedPaths: [],
|
|
854
|
-
walkStats,
|
|
855
|
-
ignoreRules: [],
|
|
856
|
-
mode: "full",
|
|
857
|
-
generatedAt: new Date(NOW_MS).toISOString(),
|
|
858
|
-
installationId: "install-abc123",
|
|
859
|
-
machineId: "machine-abc123",
|
|
860
|
-
scope: { kind: "personal" },
|
|
861
|
-
source: "cli",
|
|
862
|
-
sequence: 7,
|
|
863
|
-
});
|
|
864
|
-
expect(plan.chunkByteBudget).toBe(SYNC_MANIFEST_DEFAULT_CHUNK_BYTE_BUDGET);
|
|
865
|
-
// The count bound ALONE would have planned 4 chunks here. It planned many
|
|
866
|
-
// more, which is the fix: bytes are what the gateway counts.
|
|
867
|
-
expect(plan.chunkCount).toBeGreaterThan(Math.ceil(ENTRY_COUNT / SYNC_MANIFEST_MAX_ENTRIES_PER_CHUNK));
|
|
868
|
-
expect(plan.chunkCount).toBeLessThanOrEqual(SYNC_MANIFEST_MAX_CHUNK_COUNT);
|
|
869
|
-
// Serialise EVERY chunk and measure it the way the server does
|
|
870
|
-
// (`Buffer.byteLength(rawBody, "utf8")`), rather than trusting the
|
|
871
|
-
// planner's own arithmetic — the bug being fixed was precisely a plan
|
|
872
|
-
// whose arithmetic bore no relation to the bytes on the wire.
|
|
873
|
-
let observedMax = 0;
|
|
874
|
-
let seenEntries = 0;
|
|
875
|
-
for (let index = 0; index < plan.chunkCount; index++) {
|
|
876
|
-
const chunkUpload = plan.chunk(index);
|
|
877
|
-
const bytes = Buffer.byteLength(JSON.stringify(chunkUpload), "utf8");
|
|
878
|
-
expect(bytes).toBeLessThanOrEqual(SYNC_MANIFEST_DEFAULT_CHUNK_BYTE_BUDGET);
|
|
879
|
-
expect(chunkUpload.entries.length).toBeLessThanOrEqual(SYNC_MANIFEST_MAX_ENTRIES_PER_CHUNK);
|
|
880
|
-
expect(chunkUpload.chunkCount).toBe(plan.chunkCount);
|
|
881
|
-
expect(chunkUpload.chunkIndex).toBe(index);
|
|
882
|
-
// Every chunk must also be one the SERVER's validator accepts, not
|
|
883
|
-
// merely one that fits: a byte-sized chunk that trips a different bound
|
|
884
|
-
// is no better than an oversized one.
|
|
885
|
-
expect(() => validateSyncManifestUpload(JSON.parse(JSON.stringify(chunkUpload)))).not.toThrow();
|
|
886
|
-
seenEntries += chunkUpload.entries.length;
|
|
887
|
-
observedMax = Math.max(observedMax, bytes);
|
|
888
|
-
}
|
|
889
|
-
// Nothing lost or duplicated across the byte-chosen seams.
|
|
890
|
-
expect(seenEntries).toBe(ENTRY_COUNT);
|
|
891
|
-
expect(plan.chunk(0).entries[0].path).toBe(entries[0].path);
|
|
892
|
-
const last = plan.chunk(plan.chunkCount - 1);
|
|
893
|
-
expect(last.entries[last.entries.length - 1].path).toBe(entries[ENTRY_COUNT - 1].path);
|
|
894
|
-
// The plan's own self-report must agree with the measurement, and must not
|
|
895
|
-
// be so conservative that it wastes most of the budget.
|
|
896
|
-
expect(plan.maxChunkBytes).toBeGreaterThanOrEqual(observedMax);
|
|
897
|
-
expect(observedMax).toBeGreaterThan(SYNC_MANIFEST_DEFAULT_CHUNK_BYTE_BUDGET * 0.9);
|
|
898
|
-
}, 120_000);
|
|
899
|
-
it("still honours the contract entry ceiling when the byte budget is not the binding bound", () => {
|
|
900
|
-
// Tiny entries, so 50,001 of them stay under the byte budget and the
|
|
901
|
-
// ENTRY bound is what cuts. Guards the ceiling itself, which the cases
|
|
902
|
-
// above deliberately stopped exercising.
|
|
903
|
-
const entries = new Array(SYNC_MANIFEST_MAX_ENTRIES_PER_CHUNK + 1);
|
|
904
|
-
for (let index = 0; index < entries.length; index++) {
|
|
905
|
-
entries[index] = { path: `n/${index}`, size: 1, mtimeMs: NOW_MS, source: "disk" };
|
|
906
|
-
}
|
|
907
|
-
const plan = planManifestChunks({
|
|
908
|
-
entries,
|
|
909
|
-
removedPaths: [],
|
|
910
|
-
walkStats,
|
|
911
|
-
ignoreRules: [],
|
|
912
|
-
mode: "full",
|
|
913
|
-
generatedAt: new Date(NOW_MS).toISOString(),
|
|
914
|
-
installationId: "install-abc123",
|
|
915
|
-
machineId: "machine-abc123",
|
|
916
|
-
scope: { kind: "personal" },
|
|
917
|
-
source: "cli",
|
|
918
|
-
sequence: 7,
|
|
919
|
-
chunkByteBudget: SYNC_MANIFEST_MAX_CHUNK_BYTES,
|
|
920
|
-
});
|
|
921
|
-
expect(plan.chunkCount).toBe(2);
|
|
922
|
-
expect(plan.chunk(0).entries).toHaveLength(SYNC_MANIFEST_MAX_ENTRIES_PER_CHUNK);
|
|
923
|
-
expect(plan.chunk(1).entries).toHaveLength(1);
|
|
924
|
-
});
|
|
925
|
-
it("puts removedPaths on the first chunk only, never repeated", () => {
|
|
926
|
-
const removedPaths = ["notes/gone-a.md", "notes/gone-b.md"];
|
|
927
|
-
const uploads = chunk(ENTRY_BOUNDARY_PER_CHUNK + 1, removedPaths);
|
|
928
|
-
expect(uploads).toHaveLength(2);
|
|
929
|
-
expect(uploads[0].removedPaths).toEqual(removedPaths);
|
|
930
|
-
// A removal repeated across chunks would make the server de-duplicate, and
|
|
931
|
-
// a removal on a LATER chunk would be lost if chunk 0 were retried alone.
|
|
932
|
-
for (const upload of uploads.slice(1)) {
|
|
933
|
-
expect(upload.removedPaths).toEqual([]);
|
|
934
|
-
}
|
|
935
|
-
expect(uploads.flatMap((upload) => upload.removedPaths)).toEqual(removedPaths);
|
|
936
|
-
});
|
|
937
|
-
it("emits a valid, chunk-consistent upload for every chunk", () => {
|
|
938
|
-
const uploads = chunk(SYNC_MANIFEST_MAX_ENTRIES_PER_CHUNK + 1, ["notes/gone.md"]);
|
|
939
|
-
for (const upload of uploads) {
|
|
940
|
-
expect(() => validateSyncManifestUpload(upload)).not.toThrow();
|
|
941
|
-
}
|
|
942
|
-
});
|
|
943
|
-
});
|
|
944
|
-
/**
|
|
945
|
-
* The personal manifest must describe exactly what the personal PUSH leg
|
|
946
|
-
* ships — no more.
|
|
947
|
-
*
|
|
948
|
-
* The builder previously walked the entire HQ root for a personal scope, while
|
|
949
|
-
* `sync-runner-company.ts` starts the personal share() at
|
|
950
|
-
* `computePersonalVaultPaths()`, which excludes `companies/` for every
|
|
951
|
-
* cloud-backed slug (plus `repos/`, `workspace/` and `.git`). The gap is not
|
|
952
|
-
* cosmetic: it reports one tenant's whole file list under another vault's
|
|
953
|
-
* scope, on every pass, and the server would then chase "missing" objects that
|
|
954
|
-
* the personal bucket was never meant to hold.
|
|
955
|
-
*/
|
|
956
|
-
describe("buildSyncManifest — the personal scope walks the personal vault, not the HQ root", () => {
|
|
957
|
-
/** Mark a slug cloud-backed the way the routing source-of-truth does. */
|
|
958
|
-
function writeCompaniesManifest(cloudSlugs) {
|
|
959
|
-
const body = cloudSlugs.map((slug) => ` ${slug}:\n cloud_uid: cmp_${slug}0000\n`).join("");
|
|
960
|
-
writeFileAt("companies/manifest.yaml", `companies:\n${body}`);
|
|
961
|
-
}
|
|
962
|
-
it("emits zero personal entries under a cloud-backed company directory", async () => {
|
|
963
|
-
writeCompaniesManifest(["northwind"]);
|
|
964
|
-
writeFileAt("companies/northwind/deals/secret.md", "tenant content");
|
|
965
|
-
writeFileAt("personal/notes.md", "mine");
|
|
966
|
-
const result = await buildSyncManifest(baseOptions());
|
|
967
|
-
const paths = result.chunks.flatMap((chunk) => chunk.entries.map((entry) => entry.path));
|
|
968
|
-
expect(paths).toContain("personal/notes.md");
|
|
969
|
-
expect(paths.filter((candidate) => candidate.startsWith("companies/northwind/"))).toEqual([]);
|
|
970
|
-
});
|
|
971
|
-
it("still carries companies/manifest.yaml, the one companies/ file the vault needs", async () => {
|
|
972
|
-
// The routing source-of-truth must travel with the personal vault so a
|
|
973
|
-
// fresh install can enumerate expected companies before its first pull —
|
|
974
|
-
// it is a deliberate single-FILE carve-out through an excluded parent, and
|
|
975
|
-
// a walk that only ever seeds directories would silently drop it.
|
|
976
|
-
writeCompaniesManifest(["northwind"]);
|
|
977
|
-
const result = await buildSyncManifest(baseOptions());
|
|
978
|
-
const paths = result.chunks.flatMap((chunk) => chunk.entries.map((entry) => entry.path));
|
|
979
|
-
expect(paths).toContain("companies/manifest.yaml");
|
|
980
|
-
});
|
|
981
|
-
it("keeps a LOCAL (non-cloud) company, which does sync to the personal vault", async () => {
|
|
982
|
-
writeCompaniesManifest(["northwind"]);
|
|
983
|
-
writeFileAt("companies/sidequest/plan.md", "local company");
|
|
984
|
-
const result = await buildSyncManifest(baseOptions());
|
|
985
|
-
const paths = result.chunks.flatMap((chunk) => chunk.entries.map((entry) => entry.path));
|
|
986
|
-
expect(paths).toContain("companies/sidequest/plan.md");
|
|
987
|
-
});
|
|
988
|
-
it("excludes a slug named in teamSyncedSlugs even with no manifest marker", async () => {
|
|
989
|
-
writeFileAt("companies/northwind/deals/secret.md", "tenant content");
|
|
990
|
-
const result = await buildSyncManifest(baseOptions({ teamSyncedSlugs: new Set(["northwind"]) }));
|
|
991
|
-
const paths = result.chunks.flatMap((chunk) => chunk.entries.map((entry) => entry.path));
|
|
992
|
-
expect(paths.filter((candidate) => candidate.startsWith("companies/northwind/"))).toEqual([]);
|
|
993
|
-
});
|
|
994
|
-
it("does not report a path outside every walk root as removed", async () => {
|
|
995
|
-
writeFileAt("companies/northwind/deals/secret.md", "tenant content");
|
|
996
|
-
const base = await buildSyncManifest(baseOptions());
|
|
997
|
-
expect(base.snapshot.entries["companies/northwind/deals/secret.md"]).toBeDefined();
|
|
998
|
-
const result = await buildSyncManifest(baseOptions({ sequence: 8, teamSyncedSlugs: new Set(["northwind"]) }));
|
|
999
|
-
expect(result.mode).toBe("delta");
|
|
1000
|
-
expect(result.chunks[0].removedPaths).toEqual([]);
|
|
1001
|
-
});
|
|
1002
|
-
it("does not report a ledger row under a carved-out company as a vanished file", async () => {
|
|
1003
|
-
// The other half of the carve-out. A path this leg no longer walks is not
|
|
1004
|
-
// a path that disappeared: synthesising a `ledger`-source entry for it
|
|
1005
|
-
// would manufacture one false finding per file of every designated
|
|
1006
|
-
// company, forever.
|
|
1007
|
-
writeCompaniesManifest(["northwind"]);
|
|
1008
|
-
const abs = writeFileAt("companies/northwind/deals/secret.md", "tenant content");
|
|
1009
|
-
const ledgerRow = journalEntryFor(abs);
|
|
1010
|
-
fs.rmSync(abs);
|
|
1011
|
-
const result = await buildSyncManifest(baseOptions({
|
|
1012
|
-
listJournalsImpl: () => summaries(PERSONAL_VAULT_JOURNAL_SLUG, { "companies/northwind/deals/secret.md": ledgerRow }),
|
|
1013
|
-
}));
|
|
1014
|
-
const paths = result.chunks.flatMap((chunk) => chunk.entries.map((entry) => entry.path));
|
|
1015
|
-
expect(paths).not.toContain("companies/northwind/deals/secret.md");
|
|
1016
|
-
});
|
|
1017
|
-
it("leaves the COMPANY scope walking its own root, unchanged", async () => {
|
|
1018
|
-
writeFileAt("companies/northwind/deals/one.md", "one");
|
|
1019
|
-
const result = await buildSyncManifest(baseOptions({ scope: { kind: "company", companyUid: "cmp_northwind", slug: "northwind" } }));
|
|
1020
|
-
const paths = result.chunks.flatMap((chunk) => chunk.entries.map((entry) => entry.path));
|
|
1021
|
-
expect(paths).toEqual(["deals/one.md"]);
|
|
1022
|
-
});
|
|
1023
|
-
});
|
|
1024
|
-
/**
|
|
1025
|
-
* A post-sync tail step on a laptop may STAT the tree. It may not READ it.
|
|
1026
|
-
*
|
|
1027
|
-
* Measured on a real personal vault: 690k files, 9m40s, 2.9 GB peak RSS, all
|
|
1028
|
-
* of it content hashing, because the journal predated hashing and so had no
|
|
1029
|
-
* reusable hash for any file. That cost is not payable after every sync cycle,
|
|
1030
|
-
* and the previous `auto` default charged it unconditionally.
|
|
1031
|
-
*/
|
|
1032
|
-
describe("buildSyncManifest — hash policy", () => {
|
|
1033
|
-
it("defaults to stat-only: entries ship WITHOUT a hash rather than being dropped", async () => {
|
|
1034
|
-
writeFileAt("notes/a.md", "hello");
|
|
1035
|
-
const result = await buildSyncManifest(baseOptions({ hashPolicy: undefined }));
|
|
1036
|
-
const entry = result.chunks[0].entries.find((candidate) => candidate.path === "notes/a.md");
|
|
1037
|
-
// Present, and honest about what it does not know. Dropping it would have
|
|
1038
|
-
// reported a file the client holds as one it does not — the exact false
|
|
1039
|
-
// finding this audit exists to avoid.
|
|
1040
|
-
expect(entry).toBeDefined();
|
|
1041
|
-
expect(entry?.hash).toBeUndefined();
|
|
1042
|
-
expect(entry?.size).toBe(5);
|
|
1043
|
-
expect(result.walkStats.hashedFiles).toBe(0);
|
|
1044
|
-
expect(result.walkStats.unhashedFiles).toBe(1);
|
|
1045
|
-
expect(result.skippedUnhashed).toBe(0);
|
|
1046
|
-
});
|
|
1047
|
-
it("never flags pendingPush from an absent hash", async () => {
|
|
1048
|
-
const abs = writeFileAt("notes/a.md", "hello");
|
|
1049
|
-
const stale = journalEntryFor(abs, { hash: "f".repeat(64), size: 999 });
|
|
1050
|
-
const result = await buildSyncManifest(baseOptions({
|
|
1051
|
-
hashPolicy: "never",
|
|
1052
|
-
listJournalsImpl: () => summaries(PERSONAL_VAULT_JOURNAL_SLUG, { "notes/a.md": stale }),
|
|
1053
|
-
}));
|
|
1054
|
-
const entry = result.chunks[0].entries.find((candidate) => candidate.path === "notes/a.md");
|
|
1055
|
-
// "The ledger's hash differs from mine" is not a claim a stat-only pass can
|
|
1056
|
-
// make. Asserting it would flag every file on every pass.
|
|
1057
|
-
expect(entry?.pendingPush).toBeUndefined();
|
|
1058
|
-
});
|
|
1059
|
-
it("still reuses a journal hash when the stat pair matches — stat-only is not hash-free", async () => {
|
|
1060
|
-
const abs = writeFileAt("notes/a.md", "hello");
|
|
1061
|
-
const result = await buildSyncManifest(baseOptions({
|
|
1062
|
-
hashPolicy: "never",
|
|
1063
|
-
listJournalsImpl: () => summaries(PERSONAL_VAULT_JOURNAL_SLUG, { "notes/a.md": journalEntryFor(abs) }),
|
|
1064
|
-
}));
|
|
1065
|
-
const entry = result.chunks[0].entries.find((candidate) => candidate.path === "notes/a.md");
|
|
1066
|
-
expect(entry?.hash).toBe(hashFile(abs));
|
|
1067
|
-
expect(result.walkStats.hashedFiles).toBe(0);
|
|
1068
|
-
expect(result.walkStats.unhashedFiles).toBe(0);
|
|
1069
|
-
});
|
|
1070
|
-
it("caps how many files one `auto` pass may read, and ships the rest unhashed", async () => {
|
|
1071
|
-
for (let index = 0; index < 10; index += 1)
|
|
1072
|
-
writeFileAt(`notes/f${index}.md`, `body-${index}`);
|
|
1073
|
-
const result = await buildSyncManifest(baseOptions({ hashPolicy: "auto", maxHashedFiles: 3 }));
|
|
1074
|
-
const entries = result.chunks.flatMap((chunk) => chunk.entries);
|
|
1075
|
-
expect(entries).toHaveLength(10);
|
|
1076
|
-
expect(result.walkStats.hashedFiles).toBe(3);
|
|
1077
|
-
expect(result.walkStats.unhashedFiles).toBe(7);
|
|
1078
|
-
// Nothing is dropped: the picture is complete, some of it is just unverified.
|
|
1079
|
-
expect(result.skippedUnhashed).toBe(0);
|
|
1080
|
-
});
|
|
1081
|
-
it("`full` hashes everything the journal cannot cover", async () => {
|
|
1082
|
-
for (let index = 0; index < 5; index += 1)
|
|
1083
|
-
writeFileAt(`notes/f${index}.md`, `body-${index}`);
|
|
1084
|
-
const result = await buildSyncManifest(baseOptions({ hashPolicy: "full" }));
|
|
1085
|
-
expect(result.walkStats.hashedFiles).toBe(5);
|
|
1086
|
-
expect(result.walkStats.unhashedFiles).toBe(0);
|
|
1087
|
-
});
|
|
1088
|
-
it("does not re-send an unhashed entry whose stat pair is unchanged since the base", async () => {
|
|
1089
|
-
// Base erosion guard: a stat-only pass has no hash to store, so a naive
|
|
1090
|
-
// implementation drops the path from the snapshot it persists and the NEXT
|
|
1091
|
-
// pass re-sends the whole scope as "changed" — a delta wearing a full
|
|
1092
|
-
// manifest's clothes.
|
|
1093
|
-
const abs = writeFileAt("notes/a.md", "hello");
|
|
1094
|
-
const stats = fs.lstatSync(abs);
|
|
1095
|
-
writeManifestSnapshot(stateDir, "personal", {
|
|
1096
|
-
snapshotId: "snap-base",
|
|
1097
|
-
generatedAt: new Date(NOW_MS - 1000).toISOString(),
|
|
1098
|
-
sequence: 1,
|
|
1099
|
-
entries: {
|
|
1100
|
-
"notes/a.md": { hash: hashFile(abs), size: stats.size, mtimeMs: Math.floor(stats.mtimeMs) },
|
|
1101
|
-
},
|
|
1102
|
-
});
|
|
1103
|
-
const result = await buildSyncManifest(baseOptions({ hashPolicy: "never", sequence: 2 }));
|
|
1104
|
-
expect(result.mode).toBe("delta");
|
|
1105
|
-
expect(result.chunks[0].entries).toEqual([]);
|
|
1106
|
-
expect(result.chunks[0].removedPaths).toEqual([]);
|
|
1107
|
-
// The hash survived into the new base rather than being dropped.
|
|
1108
|
-
expect(result.snapshot.entries["notes/a.md"]?.hash).toBe(hashFile(abs));
|
|
1109
|
-
});
|
|
1110
|
-
it("never reports an unhashed entry as removed on the next delta", async () => {
|
|
1111
|
-
const abs = writeFileAt("notes/a.md", "hello");
|
|
1112
|
-
const stats = fs.lstatSync(abs);
|
|
1113
|
-
writeManifestSnapshot(stateDir, "personal", {
|
|
1114
|
-
snapshotId: "snap-base",
|
|
1115
|
-
generatedAt: new Date(NOW_MS - 1000).toISOString(),
|
|
1116
|
-
sequence: 1,
|
|
1117
|
-
entries: {
|
|
1118
|
-
// Different hash AND different stat pair: the file has changed, so it
|
|
1119
|
-
// cannot be carried forward — but it is still very much present.
|
|
1120
|
-
"notes/a.md": { hash: "a".repeat(64), size: 1, mtimeMs: 1 },
|
|
1121
|
-
},
|
|
1122
|
-
});
|
|
1123
|
-
const result = await buildSyncManifest(baseOptions({ hashPolicy: "never", sequence: 2 }));
|
|
1124
|
-
expect(result.chunks[0].removedPaths).toEqual([]);
|
|
1125
|
-
expect(result.chunks[0].entries.map((entry) => entry.path)).toContain("notes/a.md");
|
|
1126
|
-
expect(stats.size).toBe(5);
|
|
1127
|
-
});
|
|
1128
|
-
});
|
|
1129
|
-
/**
|
|
1130
|
-
* The operator's machine, reproduced against the REAL journal reader.
|
|
1131
|
-
*
|
|
1132
|
-
* No `listJournalsImpl` stub here on purpose: the incident lived in the seam
|
|
1133
|
-
* the stub bypasses. Every v3 generation (`snapshot-*.bin`, `wal-*.bin`) was
|
|
1134
|
-
* truncated and no legacy shard remained, the ledger read answered `null`, the
|
|
1135
|
-
* builder coalesced that `null` to an empty Map, and 188,997 files were
|
|
1136
|
-
* reported as on-disk-never-tracked with no `ledgerUnavailable` marker to warn
|
|
1137
|
-
* anyone.
|
|
1138
|
-
*/
|
|
1139
|
-
describe("buildSyncManifest — unreadable v3 ledger", () => {
|
|
1140
|
-
let previousStateDir;
|
|
1141
|
-
beforeEach(() => {
|
|
1142
|
-
previousStateDir = process.env.HQ_STATE_DIR;
|
|
1143
|
-
process.env.HQ_STATE_DIR = stateDir;
|
|
1144
|
-
});
|
|
1145
|
-
afterEach(() => {
|
|
1146
|
-
if (previousStateDir === undefined)
|
|
1147
|
-
delete process.env.HQ_STATE_DIR;
|
|
1148
|
-
else
|
|
1149
|
-
process.env.HQ_STATE_DIR = previousStateDir;
|
|
1150
|
-
});
|
|
1151
|
-
/** Promote a legacy shard into a v3 store, then drop the legacy locator. */
|
|
1152
|
-
function seedV3OnlyLedger(files) {
|
|
1153
|
-
fs.writeFileSync(path.join(stateDir, `sync-journal.${PERSONAL_VAULT_JOURNAL_SLUG}.json`), JSON.stringify(journalWith(files)), "utf8");
|
|
1154
|
-
openJournalStoreSession(PERSONAL_VAULT_JOURNAL_SLUG).dispose();
|
|
1155
|
-
fs.rmSync(path.join(stateDir, `sync-journal.${PERSONAL_VAULT_JOURNAL_SLUG}.json`));
|
|
1156
|
-
}
|
|
1157
|
-
function truncateEveryGeneration() {
|
|
1158
|
-
const root = path.join(stateDir, "sync-state-v3");
|
|
1159
|
-
let truncated = 0;
|
|
1160
|
-
for (const scope of fs.readdirSync(root)) {
|
|
1161
|
-
const dir = path.join(root, scope);
|
|
1162
|
-
if (!fs.statSync(dir).isDirectory())
|
|
1163
|
-
continue;
|
|
1164
|
-
for (const name of fs.readdirSync(dir)) {
|
|
1165
|
-
if (!/^(?:snapshot|wal)-\d+\.bin$/.test(name))
|
|
1166
|
-
continue;
|
|
1167
|
-
fs.writeFileSync(path.join(dir, name), "");
|
|
1168
|
-
truncated += 1;
|
|
1169
|
-
}
|
|
1170
|
-
}
|
|
1171
|
-
expect(truncated).toBeGreaterThan(0);
|
|
1172
|
-
}
|
|
1173
|
-
it("marks the manifest ledgerUnavailable instead of reporting every file as untracked", async () => {
|
|
1174
|
-
const abs = writeFileAt("notes/tracked.md", "hello");
|
|
1175
|
-
seedV3OnlyLedger({ "notes/tracked.md": journalEntryFor(abs) });
|
|
1176
|
-
truncateEveryGeneration();
|
|
1177
|
-
const result = await buildSyncManifest(baseOptions({ listJournalsImpl: undefined, persistSnapshot: false }));
|
|
1178
|
-
// THE assertion. Without it the pass looks exactly like a healthy machine
|
|
1179
|
-
// that has never tracked anything, and uploads on that basis.
|
|
1180
|
-
expect(result.chunks[0].ledgerUnavailable).toEqual({ errorClass: "StateStoreCorruptionError" });
|
|
1181
|
-
// The disk picture is still reported — the audit wants it, and every entry
|
|
1182
|
-
// honestly says "ledger unknown" rather than "ledger says no".
|
|
1183
|
-
expect(result.chunks[0].entries.map((entry) => entry.path)).toEqual(["notes/tracked.md"]);
|
|
1184
|
-
expect(result.chunks[0].entries.every((entry) => entry.source === "disk")).toBe(true);
|
|
1185
|
-
});
|
|
1186
|
-
it("reads a healthy v3 store that has no legacy shard beside it", async () => {
|
|
1187
|
-
const abs = writeFileAt("notes/tracked.md", "hello");
|
|
1188
|
-
seedV3OnlyLedger({ "notes/tracked.md": journalEntryFor(abs) });
|
|
1189
|
-
const result = await buildSyncManifest(baseOptions({ listJournalsImpl: undefined, persistSnapshot: false }));
|
|
1190
|
-
// The other half of the same bug: the pre-fix gate only enumerated LEGACY
|
|
1191
|
-
// locator files, so a fully-migrated scope read as "no ledger" even when
|
|
1192
|
-
// its store was perfectly healthy.
|
|
1193
|
-
expect(result.chunks[0].ledgerUnavailable).toBeUndefined();
|
|
1194
|
-
expect(result.chunks[0].entries[0].source).toBe("both");
|
|
1195
|
-
});
|
|
1196
|
-
it("treats a fresh install with no ledger artifacts as an EMPTY ledger, not an unavailable one", async () => {
|
|
1197
|
-
writeFileAt("notes/new.md", "hello");
|
|
1198
|
-
const result = await buildSyncManifest(baseOptions({ listJournalsImpl: undefined, persistSnapshot: false }));
|
|
1199
|
-
expect(result.chunks[0].ledgerUnavailable).toBeUndefined();
|
|
1200
|
-
expect(result.chunks[0].entries[0].source).toBe("disk");
|
|
1201
|
-
});
|
|
1202
|
-
});
|
|
1203
|
-
describe("buildSyncManifest — company anchor in the wire scope", () => {
|
|
1204
|
-
it("ships the slug and scope root it actually walked for a company scope", async () => {
|
|
1205
|
-
writeFileAt("companies/acme/deal.md", "acme");
|
|
1206
|
-
const result = await buildSyncManifest(baseOptions({ scope: { kind: "company", companyUid: "co-acme", slug: "acme" } }));
|
|
1207
|
-
expect(result.chunks[0].scope.companySlug).toBe("acme");
|
|
1208
|
-
expect(result.chunks[0].scope.scopeRoot).toBe("companies/acme");
|
|
1209
|
-
});
|
|
1210
|
-
it("ships neither anchor field for a personal scope", async () => {
|
|
1211
|
-
writeFileAt("notes/keep.md", "keep");
|
|
1212
|
-
const result = await buildSyncManifest(baseOptions());
|
|
1213
|
-
expect(result.chunks[0].scope.companySlug).toBeUndefined();
|
|
1214
|
-
expect(result.chunks[0].scope.scopeRoot).toBeUndefined();
|
|
1215
|
-
});
|
|
1216
|
-
});
|
|
1217
|
-
//# sourceMappingURL=build-manifest.test.js.map
|