@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,1335 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Unit tests for the shared manifest upload pass (US-004).
|
|
3
|
-
*
|
|
4
|
-
* Every acceptance criterion of the story is a `it()` here, and the suite is
|
|
5
|
-
* deliberately network-free: the transport is the injected seam, so the whole
|
|
6
|
-
* protocol — resend_full, soft skip, 413, the `materialised` gate — is
|
|
7
|
-
* exercised by returning literal server bodies.
|
|
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
|
-
import { lockPathFor } from "../operation-lock.js";
|
|
14
|
-
import { PERSONAL_VAULT_JOURNAL_SLUG } from "../journal.js";
|
|
15
|
-
import { validateSyncManifestUpload } from "./contract.js";
|
|
16
|
-
import { SYNC_MANIFEST_DEFAULT_CHUNK_BYTE_BUDGET, SYNC_MANIFEST_TOO_LARGE_REASON_CODE, } from "./contract.js";
|
|
17
|
-
import { MANIFEST_SNAPSHOT_DIRNAME, manifestSnapshotScopeKey, manifestSnapshotScopeSlug, readManifestSnapshot, writeManifestSnapshot, } from "./snapshot-store.js";
|
|
18
|
-
import { DEFAULT_MANIFEST_UPLOAD_MIN_INTERVAL_MS, MANIFEST_CHUNK_BYTE_BUDGET_FLOOR, MANIFEST_UPLOAD_DISABLED_ENV, MANIFEST_UPLOAD_LOCK_COMMAND, readManifestUploadStatus, MANIFEST_FAILURE_BACKOFF_BASE_MS, manifestFailureBackoffMs, runManifestUploadPass, } from "./upload-manifest.js";
|
|
19
|
-
const NOW_MS = Date.parse("2026-09-04T12:00:00.000Z");
|
|
20
|
-
const SCOPE_KEY = manifestSnapshotScopeKey({ kind: "personal" });
|
|
21
|
-
let hqRoot;
|
|
22
|
-
let stateDir;
|
|
23
|
-
let previousLockStateDir;
|
|
24
|
-
beforeEach(() => {
|
|
25
|
-
const base = fs.mkdtempSync(path.join(os.tmpdir(), "hq-manifest-upload-"));
|
|
26
|
-
hqRoot = path.join(base, "hq");
|
|
27
|
-
stateDir = path.join(base, "state");
|
|
28
|
-
// The per-scope upload lock lives under `HQ_STATE_DIR || ~/.hq` (it is
|
|
29
|
-
// machine-local and must never round-trip to the cloud). Point it at this
|
|
30
|
-
// test's temp dir so the suite never writes into the developer's real ~/.hq.
|
|
31
|
-
previousLockStateDir = process.env.HQ_STATE_DIR;
|
|
32
|
-
process.env.HQ_STATE_DIR = path.join(base, "lock-state");
|
|
33
|
-
fs.mkdirSync(hqRoot, { recursive: true });
|
|
34
|
-
fs.mkdirSync(stateDir, { recursive: true });
|
|
35
|
-
fs.mkdirSync(path.join(hqRoot, "notes"), { recursive: true });
|
|
36
|
-
fs.writeFileSync(path.join(hqRoot, "notes", "a.md"), "alpha", "utf8");
|
|
37
|
-
// More than one file so a shrunk `maxEntriesPerChunk` actually produces a
|
|
38
|
-
// multi-chunk pass.
|
|
39
|
-
fs.writeFileSync(path.join(hqRoot, "notes", "b.md"), "bravo", "utf8");
|
|
40
|
-
fs.writeFileSync(path.join(hqRoot, "notes", "c.md"), "charlie", "utf8");
|
|
41
|
-
});
|
|
42
|
-
afterEach(() => {
|
|
43
|
-
vi.useRealTimers();
|
|
44
|
-
if (previousLockStateDir === undefined)
|
|
45
|
-
delete process.env.HQ_STATE_DIR;
|
|
46
|
-
else
|
|
47
|
-
process.env.HQ_STATE_DIR = previousLockStateDir;
|
|
48
|
-
});
|
|
49
|
-
/** The happy-path server answer: folded and materialised. */
|
|
50
|
-
function accepted(overrides = {}) {
|
|
51
|
-
return {
|
|
52
|
-
status: 200,
|
|
53
|
-
body: {
|
|
54
|
-
status: "accepted",
|
|
55
|
-
snapshotId: "snap-server",
|
|
56
|
-
scopeKey: SCOPE_KEY,
|
|
57
|
-
chunkIndex: 0,
|
|
58
|
-
receivedChunks: 1,
|
|
59
|
-
chunkCount: 1,
|
|
60
|
-
completed: true,
|
|
61
|
-
materialised: true,
|
|
62
|
-
...overrides,
|
|
63
|
-
},
|
|
64
|
-
};
|
|
65
|
-
}
|
|
66
|
-
function transportReturning(...responses) {
|
|
67
|
-
const queue = [...responses];
|
|
68
|
-
return vi.fn(async () => queue.length > 1 ? queue.shift() : queue[0]);
|
|
69
|
-
}
|
|
70
|
-
function baseOptions(overrides = {}) {
|
|
71
|
-
return {
|
|
72
|
-
scope: { kind: "personal" },
|
|
73
|
-
hqRoot,
|
|
74
|
-
stateDir,
|
|
75
|
-
installationId: "install-abc123",
|
|
76
|
-
machineId: "machine-abc123",
|
|
77
|
-
source: "cli",
|
|
78
|
-
now: NOW_MS,
|
|
79
|
-
env: {},
|
|
80
|
-
listJournalsImpl: () => [],
|
|
81
|
-
transport: transportReturning(accepted()),
|
|
82
|
-
// These suites predate the stat-only default and assert on computed
|
|
83
|
-
// hashes throughout. `full` preserves exactly what they were written to
|
|
84
|
-
// exercise; the stat-only DEFAULT gets its own dedicated coverage below.
|
|
85
|
-
hashPolicy: "full",
|
|
86
|
-
...overrides,
|
|
87
|
-
};
|
|
88
|
-
}
|
|
89
|
-
/**
|
|
90
|
-
* A stubbed ledger that TRACKS the fixture's files.
|
|
91
|
-
*
|
|
92
|
-
* `baseOptions` stubs the ledger away entirely (`listJournalsImpl: () => []`),
|
|
93
|
-
* which makes every entry `source: "disk"` — on-disk-never-tracked. That is a
|
|
94
|
-
* perfectly good stub for a first-sync scope, but it is exactly the shape the
|
|
95
|
-
* `ledger_implausible` guard refuses when a prior snapshot says the scope WAS
|
|
96
|
-
* tracked. A test that wants a full upload against a seeded snapshot has to
|
|
97
|
-
* describe a machine that could really exist: one whose ledger knows its files.
|
|
98
|
-
*/
|
|
99
|
-
function ledgerTracking(...relativePaths) {
|
|
100
|
-
const files = {};
|
|
101
|
-
for (const relative of relativePaths) {
|
|
102
|
-
files[relative] = {
|
|
103
|
-
hash: `sha256:${"a".repeat(64)}`,
|
|
104
|
-
size: 5,
|
|
105
|
-
syncedAt: new Date(NOW_MS - 60_000).toISOString(),
|
|
106
|
-
direction: "up",
|
|
107
|
-
};
|
|
108
|
-
}
|
|
109
|
-
return () => [
|
|
110
|
-
{
|
|
111
|
-
slug: PERSONAL_VAULT_JOURNAL_SLUG,
|
|
112
|
-
path: path.join(stateDir, `sync-journal.${PERSONAL_VAULT_JOURNAL_SLUG}.json`),
|
|
113
|
-
journal: { version: "2", lastSync: "", files, pulls: [] },
|
|
114
|
-
},
|
|
115
|
-
];
|
|
116
|
-
}
|
|
117
|
-
function seedSnapshot(overrides = {}) {
|
|
118
|
-
const snapshot = {
|
|
119
|
-
snapshotId: "snap-previous",
|
|
120
|
-
generatedAt: new Date(NOW_MS - 60_000).toISOString(),
|
|
121
|
-
sequence: 4,
|
|
122
|
-
entries: { "notes/a.md": { hash: "a".repeat(64), size: 5, mtimeMs: 1 } },
|
|
123
|
-
...overrides,
|
|
124
|
-
};
|
|
125
|
-
writeManifestSnapshot(stateDir, SCOPE_KEY, snapshot);
|
|
126
|
-
return snapshot;
|
|
127
|
-
}
|
|
128
|
-
describe("runManifestUploadPass — kill switch", () => {
|
|
129
|
-
it.each(["1", "true", "TRUE", " 1 "])("is disabled by %s without building or uploading", async (value) => {
|
|
130
|
-
const transport = transportReturning(accepted());
|
|
131
|
-
const result = await runManifestUploadPass(baseOptions({ transport, env: { [MANIFEST_UPLOAD_DISABLED_ENV]: value } }));
|
|
132
|
-
expect(result.status).toBe("disabled");
|
|
133
|
-
expect(transport).not.toHaveBeenCalled();
|
|
134
|
-
// Nothing was built, so nothing was written.
|
|
135
|
-
expect(readManifestSnapshot(stateDir, SCOPE_KEY)).toBeNull();
|
|
136
|
-
});
|
|
137
|
-
it.each(["", "0", "false", "no"])("is NOT disabled by %s", async (value) => {
|
|
138
|
-
const transport = transportReturning(accepted());
|
|
139
|
-
const result = await runManifestUploadPass(baseOptions({ transport, env: { [MANIFEST_UPLOAD_DISABLED_ENV]: value } }));
|
|
140
|
-
expect(result.status).toBe("uploaded");
|
|
141
|
-
expect(transport).toHaveBeenCalled();
|
|
142
|
-
});
|
|
143
|
-
});
|
|
144
|
-
describe("runManifestUploadPass — throttle", () => {
|
|
145
|
-
it("skips a scope uploaded less than the interval ago, without building", async () => {
|
|
146
|
-
seedSnapshot({ lastUploadAt: new Date(NOW_MS - 60_000).toISOString() });
|
|
147
|
-
const transport = transportReturning(accepted());
|
|
148
|
-
const result = await runManifestUploadPass(baseOptions({ transport }));
|
|
149
|
-
expect(result).toEqual({ status: "throttled", scopeKey: SCOPE_KEY });
|
|
150
|
-
expect(transport).not.toHaveBeenCalled();
|
|
151
|
-
});
|
|
152
|
-
it("runs once the interval has elapsed", async () => {
|
|
153
|
-
seedSnapshot({
|
|
154
|
-
lastUploadAt: new Date(NOW_MS - DEFAULT_MANIFEST_UPLOAD_MIN_INTERVAL_MS - 1).toISOString(),
|
|
155
|
-
});
|
|
156
|
-
const result = await runManifestUploadPass(baseOptions());
|
|
157
|
-
expect(result.status).toBe("uploaded");
|
|
158
|
-
});
|
|
159
|
-
it("bypasses the throttle for an explicit invocation (ignoreThrottle)", async () => {
|
|
160
|
-
seedSnapshot({ lastUploadAt: new Date(NOW_MS - 60_000).toISOString() });
|
|
161
|
-
const result = await runManifestUploadPass(baseOptions({ ignoreThrottle: true }));
|
|
162
|
-
expect(result.status).toBe("uploaded");
|
|
163
|
-
});
|
|
164
|
-
it("bypasses the throttle for dryRun (printing is read-only)", async () => {
|
|
165
|
-
seedSnapshot({ lastUploadAt: new Date(NOW_MS - 60_000).toISOString() });
|
|
166
|
-
const transport = transportReturning(accepted());
|
|
167
|
-
const result = await runManifestUploadPass(baseOptions({ transport, dryRun: true }));
|
|
168
|
-
expect(result.status).toBe("printed");
|
|
169
|
-
expect(transport).not.toHaveBeenCalled();
|
|
170
|
-
});
|
|
171
|
-
it("never throttles on a future lastUploadAt (clock skew must not wedge uploads off)", async () => {
|
|
172
|
-
seedSnapshot({ lastUploadAt: new Date(NOW_MS + 5 * 60_000).toISOString() });
|
|
173
|
-
const result = await runManifestUploadPass(baseOptions());
|
|
174
|
-
expect(result.status).toBe("uploaded");
|
|
175
|
-
});
|
|
176
|
-
});
|
|
177
|
-
describe("runManifestUploadPass — dry run", () => {
|
|
178
|
-
it("returns valid chunks, uploads nothing, and persists nothing", async () => {
|
|
179
|
-
const transport = transportReturning(accepted());
|
|
180
|
-
const result = await runManifestUploadPass(baseOptions({ transport, dryRun: true }));
|
|
181
|
-
expect(result.status).toBe("printed");
|
|
182
|
-
expect(transport).not.toHaveBeenCalled();
|
|
183
|
-
expect(result.chunks?.length).toBeGreaterThan(0);
|
|
184
|
-
for (const chunk of result.chunks) {
|
|
185
|
-
expect(() => validateSyncManifestUpload(chunk)).not.toThrow();
|
|
186
|
-
}
|
|
187
|
-
expect(readManifestSnapshot(stateDir, SCOPE_KEY)).toBeNull();
|
|
188
|
-
});
|
|
189
|
-
});
|
|
190
|
-
describe("runManifestUploadPass — upload", () => {
|
|
191
|
-
it("persists the snapshot as the delta base only once materialised", async () => {
|
|
192
|
-
const result = await runManifestUploadPass(baseOptions());
|
|
193
|
-
expect(result.status).toBe("uploaded");
|
|
194
|
-
expect(result.nextModeFull).toBe(false);
|
|
195
|
-
const stored = readManifestSnapshot(stateDir, SCOPE_KEY);
|
|
196
|
-
expect(stored?.snapshotId).toBe(result.snapshotId);
|
|
197
|
-
expect(stored?.baseValid).toBe(true);
|
|
198
|
-
expect(stored?.lastUploadAt).toBe(new Date(NOW_MS).toISOString());
|
|
199
|
-
expect(Object.keys(stored?.entries ?? {})).toContain("notes/a.md");
|
|
200
|
-
});
|
|
201
|
-
it("reports a REFUSED fold as failed, not uploaded, and takes the failure backoff", async () => {
|
|
202
|
-
// The canary regression. Every chunk is accepted, so the transport looks
|
|
203
|
-
// perfect — but `materialised: false` means the server wrote nothing. This
|
|
204
|
-
// used to return `uploaded` and stamp `lastUploadAt`, which armed the full
|
|
205
|
-
// 24h throttle on a failure and reported it to the daemon as a success.
|
|
206
|
-
seedSnapshot();
|
|
207
|
-
const result = await runManifestUploadPass(baseOptions({
|
|
208
|
-
ignoreThrottle: true,
|
|
209
|
-
transport: transportReturning(accepted({ materialised: false })),
|
|
210
|
-
}));
|
|
211
|
-
expect(result.status).toBe("failed");
|
|
212
|
-
expect(result.error?.kind).toBe("not_materialised");
|
|
213
|
-
expect(result.nextModeFull).toBe(true);
|
|
214
|
-
const stored = readManifestSnapshot(stateDir, SCOPE_KEY);
|
|
215
|
-
expect(stored?.snapshotId).not.toBe(result.snapshotId);
|
|
216
|
-
expect(stored?.baseValid).toBe(false);
|
|
217
|
-
expect(stored?.lastUploadAt).toBeUndefined();
|
|
218
|
-
expect(stored?.lastAttemptAt).toBe(new Date(NOW_MS).toISOString());
|
|
219
|
-
expect(stored?.consecutiveFailures).toBe(1);
|
|
220
|
-
});
|
|
221
|
-
it("sends chunks sequentially in ascending chunkIndex with identical snapshot-id inputs", async () => {
|
|
222
|
-
// Three files + one entry per chunk = a genuinely multi-chunk pass. With
|
|
223
|
-
// the default 50 000-entry chunk size this fixture is ONE chunk, and every
|
|
224
|
-
// assertion below is vacuously true.
|
|
225
|
-
const seen = [];
|
|
226
|
-
const transport = vi.fn(async (chunk) => {
|
|
227
|
-
seen.push(chunk);
|
|
228
|
-
return accepted({
|
|
229
|
-
chunkIndex: chunk.chunkIndex,
|
|
230
|
-
chunkCount: chunk.chunkCount,
|
|
231
|
-
completed: chunk.chunkIndex === chunk.chunkCount - 1,
|
|
232
|
-
materialised: chunk.chunkIndex === chunk.chunkCount - 1,
|
|
233
|
-
});
|
|
234
|
-
});
|
|
235
|
-
const result = await runManifestUploadPass(baseOptions({ transport, maxEntriesPerChunk: 1 }));
|
|
236
|
-
expect(result.status).toBe("uploaded");
|
|
237
|
-
expect(seen.length).toBeGreaterThan(1);
|
|
238
|
-
expect(result.chunkCount).toBe(seen.length);
|
|
239
|
-
expect(result.uploadedChunks).toBe(seen.length);
|
|
240
|
-
expect(seen.map((chunk) => chunk.chunkIndex)).toEqual(seen.map((_chunk, index) => index));
|
|
241
|
-
const first = seen[0];
|
|
242
|
-
for (const chunk of seen) {
|
|
243
|
-
// The server hashes exactly these fields into the snapshotId, so a
|
|
244
|
-
// single drifting field scatters one pass across several snapshots.
|
|
245
|
-
expect(chunk.generatedAt).toBe(first.generatedAt);
|
|
246
|
-
expect(chunk.sequence).toBe(first.sequence);
|
|
247
|
-
expect(chunk.chunkCount).toBe(first.chunkCount);
|
|
248
|
-
expect(chunk.machineId).toBe(first.machineId);
|
|
249
|
-
expect(chunk.installationId).toBe(first.installationId);
|
|
250
|
-
expect(chunk.mode).toBe(first.mode);
|
|
251
|
-
expect(chunk.scope).toEqual(first.scope);
|
|
252
|
-
}
|
|
253
|
-
});
|
|
254
|
-
it("refuses the delta base when the server's snapshotId drifts between chunks", async () => {
|
|
255
|
-
let call = 0;
|
|
256
|
-
const transport = vi.fn(async (chunk) => {
|
|
257
|
-
call += 1;
|
|
258
|
-
return accepted({
|
|
259
|
-
snapshotId: `snap-server-${call}`,
|
|
260
|
-
chunkIndex: chunk.chunkIndex,
|
|
261
|
-
chunkCount: chunk.chunkCount,
|
|
262
|
-
completed: chunk.chunkIndex === chunk.chunkCount - 1,
|
|
263
|
-
materialised: true,
|
|
264
|
-
});
|
|
265
|
-
});
|
|
266
|
-
const error = vi.fn();
|
|
267
|
-
const result = await runManifestUploadPass(baseOptions({ transport, maxEntriesPerChunk: 1, logger: { error } }));
|
|
268
|
-
// Two ids for one pass means the snapshot is scattered across rows that
|
|
269
|
-
// can each only ever stay partial — none of them is a legal base.
|
|
270
|
-
expect(result.nextModeFull).toBe(true);
|
|
271
|
-
expect(error).toHaveBeenCalled();
|
|
272
|
-
expect(readManifestSnapshot(stateDir, SCOPE_KEY)?.baseValid).toBe(false);
|
|
273
|
-
});
|
|
274
|
-
it("stops mid-pass on an abort and still burns the sequence", async () => {
|
|
275
|
-
seedSnapshot({ sequence: 30 });
|
|
276
|
-
const controller = new AbortController();
|
|
277
|
-
const seen = [];
|
|
278
|
-
const transport = vi.fn(async (chunk) => {
|
|
279
|
-
seen.push(chunk);
|
|
280
|
-
controller.abort();
|
|
281
|
-
return accepted({ chunkIndex: chunk.chunkIndex, completed: false, materialised: false });
|
|
282
|
-
});
|
|
283
|
-
const result = await runManifestUploadPass(baseOptions({
|
|
284
|
-
transport,
|
|
285
|
-
ignoreThrottle: true,
|
|
286
|
-
maxEntriesPerChunk: 1,
|
|
287
|
-
abortSignal: controller.signal,
|
|
288
|
-
}));
|
|
289
|
-
expect(result.status).toBe("failed");
|
|
290
|
-
expect(result.error?.kind).toBe("aborted");
|
|
291
|
-
// The remaining chunks were NOT sent...
|
|
292
|
-
expect(seen).toHaveLength(1);
|
|
293
|
-
expect((result.chunkCount ?? 0) > seen.length).toBe(true);
|
|
294
|
-
// ...and the half-spent sequence is never handed out again.
|
|
295
|
-
expect(readManifestSnapshot(stateDir, SCOPE_KEY)?.sequence).toBe(31);
|
|
296
|
-
// Nothing materialised, so this must not have armed the 24h throttle.
|
|
297
|
-
expect(readManifestSnapshot(stateDir, SCOPE_KEY)?.lastUploadAt).toBeUndefined();
|
|
298
|
-
});
|
|
299
|
-
it("never puts a caller-selected identity on the wire", async () => {
|
|
300
|
-
const seen = [];
|
|
301
|
-
const transport = vi.fn(async (chunk) => {
|
|
302
|
-
seen.push(chunk);
|
|
303
|
-
return accepted();
|
|
304
|
-
});
|
|
305
|
-
await runManifestUploadPass(baseOptions({ transport }));
|
|
306
|
-
for (const chunk of seen) {
|
|
307
|
-
const raw = chunk;
|
|
308
|
-
expect(raw.personUid).toBeUndefined();
|
|
309
|
-
expect(raw.person_uid).toBeUndefined();
|
|
310
|
-
expect(raw.ownerUid).toBeUndefined();
|
|
311
|
-
expect(raw.callerSub).toBeUndefined();
|
|
312
|
-
}
|
|
313
|
-
});
|
|
314
|
-
});
|
|
315
|
-
describe("runManifestUploadPass — resend_full", () => {
|
|
316
|
-
it("stops the pass, clears the base, and schedules a full manifest", async () => {
|
|
317
|
-
seedSnapshot();
|
|
318
|
-
const transport = transportReturning({ status: 200, body: { status: "resend_full", scopeKey: SCOPE_KEY } });
|
|
319
|
-
const result = await runManifestUploadPass(baseOptions({ ignoreThrottle: true, transport }));
|
|
320
|
-
expect(result.status).toBe("resend_full_scheduled");
|
|
321
|
-
expect(result.nextModeFull).toBe(true);
|
|
322
|
-
expect(transport).toHaveBeenCalledTimes(1);
|
|
323
|
-
const stored = readManifestSnapshot(stateDir, SCOPE_KEY);
|
|
324
|
-
expect(stored?.baseValid).toBe(false);
|
|
325
|
-
expect(stored?.entries).toEqual({});
|
|
326
|
-
});
|
|
327
|
-
it("abandons the pass on snapshot_abandoned without retrying the same snapshot", async () => {
|
|
328
|
-
const transport = transportReturning({
|
|
329
|
-
status: 200,
|
|
330
|
-
body: { status: "resend_full", scopeKey: SCOPE_KEY, reason: "snapshot_abandoned" },
|
|
331
|
-
});
|
|
332
|
-
const result = await runManifestUploadPass(baseOptions({ transport }));
|
|
333
|
-
expect(result.status).toBe("resend_full_scheduled");
|
|
334
|
-
expect(result.error?.detail).toBe("snapshot_abandoned");
|
|
335
|
-
expect(transport).toHaveBeenCalledTimes(1);
|
|
336
|
-
});
|
|
337
|
-
it("does NOT read a non-2xx as resend_full (the server's 409 is VAULT_NOT_PROVISIONED)", async () => {
|
|
338
|
-
const result = await runManifestUploadPass(baseOptions({
|
|
339
|
-
transport: transportReturning({
|
|
340
|
-
status: 409,
|
|
341
|
-
body: { error: "Vault bucket is not provisioned", code: "VAULT_NOT_PROVISIONED" },
|
|
342
|
-
}),
|
|
343
|
-
}));
|
|
344
|
-
// A hard misconfiguration must surface as a failure, and must NOT arm the
|
|
345
|
-
// 24h throttle — otherwise each attempt hides the fault for a day.
|
|
346
|
-
expect(result.status).toBe("failed");
|
|
347
|
-
expect(result.error?.kind).toBe("rejected");
|
|
348
|
-
expect(result.error?.status).toBe(409);
|
|
349
|
-
expect(result.error?.detail).toBe("VAULT_NOT_PROVISIONED");
|
|
350
|
-
expect(readManifestSnapshot(stateDir, SCOPE_KEY)?.lastUploadAt).toBeUndefined();
|
|
351
|
-
});
|
|
352
|
-
it("still reads a 200 body that says resend_full", async () => {
|
|
353
|
-
const result = await runManifestUploadPass(baseOptions({ transport: transportReturning({ status: 200, body: { status: "resend_full" } }) }));
|
|
354
|
-
expect(result.status).toBe("resend_full_scheduled");
|
|
355
|
-
});
|
|
356
|
-
it("makes the NEXT pass full after the base is invalidated", async () => {
|
|
357
|
-
seedSnapshot();
|
|
358
|
-
await runManifestUploadPass(baseOptions({
|
|
359
|
-
ignoreThrottle: true,
|
|
360
|
-
transport: transportReturning({ status: 200, body: { status: "resend_full", scopeKey: SCOPE_KEY } }),
|
|
361
|
-
}));
|
|
362
|
-
const seen = [];
|
|
363
|
-
const second = await runManifestUploadPass(baseOptions({
|
|
364
|
-
ignoreThrottle: true,
|
|
365
|
-
transport: vi.fn(async (chunk) => {
|
|
366
|
-
seen.push(chunk);
|
|
367
|
-
return accepted();
|
|
368
|
-
}),
|
|
369
|
-
}));
|
|
370
|
-
expect(second.mode).toBe("full");
|
|
371
|
-
expect(seen[0].mode).toBe("full");
|
|
372
|
-
expect(seen[0].baseSnapshotId).toBeUndefined();
|
|
373
|
-
});
|
|
374
|
-
});
|
|
375
|
-
describe("runManifestUploadPass — soft skip and size limits", () => {
|
|
376
|
-
it("treats 404 as a soft skip with no error and no delta base", async () => {
|
|
377
|
-
const info = vi.fn();
|
|
378
|
-
const result = await runManifestUploadPass(baseOptions({ transport: transportReturning({ status: 404 }), logger: { info } }));
|
|
379
|
-
expect(result.status).toBe("soft_skipped");
|
|
380
|
-
expect(result.error).toBeUndefined();
|
|
381
|
-
expect(info).toHaveBeenCalled();
|
|
382
|
-
expect(readManifestSnapshot(stateDir, SCOPE_KEY)?.baseValid).toBe(false);
|
|
383
|
-
});
|
|
384
|
-
/**
|
|
385
|
-
* THE 6.16.21 CANARY REGRESSION, PINNED.
|
|
386
|
-
*
|
|
387
|
-
* `chunk_too_large` used to take `recordUploadAttempt`, which writes
|
|
388
|
-
* `lastUploadAt` — the field the 24h throttle reads. A scope that could not
|
|
389
|
-
* physically upload therefore reported a healthy last-upload time and then
|
|
390
|
-
* refused to try again for a day. These cases assert the opposite of the
|
|
391
|
-
* three things that made that possible: no `lastUploadAt`, a real failure
|
|
392
|
-
* count, and a smaller budget waiting for the next pass.
|
|
393
|
-
*/
|
|
394
|
-
it("treats chunk_too_large as a CLIENT fault: failure backoff, never the 24h throttle", async () => {
|
|
395
|
-
const result = await runManifestUploadPass(baseOptions({ transport: transportReturning({ status: 413, body: { reason: "chunk_too_large" } }) }));
|
|
396
|
-
expect(result.status).toBe("failed");
|
|
397
|
-
expect(result.error?.kind).toBe("chunk_too_large");
|
|
398
|
-
const stored = readManifestSnapshot(stateDir, SCOPE_KEY);
|
|
399
|
-
// The load-bearing assertion. `lastUploadAt` means "the server has a
|
|
400
|
-
// picture from this time"; a 413 is proof that it does not.
|
|
401
|
-
expect(stored?.lastUploadAt).toBeUndefined();
|
|
402
|
-
expect(stored?.lastAttemptAt).toBe(new Date(NOW_MS).toISOString());
|
|
403
|
-
expect(stored?.consecutiveFailures).toBe(1);
|
|
404
|
-
});
|
|
405
|
-
it("halves the byte budget on a 413 and persists it for the NEXT pass", async () => {
|
|
406
|
-
const first = await runManifestUploadPass(baseOptions({ transport: transportReturning({ status: 413, body: { reason: "chunk_too_large" } }) }));
|
|
407
|
-
const halved = SYNC_MANIFEST_DEFAULT_CHUNK_BYTE_BUDGET / 2;
|
|
408
|
-
expect(first.chunkByteBudget).toBe(halved);
|
|
409
|
-
expect(readManifestSnapshot(stateDir, SCOPE_KEY)?.chunkByteBudget).toBe(halved);
|
|
410
|
-
// The next pass PLANS against the reduced budget rather than re-deriving
|
|
411
|
-
// the default and failing identically forever. `ignoreThrottle` stands in
|
|
412
|
-
// for the hour the failure backoff would otherwise impose.
|
|
413
|
-
const second = await runManifestUploadPass(baseOptions({
|
|
414
|
-
ignoreThrottle: true,
|
|
415
|
-
transport: transportReturning({ status: 413, body: { reason: "chunk_too_large" } }),
|
|
416
|
-
}));
|
|
417
|
-
expect(second.chunkByteBudget).toBe(halved / 2);
|
|
418
|
-
expect(readManifestSnapshot(stateDir, SCOPE_KEY)?.chunkByteBudget).toBe(halved / 2);
|
|
419
|
-
expect(readManifestSnapshot(stateDir, SCOPE_KEY)?.consecutiveFailures).toBe(2);
|
|
420
|
-
});
|
|
421
|
-
it("halves only ONCE per pass — never retries a resized plan mid-flight", async () => {
|
|
422
|
-
// A mid-pass re-plan would move `chunkCount`, which the server hashes into
|
|
423
|
-
// the snapshotId, so one 413 must end the pass rather than shrink and
|
|
424
|
-
// resend. Asserted on the transport: exactly one POST, not two.
|
|
425
|
-
const transport = transportReturning({ status: 413, body: { reason: "chunk_too_large" } });
|
|
426
|
-
const result = await runManifestUploadPass(baseOptions({ transport }));
|
|
427
|
-
expect(transport).toHaveBeenCalledTimes(1);
|
|
428
|
-
expect(result.chunkByteBudget).toBe(SYNC_MANIFEST_DEFAULT_CHUNK_BYTE_BUDGET / 2);
|
|
429
|
-
});
|
|
430
|
-
it("stops halving at the floor rather than shrinking towards zero", async () => {
|
|
431
|
-
const result = await runManifestUploadPass(baseOptions({
|
|
432
|
-
chunkByteBudget: MANIFEST_CHUNK_BYTE_BUDGET_FLOOR,
|
|
433
|
-
transport: transportReturning({ status: 413, body: { reason: "chunk_too_large" } }),
|
|
434
|
-
}));
|
|
435
|
-
expect(result.chunkByteBudget).toBe(MANIFEST_CHUNK_BYTE_BUDGET_FLOOR);
|
|
436
|
-
expect(readManifestSnapshot(stateDir, SCOPE_KEY)?.chunkByteBudget).toBe(MANIFEST_CHUNK_BYTE_BUDGET_FLOOR);
|
|
437
|
-
});
|
|
438
|
-
it("treats a bare 413 with no reason (the gateway's own answer) as a sizing fault", async () => {
|
|
439
|
-
// API Gateway refuses an oversized body BEFORE the handler runs and
|
|
440
|
-
// answers with an opaque error carrying no reason code. That is the exact
|
|
441
|
-
// shape the operator's canary hit, so it must self-heal identically.
|
|
442
|
-
const result = await runManifestUploadPass(baseOptions({ transport: transportReturning({ status: 413, body: { message: "Request Entity Too Large" } }) }));
|
|
443
|
-
expect(result.error?.kind).toBe("chunk_too_large");
|
|
444
|
-
expect(result.chunkByteBudget).toBe(SYNC_MANIFEST_DEFAULT_CHUNK_BYTE_BUDGET / 2);
|
|
445
|
-
expect(readManifestSnapshot(stateDir, SCOPE_KEY)?.lastUploadAt).toBeUndefined();
|
|
446
|
-
});
|
|
447
|
-
it("distinguishes manifest_too_large on a 413 — non-retryable, so it backs off without shrinking", async () => {
|
|
448
|
-
const result = await runManifestUploadPass(baseOptions({
|
|
449
|
-
transport: transportReturning({
|
|
450
|
-
status: 413,
|
|
451
|
-
body: { reason: "manifest_too_large", maxTotalEntries: 10, scopeKey: SCOPE_KEY },
|
|
452
|
-
}),
|
|
453
|
-
}));
|
|
454
|
-
expect(result.error?.kind).toBe("manifest_too_large");
|
|
455
|
-
expect(result.nextModeFull).toBe(true);
|
|
456
|
-
const stored = readManifestSnapshot(stateDir, SCOPE_KEY);
|
|
457
|
-
expect(stored?.baseValid).toBe(false);
|
|
458
|
-
// Re-sending at ANY chunk size gets the same answer — the ceiling counts
|
|
459
|
-
// entries, not bytes — so this one must NOT shrink the budget. It is still
|
|
460
|
-
// a failure, so it takes the escalating backoff (1h, climbing to the same
|
|
461
|
-
// 24h ceiling) rather than claiming an upload that never landed.
|
|
462
|
-
expect(stored?.lastUploadAt).toBeUndefined();
|
|
463
|
-
expect(stored?.lastAttemptAt).toBe(new Date(NOW_MS).toISOString());
|
|
464
|
-
expect(stored?.consecutiveFailures).toBe(1);
|
|
465
|
-
expect(stored?.chunkByteBudget).toBeUndefined();
|
|
466
|
-
});
|
|
467
|
-
});
|
|
468
|
-
describe("runManifestUploadPass — sequence", () => {
|
|
469
|
-
it("starts at 1 with no snapshot and strictly increases across passes", async () => {
|
|
470
|
-
const seen = [];
|
|
471
|
-
const transport = vi.fn(async (chunk) => {
|
|
472
|
-
seen.push(chunk);
|
|
473
|
-
return accepted();
|
|
474
|
-
});
|
|
475
|
-
await runManifestUploadPass(baseOptions({ transport }));
|
|
476
|
-
await runManifestUploadPass(baseOptions({ transport, ignoreThrottle: true }));
|
|
477
|
-
await runManifestUploadPass(baseOptions({ transport, ignoreThrottle: true }));
|
|
478
|
-
const sequences = seen.map((chunk) => chunk.sequence);
|
|
479
|
-
expect(sequences[0]).toBe(1);
|
|
480
|
-
for (let index = 1; index < sequences.length; index += 1) {
|
|
481
|
-
expect(sequences[index]).toBeGreaterThan(sequences[index - 1]);
|
|
482
|
-
}
|
|
483
|
-
});
|
|
484
|
-
it("advances the sequence even when the pass did not materialise", async () => {
|
|
485
|
-
seedSnapshot({ sequence: 9 });
|
|
486
|
-
await runManifestUploadPass(baseOptions({ ignoreThrottle: true, transport: transportReturning({ status: 404 }) }));
|
|
487
|
-
expect(readManifestSnapshot(stateDir, SCOPE_KEY)?.sequence).toBe(10);
|
|
488
|
-
});
|
|
489
|
-
});
|
|
490
|
-
describe("runManifestUploadPass — failure containment", () => {
|
|
491
|
-
it("returns a failed result rather than rejecting when the transport throws", async () => {
|
|
492
|
-
const result = await runManifestUploadPass(baseOptions({
|
|
493
|
-
transport: vi.fn(async () => {
|
|
494
|
-
throw new TypeError("fetch failed");
|
|
495
|
-
}),
|
|
496
|
-
}));
|
|
497
|
-
expect(result.status).toBe("failed");
|
|
498
|
-
expect(result.error?.kind).toBe("transport_error");
|
|
499
|
-
expect(result.error?.detail).toBe("TypeError");
|
|
500
|
-
// A network blip must not be recorded as an upload, or the throttle would
|
|
501
|
-
// suppress the retry for a day...
|
|
502
|
-
const stored = readManifestSnapshot(stateDir, SCOPE_KEY);
|
|
503
|
-
expect(stored?.lastUploadAt).toBeUndefined();
|
|
504
|
-
expect(stored?.baseValid).toBe(false);
|
|
505
|
-
// ...but the sequence it burned must never be handed out a second time.
|
|
506
|
-
expect(stored?.sequence).toBe(1);
|
|
507
|
-
});
|
|
508
|
-
it("advances the sequence on a transport failure so the next pass cannot replay it", async () => {
|
|
509
|
-
seedSnapshot({ sequence: 7 });
|
|
510
|
-
await runManifestUploadPass(baseOptions({
|
|
511
|
-
ignoreThrottle: true,
|
|
512
|
-
transport: vi.fn(async () => {
|
|
513
|
-
throw new TypeError("fetch failed");
|
|
514
|
-
}),
|
|
515
|
-
}));
|
|
516
|
-
expect(readManifestSnapshot(stateDir, SCOPE_KEY)?.sequence).toBe(8);
|
|
517
|
-
const seen = [];
|
|
518
|
-
await runManifestUploadPass(baseOptions({
|
|
519
|
-
ignoreThrottle: true,
|
|
520
|
-
transport: vi.fn(async (chunk) => {
|
|
521
|
-
seen.push(chunk);
|
|
522
|
-
return accepted();
|
|
523
|
-
}),
|
|
524
|
-
}));
|
|
525
|
-
expect(seen[0].sequence).toBe(9);
|
|
526
|
-
});
|
|
527
|
-
it("advances the sequence on an unexpected status and on an abort", async () => {
|
|
528
|
-
seedSnapshot({ sequence: 20 });
|
|
529
|
-
await runManifestUploadPass(baseOptions({ ignoreThrottle: true, transport: transportReturning({ status: 500 }) }));
|
|
530
|
-
expect(readManifestSnapshot(stateDir, SCOPE_KEY)?.sequence).toBe(21);
|
|
531
|
-
// An abort that trips before the first chunk still burned sequence 22.
|
|
532
|
-
const controller = new AbortController();
|
|
533
|
-
controller.abort();
|
|
534
|
-
await runManifestUploadPass(baseOptions({ ignoreThrottle: true, abortSignal: controller.signal }));
|
|
535
|
-
expect(readManifestSnapshot(stateDir, SCOPE_KEY)?.sequence).toBe(22);
|
|
536
|
-
});
|
|
537
|
-
it("returns a failed result on an unexpected status", async () => {
|
|
538
|
-
const result = await runManifestUploadPass(baseOptions({ transport: transportReturning({ status: 500, body: { code: "INTERNAL" } }) }));
|
|
539
|
-
expect(result.status).toBe("failed");
|
|
540
|
-
expect(result.error?.kind).toBe("rejected");
|
|
541
|
-
expect(result.error?.status).toBe(500);
|
|
542
|
-
});
|
|
543
|
-
it("returns a non-throwing result when the pass is aborted before the walk finishes", async () => {
|
|
544
|
-
const controller = new AbortController();
|
|
545
|
-
controller.abort();
|
|
546
|
-
const result = await runManifestUploadPass(baseOptions({ abortSignal: controller.signal }));
|
|
547
|
-
// An abort raised before/during the walk truncates it, and a truncated
|
|
548
|
-
// walk is now refused at the upload gate rather than sent as a (possibly
|
|
549
|
-
// empty) full manifest. Either way the contract that matters is unchanged:
|
|
550
|
-
// it resolves, it never throws, and nothing was uploaded.
|
|
551
|
-
expect(["walk_truncated", "failed"]).toContain(result.status);
|
|
552
|
-
expect(result.uploadedChunks ?? 0).toBe(0);
|
|
553
|
-
});
|
|
554
|
-
it("throws only for programmer errors (invalid options)", async () => {
|
|
555
|
-
await expect(runManifestUploadPass(baseOptions({ installationId: "" }))).rejects.toThrow(/installationId is required/);
|
|
556
|
-
});
|
|
557
|
-
});
|
|
558
|
-
describe("runManifestUploadPass — staleness", () => {
|
|
559
|
-
it("forces a full manifest when the persisted base is older than the max age", async () => {
|
|
560
|
-
seedSnapshot({
|
|
561
|
-
generatedAt: new Date(NOW_MS - 8 * 24 * 60 * 60 * 1000).toISOString(),
|
|
562
|
-
lastUploadAt: new Date(NOW_MS - 8 * 24 * 60 * 60 * 1000).toISOString(),
|
|
563
|
-
});
|
|
564
|
-
const seen = [];
|
|
565
|
-
const result = await runManifestUploadPass(baseOptions({
|
|
566
|
-
// A tracked ledger, because this scope demonstrably HAS synced before
|
|
567
|
-
// (it has a snapshot); a 100%-disk full manifest here would be the
|
|
568
|
-
// implausible picture the guard exists to refuse.
|
|
569
|
-
listJournalsImpl: ledgerTracking("notes/a.md", "notes/b.md", "notes/c.md"),
|
|
570
|
-
transport: vi.fn(async (chunk) => {
|
|
571
|
-
seen.push(chunk);
|
|
572
|
-
return accepted();
|
|
573
|
-
}),
|
|
574
|
-
}));
|
|
575
|
-
expect(result.mode).toBe("full");
|
|
576
|
-
expect(seen[0].baseSnapshotId).toBeUndefined();
|
|
577
|
-
});
|
|
578
|
-
it("quotes the SERVER's snapshotId as baseSnapshotId, never the local snap-* handle", async () => {
|
|
579
|
-
const first = await runManifestUploadPass(baseOptions({ transport: transportReturning(accepted({ snapshotId: "0123456789abcdef0123456789abcdef" })) }));
|
|
580
|
-
expect(first.snapshotId).toBe("0123456789abcdef0123456789abcdef");
|
|
581
|
-
const stored = readManifestSnapshot(stateDir, SCOPE_KEY);
|
|
582
|
-
expect(stored?.snapshotId).toBe("0123456789abcdef0123456789abcdef");
|
|
583
|
-
expect(stored?.snapshotId?.startsWith("snap-")).toBe(false);
|
|
584
|
-
const seen = [];
|
|
585
|
-
await runManifestUploadPass(baseOptions({
|
|
586
|
-
ignoreThrottle: true,
|
|
587
|
-
transport: vi.fn(async (chunk) => {
|
|
588
|
-
seen.push(chunk);
|
|
589
|
-
return accepted();
|
|
590
|
-
}),
|
|
591
|
-
}));
|
|
592
|
-
// The server compares this against the snapshotId on its materialised
|
|
593
|
-
// view. A local `snap-*` id can never match, so a delta would be answered
|
|
594
|
-
// resend_full on every single pass.
|
|
595
|
-
expect(seen[0].mode).toBe("delta");
|
|
596
|
-
expect(seen[0].baseSnapshotId).toBe("0123456789abcdef0123456789abcdef");
|
|
597
|
-
});
|
|
598
|
-
it("does NOT persist a base when an accepted response carries no snapshotId", async () => {
|
|
599
|
-
const result = await runManifestUploadPass(baseOptions({
|
|
600
|
-
transport: transportReturning({
|
|
601
|
-
status: 200,
|
|
602
|
-
body: { status: "accepted", completed: true, materialised: true },
|
|
603
|
-
}),
|
|
604
|
-
}));
|
|
605
|
-
expect(result.nextModeFull).toBe(true);
|
|
606
|
-
expect(readManifestSnapshot(stateDir, SCOPE_KEY)?.baseValid).toBe(false);
|
|
607
|
-
});
|
|
608
|
-
it("sends a delta against a fresh, materialised base", async () => {
|
|
609
|
-
const first = await runManifestUploadPass(baseOptions());
|
|
610
|
-
const seen = [];
|
|
611
|
-
const second = await runManifestUploadPass(baseOptions({
|
|
612
|
-
ignoreThrottle: true,
|
|
613
|
-
transport: vi.fn(async (chunk) => {
|
|
614
|
-
seen.push(chunk);
|
|
615
|
-
return accepted();
|
|
616
|
-
}),
|
|
617
|
-
}));
|
|
618
|
-
expect(second.mode).toBe("delta");
|
|
619
|
-
expect(seen[0].baseSnapshotId).toBe(first.snapshotId);
|
|
620
|
-
});
|
|
621
|
-
});
|
|
622
|
-
describe("readManifestUploadStatus", () => {
|
|
623
|
-
it("reports never-uploaded for a missing snapshot", () => {
|
|
624
|
-
expect(readManifestUploadStatus(stateDir, [{ kind: "personal" }])).toEqual([
|
|
625
|
-
{ scopeKey: SCOPE_KEY, lastUploadAt: null, snapshotId: null, sequence: 0, baseUsable: false },
|
|
626
|
-
]);
|
|
627
|
-
});
|
|
628
|
-
it("reports never-uploaded for a snapshot written by an older version", () => {
|
|
629
|
-
seedSnapshot();
|
|
630
|
-
expect(readManifestUploadStatus(stateDir, [{ kind: "personal" }])[0]).toEqual({
|
|
631
|
-
scopeKey: SCOPE_KEY,
|
|
632
|
-
lastUploadAt: null,
|
|
633
|
-
snapshotId: "snap-previous",
|
|
634
|
-
sequence: 4,
|
|
635
|
-
baseUsable: true,
|
|
636
|
-
});
|
|
637
|
-
});
|
|
638
|
-
it("reports the last upload time after a successful pass", async () => {
|
|
639
|
-
const result = await runManifestUploadPass(baseOptions());
|
|
640
|
-
expect(readManifestUploadStatus(stateDir, [{ kind: "personal" }])[0]).toEqual({
|
|
641
|
-
scopeKey: SCOPE_KEY,
|
|
642
|
-
lastUploadAt: new Date(NOW_MS).toISOString(),
|
|
643
|
-
snapshotId: result.snapshotId,
|
|
644
|
-
sequence: 1,
|
|
645
|
-
baseUsable: true,
|
|
646
|
-
});
|
|
647
|
-
});
|
|
648
|
-
});
|
|
649
|
-
describe("runManifestUploadPass — persistence failures", () => {
|
|
650
|
-
/**
|
|
651
|
-
* `writeManifestSnapshot` swallows disk errors and returns `false`. Making
|
|
652
|
-
* the snapshot DIRECTORY a regular file is the cheapest faithful way to
|
|
653
|
-
* reproduce that (mkdir/rename both fail) without stubbing the store.
|
|
654
|
-
*/
|
|
655
|
-
function blockSnapshotWrites() {
|
|
656
|
-
fs.mkdirSync(stateDir, { recursive: true });
|
|
657
|
-
fs.writeFileSync(path.join(stateDir, MANIFEST_SNAPSHOT_DIRNAME), "not a directory", "utf8");
|
|
658
|
-
}
|
|
659
|
-
it("reports a full next pass and logs when the delta base cannot be written", async () => {
|
|
660
|
-
blockSnapshotWrites();
|
|
661
|
-
const error = vi.fn();
|
|
662
|
-
const result = await runManifestUploadPass(baseOptions({ logger: { error } }));
|
|
663
|
-
expect(result.status).toBe("uploaded");
|
|
664
|
-
// No base on disk means the next pass CANNOT be a delta. Reporting
|
|
665
|
-
// `false` here would promise the caller a base that does not exist.
|
|
666
|
-
expect(result.nextModeFull).toBe(true);
|
|
667
|
-
// And it must be loud: an unwritable snapshot silently disables both the
|
|
668
|
-
// 24h throttle and the monotonic sequence.
|
|
669
|
-
expect(error).toHaveBeenCalled();
|
|
670
|
-
});
|
|
671
|
-
it("logs when a failure path cannot burn the sequence", async () => {
|
|
672
|
-
blockSnapshotWrites();
|
|
673
|
-
const error = vi.fn();
|
|
674
|
-
const result = await runManifestUploadPass(baseOptions({
|
|
675
|
-
logger: { error },
|
|
676
|
-
transport: vi.fn(async () => {
|
|
677
|
-
throw new TypeError("fetch failed");
|
|
678
|
-
}),
|
|
679
|
-
}));
|
|
680
|
-
expect(result.status).toBe("failed");
|
|
681
|
-
expect(error).toHaveBeenCalled();
|
|
682
|
-
});
|
|
683
|
-
});
|
|
684
|
-
describe("runManifestUploadPass — failure paths all advance the sequence", () => {
|
|
685
|
-
it("burns the sequence when the BUILD fails before anything is uploaded", async () => {
|
|
686
|
-
// A company scope that carries a companyUid but no slug: the snapshot key
|
|
687
|
-
// derives fine, then the builder rejects it. That is the one shape of
|
|
688
|
-
// build failure this seam can actually see — the walk itself degrades
|
|
689
|
-
// rather than throwing.
|
|
690
|
-
const scope = { kind: "company", companyUid: "co-alpha" };
|
|
691
|
-
const companyScopeKey = manifestSnapshotScopeKey({ kind: "company", companyUid: "co-alpha" });
|
|
692
|
-
writeManifestSnapshot(stateDir, companyScopeKey, {
|
|
693
|
-
snapshotId: "snap-previous",
|
|
694
|
-
generatedAt: new Date(NOW_MS - 60_000).toISOString(),
|
|
695
|
-
sequence: 40,
|
|
696
|
-
entries: {},
|
|
697
|
-
});
|
|
698
|
-
const result = await runManifestUploadPass(baseOptions({ ignoreThrottle: true, scope }));
|
|
699
|
-
expect(result.status).toBe("failed");
|
|
700
|
-
expect(result.error?.kind).toBe("build_error");
|
|
701
|
-
// The rule is unconditional: EVERY outcome advances the sequence, so no
|
|
702
|
-
// future edit to this path can silently reuse one.
|
|
703
|
-
expect(readManifestSnapshot(stateDir, companyScopeKey)?.sequence).toBe(41);
|
|
704
|
-
// ...but a build failure uploaded nothing, so the throttle must not arm.
|
|
705
|
-
expect(readManifestSnapshot(stateDir, companyScopeKey)?.lastUploadAt).toBeUndefined();
|
|
706
|
-
});
|
|
707
|
-
it("keeps a usable base across a transport blip instead of forcing a full re-send", async () => {
|
|
708
|
-
// A materialised pass first, so there IS a base to preserve.
|
|
709
|
-
await runManifestUploadPass(baseOptions());
|
|
710
|
-
const result = await runManifestUploadPass(baseOptions({
|
|
711
|
-
ignoreThrottle: true,
|
|
712
|
-
transport: vi.fn(async () => {
|
|
713
|
-
throw new TypeError("fetch failed");
|
|
714
|
-
}),
|
|
715
|
-
}));
|
|
716
|
-
expect(result.status).toBe("failed");
|
|
717
|
-
// Nothing reached the server, so its materialised view still matches the
|
|
718
|
-
// base we hold: forcing a full 70k re-send on every network hiccup would
|
|
719
|
-
// be a self-inflicted outage.
|
|
720
|
-
expect(result.nextModeFull).toBe(false);
|
|
721
|
-
expect(readManifestSnapshot(stateDir, SCOPE_KEY)?.baseValid).not.toBe(false);
|
|
722
|
-
});
|
|
723
|
-
});
|
|
724
|
-
// ─── Fix 1: a wedged transport must not wedge the pass ───────────────────────
|
|
725
|
-
/** A promise the test settles by hand — stands in for a black-holed socket. */
|
|
726
|
-
function deferred() {
|
|
727
|
-
let resolve;
|
|
728
|
-
let reject;
|
|
729
|
-
const promise = new Promise((res, rej) => {
|
|
730
|
-
resolve = res;
|
|
731
|
-
reject = rej;
|
|
732
|
-
});
|
|
733
|
-
return { promise, resolve, reject };
|
|
734
|
-
}
|
|
735
|
-
describe("runManifestUploadPass — the pass is bounded", () => {
|
|
736
|
-
it("returns (rather than hanging) when the transport never settles, and burns the sequence", async () => {
|
|
737
|
-
// A materialised pass first, so there IS a delta base for the wedged pass
|
|
738
|
-
// to leave alone.
|
|
739
|
-
await runManifestUploadPass(baseOptions());
|
|
740
|
-
const pending = deferred();
|
|
741
|
-
const transport = vi.fn(() => pending.promise);
|
|
742
|
-
const result = await runManifestUploadPass(baseOptions({ transport, uploadWallMs: 50, ignoreThrottle: true }));
|
|
743
|
-
// The daemon `await`s this after every sync cycle; the ONLY thing that
|
|
744
|
-
// matters here is that the await completed at all.
|
|
745
|
-
expect(result.status).toBe("failed");
|
|
746
|
-
expect(result.error?.kind).toBe("upload_timeout");
|
|
747
|
-
expect(result.uploadedChunks).toBe(0);
|
|
748
|
-
expect(transport).toHaveBeenCalledTimes(1);
|
|
749
|
-
// Nothing reached the server's materialised view, so the base it holds is
|
|
750
|
-
// still right — a wall-clock stop must not force a full re-send.
|
|
751
|
-
expect(result.nextModeFull).toBe(false);
|
|
752
|
-
// The sequence is burned even though nothing was uploaded: the server may
|
|
753
|
-
// already have folded a chunk under it, and handing it out twice is the
|
|
754
|
-
// collision this module exists to avoid.
|
|
755
|
-
expect(readManifestSnapshot(stateDir, SCOPE_KEY)?.sequence).toBe(2);
|
|
756
|
-
// `lastUploadAt` still names the FIRST pass — the wedged one recorded no
|
|
757
|
-
// upload, so the 24h throttle cannot suppress its retry.
|
|
758
|
-
expect(readManifestSnapshot(stateDir, SCOPE_KEY)?.lastUploadAt).toBe(new Date(NOW_MS).toISOString());
|
|
759
|
-
});
|
|
760
|
-
it("does not produce an unhandled rejection when the abandoned transport later fails", async () => {
|
|
761
|
-
const seen = [];
|
|
762
|
-
const onUnhandled = (reason) => seen.push(reason);
|
|
763
|
-
process.on("unhandledRejection", onUnhandled);
|
|
764
|
-
try {
|
|
765
|
-
const pending = deferred();
|
|
766
|
-
const result = await runManifestUploadPass(baseOptions({ transport: vi.fn(() => pending.promise), uploadWallMs: 50 }));
|
|
767
|
-
expect(result.status).toBe("failed");
|
|
768
|
-
// The pass has already returned; the socket it walked away from now
|
|
769
|
-
// dies. On a long-lived daemon an unhandled rejection here is a crash.
|
|
770
|
-
pending.reject(new TypeError("socket hang up"));
|
|
771
|
-
await new Promise((resolve) => setTimeout(resolve, 20));
|
|
772
|
-
expect(seen).toEqual([]);
|
|
773
|
-
}
|
|
774
|
-
finally {
|
|
775
|
-
process.off("unhandledRejection", onUnhandled);
|
|
776
|
-
}
|
|
777
|
-
});
|
|
778
|
-
it("stops sending when the caller aborts while a chunk is in flight", async () => {
|
|
779
|
-
const controller = new AbortController();
|
|
780
|
-
const pending = deferred();
|
|
781
|
-
const transport = vi.fn(() => {
|
|
782
|
-
// Abort only once the call is genuinely in flight — the pre-loop check
|
|
783
|
-
// cannot catch this, which is the whole point of the race.
|
|
784
|
-
setTimeout(() => controller.abort(), 5);
|
|
785
|
-
return pending.promise;
|
|
786
|
-
});
|
|
787
|
-
const result = await runManifestUploadPass(baseOptions({ transport, abortSignal: controller.signal, uploadWallMs: 60_000 }));
|
|
788
|
-
expect(result.status).toBe("failed");
|
|
789
|
-
expect(result.error?.kind).toBe("aborted");
|
|
790
|
-
expect(transport).toHaveBeenCalledTimes(1);
|
|
791
|
-
expect(readManifestSnapshot(stateDir, SCOPE_KEY)?.sequence).toBe(1);
|
|
792
|
-
});
|
|
793
|
-
it("leaves no pending timer behind after a pass that finished normally", async () => {
|
|
794
|
-
// `shouldAdvanceTime` keeps real time flowing (the builder's fs work still
|
|
795
|
-
// completes) while making the timer queue observable.
|
|
796
|
-
vi.useFakeTimers({ shouldAdvanceTime: true });
|
|
797
|
-
const result = await runManifestUploadPass(baseOptions({ uploadWallMs: 10 * 60_000 }));
|
|
798
|
-
expect(result.status).toBe("uploaded");
|
|
799
|
-
// A 10-minute deadline timer left armed would keep a short-lived `hq sync
|
|
800
|
-
// manifest` process alive long after it printed its result.
|
|
801
|
-
expect(vi.getTimerCount()).toBe(0);
|
|
802
|
-
});
|
|
803
|
-
});
|
|
804
|
-
// ─── Fix 2: the per-scope sequence is not a read-modify-write race ───────────
|
|
805
|
-
describe("runManifestUploadPass — per-scope serialisation", () => {
|
|
806
|
-
it("cannot let two concurrent passes derive the same sequence", async () => {
|
|
807
|
-
const sent = [];
|
|
808
|
-
const transport = vi.fn(async (chunk) => {
|
|
809
|
-
sent.push(chunk);
|
|
810
|
-
return accepted();
|
|
811
|
-
});
|
|
812
|
-
// The exact production collision: a daemon pass and a `hq sync manifest`
|
|
813
|
-
// (which sets `ignoreThrottle`, so nothing spaces them out) starting
|
|
814
|
-
// together on one scope.
|
|
815
|
-
const [a, b] = await Promise.all([
|
|
816
|
-
runManifestUploadPass(baseOptions({ transport, source: "desktop" })),
|
|
817
|
-
runManifestUploadPass(baseOptions({ transport, source: "cli", ignoreThrottle: true })),
|
|
818
|
-
]);
|
|
819
|
-
expect([a.status, b.status].sort()).toEqual(["locked", "uploaded"]);
|
|
820
|
-
// The refused pass is a benign skip, not a throw and not an error result:
|
|
821
|
-
// an audit must never block a sync pass.
|
|
822
|
-
const skipped = a.status === "locked" ? a : b;
|
|
823
|
-
expect(skipped.error).toBeUndefined();
|
|
824
|
-
expect(skipped.scopeKey).toBe(SCOPE_KEY);
|
|
825
|
-
// ONE sequence on the wire. Two passes sharing a sequence (with the same
|
|
826
|
-
// pinned `generatedAt` and chunk count) is exactly what makes the server
|
|
827
|
-
// derive a single snapshotId and merge both chunk sets into it.
|
|
828
|
-
expect([...new Set(sent.map((chunk) => chunk.sequence))]).toEqual([1]);
|
|
829
|
-
expect(readManifestSnapshot(stateDir, SCOPE_KEY)?.sequence).toBe(1);
|
|
830
|
-
// And the next pass moves on rather than replaying.
|
|
831
|
-
const next = await runManifestUploadPass(baseOptions({ transport, ignoreThrottle: true, now: NOW_MS + 1000 }));
|
|
832
|
-
expect(next.status).toBe("uploaded");
|
|
833
|
-
expect(readManifestSnapshot(stateDir, SCOPE_KEY)?.sequence).toBe(2);
|
|
834
|
-
});
|
|
835
|
-
it("reclaims a lock held by a crashed process rather than wedging the scope", async () => {
|
|
836
|
-
const lockPath = lockPathFor(hqRoot, `manifest-${manifestSnapshotScopeSlug(SCOPE_KEY)}`);
|
|
837
|
-
fs.mkdirSync(path.dirname(lockPath), { recursive: true });
|
|
838
|
-
fs.writeFileSync(lockPath, JSON.stringify({
|
|
839
|
-
// No such process: above every platform's pid ceiling, so `kill(pid,0)`
|
|
840
|
-
// answers ESRCH and the holder reads as dead.
|
|
841
|
-
pid: 999_999,
|
|
842
|
-
command: MANIFEST_UPLOAD_LOCK_COMMAND,
|
|
843
|
-
startedAt: new Date(NOW_MS - 3_600_000).toISOString(),
|
|
844
|
-
hqRoot,
|
|
845
|
-
mode: "exclusive",
|
|
846
|
-
}), "utf8");
|
|
847
|
-
const result = await runManifestUploadPass(baseOptions());
|
|
848
|
-
expect(result.status).toBe("uploaded");
|
|
849
|
-
});
|
|
850
|
-
it("does not serialise `--print`, which uploads nothing and burns no sequence", async () => {
|
|
851
|
-
const transport = vi.fn(async () => accepted());
|
|
852
|
-
const [printed, uploaded] = await Promise.all([
|
|
853
|
-
runManifestUploadPass(baseOptions({ transport, dryRun: true })),
|
|
854
|
-
runManifestUploadPass(baseOptions({ transport })),
|
|
855
|
-
]);
|
|
856
|
-
expect(printed.status).toBe("printed");
|
|
857
|
-
expect(uploaded.status).toBe("uploaded");
|
|
858
|
-
});
|
|
859
|
-
});
|
|
860
|
-
/**
|
|
861
|
-
* A truncated walk describes only the part of the scope the walker reached.
|
|
862
|
-
* It must never go on the wire.
|
|
863
|
-
*
|
|
864
|
-
* Found on a real 690k-file personal vault: the 30s walk budget expired after
|
|
865
|
-
* ~179k files with ZERO entries assembled, and the pass POSTed that as a FULL
|
|
866
|
-
* manifest. A full manifest is a complete statement — "this is everything I
|
|
867
|
-
* hold" — so the server's materialised view for the scope became empty and
|
|
868
|
-
* every file the client actually held would read as missing. Suppressing the
|
|
869
|
-
* delta base (which the builder already did) does not help: the damage is done
|
|
870
|
-
* server-side by the upload itself.
|
|
871
|
-
*/
|
|
872
|
-
describe("runManifestUploadPass — a truncated walk is never uploaded", () => {
|
|
873
|
-
/**
|
|
874
|
-
* Force truncation by putting the walk's deadline in the PAST.
|
|
875
|
-
*
|
|
876
|
-
* `0` is not enough: the walk compares `Date.now() > deadlineMs`, and a
|
|
877
|
-
* three-file fixture finishes inside the same millisecond, so a zero budget
|
|
878
|
-
* completes normally and the test silently exercises the happy path instead.
|
|
879
|
-
*/
|
|
880
|
-
function truncatingOptions(overrides = {}) {
|
|
881
|
-
return baseOptions({ maxWallMs: -1, ...overrides });
|
|
882
|
-
}
|
|
883
|
-
it("refuses the POST entirely and reports walk_truncated", async () => {
|
|
884
|
-
const transport = transportReturning(accepted());
|
|
885
|
-
const result = await runManifestUploadPass(truncatingOptions({ transport }));
|
|
886
|
-
expect(result.status).toBe("walk_truncated");
|
|
887
|
-
expect(result.uploadedChunks).toBe(0);
|
|
888
|
-
expect(transport).not.toHaveBeenCalled();
|
|
889
|
-
});
|
|
890
|
-
it("does not become the delta base, and does not arm the 24h throttle", async () => {
|
|
891
|
-
await runManifestUploadPass(truncatingOptions());
|
|
892
|
-
const stored = readManifestSnapshot(stateDir, SCOPE_KEY);
|
|
893
|
-
expect(stored?.baseValid).toBe(false);
|
|
894
|
-
// `lastUploadAt` means "the server has a picture from this time". Nothing
|
|
895
|
-
// was sent, so claiming one would hide a scope that can never complete.
|
|
896
|
-
expect(stored?.lastUploadAt).toBeUndefined();
|
|
897
|
-
expect(stored?.lastAttemptAt).toBe(new Date(NOW_MS).toISOString());
|
|
898
|
-
});
|
|
899
|
-
it("leaves an existing, complete delta base intact", async () => {
|
|
900
|
-
// A base captured from a COMPLETE walk is still the right thing to delta
|
|
901
|
-
// against; a later truncated pass says nothing about it.
|
|
902
|
-
seedSnapshot({ lastUploadAt: undefined });
|
|
903
|
-
const result = await runManifestUploadPass(truncatingOptions());
|
|
904
|
-
expect(result.status).toBe("walk_truncated");
|
|
905
|
-
const stored = readManifestSnapshot(stateDir, SCOPE_KEY);
|
|
906
|
-
expect(Object.keys(stored?.entries ?? {})).toContain("notes/a.md");
|
|
907
|
-
});
|
|
908
|
-
it("the next pass is a full walk, not a delta with mass removedPaths", async () => {
|
|
909
|
-
// The regression this guards: a truncated walk persisted as the base would
|
|
910
|
-
// make the following pass emit every unreached file as REMOVED.
|
|
911
|
-
seedSnapshot({ lastUploadAt: undefined });
|
|
912
|
-
await runManifestUploadPass(truncatingOptions());
|
|
913
|
-
const sent = [];
|
|
914
|
-
await runManifestUploadPass(baseOptions({
|
|
915
|
-
// Past this scope's failure floor, so the retry is allowed to run.
|
|
916
|
-
now: NOW_MS + MANIFEST_FAILURE_BACKOFF_BASE_MS + 1,
|
|
917
|
-
transport: vi.fn(async (chunk) => {
|
|
918
|
-
sent.push(chunk);
|
|
919
|
-
return accepted();
|
|
920
|
-
}),
|
|
921
|
-
}));
|
|
922
|
-
expect(sent).toHaveLength(1);
|
|
923
|
-
expect(sent[0]?.removedPaths).toEqual([]);
|
|
924
|
-
expect(sent[0]?.entries.map((entry) => entry.path)).toContain("notes/a.md");
|
|
925
|
-
});
|
|
926
|
-
it("still PRINTS a truncated manifest for --print, which is what a debugger needs", async () => {
|
|
927
|
-
const result = await runManifestUploadPass(truncatingOptions({ dryRun: true }));
|
|
928
|
-
expect(result.status).toBe("printed");
|
|
929
|
-
});
|
|
930
|
-
});
|
|
931
|
-
/**
|
|
932
|
-
* A pass that fails before the server answers must back off.
|
|
933
|
-
*
|
|
934
|
-
* `lastUploadAt` — the field arming the 24h throttle — is only written by a
|
|
935
|
-
* pass the server ANSWERED. That is deliberate (a network blip must not
|
|
936
|
-
* suppress the audit for a day), but on its own it means a scope that can
|
|
937
|
-
* never succeed re-walks (and, under the old default, re-HASHED) the entire
|
|
938
|
-
* tree on every single sync cycle, forever, to produce an upload guaranteed to
|
|
939
|
-
* fail again.
|
|
940
|
-
*/
|
|
941
|
-
describe("runManifestUploadPass — failure backoff", () => {
|
|
942
|
-
it("records a retry floor after a 403 without claiming an upload", async () => {
|
|
943
|
-
const result = await runManifestUploadPass(baseOptions({ transport: transportReturning({ status: 403, body: { code: "INSTALLATION_NOT_ENROLLED" } }) }));
|
|
944
|
-
expect(result.status).toBe("failed");
|
|
945
|
-
const stored = readManifestSnapshot(stateDir, SCOPE_KEY);
|
|
946
|
-
expect(stored?.lastAttemptAt).toBe(new Date(NOW_MS).toISOString());
|
|
947
|
-
expect(stored?.consecutiveFailures).toBe(1);
|
|
948
|
-
expect(stored?.lastUploadAt).toBeUndefined();
|
|
949
|
-
});
|
|
950
|
-
it("skips the next pass inside the floor — no walk, no POST", async () => {
|
|
951
|
-
await runManifestUploadPass(baseOptions({ transport: transportReturning({ status: 403, body: { code: "INSTALLATION_NOT_ENROLLED" } }) }));
|
|
952
|
-
const transport = transportReturning(accepted());
|
|
953
|
-
const result = await runManifestUploadPass(baseOptions({ now: NOW_MS + MANIFEST_FAILURE_BACKOFF_BASE_MS - 1, transport }));
|
|
954
|
-
expect(result.status).toBe("throttled");
|
|
955
|
-
expect(transport).not.toHaveBeenCalled();
|
|
956
|
-
});
|
|
957
|
-
it("retries once the floor expires", async () => {
|
|
958
|
-
await runManifestUploadPass(baseOptions({ transport: transportReturning({ status: 500, body: {} }) }));
|
|
959
|
-
const transport = transportReturning(accepted());
|
|
960
|
-
const result = await runManifestUploadPass(baseOptions({ now: NOW_MS + MANIFEST_FAILURE_BACKOFF_BASE_MS + 1, transport }));
|
|
961
|
-
expect(result.status).toBe("uploaded");
|
|
962
|
-
expect(transport).toHaveBeenCalled();
|
|
963
|
-
});
|
|
964
|
-
it("doubles the floor on each consecutive failure", async () => {
|
|
965
|
-
const failing = () => transportReturning({ status: 500, body: {} });
|
|
966
|
-
await runManifestUploadPass(baseOptions({ transport: failing() }));
|
|
967
|
-
await runManifestUploadPass(baseOptions({ now: NOW_MS + MANIFEST_FAILURE_BACKOFF_BASE_MS + 1, transport: failing() }));
|
|
968
|
-
const stored = readManifestSnapshot(stateDir, SCOPE_KEY);
|
|
969
|
-
expect(stored?.consecutiveFailures).toBe(2);
|
|
970
|
-
expect(manifestFailureBackoffMs(2)).toBe(MANIFEST_FAILURE_BACKOFF_BASE_MS * 2);
|
|
971
|
-
});
|
|
972
|
-
it("caps the floor at 24h however many times it has failed", () => {
|
|
973
|
-
expect(manifestFailureBackoffMs(0)).toBe(0);
|
|
974
|
-
expect(manifestFailureBackoffMs(1)).toBe(MANIFEST_FAILURE_BACKOFF_BASE_MS);
|
|
975
|
-
expect(manifestFailureBackoffMs(50)).toBe(24 * 60 * 60 * 1000);
|
|
976
|
-
// A corrupt count must not produce a floor that never expires.
|
|
977
|
-
expect(manifestFailureBackoffMs(Number.POSITIVE_INFINITY)).toBe(0);
|
|
978
|
-
});
|
|
979
|
-
it("clears the floor as soon as the server answers", async () => {
|
|
980
|
-
await runManifestUploadPass(baseOptions({ transport: transportReturning({ status: 500, body: {} }) }));
|
|
981
|
-
await runManifestUploadPass(baseOptions({ now: NOW_MS + MANIFEST_FAILURE_BACKOFF_BASE_MS + 1, transport: transportReturning(accepted()) }));
|
|
982
|
-
const stored = readManifestSnapshot(stateDir, SCOPE_KEY);
|
|
983
|
-
expect(stored?.lastAttemptAt).toBeUndefined();
|
|
984
|
-
expect(stored?.consecutiveFailures).toBeUndefined();
|
|
985
|
-
});
|
|
986
|
-
it("KEEPS escalating the floor on a 404 soft skip — an answer is not an upload", async () => {
|
|
987
|
-
// A 404 means the route is not deployed on this server yet. That is a
|
|
988
|
-
// healthy machine, but it is emphatically not an upload: stamping
|
|
989
|
-
// `lastUploadAt` for it (as the old "the endpoint answered" rule did) told
|
|
990
|
-
// `hq doctor` the scope was audited when nothing had ever been sent.
|
|
991
|
-
await runManifestUploadPass(baseOptions({ transport: transportReturning({ status: 500, body: {} }) }));
|
|
992
|
-
await runManifestUploadPass(baseOptions({
|
|
993
|
-
now: NOW_MS + MANIFEST_FAILURE_BACKOFF_BASE_MS + 1,
|
|
994
|
-
transport: transportReturning({ status: 404, body: {} }),
|
|
995
|
-
}));
|
|
996
|
-
const stored = readManifestSnapshot(stateDir, SCOPE_KEY);
|
|
997
|
-
expect(stored?.consecutiveFailures).toBe(2);
|
|
998
|
-
expect(stored?.lastUploadAt).toBeUndefined();
|
|
999
|
-
});
|
|
1000
|
-
it("is bypassed by an explicit human invocation", async () => {
|
|
1001
|
-
await runManifestUploadPass(baseOptions({ transport: transportReturning({ status: 500, body: {} }) }));
|
|
1002
|
-
const transport = transportReturning(accepted());
|
|
1003
|
-
const result = await runManifestUploadPass(baseOptions({ now: NOW_MS + 1000, ignoreThrottle: true, transport }));
|
|
1004
|
-
expect(result.status).toBe("uploaded");
|
|
1005
|
-
expect(transport).toHaveBeenCalled();
|
|
1006
|
-
});
|
|
1007
|
-
it("does not trust a floor stamped in the future", async () => {
|
|
1008
|
-
// Clock skew or a restored backup must not wedge a scope off the audit.
|
|
1009
|
-
writeManifestSnapshot(stateDir, SCOPE_KEY, {
|
|
1010
|
-
snapshotId: "snap-none",
|
|
1011
|
-
generatedAt: new Date(NOW_MS).toISOString(),
|
|
1012
|
-
sequence: 1,
|
|
1013
|
-
entries: {},
|
|
1014
|
-
baseValid: false,
|
|
1015
|
-
lastAttemptAt: new Date(NOW_MS + 60 * 60 * 1000).toISOString(),
|
|
1016
|
-
consecutiveFailures: 3,
|
|
1017
|
-
});
|
|
1018
|
-
const result = await runManifestUploadPass(baseOptions());
|
|
1019
|
-
expect(result.status).toBe("uploaded");
|
|
1020
|
-
});
|
|
1021
|
-
});
|
|
1022
|
-
/**
|
|
1023
|
-
* The incident: a machine whose v3 ledger was corrupt in EVERY generation
|
|
1024
|
-
* reported all 188,997 of its files as `source: "disk"` — on-disk,
|
|
1025
|
-
* never-tracked — and uploaded that as a complete FULL manifest. `full` is a
|
|
1026
|
-
* total statement, so the server's materialised view for the scope became
|
|
1027
|
-
* "this client tracks nothing", and every file the user actually held read as
|
|
1028
|
-
* drift.
|
|
1029
|
-
*
|
|
1030
|
-
* The ledger read is fixed upstream (an unreadable ledger is no longer an
|
|
1031
|
-
* empty one). These pin the BACKSTOP: whatever makes a ledger disappear next
|
|
1032
|
-
* time, a scope that was tracked yesterday does not become untracked today.
|
|
1033
|
-
*/
|
|
1034
|
-
describe("runManifestUploadPass — implausible ledger guard", () => {
|
|
1035
|
-
it("refuses to upload a 100%-disk FULL manifest for a scope with a tracked snapshot", async () => {
|
|
1036
|
-
// The scope HAS synced: the server materialised a snapshot for it.
|
|
1037
|
-
seedSnapshot({
|
|
1038
|
-
generatedAt: new Date(NOW_MS - 8 * 24 * 60 * 60 * 1000).toISOString(),
|
|
1039
|
-
lastUploadAt: new Date(NOW_MS - 8 * 24 * 60 * 60 * 1000).toISOString(),
|
|
1040
|
-
});
|
|
1041
|
-
const transport = transportReturning(accepted());
|
|
1042
|
-
const result = await runManifestUploadPass(
|
|
1043
|
-
// The stale base forces `full`; the empty ledger stub is the corrupt
|
|
1044
|
-
// machine's view of itself.
|
|
1045
|
-
baseOptions({ transport, listJournalsImpl: () => [] }));
|
|
1046
|
-
expect(result.status).toBe("ledger_implausible");
|
|
1047
|
-
expect(result.uploadedChunks).toBe(0);
|
|
1048
|
-
// The load-bearing assertion: not one byte reached the server.
|
|
1049
|
-
expect(transport).not.toHaveBeenCalled();
|
|
1050
|
-
});
|
|
1051
|
-
it("leaves the stored delta base intact and only burns the sequence", async () => {
|
|
1052
|
-
const seeded = seedSnapshot({
|
|
1053
|
-
generatedAt: new Date(NOW_MS - 8 * 24 * 60 * 60 * 1000).toISOString(),
|
|
1054
|
-
lastUploadAt: new Date(NOW_MS - 8 * 24 * 60 * 60 * 1000).toISOString(),
|
|
1055
|
-
});
|
|
1056
|
-
await runManifestUploadPass(baseOptions({ listJournalsImpl: () => [] }));
|
|
1057
|
-
const stored = readManifestSnapshot(stateDir, SCOPE_KEY);
|
|
1058
|
-
// The base still describes a real, tracked scope — refusing this pass must
|
|
1059
|
-
// not also destroy the good record that proved it was wrong.
|
|
1060
|
-
expect(stored?.entries).toEqual(seeded.entries);
|
|
1061
|
-
expect(stored?.lastUploadAt).toBe(seeded.lastUploadAt);
|
|
1062
|
-
// The sequence advances on every outcome, including this one.
|
|
1063
|
-
expect(readManifestUploadStatus(stateDir, [{ kind: "personal" }])[0].sequence).toBe(seeded.sequence + 1);
|
|
1064
|
-
});
|
|
1065
|
-
it("allows a fresh install: no prior snapshot, so a disk-only manifest is the truth", async () => {
|
|
1066
|
-
// A genuinely first-ever pass has nothing to contradict. Refusing it would
|
|
1067
|
-
// mean no machine could ever enrol.
|
|
1068
|
-
const transport = transportReturning(accepted());
|
|
1069
|
-
const result = await runManifestUploadPass(baseOptions({ transport, listJournalsImpl: () => [] }));
|
|
1070
|
-
expect(result.status).toBe("uploaded");
|
|
1071
|
-
expect(result.mode).toBe("full");
|
|
1072
|
-
expect(transport).toHaveBeenCalled();
|
|
1073
|
-
});
|
|
1074
|
-
it("allows a full manifest whose entries the ledger actually knows", async () => {
|
|
1075
|
-
seedSnapshot({
|
|
1076
|
-
generatedAt: new Date(NOW_MS - 8 * 24 * 60 * 60 * 1000).toISOString(),
|
|
1077
|
-
lastUploadAt: new Date(NOW_MS - 8 * 24 * 60 * 60 * 1000).toISOString(),
|
|
1078
|
-
});
|
|
1079
|
-
const transport = transportReturning(accepted());
|
|
1080
|
-
const result = await runManifestUploadPass(baseOptions({ transport, listJournalsImpl: ledgerTracking("notes/a.md", "notes/b.md", "notes/c.md") }));
|
|
1081
|
-
expect(result.status).toBe("uploaded");
|
|
1082
|
-
expect(transport).toHaveBeenCalled();
|
|
1083
|
-
});
|
|
1084
|
-
it("still PRINTS the refused manifest under dryRun, which is what debugging it needs", async () => {
|
|
1085
|
-
seedSnapshot({
|
|
1086
|
-
generatedAt: new Date(NOW_MS - 8 * 24 * 60 * 60 * 1000).toISOString(),
|
|
1087
|
-
lastUploadAt: new Date(NOW_MS - 8 * 24 * 60 * 60 * 1000).toISOString(),
|
|
1088
|
-
});
|
|
1089
|
-
const transport = transportReturning(accepted());
|
|
1090
|
-
const result = await runManifestUploadPass(baseOptions({ transport, dryRun: true, listJournalsImpl: () => [] }));
|
|
1091
|
-
expect(result.status).toBe("printed");
|
|
1092
|
-
expect(transport).not.toHaveBeenCalled();
|
|
1093
|
-
expect(result.chunks?.[0].entries.every((entry) => entry.source === "disk")).toBe(true);
|
|
1094
|
-
});
|
|
1095
|
-
});
|
|
1096
|
-
/**
|
|
1097
|
-
* The chunk set is streamed, not materialised (see `ManifestChunkPlan`). These
|
|
1098
|
-
* pin the two properties that makes that safe on the wire: the header fields
|
|
1099
|
-
* the server hashes into the snapshotId are identical across a multi-chunk
|
|
1100
|
-
* pass, and every chunk still validates against the contract.
|
|
1101
|
-
*/
|
|
1102
|
-
describe("runManifestUploadPass — streamed chunks", () => {
|
|
1103
|
-
it("sends byte-identical headers across a multi-chunk pass", async () => {
|
|
1104
|
-
const seen = [];
|
|
1105
|
-
const result = await runManifestUploadPass(baseOptions({
|
|
1106
|
-
maxEntriesPerChunk: 1,
|
|
1107
|
-
transport: vi.fn(async (chunk) => {
|
|
1108
|
-
// Snapshot the chunk as it was SENT: the plan is free to discard the
|
|
1109
|
-
// object afterwards, so a late read would prove nothing.
|
|
1110
|
-
seen.push(JSON.parse(JSON.stringify(chunk)));
|
|
1111
|
-
return accepted({ chunkCount: 3, receivedChunks: seen.length, completed: seen.length === 3 });
|
|
1112
|
-
}),
|
|
1113
|
-
}));
|
|
1114
|
-
expect(result.status).toBe("uploaded");
|
|
1115
|
-
expect(seen.length).toBe(3);
|
|
1116
|
-
expect(seen.map((chunk) => chunk.chunkIndex)).toEqual([0, 1, 2]);
|
|
1117
|
-
for (const chunk of seen) {
|
|
1118
|
-
validateSyncManifestUpload(chunk);
|
|
1119
|
-
// Every field the server hashes into its derived snapshotId.
|
|
1120
|
-
expect(chunk.chunkCount).toBe(seen[0].chunkCount);
|
|
1121
|
-
expect(chunk.generatedAt).toBe(seen[0].generatedAt);
|
|
1122
|
-
expect(chunk.sequence).toBe(seen[0].sequence);
|
|
1123
|
-
expect(chunk.machineId).toBe(seen[0].machineId);
|
|
1124
|
-
expect(chunk.mode).toBe(seen[0].mode);
|
|
1125
|
-
expect(chunk.scope).toEqual(seen[0].scope);
|
|
1126
|
-
}
|
|
1127
|
-
// `removedPaths` ride the first chunk only, still.
|
|
1128
|
-
expect(seen.slice(1).every((chunk) => chunk.removedPaths.length === 0)).toBe(true);
|
|
1129
|
-
});
|
|
1130
|
-
});
|
|
1131
|
-
// ─── Failure reasons are DURABLE (2026-09-07 personal-scope incident) ────────
|
|
1132
|
-
//
|
|
1133
|
-
// The failure state used to be write-only: `lastAttemptAt` and
|
|
1134
|
-
// `consecutiveFailures` were persisted, but WHY the pass failed survived only
|
|
1135
|
-
// as the label on a log line that fires when the snapshot write itself fails.
|
|
1136
|
-
// A scope stuck failing every cycle was therefore indistinguishable from any
|
|
1137
|
-
// other stuck scope, and the 2026-09-07 incident had to be diagnosed by
|
|
1138
|
-
// eliminating branches and re-executing the pass.
|
|
1139
|
-
//
|
|
1140
|
-
// These tests pin the invariant that replaces that: NO pass arms the failure
|
|
1141
|
-
// backoff without also recording, on disk, the reason it failed.
|
|
1142
|
-
describe("runManifestUploadPass — persists a failure reason on every path", () => {
|
|
1143
|
-
/**
|
|
1144
|
-
* Each case drives one distinct failure branch and names the tokens it must
|
|
1145
|
-
* leave behind. `status` is asserted only where the failure came from an
|
|
1146
|
-
* answered request — a failure that never reached the wire must NOT invent
|
|
1147
|
-
* an HTTP status.
|
|
1148
|
-
*/
|
|
1149
|
-
const cases = [
|
|
1150
|
-
{
|
|
1151
|
-
name: "transport threw",
|
|
1152
|
-
options: () => ({
|
|
1153
|
-
transport: vi.fn(async () => {
|
|
1154
|
-
throw new Error("ECONNRESET");
|
|
1155
|
-
}),
|
|
1156
|
-
}),
|
|
1157
|
-
reason: "transport_error",
|
|
1158
|
-
kind: "transport_error",
|
|
1159
|
-
},
|
|
1160
|
-
{
|
|
1161
|
-
name: "server answered non-2xx",
|
|
1162
|
-
options: () => ({ transport: transportReturning({ status: 500, body: {} }) }),
|
|
1163
|
-
reason: "http_error",
|
|
1164
|
-
kind: "rejected",
|
|
1165
|
-
httpStatus: 500,
|
|
1166
|
-
},
|
|
1167
|
-
{
|
|
1168
|
-
name: "route not deployed (404 soft skip)",
|
|
1169
|
-
options: () => ({ transport: transportReturning({ status: 404, body: {} }) }),
|
|
1170
|
-
reason: "soft_skip",
|
|
1171
|
-
httpStatus: 404,
|
|
1172
|
-
},
|
|
1173
|
-
{
|
|
1174
|
-
name: "server refused the fold",
|
|
1175
|
-
options: () => ({ transport: transportReturning(accepted({ materialised: false })) }),
|
|
1176
|
-
reason: "not_materialised",
|
|
1177
|
-
kind: "not_materialised",
|
|
1178
|
-
},
|
|
1179
|
-
{
|
|
1180
|
-
name: "server asked for a full resend",
|
|
1181
|
-
options: () => ({
|
|
1182
|
-
transport: transportReturning({
|
|
1183
|
-
status: 200,
|
|
1184
|
-
body: { status: "resend_full", scopeKey: SCOPE_KEY },
|
|
1185
|
-
}),
|
|
1186
|
-
}),
|
|
1187
|
-
reason: "resend_full",
|
|
1188
|
-
kind: "rejected",
|
|
1189
|
-
httpStatus: 200,
|
|
1190
|
-
},
|
|
1191
|
-
{
|
|
1192
|
-
name: "chunk too large (413 sizing fault)",
|
|
1193
|
-
options: () => ({
|
|
1194
|
-
transport: transportReturning({ status: 413, body: { reason: "chunk_too_large" } }),
|
|
1195
|
-
}),
|
|
1196
|
-
reason: "chunk_too_large",
|
|
1197
|
-
kind: "chunk_too_large",
|
|
1198
|
-
httpStatus: 413,
|
|
1199
|
-
},
|
|
1200
|
-
{
|
|
1201
|
-
name: "scope over the server's entry ceiling (413)",
|
|
1202
|
-
options: () => ({
|
|
1203
|
-
transport: transportReturning({
|
|
1204
|
-
status: 413,
|
|
1205
|
-
body: { reason: SYNC_MANIFEST_TOO_LARGE_REASON_CODE },
|
|
1206
|
-
}),
|
|
1207
|
-
}),
|
|
1208
|
-
reason: "too_large",
|
|
1209
|
-
kind: "manifest_too_large",
|
|
1210
|
-
httpStatus: 413,
|
|
1211
|
-
},
|
|
1212
|
-
];
|
|
1213
|
-
it.each(cases)("records $reason when $name", async ({ options, reason, kind, httpStatus }) => {
|
|
1214
|
-
const result = await runManifestUploadPass(baseOptions(options()));
|
|
1215
|
-
// Guard the premise: these cases must actually be failures, otherwise the
|
|
1216
|
-
// assertions below would pass vacuously against a healthy pass.
|
|
1217
|
-
expect(result.status).not.toBe("uploaded");
|
|
1218
|
-
const snapshot = readManifestSnapshot(stateDir, SCOPE_KEY);
|
|
1219
|
-
expect(snapshot).not.toBeNull();
|
|
1220
|
-
expect(snapshot?.lastFailureReason).toBe(reason);
|
|
1221
|
-
// The backoff and the reason must be armed together — a reason without a
|
|
1222
|
-
// failure count (or vice versa) is the half-written state this replaces.
|
|
1223
|
-
expect(snapshot?.consecutiveFailures).toBe(1);
|
|
1224
|
-
expect(snapshot?.lastAttemptAt).toBeDefined();
|
|
1225
|
-
if (kind === undefined)
|
|
1226
|
-
expect(snapshot?.lastFailureKind).toBeUndefined();
|
|
1227
|
-
else
|
|
1228
|
-
expect(snapshot?.lastFailureKind).toBe(kind);
|
|
1229
|
-
if (httpStatus === undefined)
|
|
1230
|
-
expect(snapshot?.lastFailureStatus).toBeUndefined();
|
|
1231
|
-
else
|
|
1232
|
-
expect(snapshot?.lastFailureStatus).toBe(httpStatus);
|
|
1233
|
-
});
|
|
1234
|
-
it("records a reason for a walk that never completed", async () => {
|
|
1235
|
-
// The walk budget is the branch the 2026-09-07 investigation had to rule
|
|
1236
|
-
// out by re-execution precisely because this field did not exist.
|
|
1237
|
-
//
|
|
1238
|
-
// A pre-raised abort is the suite's established way to cut the walk short.
|
|
1239
|
-
// Whether it lands as `walk_truncated` (the walk noticed) or `failed`
|
|
1240
|
-
// with `aborted` (the chunk loop noticed first) is a race this test does
|
|
1241
|
-
// not care about — the INVARIANT is that whichever branch wins persists
|
|
1242
|
-
// its own reason, and that neither is allowed to be silent.
|
|
1243
|
-
const controller = new AbortController();
|
|
1244
|
-
controller.abort();
|
|
1245
|
-
const transport = transportReturning(accepted());
|
|
1246
|
-
const result = await runManifestUploadPass(baseOptions({ transport, abortSignal: controller.signal }));
|
|
1247
|
-
const expectedReason = result.status === "walk_truncated" ? "walk_truncated" : "aborted";
|
|
1248
|
-
const snapshot = readManifestSnapshot(stateDir, SCOPE_KEY);
|
|
1249
|
-
expect(snapshot?.lastFailureReason).toBe(expectedReason);
|
|
1250
|
-
expect(snapshot?.lastFailureStatus).toBeUndefined();
|
|
1251
|
-
expect(snapshot?.consecutiveFailures).toBe(1);
|
|
1252
|
-
// Neither branch may reach the wire.
|
|
1253
|
-
expect(transport).not.toHaveBeenCalled();
|
|
1254
|
-
expect(result.uploadedChunks ?? 0).toBe(0);
|
|
1255
|
-
});
|
|
1256
|
-
it("records walk_truncated with no status when the builder reports a truncated walk", async () => {
|
|
1257
|
-
// Pinned directly on the branch rather than through a timing race: a
|
|
1258
|
-
// truncated walk is the failure the incident report had to eliminate by
|
|
1259
|
-
// hand, so it gets an assertion that cannot go vacuous.
|
|
1260
|
-
const transport = transportReturning(accepted());
|
|
1261
|
-
const result = await runManifestUploadPass(baseOptions({
|
|
1262
|
-
transport,
|
|
1263
|
-
// Budget spent before the first directory is read.
|
|
1264
|
-
maxWallMs: -1,
|
|
1265
|
-
}));
|
|
1266
|
-
expect(result.status).toBe("walk_truncated");
|
|
1267
|
-
const snapshot = readManifestSnapshot(stateDir, SCOPE_KEY);
|
|
1268
|
-
expect(snapshot?.lastFailureReason).toBe("walk_truncated");
|
|
1269
|
-
expect(snapshot?.lastFailureKind).toBeUndefined();
|
|
1270
|
-
expect(snapshot?.lastFailureStatus).toBeUndefined();
|
|
1271
|
-
expect(transport).not.toHaveBeenCalled();
|
|
1272
|
-
});
|
|
1273
|
-
it("records ledger_implausible when a tracked scope reads back as disk-only", async () => {
|
|
1274
|
-
// A prior snapshot proves the scope WAS tracked; the stubbed-away ledger
|
|
1275
|
-
// (`listJournalsImpl: () => []`) makes every entry disk-only.
|
|
1276
|
-
seedSnapshot({ sequence: 7 });
|
|
1277
|
-
const transport = transportReturning(accepted());
|
|
1278
|
-
const result = await runManifestUploadPass(baseOptions({ transport, forceFull: true, listJournalsImpl: () => [] }));
|
|
1279
|
-
expect(result.status).toBe("ledger_implausible");
|
|
1280
|
-
{
|
|
1281
|
-
const snapshot = readManifestSnapshot(stateDir, SCOPE_KEY);
|
|
1282
|
-
expect(snapshot?.lastFailureReason).toBe("ledger_implausible");
|
|
1283
|
-
expect(transport).not.toHaveBeenCalled();
|
|
1284
|
-
}
|
|
1285
|
-
});
|
|
1286
|
-
it("clears the failure reason once a pass succeeds", async () => {
|
|
1287
|
-
// A stale reason is worse than none: it would describe a fault the scope
|
|
1288
|
-
// has already recovered from.
|
|
1289
|
-
seedSnapshot({
|
|
1290
|
-
sequence: 3,
|
|
1291
|
-
lastFailureReason: "transport_error",
|
|
1292
|
-
lastFailureKind: "transport_error",
|
|
1293
|
-
lastFailureStatus: 500,
|
|
1294
|
-
consecutiveFailures: 4,
|
|
1295
|
-
lastAttemptAt: new Date(NOW_MS - 60_000).toISOString(),
|
|
1296
|
-
});
|
|
1297
|
-
const result = await runManifestUploadPass(baseOptions({
|
|
1298
|
-
// The seeded failure state arms the backoff; this test is about what a
|
|
1299
|
-
// SUCCESS clears, not about the backoff itself.
|
|
1300
|
-
ignoreThrottle: true,
|
|
1301
|
-
transport: transportReturning(accepted()),
|
|
1302
|
-
listJournalsImpl: ledgerTracking("notes/a.md", "notes/b.md", "notes/c.md"),
|
|
1303
|
-
}));
|
|
1304
|
-
expect(result.status).toBe("uploaded");
|
|
1305
|
-
const snapshot = readManifestSnapshot(stateDir, SCOPE_KEY);
|
|
1306
|
-
expect(snapshot?.lastFailureReason).toBeUndefined();
|
|
1307
|
-
expect(snapshot?.lastFailureKind).toBeUndefined();
|
|
1308
|
-
expect(snapshot?.lastFailureStatus).toBeUndefined();
|
|
1309
|
-
expect(snapshot?.consecutiveFailures).toBeUndefined();
|
|
1310
|
-
expect(snapshot?.lastUploadAt).toBeDefined();
|
|
1311
|
-
});
|
|
1312
|
-
it("replaces a previous reason rather than leaving a stale kind/status behind", async () => {
|
|
1313
|
-
// The earlier failure carried an HTTP status; the new one never reached
|
|
1314
|
-
// the wire, so the status must be GONE, not carried forward.
|
|
1315
|
-
seedSnapshot({
|
|
1316
|
-
sequence: 3,
|
|
1317
|
-
lastFailureReason: "http_error",
|
|
1318
|
-
lastFailureKind: "rejected",
|
|
1319
|
-
lastFailureStatus: 500,
|
|
1320
|
-
consecutiveFailures: 1,
|
|
1321
|
-
});
|
|
1322
|
-
await runManifestUploadPass(baseOptions({
|
|
1323
|
-
ignoreThrottle: true,
|
|
1324
|
-
transport: vi.fn(async () => {
|
|
1325
|
-
throw new Error("ECONNRESET");
|
|
1326
|
-
}),
|
|
1327
|
-
}));
|
|
1328
|
-
const snapshot = readManifestSnapshot(stateDir, SCOPE_KEY);
|
|
1329
|
-
expect(snapshot?.lastFailureReason).toBe("transport_error");
|
|
1330
|
-
expect(snapshot?.lastFailureKind).toBe("transport_error");
|
|
1331
|
-
expect(snapshot?.lastFailureStatus).toBeUndefined();
|
|
1332
|
-
expect(snapshot?.consecutiveFailures).toBe(2);
|
|
1333
|
-
});
|
|
1334
|
-
});
|
|
1335
|
-
//# sourceMappingURL=upload-manifest.test.js.map
|