@indigoai-us/hq-cloud 6.18.44-alpha.8 → 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 -1482
- 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,1556 +0,0 @@
|
|
|
1
|
-
import * as fs from "node:fs";
|
|
2
|
-
import * as crypto from "node:crypto";
|
|
3
|
-
import { spawn } from "node:child_process";
|
|
4
|
-
import { once } from "node:events";
|
|
5
|
-
import * as os from "node:os";
|
|
6
|
-
import * as path from "node:path";
|
|
7
|
-
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
8
|
-
import { StateStore, StateStoreCorruptionError, StateStoreLockError, StateStoreUnsupportedFormatError, SNAPSHOT_V4_MAGIC, getStateStoreSnapshotEncodeCountForTest, getStateStoreSnapshotMaterializationCountForTest, getStateStoreSnapshotRecoveryCountForTest, resetStateStoreSnapshotEncodeCountForTest, resetStateStoreSnapshotMaterializationCountForTest, resetStateStoreSnapshotRecoveryCountForTest, setStateStoreAfterGenerationStageHookForTest, setStateStoreAfterOptimisticRecoveryHookForTest, setStateStoreAsyncLockTimeoutForTest, setStateStorePositionalWriteForTest, setStateStoreReflinkCopyHookForTest, setStateStoreRotationClaimReleaseHookForTest, setStateStoreRotationClaimWriteHookForTest, setStateStoreScopeLockAcquiredHookForTest, setStateStoreScopeLockOpenedHookForTest, setStateStoreScopeLockContendedHookForTest, setStateStoreScopeLockWriteHookForTest, setStateStoreSyncLockTimeoutForTest, setStateStoreSnapshotReadHookForTest, setStateStoreSnapshotWriteHookForTest, setStateStoreWalReadHookForTest, setStateStoreNowMsForTest, setStateStoreSleepForTest, setStateStoreRandomForTest, encodeSnapshotBytesForTest, encodeSnapshotChunksForTest, } from "./state-store.js";
|
|
9
|
-
const RECORD = 7;
|
|
10
|
-
const roots = [];
|
|
11
|
-
afterEach(() => {
|
|
12
|
-
setStateStoreSnapshotWriteHookForTest(undefined);
|
|
13
|
-
setStateStoreSnapshotReadHookForTest(undefined);
|
|
14
|
-
setStateStoreWalReadHookForTest(undefined);
|
|
15
|
-
setStateStoreAfterGenerationStageHookForTest(undefined);
|
|
16
|
-
setStateStoreAfterOptimisticRecoveryHookForTest(undefined);
|
|
17
|
-
setStateStoreReflinkCopyHookForTest(undefined);
|
|
18
|
-
setStateStorePositionalWriteForTest(undefined);
|
|
19
|
-
setStateStoreRotationClaimReleaseHookForTest(undefined);
|
|
20
|
-
setStateStoreRotationClaimWriteHookForTest(undefined);
|
|
21
|
-
setStateStoreScopeLockAcquiredHookForTest(undefined);
|
|
22
|
-
setStateStoreScopeLockOpenedHookForTest(undefined);
|
|
23
|
-
setStateStoreScopeLockContendedHookForTest(undefined);
|
|
24
|
-
setStateStoreScopeLockWriteHookForTest(undefined);
|
|
25
|
-
setStateStoreAsyncLockTimeoutForTest(undefined);
|
|
26
|
-
setStateStoreSyncLockTimeoutForTest(undefined);
|
|
27
|
-
setStateStoreNowMsForTest(undefined);
|
|
28
|
-
setStateStoreSleepForTest(undefined);
|
|
29
|
-
setStateStoreRandomForTest(undefined);
|
|
30
|
-
for (const root of roots.splice(0))
|
|
31
|
-
fs.rmSync(root, { recursive: true, force: true });
|
|
32
|
-
});
|
|
33
|
-
function openStore(root = makeRoot(), threshold = 256) {
|
|
34
|
-
return StateStore.open({
|
|
35
|
-
rootDir: root,
|
|
36
|
-
scopeId: "prs_test_scope",
|
|
37
|
-
initialState: { values: [] },
|
|
38
|
-
maxRecordsBeforeCompaction: threshold,
|
|
39
|
-
reduce: (state, record) => {
|
|
40
|
-
if (record.type !== RECORD || typeof record.payload !== "string") {
|
|
41
|
-
throw new Error("test reducer received an invalid record");
|
|
42
|
-
}
|
|
43
|
-
return { values: [...state.values, record.payload] };
|
|
44
|
-
},
|
|
45
|
-
});
|
|
46
|
-
}
|
|
47
|
-
function snapshotDecoder(chunks) {
|
|
48
|
-
return JSON.parse(Buffer.concat([...chunks]).toString("utf8"));
|
|
49
|
-
}
|
|
50
|
-
function snapshotPathFor(store) {
|
|
51
|
-
return path.join(store.stateDir, `snapshot-${store.getGeneration()}.bin`);
|
|
52
|
-
}
|
|
53
|
-
function rewriteSnapshotPayload(store, payload) {
|
|
54
|
-
const snapshotPath = snapshotPathFor(store);
|
|
55
|
-
const existing = fs.readFileSync(snapshotPath);
|
|
56
|
-
const header = Buffer.from(existing.subarray(0, 52));
|
|
57
|
-
header.writeUInt32BE(payload.length, 16);
|
|
58
|
-
const checksum = crypto.createHash("sha256").update(header).update(payload).digest();
|
|
59
|
-
fs.writeFileSync(snapshotPath, Buffer.concat([header, payload, checksum]));
|
|
60
|
-
}
|
|
61
|
-
function rewriteSnapshotMagic(store, magic, generation = store.getGeneration()) {
|
|
62
|
-
const snapshotPath = path.join(store.stateDir, `snapshot-${generation}.bin`);
|
|
63
|
-
const body = Buffer.from(fs.readFileSync(snapshotPath));
|
|
64
|
-
magic.copy(body, 0);
|
|
65
|
-
crypto.createHash("sha256").update(body.subarray(0, body.length - 32)).digest()
|
|
66
|
-
.copy(body, body.length - 32);
|
|
67
|
-
fs.writeFileSync(snapshotPath, body);
|
|
68
|
-
}
|
|
69
|
-
function expectUnsupportedSnapshotFormat(open, found, accepted) {
|
|
70
|
-
let thrown;
|
|
71
|
-
try {
|
|
72
|
-
open();
|
|
73
|
-
}
|
|
74
|
-
catch (error) {
|
|
75
|
-
thrown = error;
|
|
76
|
-
}
|
|
77
|
-
expect(thrown).toBeInstanceOf(StateStoreUnsupportedFormatError);
|
|
78
|
-
expect(thrown).not.toBeInstanceOf(StateStoreCorruptionError);
|
|
79
|
-
const error = thrown;
|
|
80
|
-
expect(error.foundMagic).toBe(found);
|
|
81
|
-
expect([...error.acceptedMagics]).toEqual(accepted);
|
|
82
|
-
expect(error.message).toContain(`is ${found}`);
|
|
83
|
-
expect(error.message).toContain(`this reader accepts ${accepted.join(", ")}`);
|
|
84
|
-
expect(error.message).toMatch(/Upgrade @indigoai-us\/hq-cloud/);
|
|
85
|
-
expect(error.message).toMatch(/Do not delete ~\/\.hq\/sync-state-v3/);
|
|
86
|
-
expect(error.message).not.toMatch(/corruption/i);
|
|
87
|
-
return error;
|
|
88
|
-
}
|
|
89
|
-
function openReplacingStore(root, options = {}) {
|
|
90
|
-
return StateStore.open({
|
|
91
|
-
rootDir: root,
|
|
92
|
-
scopeId: "replacing_test_scope",
|
|
93
|
-
initialState: { value: "" },
|
|
94
|
-
...options,
|
|
95
|
-
reduce: (_state, record) => ({ value: String(record.payload) }),
|
|
96
|
-
});
|
|
97
|
-
}
|
|
98
|
-
function makeRoot() {
|
|
99
|
-
const root = fs.mkdtempSync(path.join(os.tmpdir(), "hq-state-store-test-"));
|
|
100
|
-
roots.push(root);
|
|
101
|
-
return root;
|
|
102
|
-
}
|
|
103
|
-
/** Deterministic fold-retry clock. `freeze` keeps elapsed at 0 so budget never expires. */
|
|
104
|
-
function installDeterministicCompactionClock(options = {}) {
|
|
105
|
-
const clock = { now: 0 };
|
|
106
|
-
setStateStoreNowMsForTest(() => clock.now);
|
|
107
|
-
setStateStoreSleepForTest((ms) => {
|
|
108
|
-
if (!options.freeze)
|
|
109
|
-
clock.now += ms;
|
|
110
|
-
});
|
|
111
|
-
setStateStoreRandomForTest(() => 1);
|
|
112
|
-
}
|
|
113
|
-
function openReplacingStoreWithoutCompacting(root, options) {
|
|
114
|
-
const skip = vi.spyOn(StateStore.prototype, "compactIfNeeded").mockReturnValue(false);
|
|
115
|
-
try {
|
|
116
|
-
return openReplacingStore(root, options);
|
|
117
|
-
}
|
|
118
|
-
finally {
|
|
119
|
-
skip.mockRestore();
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
function seedReplacingWal(root, payloadSize) {
|
|
123
|
-
const writer = openReplacingStore(root, { maxWalBytesBeforeCompaction: 16 * 1024 * 1024 });
|
|
124
|
-
writer.append(RECORD, "x".repeat(payloadSize));
|
|
125
|
-
return writer.getRecoveryStats().walBytes;
|
|
126
|
-
}
|
|
127
|
-
function countFailedReplacementAttempts(overBudgetRatio) {
|
|
128
|
-
installDeterministicCompactionClock();
|
|
129
|
-
const root = makeRoot();
|
|
130
|
-
const walBytes = seedReplacingWal(root, 2_000);
|
|
131
|
-
const maxWalBytesBeforeCompaction = Math.max(81, Math.floor(walBytes / overBudgetRatio));
|
|
132
|
-
const replacing = openReplacingStoreWithoutCompacting(root, {
|
|
133
|
-
maxWalBytesBeforeCompaction,
|
|
134
|
-
maxRecordsBeforeCompaction: 1_000_000,
|
|
135
|
-
});
|
|
136
|
-
const racing = openReplacingStore(root, {
|
|
137
|
-
maxWalBytesBeforeCompaction: 16 * 1024 * 1024,
|
|
138
|
-
maxRecordsBeforeCompaction: 1_000_000,
|
|
139
|
-
});
|
|
140
|
-
let attempts = 0;
|
|
141
|
-
setStateStoreSnapshotWriteHookForTest(() => {
|
|
142
|
-
attempts += 1;
|
|
143
|
-
racing.append(RECORD, `race-${attempts}`);
|
|
144
|
-
});
|
|
145
|
-
expect(() => replacing.replaceState({ value: "never" })).toThrow(StateStoreLockError);
|
|
146
|
-
return attempts;
|
|
147
|
-
}
|
|
148
|
-
describe("StateStore frame ceiling", () => {
|
|
149
|
-
// A journal delta carries one upsert per changed file. On a 455k-entry
|
|
150
|
-
// personal vault a mass restamp encodes to roughly 87 MB, which the previous
|
|
151
|
-
// 64 MiB ceiling rejected outright — `writeJournal` threw, the pass recorded
|
|
152
|
-
// nothing, and the next pass produced the identical oversized record. That is
|
|
153
|
-
// a permanent stall for a large scope, not a transient error, so the ceiling
|
|
154
|
-
// has to clear a realistic worst case rather than merely a typical one.
|
|
155
|
-
//
|
|
156
|
-
// Sized just past the OLD limit on purpose: this fails on a 64 MiB ceiling
|
|
157
|
-
// and passes on a larger one, which is exactly the regression being pinned.
|
|
158
|
-
const JUST_OVER_OLD_LIMIT = 68 * 1024 * 1024;
|
|
159
|
-
/**
|
|
160
|
-
* A store that will NOT compact the oversized frame away.
|
|
161
|
-
*
|
|
162
|
-
* Load-bearing. With the default 16 MiB byte threshold, appending a 68 MiB
|
|
163
|
-
* record trips compaction immediately: the state lands in a fresh snapshot
|
|
164
|
-
* and the generation's WAL is left header-only. A reopen then recovers from
|
|
165
|
-
* the snapshot and never replays the big frame — so the test would pass even
|
|
166
|
-
* if the writer accepted 256 MiB while the reader kept its old 64 MiB cap,
|
|
167
|
-
* which is the half of the change most likely to be missed. Raising both
|
|
168
|
-
* thresholds past the payload keeps the frame in the WAL, so the reopen
|
|
169
|
-
* exercises the reader's frame-length validation for real.
|
|
170
|
-
*/
|
|
171
|
-
function openNonCompactingStore(root) {
|
|
172
|
-
return StateStore.open({
|
|
173
|
-
rootDir: root,
|
|
174
|
-
scopeId: "prs_test_scope",
|
|
175
|
-
initialState: { values: [] },
|
|
176
|
-
maxRecordsBeforeCompaction: 1024,
|
|
177
|
-
maxWalBytesBeforeCompaction: JUST_OVER_OLD_LIMIT * 4,
|
|
178
|
-
reduce: (state, record) => {
|
|
179
|
-
if (record.type !== RECORD || typeof record.payload !== "string") {
|
|
180
|
-
throw new Error("test reducer received an invalid record");
|
|
181
|
-
}
|
|
182
|
-
return { values: [...state.values, record.payload] };
|
|
183
|
-
},
|
|
184
|
-
});
|
|
185
|
-
}
|
|
186
|
-
it("round-trips a record larger than the previous 64 MiB ceiling", () => {
|
|
187
|
-
const root = makeRoot();
|
|
188
|
-
const store = openNonCompactingStore(root);
|
|
189
|
-
const payload = "x".repeat(JUST_OVER_OLD_LIMIT);
|
|
190
|
-
store.append(RECORD, payload);
|
|
191
|
-
// The frame must still be in the WAL — if it compacted away, the reopen
|
|
192
|
-
// below proves nothing about the reader.
|
|
193
|
-
const walBytes = fs
|
|
194
|
-
.readdirSync(store.stateDir)
|
|
195
|
-
.filter((name) => name.startsWith("wal-"))
|
|
196
|
-
.reduce((total, name) => total + fs.statSync(path.join(store.stateDir, name)).size, 0);
|
|
197
|
-
expect(walBytes).toBeGreaterThan(JUST_OVER_OLD_LIMIT);
|
|
198
|
-
// Durable, not just in memory: reopening REPLAYS that frame off disk, so a
|
|
199
|
-
// ceiling raised only on the writer surfaces here as a rejected frame.
|
|
200
|
-
const reopened = openNonCompactingStore(root);
|
|
201
|
-
expect(reopened.getState().values).toHaveLength(1);
|
|
202
|
-
expect(reopened.getState().values[0].length).toBe(JUST_OVER_OLD_LIMIT);
|
|
203
|
-
}, 60_000); // Encoding and replaying 68 MiB can exceed the suite default under coverage.
|
|
204
|
-
// NOT covered by a test, deliberately: that a payload past the NEW ceiling is
|
|
205
|
-
// still refused. Proving it requires materializing a >256 MiB JSON string,
|
|
206
|
-
// since `canonicalJson` has to encode the payload before the guard can
|
|
207
|
-
// measure it — no amount of structural sharing in the input avoids the
|
|
208
|
-
// output allocation. That is a poor trade to make on every CI run to assert a
|
|
209
|
-
// `throw` that is three lines from the constant. The guard is at
|
|
210
|
-
// `encodeFrame`; if you move the ceiling, move it there.
|
|
211
|
-
});
|
|
212
|
-
describe("StateStore asynchronous lock acquisition", () => {
|
|
213
|
-
it("fails fast when a synchronous contender meets an in-process async holder", async () => {
|
|
214
|
-
const store = openStore();
|
|
215
|
-
const lockPath = path.join(store.stateDir, "append.lock");
|
|
216
|
-
let releaseHolder;
|
|
217
|
-
let held = false;
|
|
218
|
-
const holderEntered = new Promise((resolve) => {
|
|
219
|
-
setStateStoreScopeLockAcquiredHookForTest(() => {
|
|
220
|
-
if (held)
|
|
221
|
-
return;
|
|
222
|
-
held = true;
|
|
223
|
-
resolve();
|
|
224
|
-
return new Promise((release) => {
|
|
225
|
-
releaseHolder = release;
|
|
226
|
-
});
|
|
227
|
-
});
|
|
228
|
-
});
|
|
229
|
-
const holder = store.appendAsync(RECORD, "holder");
|
|
230
|
-
await holderEntered;
|
|
231
|
-
const started = Date.now();
|
|
232
|
-
let error;
|
|
233
|
-
try {
|
|
234
|
-
store.append(RECORD, "sync-contender");
|
|
235
|
-
}
|
|
236
|
-
catch (caught) {
|
|
237
|
-
error = caught;
|
|
238
|
-
}
|
|
239
|
-
expect(Date.now() - started).toBeLessThan(250);
|
|
240
|
-
expect(error).toMatchObject({
|
|
241
|
-
name: "StateStoreLockError",
|
|
242
|
-
reason: "in-process-async-holder",
|
|
243
|
-
});
|
|
244
|
-
expect(error).toBeInstanceOf(StateStoreLockError);
|
|
245
|
-
expect(fs.existsSync(lockPath)).toBe(true);
|
|
246
|
-
releaseHolder();
|
|
247
|
-
await expect(holder).resolves.toMatchObject({ payload: "holder" });
|
|
248
|
-
expect(fs.existsSync(lockPath)).toBe(false);
|
|
249
|
-
});
|
|
250
|
-
it("does not break an append.lock held by this process when stale probing reports ESRCH", () => {
|
|
251
|
-
const store = openStore();
|
|
252
|
-
const lockPath = path.join(store.stateDir, "append.lock");
|
|
253
|
-
fs.writeFileSync(lockPath, String(process.pid), { flag: "wx" });
|
|
254
|
-
setStateStoreSyncLockTimeoutForTest(25);
|
|
255
|
-
const kill = vi.spyOn(process, "kill").mockImplementation(() => {
|
|
256
|
-
const error = new Error("simulated stale probe");
|
|
257
|
-
error.code = "ESRCH";
|
|
258
|
-
throw error;
|
|
259
|
-
});
|
|
260
|
-
try {
|
|
261
|
-
expect(() => store.append(RECORD, "contender")).toThrow(StateStoreLockError);
|
|
262
|
-
expect(kill).not.toHaveBeenCalled();
|
|
263
|
-
expect(fs.existsSync(lockPath)).toBe(true);
|
|
264
|
-
}
|
|
265
|
-
finally {
|
|
266
|
-
kill.mockRestore();
|
|
267
|
-
}
|
|
268
|
-
});
|
|
269
|
-
it("keeps a newly created empty append.lock for its synchronous owner", () => {
|
|
270
|
-
const store = openStore();
|
|
271
|
-
const lockPath = path.join(store.stateDir, "append.lock");
|
|
272
|
-
fs.writeFileSync(lockPath, "", { flag: "wx" });
|
|
273
|
-
setStateStoreSyncLockTimeoutForTest(25);
|
|
274
|
-
expect(() => store.append(RECORD, "contender")).toThrow(StateStoreLockError);
|
|
275
|
-
expect(fs.existsSync(lockPath)).toBe(true);
|
|
276
|
-
// The creator can still publish its PID and release the lock safely.
|
|
277
|
-
fs.writeFileSync(lockPath, String(process.pid));
|
|
278
|
-
fs.unlinkSync(lockPath);
|
|
279
|
-
expect(store.append(RECORD, "after-owner-release")).toMatchObject({ payload: "after-owner-release" });
|
|
280
|
-
});
|
|
281
|
-
it("writes its PID before an async lock acquisition yields", async () => {
|
|
282
|
-
const store = openStore();
|
|
283
|
-
const lockPath = path.join(store.stateDir, "append.lock");
|
|
284
|
-
let releaseOpenWindow;
|
|
285
|
-
let signalOpenWindow;
|
|
286
|
-
const openWindow = new Promise((resolve) => {
|
|
287
|
-
signalOpenWindow = resolve;
|
|
288
|
-
});
|
|
289
|
-
setStateStoreScopeLockOpenedHookForTest(async () => {
|
|
290
|
-
signalOpenWindow();
|
|
291
|
-
await new Promise((resolve) => {
|
|
292
|
-
releaseOpenWindow = resolve;
|
|
293
|
-
});
|
|
294
|
-
});
|
|
295
|
-
const asyncAppend = store.appendAsync(RECORD, "async-owner").then((value) => ({ value }), (error) => ({ error }));
|
|
296
|
-
await openWindow;
|
|
297
|
-
const started = Date.now();
|
|
298
|
-
let syncError;
|
|
299
|
-
try {
|
|
300
|
-
store.append(RECORD, "sync-contender");
|
|
301
|
-
}
|
|
302
|
-
catch (error) {
|
|
303
|
-
syncError = error;
|
|
304
|
-
}
|
|
305
|
-
const syncElapsedMs = Date.now() - started;
|
|
306
|
-
const lockStillExists = fs.existsSync(lockPath);
|
|
307
|
-
const lockContents = lockStillExists ? fs.readFileSync(lockPath, "utf8") : undefined;
|
|
308
|
-
releaseOpenWindow();
|
|
309
|
-
const asyncResult = await asyncAppend;
|
|
310
|
-
expect(syncError).toBeInstanceOf(StateStoreLockError);
|
|
311
|
-
expect(syncError).toMatchObject({
|
|
312
|
-
name: "StateStoreLockError",
|
|
313
|
-
reason: "in-process-async-holder",
|
|
314
|
-
});
|
|
315
|
-
expect(lockStillExists).toBe(true);
|
|
316
|
-
expect(lockContents).toBe(String(process.pid));
|
|
317
|
-
expect(syncElapsedMs).toBeLessThan(1_000);
|
|
318
|
-
expect(asyncResult).toMatchObject({ value: { payload: "async-owner" } });
|
|
319
|
-
expect(store.getState().values).toEqual(["async-owner"]);
|
|
320
|
-
expect(fs.existsSync(lockPath)).toBe(false);
|
|
321
|
-
});
|
|
322
|
-
it("retries a newly created empty append.lock until its async owner releases it", async () => {
|
|
323
|
-
const store = openStore();
|
|
324
|
-
const lockPath = path.join(store.stateDir, "append.lock");
|
|
325
|
-
fs.writeFileSync(lockPath, "", { flag: "wx" });
|
|
326
|
-
setStateStoreAsyncLockTimeoutForTest(2_000);
|
|
327
|
-
let retainedUntilOwnerWrites = false;
|
|
328
|
-
let releaseRetry;
|
|
329
|
-
let signalRetry;
|
|
330
|
-
const retryObserved = new Promise((resolve) => {
|
|
331
|
-
signalRetry = resolve;
|
|
332
|
-
});
|
|
333
|
-
setStateStoreScopeLockContendedHookForTest(async () => {
|
|
334
|
-
retainedUntilOwnerWrites = fs.existsSync(lockPath);
|
|
335
|
-
signalRetry();
|
|
336
|
-
await new Promise((resolve) => {
|
|
337
|
-
releaseRetry = resolve;
|
|
338
|
-
});
|
|
339
|
-
});
|
|
340
|
-
const append = store.appendAsync(RECORD, "contender");
|
|
341
|
-
await retryObserved;
|
|
342
|
-
expect(retainedUntilOwnerWrites).toBe(true);
|
|
343
|
-
fs.writeFileSync(lockPath, String(process.pid));
|
|
344
|
-
fs.unlinkSync(lockPath);
|
|
345
|
-
releaseRetry();
|
|
346
|
-
await expect(append).resolves.toMatchObject({ payload: "contender" });
|
|
347
|
-
});
|
|
348
|
-
it("queues same-process async contenders without parking the event loop", async () => {
|
|
349
|
-
const store = openStore();
|
|
350
|
-
let releaseHolder;
|
|
351
|
-
let held = false;
|
|
352
|
-
const holderEntered = new Promise((resolve) => {
|
|
353
|
-
setStateStoreScopeLockAcquiredHookForTest(() => {
|
|
354
|
-
if (held)
|
|
355
|
-
return;
|
|
356
|
-
held = true;
|
|
357
|
-
resolve();
|
|
358
|
-
return new Promise((release) => {
|
|
359
|
-
releaseHolder = release;
|
|
360
|
-
});
|
|
361
|
-
});
|
|
362
|
-
});
|
|
363
|
-
const holder = store.appendAsync(RECORD, "holder");
|
|
364
|
-
await holderEntered;
|
|
365
|
-
let timerFired = false;
|
|
366
|
-
const contender = store.appendAsync(RECORD, "contender");
|
|
367
|
-
await new Promise((resolve) => setTimeout(() => {
|
|
368
|
-
timerFired = true;
|
|
369
|
-
resolve();
|
|
370
|
-
}, 25));
|
|
371
|
-
expect(timerFired).toBe(true);
|
|
372
|
-
releaseHolder();
|
|
373
|
-
await expect(Promise.all([holder, contender])).resolves.toHaveLength(2);
|
|
374
|
-
expect(store.getState().values).toEqual(["holder", "contender"]);
|
|
375
|
-
});
|
|
376
|
-
it("compacts queued async appends without rejecting their durable records", async () => {
|
|
377
|
-
const root = makeRoot();
|
|
378
|
-
const store = openStore(root, 2);
|
|
379
|
-
const payloads = Array.from({ length: 12 }, (_value, index) => `record-${index}`);
|
|
380
|
-
const appended = await Promise.allSettled(payloads.map((payload) => store.appendAsync(RECORD, payload)));
|
|
381
|
-
expect(appended).not.toContainEqual(expect.objectContaining({
|
|
382
|
-
status: "rejected",
|
|
383
|
-
reason: expect.objectContaining({
|
|
384
|
-
name: "StateStoreLockError",
|
|
385
|
-
reason: "in-process-async-holder",
|
|
386
|
-
}),
|
|
387
|
-
}));
|
|
388
|
-
expect(appended.map((result) => result.status)).toEqual(payloads.map(() => "fulfilled"));
|
|
389
|
-
expect(store.getGeneration()).toBeGreaterThan(1);
|
|
390
|
-
expect(fs.statSync(path.join(store.stateDir, `wal-${store.getGeneration()}.bin`)).size).toBe(80);
|
|
391
|
-
expect(openStore(root, 2).getState().values).toEqual(payloads);
|
|
392
|
-
});
|
|
393
|
-
it("publishes staged async compaction ahead of sustained queued appenders", async () => {
|
|
394
|
-
const root = makeRoot();
|
|
395
|
-
const store = openStore(root, 2);
|
|
396
|
-
const initialPayloads = Array.from({ length: 12 }, (_value, index) => `record-${index}`);
|
|
397
|
-
const injected = [];
|
|
398
|
-
let stageAttempts = 0;
|
|
399
|
-
setStateStoreAfterGenerationStageHookForTest(() => {
|
|
400
|
-
stageAttempts++;
|
|
401
|
-
injected.push(store.appendAsync(RECORD, `during-stage-${stageAttempts}`));
|
|
402
|
-
// The old FIFO queue lets this newly queued append invalidate each plan.
|
|
403
|
-
// Stop that deterministic reproduction after several turns so a broken
|
|
404
|
-
// implementation fails the generation assertion instead of hanging.
|
|
405
|
-
if (stageAttempts === 4)
|
|
406
|
-
throw new Error("compaction publish starved");
|
|
407
|
-
});
|
|
408
|
-
const initial = await Promise.allSettled(initialPayloads.map((payload) => store.appendAsync(RECORD, payload)));
|
|
409
|
-
const duringStaging = await Promise.allSettled(injected);
|
|
410
|
-
expect(initial).toEqual(initialPayloads.map(() => expect.objectContaining({ status: "fulfilled" })));
|
|
411
|
-
expect(duringStaging).toEqual(injected.map(() => expect.objectContaining({ status: "fulfilled" })));
|
|
412
|
-
expect(stageAttempts).toBe(1);
|
|
413
|
-
expect(store.getGeneration()).toBeGreaterThan(1);
|
|
414
|
-
expect(openStore(root, 2).getState().values).toEqual([
|
|
415
|
-
...initialPayloads,
|
|
416
|
-
"during-stage-1",
|
|
417
|
-
]);
|
|
418
|
-
});
|
|
419
|
-
it("keeps an async append durable when compaction fails", async () => {
|
|
420
|
-
const store = openStore(makeRoot(), 1);
|
|
421
|
-
setStateStoreSnapshotWriteHookForTest(() => {
|
|
422
|
-
throw new Error("simulated compaction failure");
|
|
423
|
-
});
|
|
424
|
-
await expect(store.appendAsync(RECORD, "durable")).resolves.toMatchObject({ payload: "durable" });
|
|
425
|
-
expect(store.getState().values).toEqual(["durable"]);
|
|
426
|
-
expect(store.getGeneration()).toBe(1);
|
|
427
|
-
});
|
|
428
|
-
it("recovers this process's leaked rotation claim without rejecting async appends", async () => {
|
|
429
|
-
const root = makeRoot();
|
|
430
|
-
const store = openStore(root, 1);
|
|
431
|
-
const claimPath = path.join(store.stateDir, "rotation.lock");
|
|
432
|
-
let failRelease = true;
|
|
433
|
-
setStateStoreRotationClaimReleaseHookForTest(() => {
|
|
434
|
-
if (!failRelease)
|
|
435
|
-
return;
|
|
436
|
-
failRelease = false;
|
|
437
|
-
throw new Error("simulated rotation-claim unlink failure");
|
|
438
|
-
});
|
|
439
|
-
await expect(store.appendAsync(RECORD, "first")).resolves.toMatchObject({ payload: "first" });
|
|
440
|
-
expect(fs.readFileSync(claimPath, "utf8").trim()).toBe(String(process.pid));
|
|
441
|
-
await expect(store.appendAsync(RECORD, "second")).resolves.toMatchObject({ payload: "second" });
|
|
442
|
-
expect(fs.existsSync(claimPath)).toBe(false);
|
|
443
|
-
expect(store.getGeneration()).toBeGreaterThan(2);
|
|
444
|
-
expect(openStore(root, 1).getState().values).toEqual(["first", "second"]);
|
|
445
|
-
});
|
|
446
|
-
it("yields timers while another writer holds append.lock", async () => {
|
|
447
|
-
const store = openStore();
|
|
448
|
-
const lockPath = path.join(store.stateDir, "append.lock");
|
|
449
|
-
fs.writeFileSync(lockPath, String(process.pid), { flag: "wx" });
|
|
450
|
-
let timerFired = false;
|
|
451
|
-
const append = store.appendAsync(RECORD, "queued");
|
|
452
|
-
const timer = new Promise((resolve) => setTimeout(() => {
|
|
453
|
-
timerFired = true;
|
|
454
|
-
resolve();
|
|
455
|
-
}, 25));
|
|
456
|
-
await timer;
|
|
457
|
-
expect(timerFired).toBe(true);
|
|
458
|
-
fs.unlinkSync(lockPath);
|
|
459
|
-
await expect(append).resolves.toMatchObject({ payload: "queued" });
|
|
460
|
-
});
|
|
461
|
-
it("shares an enqueue-time deadline behind a cross-process lock holder", async () => {
|
|
462
|
-
const store = openStore();
|
|
463
|
-
const lockPath = path.join(store.stateDir, "append.lock");
|
|
464
|
-
setStateStoreAsyncLockTimeoutForTest(100);
|
|
465
|
-
const holder = spawn(process.execPath, ["-e", [
|
|
466
|
-
"const fs = require('node:fs');",
|
|
467
|
-
"fs.writeFileSync(process.argv[1], String(process.pid), { flag: 'wx' });",
|
|
468
|
-
"process.stdout.write('ready\\n');",
|
|
469
|
-
"setInterval(() => {}, 1_000);",
|
|
470
|
-
].join(""), lockPath], { stdio: ["ignore", "pipe", "ignore"] });
|
|
471
|
-
try {
|
|
472
|
-
await once(holder.stdout, "data");
|
|
473
|
-
const first = store.appendAsync(RECORD, "first");
|
|
474
|
-
const second = store.appendAsync(RECORD, "second");
|
|
475
|
-
const settled = await Promise.race([
|
|
476
|
-
Promise.allSettled([first, second]),
|
|
477
|
-
new Promise((resolve) => setTimeout(() => resolve(undefined), 150)),
|
|
478
|
-
]);
|
|
479
|
-
expect(settled).toEqual([
|
|
480
|
-
expect.objectContaining({ status: "rejected", reason: expect.any(StateStoreLockError) }),
|
|
481
|
-
expect.objectContaining({ status: "rejected", reason: expect.any(StateStoreLockError) }),
|
|
482
|
-
]);
|
|
483
|
-
}
|
|
484
|
-
finally {
|
|
485
|
-
holder.kill();
|
|
486
|
-
await once(holder, "exit");
|
|
487
|
-
fs.rmSync(lockPath, { force: true });
|
|
488
|
-
}
|
|
489
|
-
});
|
|
490
|
-
it("does not append when a queued observation is superseded while waiting", async () => {
|
|
491
|
-
const store = openStore();
|
|
492
|
-
const lockPath = path.join(store.stateDir, "append.lock");
|
|
493
|
-
fs.writeFileSync(lockPath, String(process.pid), { flag: "wx" });
|
|
494
|
-
let current = true;
|
|
495
|
-
const append = store.appendIfAsync(RECORD, "stale", () => current);
|
|
496
|
-
current = false;
|
|
497
|
-
fs.unlinkSync(lockPath);
|
|
498
|
-
await expect(append).resolves.toBeUndefined();
|
|
499
|
-
expect(store.getLastRecordSequence()).toBe(0);
|
|
500
|
-
});
|
|
501
|
-
});
|
|
502
|
-
describe("StateStore ordered batch append", () => {
|
|
503
|
-
it("acquires independent scopes as one lexical all-or-none publication", () => {
|
|
504
|
-
const root = makeRoot();
|
|
505
|
-
const make = (scopeId) => StateStore.open({
|
|
506
|
-
rootDir: root,
|
|
507
|
-
scopeId,
|
|
508
|
-
initialState: { values: [] },
|
|
509
|
-
reduce: (state, record) => ({ values: [...state.values, String(record.payload)] }),
|
|
510
|
-
});
|
|
511
|
-
const alpha = make("batch-alpha");
|
|
512
|
-
const beta = make("batch-beta");
|
|
513
|
-
// Deliberately reverse caller order. StateStore orders by areaId, but
|
|
514
|
-
// returns results in caller order so a transaction owner can correlate
|
|
515
|
-
// records without exposing lock paths or sort details to its callers.
|
|
516
|
-
const appended = StateStore.appendBatch([
|
|
517
|
-
{ areaId: "root:beta", store: beta, type: RECORD, payload: "beta" },
|
|
518
|
-
{ areaId: "root:alpha", store: alpha, type: RECORD, payload: "alpha" },
|
|
519
|
-
]);
|
|
520
|
-
expect(appended.map((record) => record.payload)).toEqual(["beta", "alpha"]);
|
|
521
|
-
expect(alpha.getState().values).toEqual(["alpha"]);
|
|
522
|
-
expect(beta.getState().values).toEqual(["beta"]);
|
|
523
|
-
expect(fs.existsSync(path.join(alpha.stateDir, "append.lock"))).toBe(false);
|
|
524
|
-
expect(fs.existsSync(path.join(beta.stateDir, "append.lock"))).toBe(false);
|
|
525
|
-
});
|
|
526
|
-
it("cleans up a newly created batch lock when writing its PID fails", () => {
|
|
527
|
-
const store = openStore();
|
|
528
|
-
const lockPath = path.join(store.stateDir, "append.lock");
|
|
529
|
-
setStateStoreScopeLockWriteHookForTest(() => { throw new Error("simulated PID write failure"); });
|
|
530
|
-
expect(() => StateStore.appendBatch([
|
|
531
|
-
{ areaId: "root:alpha", store, type: RECORD, payload: "blocked" },
|
|
532
|
-
])).toThrow(StateStoreLockError);
|
|
533
|
-
expect(fs.existsSync(lockPath)).toBe(false);
|
|
534
|
-
setStateStoreScopeLockWriteHookForTest(undefined);
|
|
535
|
-
store.append(RECORD, "works-after-cleanup");
|
|
536
|
-
expect(store.getState().values).toEqual(["works-after-cleanup"]);
|
|
537
|
-
});
|
|
538
|
-
});
|
|
539
|
-
describe("StateStore WAL v3", () => {
|
|
540
|
-
it("writes protocol byte headers and appends frames without rewriting the snapshot hot path", () => {
|
|
541
|
-
const root = makeRoot();
|
|
542
|
-
const store = openStore(root);
|
|
543
|
-
const snapshot = path.join(store.stateDir, "snapshot-1.bin");
|
|
544
|
-
const wal = path.join(store.stateDir, "wal-1.bin");
|
|
545
|
-
const before = fs.statSync(snapshot);
|
|
546
|
-
const beforeWalSize = fs.statSync(wal).size;
|
|
547
|
-
store.append(RECORD, "first", 3);
|
|
548
|
-
const after = fs.statSync(snapshot);
|
|
549
|
-
const walBytes = fs.readFileSync(wal);
|
|
550
|
-
expect(after.ino).toBe(before.ino);
|
|
551
|
-
expect(fs.statSync(wal).size).toBeGreaterThan(beforeWalSize);
|
|
552
|
-
expect(walBytes.subarray(0, 8).toString("ascii")).toBe("HQWAL3\0\0");
|
|
553
|
-
expect(walBytes.readUInt32BE(80)).toBe(0x48515733);
|
|
554
|
-
expect(walBytes.readBigUInt64BE(88)).toBe(1n);
|
|
555
|
-
expect(walBytes.readUInt16BE(96)).toBe(RECORD);
|
|
556
|
-
expect(walBytes.readUInt16BE(98)).toBe(3);
|
|
557
|
-
});
|
|
558
|
-
it("truncates every incomplete tail at the last good frame boundary", () => {
|
|
559
|
-
const root = makeRoot();
|
|
560
|
-
const first = openStore(root);
|
|
561
|
-
first.append(RECORD, "durable");
|
|
562
|
-
const wal = path.join(first.stateDir, "wal-1.bin");
|
|
563
|
-
const validLength = fs.statSync(wal).size;
|
|
564
|
-
fs.appendFileSync(wal, Buffer.from([0x48, 0x51, 0x57]));
|
|
565
|
-
const recovered = openStore(root);
|
|
566
|
-
expect(recovered.getState()).toEqual({ values: ["durable"] });
|
|
567
|
-
expect(fs.statSync(wal).size).toBe(validLength);
|
|
568
|
-
});
|
|
569
|
-
it("never skips a complete frame with a bad checksum", () => {
|
|
570
|
-
const root = makeRoot();
|
|
571
|
-
const store = openStore(root);
|
|
572
|
-
store.append(RECORD, "bad");
|
|
573
|
-
const wal = path.join(store.stateDir, "wal-1.bin");
|
|
574
|
-
const bytes = fs.readFileSync(wal);
|
|
575
|
-
bytes[bytes.length - 1] ^= 0xff;
|
|
576
|
-
fs.writeFileSync(wal, bytes);
|
|
577
|
-
expect(() => openStore(root)).toThrow(StateStoreCorruptionError);
|
|
578
|
-
});
|
|
579
|
-
it("falls back to the retained last-good generation after a later complete frame corrupts", () => {
|
|
580
|
-
const root = makeRoot();
|
|
581
|
-
const store = openStore(root);
|
|
582
|
-
store.append(RECORD, "last-good");
|
|
583
|
-
store.compact();
|
|
584
|
-
store.append(RECORD, "corrupt-later");
|
|
585
|
-
const wal = path.join(store.stateDir, "wal-2.bin");
|
|
586
|
-
const bytes = fs.readFileSync(wal);
|
|
587
|
-
bytes[bytes.length - 1] ^= 0xff;
|
|
588
|
-
fs.writeFileSync(wal, bytes);
|
|
589
|
-
const recovered = openStore(root);
|
|
590
|
-
expect(recovered.getGeneration()).toBe(1);
|
|
591
|
-
expect(recovered.getState()).toEqual({ values: ["last-good"] });
|
|
592
|
-
});
|
|
593
|
-
it("allocates above a rejected generation when compaction resumes from last-good", () => {
|
|
594
|
-
const root = makeRoot();
|
|
595
|
-
const store = openStore(root);
|
|
596
|
-
store.append(RECORD, "last-good");
|
|
597
|
-
store.compact();
|
|
598
|
-
store.append(RECORD, "corrupt-later");
|
|
599
|
-
const wal = path.join(store.stateDir, "wal-2.bin");
|
|
600
|
-
const bytes = fs.readFileSync(wal);
|
|
601
|
-
bytes[bytes.length - 1] ^= 0xff;
|
|
602
|
-
fs.writeFileSync(wal, bytes);
|
|
603
|
-
const recovered = openStore(root);
|
|
604
|
-
recovered.compact();
|
|
605
|
-
expect(recovered.getGeneration()).toBe(3);
|
|
606
|
-
expect(openStore(root).getState()).toEqual({ values: ["last-good"] });
|
|
607
|
-
});
|
|
608
|
-
it("falls back when a crash leaves a newest snapshot without its WAL", () => {
|
|
609
|
-
const root = makeRoot();
|
|
610
|
-
const store = openStore(root);
|
|
611
|
-
store.append(RECORD, "last-good");
|
|
612
|
-
store.compact();
|
|
613
|
-
fs.unlinkSync(path.join(store.stateDir, "wal-2.bin"));
|
|
614
|
-
const recovered = openStore(root);
|
|
615
|
-
expect(recovered.getGeneration()).toBe(1);
|
|
616
|
-
expect(recovered.getState()).toEqual({ values: ["last-good"] });
|
|
617
|
-
});
|
|
618
|
-
it("refuses to initialize over a damaged published store with no complete generation", async () => {
|
|
619
|
-
const root = makeRoot();
|
|
620
|
-
const store = openStore(root);
|
|
621
|
-
const snapshot = path.join(store.stateDir, "snapshot-1.bin");
|
|
622
|
-
fs.unlinkSync(path.join(store.stateDir, "wal-1.bin"));
|
|
623
|
-
expect(() => openStore(root)).toThrow(StateStoreCorruptionError);
|
|
624
|
-
await expect(StateStore.openAsync({
|
|
625
|
-
rootDir: root,
|
|
626
|
-
scopeId: "prs_test_scope",
|
|
627
|
-
initialState: { values: [] },
|
|
628
|
-
reduce: (state, record) => ({ values: [...state.values, String(record.payload)] }),
|
|
629
|
-
})).rejects.toThrow(StateStoreCorruptionError);
|
|
630
|
-
expect(fs.existsSync(snapshot)).toBe(true);
|
|
631
|
-
});
|
|
632
|
-
it("does not silently fall back to stale state on an operational read failure", () => {
|
|
633
|
-
const root = makeRoot();
|
|
634
|
-
const store = openStore(root);
|
|
635
|
-
store.append(RECORD, "last-good");
|
|
636
|
-
store.compact();
|
|
637
|
-
const newestSnapshot = path.join(store.stateDir, "snapshot-2.bin");
|
|
638
|
-
fs.unlinkSync(newestSnapshot);
|
|
639
|
-
fs.mkdirSync(newestSnapshot);
|
|
640
|
-
expect(() => openStore(root)).toThrow(/cannot read snapshot generation 2/);
|
|
641
|
-
});
|
|
642
|
-
it("rejects a corrupted final frame length instead of truncating it as torn", () => {
|
|
643
|
-
const root = makeRoot();
|
|
644
|
-
const store = openStore(root);
|
|
645
|
-
store.append(RECORD, "durable");
|
|
646
|
-
const wal = path.join(store.stateDir, "wal-1.bin");
|
|
647
|
-
const bytes = fs.readFileSync(wal);
|
|
648
|
-
bytes.writeUInt32BE(bytes.readUInt32BE(84) + 1, 84);
|
|
649
|
-
fs.writeFileSync(wal, bytes);
|
|
650
|
-
expect(() => openStore(root)).toThrow(StateStoreCorruptionError);
|
|
651
|
-
expect(fs.statSync(wal).size).toBe(bytes.length);
|
|
652
|
-
});
|
|
653
|
-
it("chooses the highest fully valid generation even when an interrupted rename left a stale pointer", () => {
|
|
654
|
-
const root = makeRoot();
|
|
655
|
-
const store = openStore(root);
|
|
656
|
-
store.append(RECORD, "one");
|
|
657
|
-
store.compact();
|
|
658
|
-
fs.writeFileSync(path.join(store.stateDir, "current"), "1\n");
|
|
659
|
-
const recovered = openStore(root);
|
|
660
|
-
expect(recovered.getGeneration()).toBe(2);
|
|
661
|
-
expect(recovered.getState()).toEqual({ values: ["one"] });
|
|
662
|
-
});
|
|
663
|
-
it("compacts bounded WAL generations and preserves record sequence", () => {
|
|
664
|
-
const root = makeRoot();
|
|
665
|
-
const store = openStore(root, 2);
|
|
666
|
-
store.append(RECORD, "one");
|
|
667
|
-
store.append(RECORD, "two");
|
|
668
|
-
expect(store.getGeneration()).toBe(2);
|
|
669
|
-
store.append(RECORD, "three");
|
|
670
|
-
const recovered = openStore(root, 2);
|
|
671
|
-
expect(recovered.getGeneration()).toBe(2);
|
|
672
|
-
expect(recovered.getLastRecordSequence()).toBe(3);
|
|
673
|
-
expect(recovered.getState()).toEqual({ values: ["one", "two", "three"] });
|
|
674
|
-
});
|
|
675
|
-
it("compacts by encoded WAL bytes before the record-count limit", () => {
|
|
676
|
-
const root = makeRoot();
|
|
677
|
-
const store = openReplacingStore(root, {
|
|
678
|
-
maxRecordsBeforeCompaction: 256,
|
|
679
|
-
maxWalBytesBeforeCompaction: 800,
|
|
680
|
-
});
|
|
681
|
-
store.append(RECORD, "x".repeat(700));
|
|
682
|
-
expect(store.getGeneration()).toBe(2);
|
|
683
|
-
expect(openReplacingStore(root).getState().value).toBe("x".repeat(700));
|
|
684
|
-
});
|
|
685
|
-
it("lets a concurrent locker through during a large snapshot write", () => {
|
|
686
|
-
const root = makeRoot();
|
|
687
|
-
const store = openStore(root);
|
|
688
|
-
const competing = openStore(root);
|
|
689
|
-
let lockWaitMs = Number.NaN;
|
|
690
|
-
let competed = false;
|
|
691
|
-
setStateStoreSnapshotWriteHookForTest(() => {
|
|
692
|
-
if (competed)
|
|
693
|
-
return;
|
|
694
|
-
competed = true;
|
|
695
|
-
const started = Date.now();
|
|
696
|
-
competing.append(RECORD, "concurrent-locker");
|
|
697
|
-
lockWaitMs = Date.now() - started;
|
|
698
|
-
});
|
|
699
|
-
// Large enough to make a real materialisation meaningful without turning
|
|
700
|
-
// this unit test into a 120 MB fixture. On origin/main the same synchronous
|
|
701
|
-
// append waits for LOCK_TIMEOUT_MS because writeGeneration owns append.lock.
|
|
702
|
-
store.replaceState({ values: ["x".repeat(8 * 1024 * 1024)] });
|
|
703
|
-
expect(lockWaitMs).toBeLessThan(250);
|
|
704
|
-
expect(competing.getState().values).toContain("concurrent-locker");
|
|
705
|
-
}, 15_000);
|
|
706
|
-
it("does not hold append.lock while a large cold open parses its snapshot", () => {
|
|
707
|
-
const root = makeRoot();
|
|
708
|
-
const writer = openStore(root);
|
|
709
|
-
writer.replaceState({ values: ["x".repeat(4 * 1024 * 1024)] });
|
|
710
|
-
const competing = openStore(root);
|
|
711
|
-
let lockWaitMs = Number.NaN;
|
|
712
|
-
let competed = false;
|
|
713
|
-
setStateStoreSnapshotReadHookForTest(() => {
|
|
714
|
-
if (competed)
|
|
715
|
-
return;
|
|
716
|
-
competed = true;
|
|
717
|
-
const started = Date.now();
|
|
718
|
-
competing.append(RECORD, "scoped-push-locker");
|
|
719
|
-
lockWaitMs = Date.now() - started;
|
|
720
|
-
});
|
|
721
|
-
const opened = openStore(root);
|
|
722
|
-
expect(opened.getState().values).toContain("scoped-push-locker");
|
|
723
|
-
// Before this change the hook runs under open's append.lock and the
|
|
724
|
-
// competing append waits for the 5,000 ms lock budget before failing.
|
|
725
|
-
expect(lockWaitMs).toBeLessThan(250);
|
|
726
|
-
}, 20_000);
|
|
727
|
-
it("keeps the asynchronous cold open lock-light too", async () => {
|
|
728
|
-
const root = makeRoot();
|
|
729
|
-
const writer = openStore(root);
|
|
730
|
-
writer.replaceState({ values: ["x".repeat(2 * 1024 * 1024)] });
|
|
731
|
-
const competing = openStore(root);
|
|
732
|
-
let lockWaitMs = Number.NaN;
|
|
733
|
-
let competed = false;
|
|
734
|
-
setStateStoreSnapshotReadHookForTest(() => {
|
|
735
|
-
if (competed)
|
|
736
|
-
return;
|
|
737
|
-
competed = true;
|
|
738
|
-
const started = Date.now();
|
|
739
|
-
competing.append(RECORD, "async-scoped-push-locker");
|
|
740
|
-
lockWaitMs = Date.now() - started;
|
|
741
|
-
});
|
|
742
|
-
const opened = await StateStore.openAsync({
|
|
743
|
-
rootDir: root,
|
|
744
|
-
scopeId: "prs_test_scope",
|
|
745
|
-
initialState: { values: [] },
|
|
746
|
-
reduce: (state, record) => ({ values: [...state.values, String(record.payload)] }),
|
|
747
|
-
});
|
|
748
|
-
expect(opened.getState().values).toContain("async-scoped-push-locker");
|
|
749
|
-
expect(lockWaitMs).toBeLessThan(250);
|
|
750
|
-
}, 20_000);
|
|
751
|
-
it("retries a cold open when a rotation publishes during its snapshot read", () => {
|
|
752
|
-
const root = makeRoot();
|
|
753
|
-
const writer = openStore(root);
|
|
754
|
-
writer.append(RECORD, "old");
|
|
755
|
-
writer.compact();
|
|
756
|
-
const rotating = openStore(root);
|
|
757
|
-
let rotated = false;
|
|
758
|
-
setStateStoreSnapshotReadHookForTest(() => {
|
|
759
|
-
if (rotated)
|
|
760
|
-
return;
|
|
761
|
-
rotated = true;
|
|
762
|
-
rotating.replaceState({ values: ["newer"] });
|
|
763
|
-
});
|
|
764
|
-
const opened = openStore(root);
|
|
765
|
-
expect(rotated).toBe(true);
|
|
766
|
-
expect(opened.getGeneration()).toBe(rotating.getGeneration());
|
|
767
|
-
expect(opened.getState()).toEqual({ values: ["newer"] });
|
|
768
|
-
});
|
|
769
|
-
it("retries a cold open when rotation prunes every sampled generation", () => {
|
|
770
|
-
const root = makeRoot();
|
|
771
|
-
const writer = openStore(root);
|
|
772
|
-
writer.append(RECORD, "old");
|
|
773
|
-
writer.compact();
|
|
774
|
-
const rotating = openStore(root);
|
|
775
|
-
let rotated = false;
|
|
776
|
-
setStateStoreSnapshotReadHookForTest(() => {
|
|
777
|
-
if (rotated)
|
|
778
|
-
return;
|
|
779
|
-
rotated = true;
|
|
780
|
-
rotating.replaceState({ values: ["first-newer"] });
|
|
781
|
-
rotating.replaceState({ values: ["latest"] });
|
|
782
|
-
});
|
|
783
|
-
const opened = openStore(root);
|
|
784
|
-
expect(rotated).toBe(true);
|
|
785
|
-
expect(opened.getGeneration()).toBe(rotating.getGeneration());
|
|
786
|
-
expect(opened.getState()).toEqual({ values: ["latest"] });
|
|
787
|
-
});
|
|
788
|
-
it("tails concurrent WAL appends after one off-lock snapshot replay", () => {
|
|
789
|
-
const root = makeRoot();
|
|
790
|
-
const writer = openStore(root);
|
|
791
|
-
writer.replaceState({ values: ["x".repeat(512 * 1024)] });
|
|
792
|
-
resetStateStoreSnapshotRecoveryCountForTest();
|
|
793
|
-
let writesDuringRecovery = 0;
|
|
794
|
-
setStateStoreWalReadHookForTest(() => {
|
|
795
|
-
writesDuringRecovery++;
|
|
796
|
-
if (writesDuringRecovery > 1) {
|
|
797
|
-
throw new Error("cold recovery replayed the snapshot instead of tailing the WAL");
|
|
798
|
-
}
|
|
799
|
-
writer.append(RECORD, "appended-during-recovery");
|
|
800
|
-
});
|
|
801
|
-
const opened = openStore(root);
|
|
802
|
-
expect(writesDuringRecovery).toBe(1);
|
|
803
|
-
expect(getStateStoreSnapshotRecoveryCountForTest()).toBe(1);
|
|
804
|
-
expect(opened.getState().values).toContain("appended-during-recovery");
|
|
805
|
-
});
|
|
806
|
-
it("falls back when tail validation encounters a newly appended corrupt frame", () => {
|
|
807
|
-
const root = makeRoot();
|
|
808
|
-
const writer = openStore(root);
|
|
809
|
-
writer.append(RECORD, "last-good");
|
|
810
|
-
writer.compact();
|
|
811
|
-
const wal = path.join(writer.stateDir, `wal-${writer.getGeneration()}.bin`);
|
|
812
|
-
let corrupted = false;
|
|
813
|
-
setStateStoreWalReadHookForTest(() => {
|
|
814
|
-
if (corrupted)
|
|
815
|
-
return;
|
|
816
|
-
corrupted = true;
|
|
817
|
-
fs.appendFileSync(wal, Buffer.alloc(8));
|
|
818
|
-
});
|
|
819
|
-
const opened = openStore(root);
|
|
820
|
-
expect(corrupted).toBe(true);
|
|
821
|
-
expect(opened.getGeneration()).toBe(1);
|
|
822
|
-
expect(opened.getState()).toEqual({ values: ["last-good"] });
|
|
823
|
-
});
|
|
824
|
-
it("repairs a torn concurrent WAL suffix before installing an optimistic read", () => {
|
|
825
|
-
const root = makeRoot();
|
|
826
|
-
const writer = openStore(root);
|
|
827
|
-
writer.replaceState({ values: ["durable"] });
|
|
828
|
-
const wal = path.join(writer.stateDir, `wal-${writer.getGeneration()}.bin`);
|
|
829
|
-
const completeBytes = fs.statSync(wal).size;
|
|
830
|
-
let appendedTornSuffix = false;
|
|
831
|
-
setStateStoreSnapshotReadHookForTest(() => {
|
|
832
|
-
if (appendedTornSuffix)
|
|
833
|
-
return;
|
|
834
|
-
appendedTornSuffix = true;
|
|
835
|
-
fs.appendFileSync(wal, Buffer.from([0x48, 0x51, 0x57]));
|
|
836
|
-
});
|
|
837
|
-
const opened = openStore(root);
|
|
838
|
-
expect(appendedTornSuffix).toBe(true);
|
|
839
|
-
expect(opened.getState()).toEqual({ values: ["durable"] });
|
|
840
|
-
expect(fs.statSync(wal).size).toBe(completeBytes);
|
|
841
|
-
});
|
|
842
|
-
it("lets a scoped-push-shaped append through repeated heavy cold opens", () => {
|
|
843
|
-
const root = makeRoot();
|
|
844
|
-
const writer = openStore(root);
|
|
845
|
-
writer.replaceState({ values: ["x".repeat(2 * 1024 * 1024)] });
|
|
846
|
-
const scopedPush = openStore(root);
|
|
847
|
-
let lockWaitMs = Number.NaN;
|
|
848
|
-
let attempted = false;
|
|
849
|
-
setStateStoreSnapshotReadHookForTest(() => {
|
|
850
|
-
if (attempted)
|
|
851
|
-
return;
|
|
852
|
-
attempted = true;
|
|
853
|
-
const started = Date.now();
|
|
854
|
-
scopedPush.append(RECORD, "small-event-driven-push");
|
|
855
|
-
lockWaitMs = Date.now() - started;
|
|
856
|
-
});
|
|
857
|
-
for (let i = 0; i < 4; i++)
|
|
858
|
-
openStore(root);
|
|
859
|
-
expect(attempted).toBe(true);
|
|
860
|
-
expect(lockWaitMs).toBeLessThan(250);
|
|
861
|
-
expect(openStore(root).getState().values).toContain("small-event-driven-push");
|
|
862
|
-
}, 30_000);
|
|
863
|
-
it("does not hold append.lock while maintenance recovers a large snapshot", () => {
|
|
864
|
-
const root = makeRoot();
|
|
865
|
-
const writer = openStore(root);
|
|
866
|
-
writer.replaceState({ values: ["x".repeat(2 * 1024 * 1024)] });
|
|
867
|
-
const competing = openStore(root);
|
|
868
|
-
let lockWaitMs = Number.NaN;
|
|
869
|
-
let competed = false;
|
|
870
|
-
setStateStoreSnapshotReadHookForTest(() => {
|
|
871
|
-
if (competed)
|
|
872
|
-
return;
|
|
873
|
-
competed = true;
|
|
874
|
-
const started = Date.now();
|
|
875
|
-
competing.append(RECORD, "maintenance-race");
|
|
876
|
-
lockWaitMs = Date.now() - started;
|
|
877
|
-
});
|
|
878
|
-
const repair = StateStore.repairIfNeeded({
|
|
879
|
-
rootDir: root,
|
|
880
|
-
scopeId: "prs_test_scope",
|
|
881
|
-
initialState: { values: [] },
|
|
882
|
-
reduce: (state, record) => ({ values: [...state.values, String(record.payload)] }),
|
|
883
|
-
}, { force: true });
|
|
884
|
-
expect(repair).toMatchObject({ status: "repaired" });
|
|
885
|
-
expect(lockWaitMs).toBeLessThan(250);
|
|
886
|
-
expect(openStore(root).getState().values).toContain("maintenance-race");
|
|
887
|
-
}, 30_000);
|
|
888
|
-
it("keeps the prior generation recoverable when staging is interrupted before swap", () => {
|
|
889
|
-
const root = makeRoot();
|
|
890
|
-
const store = openStore(root);
|
|
891
|
-
store.append(RECORD, "old-generation");
|
|
892
|
-
const before = store.getGeneration();
|
|
893
|
-
setStateStoreAfterGenerationStageHookForTest(() => {
|
|
894
|
-
throw new Error("injected crash after durable staging");
|
|
895
|
-
});
|
|
896
|
-
expect(() => store.compact()).toThrow("injected crash after durable staging");
|
|
897
|
-
expect(openStore(root).getGeneration()).toBe(before);
|
|
898
|
-
expect(openStore(root).getState()).toEqual({ values: ["old-generation"] });
|
|
899
|
-
});
|
|
900
|
-
it("retries compaction after a racing append skips the rotation claim", () => {
|
|
901
|
-
const root = makeRoot();
|
|
902
|
-
const compacting = openStore(root);
|
|
903
|
-
const racing = openStore(root);
|
|
904
|
-
compacting.append(RECORD, "before-race");
|
|
905
|
-
let raced = false;
|
|
906
|
-
setStateStoreSnapshotWriteHookForTest(() => {
|
|
907
|
-
if (raced)
|
|
908
|
-
return;
|
|
909
|
-
raced = true;
|
|
910
|
-
racing.append(RECORD, "during-race");
|
|
911
|
-
});
|
|
912
|
-
compacting.compact();
|
|
913
|
-
expect(raced).toBe(true);
|
|
914
|
-
expect(compacting.getGeneration()).toBe(2);
|
|
915
|
-
expect(openStore(root).getState()).toEqual({ values: ["before-race", "during-race"] });
|
|
916
|
-
expect(fs.statSync(path.join(compacting.stateDir, "wal-2.bin")).size).toBe(80);
|
|
917
|
-
});
|
|
918
|
-
it("compacts an over-budget WAL while opening a recovered store", () => {
|
|
919
|
-
const root = makeRoot();
|
|
920
|
-
const writer = openStore(root, 10_000);
|
|
921
|
-
writer.append(RECORD, "first");
|
|
922
|
-
writer.append(RECORD, "second");
|
|
923
|
-
writer.append(RECORD, "third");
|
|
924
|
-
expect(fs.statSync(path.join(writer.stateDir, "wal-1.bin")).size).toBeGreaterThan(80);
|
|
925
|
-
const reopened = openStore(root, 2);
|
|
926
|
-
expect(reopened.getState()).toEqual({ values: ["first", "second", "third"] });
|
|
927
|
-
expect(reopened.getGeneration()).toBeGreaterThan(1);
|
|
928
|
-
expect(fs.statSync(path.join(reopened.stateDir, `wal-${reopened.getGeneration()}.bin`)).size).toBe(80);
|
|
929
|
-
});
|
|
930
|
-
it("cooperatively compacts a recovered over-budget WAL during async open", async () => {
|
|
931
|
-
const root = makeRoot();
|
|
932
|
-
const writer = openStore(root, 10_000);
|
|
933
|
-
writer.append(RECORD, "first");
|
|
934
|
-
writer.append(RECORD, "second");
|
|
935
|
-
writer.append(RECORD, "third");
|
|
936
|
-
let releaseHolder;
|
|
937
|
-
let held = false;
|
|
938
|
-
const holderEntered = new Promise((resolve) => {
|
|
939
|
-
setStateStoreScopeLockAcquiredHookForTest(() => {
|
|
940
|
-
if (held)
|
|
941
|
-
return;
|
|
942
|
-
held = true;
|
|
943
|
-
resolve();
|
|
944
|
-
return new Promise((release) => {
|
|
945
|
-
releaseHolder = release;
|
|
946
|
-
});
|
|
947
|
-
});
|
|
948
|
-
});
|
|
949
|
-
let holder;
|
|
950
|
-
setStateStoreAfterOptimisticRecoveryHookForTest(async () => {
|
|
951
|
-
holder = writer.appendAsync(RECORD, "holder");
|
|
952
|
-
await holderEntered;
|
|
953
|
-
});
|
|
954
|
-
const opened = StateStore.openAsync({
|
|
955
|
-
rootDir: root,
|
|
956
|
-
scopeId: "prs_test_scope",
|
|
957
|
-
initialState: { values: [] },
|
|
958
|
-
maxRecordsBeforeCompaction: 2,
|
|
959
|
-
reduce: (state, record) => ({ values: [...state.values, String(record.payload)] }),
|
|
960
|
-
});
|
|
961
|
-
await holderEntered;
|
|
962
|
-
setTimeout(() => releaseHolder(), 25);
|
|
963
|
-
await expect(opened).resolves.toMatchObject({ state: { values: ["first", "second", "third", "holder"] } });
|
|
964
|
-
await expect(holder).resolves.toMatchObject({ payload: "holder" });
|
|
965
|
-
const reopened = await opened;
|
|
966
|
-
expect(reopened.getGeneration()).toBeGreaterThan(1);
|
|
967
|
-
expect(fs.statSync(path.join(reopened.stateDir, `wal-${reopened.getGeneration()}.bin`)).size).toBe(80);
|
|
968
|
-
});
|
|
969
|
-
it("replaces state with rollback-readable header-only generations", () => {
|
|
970
|
-
const root = makeRoot();
|
|
971
|
-
const store = openReplacingStore(root);
|
|
972
|
-
store.append(RECORD, "legacy-record");
|
|
973
|
-
store.replaceState({ value: "replacement" });
|
|
974
|
-
expect(store.getState()).toEqual({ value: "replacement" });
|
|
975
|
-
expect(store.getLastRecordSequence()).toBe(1);
|
|
976
|
-
const walSizes = fs.readdirSync(store.stateDir)
|
|
977
|
-
.filter((name) => /^wal-\d+\.bin$/.test(name))
|
|
978
|
-
.map((name) => fs.statSync(path.join(store.stateDir, name)).size);
|
|
979
|
-
expect(walSizes).toEqual([80, 80]);
|
|
980
|
-
expect(openReplacingStore(root).getState()).toEqual({ value: "replacement" });
|
|
981
|
-
});
|
|
982
|
-
it("retries a replacement when an append advances its staged source", () => {
|
|
983
|
-
const root = makeRoot();
|
|
984
|
-
const replacing = openReplacingStore(root);
|
|
985
|
-
const racing = openReplacingStore(root);
|
|
986
|
-
let raced = false;
|
|
987
|
-
setStateStoreSnapshotWriteHookForTest(() => {
|
|
988
|
-
if (raced)
|
|
989
|
-
return;
|
|
990
|
-
raced = true;
|
|
991
|
-
racing.append(RECORD, "during-replacement");
|
|
992
|
-
});
|
|
993
|
-
replacing.replaceState({ value: "replacement" });
|
|
994
|
-
expect(raced).toBe(true);
|
|
995
|
-
expect(replacing.getState()).toEqual({ value: "replacement" });
|
|
996
|
-
expect(openReplacingStore(root).getState()).toEqual({ value: "replacement" });
|
|
997
|
-
});
|
|
998
|
-
it("compacts a replacement under sustained concurrent writes that beat three immediate retries", () => {
|
|
999
|
-
installDeterministicCompactionClock({ freeze: true });
|
|
1000
|
-
const root = makeRoot();
|
|
1001
|
-
const replacing = openReplacingStore(root);
|
|
1002
|
-
const racing = openReplacingStore(root);
|
|
1003
|
-
const racesBeforeQuiet = 8;
|
|
1004
|
-
let attempts = 0;
|
|
1005
|
-
setStateStoreSnapshotWriteHookForTest(() => {
|
|
1006
|
-
attempts += 1;
|
|
1007
|
-
if (attempts <= racesBeforeQuiet)
|
|
1008
|
-
racing.append(RECORD, `during-replacement-${attempts}`);
|
|
1009
|
-
});
|
|
1010
|
-
replacing.replaceState({ value: "replacement" });
|
|
1011
|
-
expect(attempts).toBe(racesBeforeQuiet + 1);
|
|
1012
|
-
expect(replacing.getState()).toEqual({ value: "replacement" });
|
|
1013
|
-
expect(openReplacingStore(root).getState()).toEqual({ value: "replacement" });
|
|
1014
|
-
expect(fs.statSync(path.join(replacing.stateDir, `wal-${replacing.getGeneration()}.bin`)).size).toBe(80);
|
|
1015
|
-
});
|
|
1016
|
-
it("retries a far-over-budget replacement harder than a barely-over-budget one", () => {
|
|
1017
|
-
const barelyAttempts = countFailedReplacementAttempts(1);
|
|
1018
|
-
const farAttempts = countFailedReplacementAttempts(12);
|
|
1019
|
-
expect(farAttempts).toBeGreaterThan(barelyAttempts);
|
|
1020
|
-
expect(barelyAttempts).toBeGreaterThan(3);
|
|
1021
|
-
installDeterministicCompactionClock({ freeze: true });
|
|
1022
|
-
const root = makeRoot();
|
|
1023
|
-
seedReplacingWal(root, 2_000);
|
|
1024
|
-
const quiet = openReplacingStore(root);
|
|
1025
|
-
quiet.replaceState({ value: "recovered" });
|
|
1026
|
-
expect(quiet.getState()).toEqual({ value: "recovered" });
|
|
1027
|
-
});
|
|
1028
|
-
it("recovers only the newest valid generation instead of replaying every retained WAL", () => {
|
|
1029
|
-
const root = makeRoot();
|
|
1030
|
-
const writer = openReplacingStore(root);
|
|
1031
|
-
writer.append(RECORD, "first");
|
|
1032
|
-
writer.compact();
|
|
1033
|
-
writer.append(RECORD, "second");
|
|
1034
|
-
let replayed = 0;
|
|
1035
|
-
const recovered = StateStore.open({
|
|
1036
|
-
rootDir: root,
|
|
1037
|
-
scopeId: "replacing_test_scope",
|
|
1038
|
-
initialState: { value: "" },
|
|
1039
|
-
reduce: (_state, record) => {
|
|
1040
|
-
replayed++;
|
|
1041
|
-
return { value: String(record.payload) };
|
|
1042
|
-
},
|
|
1043
|
-
});
|
|
1044
|
-
expect(recovered.getState()).toEqual({ value: "second" });
|
|
1045
|
-
expect(replayed).toBe(1);
|
|
1046
|
-
});
|
|
1047
|
-
it("bounds recovery reads to one WAL frame instead of the whole WAL", () => {
|
|
1048
|
-
const root = makeRoot();
|
|
1049
|
-
const writer = openReplacingStore(root, { maxWalBytesBeforeCompaction: 1024 * 1024 });
|
|
1050
|
-
for (let i = 0; i < 8; i++)
|
|
1051
|
-
writer.append(RECORD, `${i}:${"x".repeat(8_192)}`);
|
|
1052
|
-
const recovered = openReplacingStore(root);
|
|
1053
|
-
const stats = recovered.getRecoveryStats();
|
|
1054
|
-
expect(stats.walBytes).toBeGreaterThan(64 * 1024);
|
|
1055
|
-
expect(stats.maxReadBytes).toBeLessThan(16 * 1024);
|
|
1056
|
-
});
|
|
1057
|
-
it("repairs an oversized generation with one full snapshot materialisation", () => {
|
|
1058
|
-
const root = makeRoot();
|
|
1059
|
-
const writer = openReplacingStore(root, { maxWalBytesBeforeCompaction: 1024 * 1024 });
|
|
1060
|
-
for (let i = 0; i < 8; i++)
|
|
1061
|
-
writer.append(RECORD, `${i}:${"x".repeat(8_192)}`);
|
|
1062
|
-
const oversizedWal = path.join(writer.stateDir, "wal-1.bin");
|
|
1063
|
-
expect(fs.statSync(oversizedWal).size).toBeGreaterThan(64 * 1024);
|
|
1064
|
-
let replayed = 0;
|
|
1065
|
-
const progress = [];
|
|
1066
|
-
setStateStoreReflinkCopyHookForTest(() => {
|
|
1067
|
-
throw new Error("reflink unavailable for test");
|
|
1068
|
-
});
|
|
1069
|
-
resetStateStoreSnapshotMaterializationCountForTest();
|
|
1070
|
-
resetStateStoreSnapshotEncodeCountForTest();
|
|
1071
|
-
const repair = StateStore.repairIfNeeded({
|
|
1072
|
-
rootDir: root,
|
|
1073
|
-
scopeId: "replacing_test_scope",
|
|
1074
|
-
initialState: { value: "" },
|
|
1075
|
-
maxWalBytesBeforeCompaction: 64 * 1024,
|
|
1076
|
-
reduce: (_state, record) => {
|
|
1077
|
-
replayed++;
|
|
1078
|
-
return { value: String(record.payload) };
|
|
1079
|
-
},
|
|
1080
|
-
}, { onProgress: (event) => progress.push(event) });
|
|
1081
|
-
expect(repair).toMatchObject({
|
|
1082
|
-
status: "repaired",
|
|
1083
|
-
recoveredGeneration: 1,
|
|
1084
|
-
firstCompactGeneration: 2,
|
|
1085
|
-
currentGeneration: 3,
|
|
1086
|
-
});
|
|
1087
|
-
expect(getStateStoreSnapshotMaterializationCountForTest()).toBe(1);
|
|
1088
|
-
expect(getStateStoreSnapshotEncodeCountForTest()).toBe(1);
|
|
1089
|
-
expect(replayed).toBe(8);
|
|
1090
|
-
expect(progress.length).toBeGreaterThan(0);
|
|
1091
|
-
expect(progress.at(-1)?.bytesRead).toBe(progress.at(-1)?.totalBytes);
|
|
1092
|
-
expect(fs.existsSync(oversizedWal)).toBe(false);
|
|
1093
|
-
// The fallback generation remains rollback-readable, while the second
|
|
1094
|
-
// snapshot is a copy-on-write clone of the first rather than another full
|
|
1095
|
-
// materialisation of the same 120 MB state.
|
|
1096
|
-
expect(fs.readdirSync(writer.stateDir).filter((name) => name.startsWith("snapshot-"))).toHaveLength(2);
|
|
1097
|
-
expect(fs.readdirSync(writer.stateDir).filter((name) => name.startsWith("wal-"))).toHaveLength(2);
|
|
1098
|
-
expect(fs.readFileSync(path.join(writer.stateDir, "current"), "utf8")).toBe("3\n");
|
|
1099
|
-
expect(openReplacingStore(root).getGeneration()).toBe(3);
|
|
1100
|
-
expect(openReplacingStore(root).getState()).toEqual({ value: `7:${"x".repeat(8_192)}` });
|
|
1101
|
-
replayed = 0;
|
|
1102
|
-
expect(StateStore.repairIfNeeded({
|
|
1103
|
-
rootDir: root,
|
|
1104
|
-
scopeId: "replacing_test_scope",
|
|
1105
|
-
initialState: { value: "" },
|
|
1106
|
-
maxWalBytesBeforeCompaction: 64 * 1024,
|
|
1107
|
-
reduce: (_state, record) => {
|
|
1108
|
-
replayed++;
|
|
1109
|
-
return { value: String(record.payload) };
|
|
1110
|
-
},
|
|
1111
|
-
})).toMatchObject({ status: "not-needed" });
|
|
1112
|
-
expect(replayed).toBe(0);
|
|
1113
|
-
// A large, healthy snapshot plus an ordinary small WAL append is normal
|
|
1114
|
-
// between passes. It must not force another two-generation repair.
|
|
1115
|
-
openReplacingStore(root, { maxWalBytesBeforeCompaction: 1024 * 1024 })
|
|
1116
|
-
.append(RECORD, "one-small-follow-up-record");
|
|
1117
|
-
expect(fs.statSync(path.join(writer.stateDir, "wal-2.bin")).size).toBe(80);
|
|
1118
|
-
expect(fs.statSync(path.join(writer.stateDir, "wal-3.bin")).size).toBeLessThan(64 * 1024);
|
|
1119
|
-
const generationsBeforeSmallAppend = fs.readdirSync(writer.stateDir)
|
|
1120
|
-
.filter((name) => name.startsWith("snapshot-"))
|
|
1121
|
-
.sort();
|
|
1122
|
-
expect(StateStore.repairIfNeeded({
|
|
1123
|
-
rootDir: root,
|
|
1124
|
-
scopeId: "replacing_test_scope",
|
|
1125
|
-
initialState: { value: "" },
|
|
1126
|
-
maxWalBytesBeforeCompaction: 64 * 1024,
|
|
1127
|
-
reduce: (_state, record) => ({ value: String(record.payload) }),
|
|
1128
|
-
})).toMatchObject({ status: "not-needed" });
|
|
1129
|
-
expect(fs.readdirSync(writer.stateDir)
|
|
1130
|
-
.filter((name) => name.startsWith("snapshot-"))
|
|
1131
|
-
.sort()).toEqual(generationsBeforeSmallAppend);
|
|
1132
|
-
const files = fs.readdirSync(writer.stateDir);
|
|
1133
|
-
const newest = Math.max(...files
|
|
1134
|
-
.map((name) => /^snapshot-(\d+)\.bin$/.exec(name)?.[1])
|
|
1135
|
-
.filter((value) => value !== undefined)
|
|
1136
|
-
.map(Number));
|
|
1137
|
-
const newestSnapshot = path.join(writer.stateDir, `snapshot-${newest}.bin`);
|
|
1138
|
-
const bytes = fs.readFileSync(newestSnapshot);
|
|
1139
|
-
bytes[bytes.length - 1] ^= 0xff;
|
|
1140
|
-
fs.writeFileSync(newestSnapshot, bytes);
|
|
1141
|
-
expect(openReplacingStore(root).getState()).toEqual({ value: `7:${"x".repeat(8_192)}` });
|
|
1142
|
-
});
|
|
1143
|
-
it("completes short positional clone-header writes before publishing", () => {
|
|
1144
|
-
const root = makeRoot();
|
|
1145
|
-
const writer = openReplacingStore(root, { maxWalBytesBeforeCompaction: 1024 * 1024 });
|
|
1146
|
-
for (let i = 0; i < 8; i++)
|
|
1147
|
-
writer.append(RECORD, `${i}:${"x".repeat(8_192)}`);
|
|
1148
|
-
let shortenedHeader = false;
|
|
1149
|
-
setStateStorePositionalWriteForTest((fd, buffer, offset, length, position) => {
|
|
1150
|
-
if (!shortenedHeader && position === 0 && Buffer.isBuffer(buffer) && length === 52) {
|
|
1151
|
-
shortenedHeader = true;
|
|
1152
|
-
return fs.writeSync(fd, buffer, offset, Math.floor(length / 2), position);
|
|
1153
|
-
}
|
|
1154
|
-
return fs.writeSync(fd, buffer, offset, length, position);
|
|
1155
|
-
});
|
|
1156
|
-
expect(StateStore.repairIfNeeded({
|
|
1157
|
-
rootDir: root,
|
|
1158
|
-
scopeId: "replacing_test_scope",
|
|
1159
|
-
initialState: { value: "" },
|
|
1160
|
-
maxWalBytesBeforeCompaction: 64 * 1024,
|
|
1161
|
-
reduce: (_state, record) => ({ value: String(record.payload) }),
|
|
1162
|
-
})).toMatchObject({ status: "repaired", currentGeneration: 3 });
|
|
1163
|
-
expect(shortenedHeader).toBe(true);
|
|
1164
|
-
expect(openReplacingStore(root).getGeneration()).toBe(3);
|
|
1165
|
-
});
|
|
1166
|
-
it("cleans a partial rotation claim and removes a malformed claim on retry", () => {
|
|
1167
|
-
const root = makeRoot();
|
|
1168
|
-
const store = openStore(root);
|
|
1169
|
-
store.append(RECORD, "ready-to-compact");
|
|
1170
|
-
const claimPath = path.join(store.stateDir, "rotation.lock");
|
|
1171
|
-
setStateStoreRotationClaimWriteHookForTest(() => {
|
|
1172
|
-
throw new Error("injected claim write failure");
|
|
1173
|
-
});
|
|
1174
|
-
store.compact();
|
|
1175
|
-
expect(fs.existsSync(claimPath)).toBe(false);
|
|
1176
|
-
setStateStoreRotationClaimWriteHookForTest(undefined);
|
|
1177
|
-
fs.writeFileSync(claimPath, "", { flag: "wx" });
|
|
1178
|
-
fs.utimesSync(claimPath, new Date(Date.now() - 1_000), new Date(Date.now() - 1_000));
|
|
1179
|
-
store.compact();
|
|
1180
|
-
expect(store.getGeneration()).toBe(2);
|
|
1181
|
-
expect(fs.existsSync(claimPath)).toBe(false);
|
|
1182
|
-
});
|
|
1183
|
-
it("finishes an interrupted repair when the current pointer is stale", () => {
|
|
1184
|
-
const root = makeRoot();
|
|
1185
|
-
const writer = openReplacingStore(root, { maxWalBytesBeforeCompaction: 1024 * 1024 });
|
|
1186
|
-
for (let i = 0; i < 8; i++)
|
|
1187
|
-
writer.append(RECORD, `${i}:${"x".repeat(8_192)}`);
|
|
1188
|
-
const options = {
|
|
1189
|
-
rootDir: root,
|
|
1190
|
-
scopeId: "replacing_test_scope",
|
|
1191
|
-
initialState: { value: "" },
|
|
1192
|
-
maxWalBytesBeforeCompaction: 64 * 1024,
|
|
1193
|
-
reduce: (_state, record) => ({
|
|
1194
|
-
value: String(record.payload),
|
|
1195
|
-
}),
|
|
1196
|
-
};
|
|
1197
|
-
const firstRepair = StateStore.repairIfNeeded(options);
|
|
1198
|
-
expect(firstRepair.status).toBe("repaired");
|
|
1199
|
-
fs.writeFileSync(path.join(writer.stateDir, "current"), "1\n");
|
|
1200
|
-
expect(StateStore.repairIfNeeded(options)).toMatchObject({ status: "repaired" });
|
|
1201
|
-
const generations = fs.readdirSync(writer.stateDir)
|
|
1202
|
-
.map((name) => /^snapshot-(\d+)\.bin$/.exec(name)?.[1])
|
|
1203
|
-
.filter((value) => value !== undefined)
|
|
1204
|
-
.map(Number);
|
|
1205
|
-
expect(generations).toHaveLength(2);
|
|
1206
|
-
expect(fs.readFileSync(path.join(writer.stateDir, "current"), "utf8"))
|
|
1207
|
-
.toBe(`${Math.max(...generations)}\n`);
|
|
1208
|
-
});
|
|
1209
|
-
it("prunes obsolete WAL history left after current pointer publication", () => {
|
|
1210
|
-
const root = makeRoot();
|
|
1211
|
-
const writer = openReplacingStore(root, { maxWalBytesBeforeCompaction: 1024 * 1024 });
|
|
1212
|
-
for (let i = 0; i < 8; i++)
|
|
1213
|
-
writer.append(RECORD, `${i}:${"x".repeat(8_192)}`);
|
|
1214
|
-
writer.compact();
|
|
1215
|
-
expect(fs.readFileSync(path.join(writer.stateDir, "current"), "utf8")).toBe("2\n");
|
|
1216
|
-
expect(fs.statSync(path.join(writer.stateDir, "wal-1.bin")).size).toBeGreaterThan(64 * 1024);
|
|
1217
|
-
const options = {
|
|
1218
|
-
rootDir: root,
|
|
1219
|
-
scopeId: "replacing_test_scope",
|
|
1220
|
-
initialState: { value: "" },
|
|
1221
|
-
maxWalBytesBeforeCompaction: 1024 * 1024,
|
|
1222
|
-
reduce: (_state, record) => ({
|
|
1223
|
-
value: String(record.payload),
|
|
1224
|
-
}),
|
|
1225
|
-
};
|
|
1226
|
-
expect(StateStore.repairIfNeeded(options)).toMatchObject({ status: "repaired" });
|
|
1227
|
-
const walSizes = fs.readdirSync(writer.stateDir)
|
|
1228
|
-
.filter((name) => /^wal-\d+\.bin$/.test(name))
|
|
1229
|
-
.map((name) => fs.statSync(path.join(writer.stateDir, name)).size);
|
|
1230
|
-
expect(walSizes).toEqual([80, 80]);
|
|
1231
|
-
const rollbackReader = StateStore.open({
|
|
1232
|
-
...options,
|
|
1233
|
-
reduce: () => {
|
|
1234
|
-
throw new Error("rollback reader must not replay a WAL record");
|
|
1235
|
-
},
|
|
1236
|
-
});
|
|
1237
|
-
expect(rollbackReader.getState()).toEqual({ value: `7:${"x".repeat(8_192)}` });
|
|
1238
|
-
});
|
|
1239
|
-
it("serializes stale handles so records keep contiguous sequence numbers", () => {
|
|
1240
|
-
const root = makeRoot();
|
|
1241
|
-
const first = openStore(root);
|
|
1242
|
-
const second = openStore(root);
|
|
1243
|
-
first.append(RECORD, "one");
|
|
1244
|
-
second.append(RECORD, "two");
|
|
1245
|
-
expect(openStore(root).getState()).toEqual({ values: ["one", "two"] });
|
|
1246
|
-
});
|
|
1247
|
-
it("tails every externally appended frame without falling back to full recovery", () => {
|
|
1248
|
-
const root = makeRoot();
|
|
1249
|
-
const writer = openStore(root);
|
|
1250
|
-
writer.append(RECORD, "already-known");
|
|
1251
|
-
let readerReducerCalls = 0;
|
|
1252
|
-
const reader = StateStore.open({
|
|
1253
|
-
rootDir: root,
|
|
1254
|
-
scopeId: "prs_test_scope",
|
|
1255
|
-
initialState: { values: [] },
|
|
1256
|
-
reduce: (state, record) => {
|
|
1257
|
-
readerReducerCalls++;
|
|
1258
|
-
if (record.type !== RECORD || typeof record.payload !== "string") {
|
|
1259
|
-
throw new Error("test reducer received an invalid record");
|
|
1260
|
-
}
|
|
1261
|
-
return { values: [...state.values, record.payload] };
|
|
1262
|
-
},
|
|
1263
|
-
});
|
|
1264
|
-
const wal = path.join(reader.stateDir, "wal-1.bin");
|
|
1265
|
-
readerReducerCalls = 0;
|
|
1266
|
-
writer.append(RECORD, "first-tail-frame");
|
|
1267
|
-
writer.append(RECORD, "second-tail-frame");
|
|
1268
|
-
writer.append(RECORD, "third-tail-frame");
|
|
1269
|
-
const refresh = reader.refresh();
|
|
1270
|
-
expect(refresh).toEqual({
|
|
1271
|
-
recovered: false,
|
|
1272
|
-
records: [
|
|
1273
|
-
expect.objectContaining({ sequence: 2, payload: "first-tail-frame" }),
|
|
1274
|
-
expect.objectContaining({ sequence: 3, payload: "second-tail-frame" }),
|
|
1275
|
-
expect.objectContaining({ sequence: 4, payload: "third-tail-frame" }),
|
|
1276
|
-
],
|
|
1277
|
-
});
|
|
1278
|
-
expect(reader.getState()).toEqual({
|
|
1279
|
-
values: ["already-known", "first-tail-frame", "second-tail-frame", "third-tail-frame"],
|
|
1280
|
-
});
|
|
1281
|
-
expect(reader.getLastRecordSequence()).toBe(4);
|
|
1282
|
-
expect(reader.getRecoveryStats().walBytes).toBe(fs.statSync(wal).size);
|
|
1283
|
-
// A fallback replays the first tail frame once before detecting corruption,
|
|
1284
|
-
// then replays the entire WAL during full recovery.
|
|
1285
|
-
expect(readerReducerCalls).toBe(3);
|
|
1286
|
-
});
|
|
1287
|
-
it("repairs a torn externally appended tail while refreshing under the append lock", () => {
|
|
1288
|
-
const root = makeRoot();
|
|
1289
|
-
const writer = openStore(root);
|
|
1290
|
-
writer.append(RECORD, "already-known");
|
|
1291
|
-
const reader = openStore(root);
|
|
1292
|
-
const wal = path.join(reader.stateDir, "wal-1.bin");
|
|
1293
|
-
writer.append(RECORD, "complete-tail-frame");
|
|
1294
|
-
const completeBoundary = fs.statSync(wal).size;
|
|
1295
|
-
fs.appendFileSync(wal, Buffer.from([0x48, 0x51, 0x57]));
|
|
1296
|
-
expect(reader.refresh()).toMatchObject({ recovered: false });
|
|
1297
|
-
expect(reader.getState()).toEqual({ values: ["already-known", "complete-tail-frame"] });
|
|
1298
|
-
expect(fs.statSync(wal).size).toBe(completeBoundary);
|
|
1299
|
-
});
|
|
1300
|
-
it("falls back to the newest valid generation when an externally appended tail frame is corrupt", () => {
|
|
1301
|
-
const root = makeRoot();
|
|
1302
|
-
const initialWriter = openStore(root);
|
|
1303
|
-
initialWriter.append(RECORD, "last-good");
|
|
1304
|
-
initialWriter.compact();
|
|
1305
|
-
const reader = openStore(root);
|
|
1306
|
-
const writer = openStore(root);
|
|
1307
|
-
const wal = path.join(reader.stateDir, "wal-2.bin");
|
|
1308
|
-
writer.append(RECORD, "corrupt-tail");
|
|
1309
|
-
const bytes = fs.readFileSync(wal);
|
|
1310
|
-
bytes[bytes.length - 1] ^= 0xff;
|
|
1311
|
-
fs.writeFileSync(wal, bytes);
|
|
1312
|
-
expect(reader.refresh()).toEqual({ records: [], recovered: true });
|
|
1313
|
-
expect(reader.getGeneration()).toBe(1);
|
|
1314
|
-
expect(reader.getState()).toEqual({ values: ["last-good"] });
|
|
1315
|
-
});
|
|
1316
|
-
it("does not durably append a record rejected by the reducer", () => {
|
|
1317
|
-
const root = makeRoot();
|
|
1318
|
-
const store = openStore(root);
|
|
1319
|
-
expect(() => store.append(99, "unsupported")).toThrow(/invalid record/);
|
|
1320
|
-
expect(openStore(root).getState()).toEqual({ values: [] });
|
|
1321
|
-
});
|
|
1322
|
-
it("surfaces incompatible reducer records instead of falling back to stale state", () => {
|
|
1323
|
-
const root = makeRoot();
|
|
1324
|
-
const writer = openStore(root);
|
|
1325
|
-
writer.append(RECORD, "newer-record");
|
|
1326
|
-
expect(() => StateStore.open({
|
|
1327
|
-
rootDir: root,
|
|
1328
|
-
scopeId: "prs_test_scope",
|
|
1329
|
-
initialState: { values: [] },
|
|
1330
|
-
reduce: () => { throw new Error("unsupported record type"); },
|
|
1331
|
-
})).toThrow(/reducer rejected generation/);
|
|
1332
|
-
});
|
|
1333
|
-
});
|
|
1334
|
-
describe("StateStore snapshot encoding — chunked, so one big state is never five copies", () => {
|
|
1335
|
-
/**
|
|
1336
|
-
* The reference is the whole-buffer encoder the chunked writer replaced. The
|
|
1337
|
-
* on-disk format is unchanged, so every payload must serialize to the exact
|
|
1338
|
-
* same bytes; a difference here is a format break that older readers would
|
|
1339
|
-
* reject as a checksum failure.
|
|
1340
|
-
*/
|
|
1341
|
-
const digest = Buffer.alloc(32, 7);
|
|
1342
|
-
function chunked(payload) {
|
|
1343
|
-
const { chunks, byteLength } = encodeSnapshotChunksForTest(1, digest, payload);
|
|
1344
|
-
const joined = Buffer.concat(chunks);
|
|
1345
|
-
expect(joined.length).toBe(byteLength);
|
|
1346
|
-
return joined;
|
|
1347
|
-
}
|
|
1348
|
-
const PAYLOADS = [
|
|
1349
|
-
["empty object", { lastRecordSequence: 0, state: {} }],
|
|
1350
|
-
["nested + arrays", { lastRecordSequence: 3, state: { a: [1, 2, { b: "c" }], d: { e: null } } }],
|
|
1351
|
-
["unsorted keys", { lastRecordSequence: 1, state: { zulu: 1, alpha: 2, Mike: 3, "": 4 } }],
|
|
1352
|
-
["multi-byte text", { lastRecordSequence: 1, state: { k: "café — 日本語 🙂", "ключ": "значение" } }],
|
|
1353
|
-
["number edges", { lastRecordSequence: 1, state: { z: -0, a: 1e21, b: 0.1, c: -5, d: 1234567890123 } }],
|
|
1354
|
-
["escapes", { lastRecordSequence: 1, state: { 'q"uote': 'back\\slash\\n"', tab: "a\tb" } }],
|
|
1355
|
-
["deep", { lastRecordSequence: 1, state: { a: { b: { c: { d: { e: [[[1]]] } } } } } }],
|
|
1356
|
-
];
|
|
1357
|
-
for (const [name, payload] of PAYLOADS) {
|
|
1358
|
-
it(`serializes ${name} byte-for-byte like the whole-buffer encoder`, () => {
|
|
1359
|
-
expect(chunked(payload).equals(encodeSnapshotBytesForTest(1, digest, payload))).toBe(true);
|
|
1360
|
-
});
|
|
1361
|
-
}
|
|
1362
|
-
it("splits a payload larger than one chunk and keeps every chunk bounded", () => {
|
|
1363
|
-
const files = {};
|
|
1364
|
-
for (let index = 0; index < 20_000; index += 1) {
|
|
1365
|
-
files[`alpha/file-${index}.md`] = { hash: `${index}`.padStart(64, "0"), size: index, direction: "up" };
|
|
1366
|
-
}
|
|
1367
|
-
const payload = { lastRecordSequence: 9, state: { journal: { files } } };
|
|
1368
|
-
const { chunks, byteLength } = encodeSnapshotChunksForTest(4, digest, payload);
|
|
1369
|
-
expect(byteLength).toBeGreaterThan(1_048_576);
|
|
1370
|
-
// The point of the change: the bytes arrive in bounded pieces, so the
|
|
1371
|
-
// encoder never holds the whole snapshot as one string AND one buffer.
|
|
1372
|
-
expect(chunks.length).toBeGreaterThan(1);
|
|
1373
|
-
for (const chunk of chunks)
|
|
1374
|
-
expect(chunk.length).toBeLessThanOrEqual(1_048_576 + 64);
|
|
1375
|
-
expect(Buffer.concat(chunks).equals(encodeSnapshotBytesForTest(4, digest, payload))).toBe(true);
|
|
1376
|
-
});
|
|
1377
|
-
it("keeps a surrogate pair intact across a chunk boundary", () => {
|
|
1378
|
-
// An unbroken run of emoji means every chunk boundary inside the token
|
|
1379
|
-
// falls between two halves of a surrogate pair unless the encoder steps
|
|
1380
|
-
// back. Splitting there would encode each half as U+FFFD, and the checksum
|
|
1381
|
-
// would happily cover the corrupted bytes — so the only way to catch it is
|
|
1382
|
-
// byte equality against the whole-buffer encoder.
|
|
1383
|
-
//
|
|
1384
|
-
// The single-character pad shifts the run's start by one UTF-16 unit, which
|
|
1385
|
-
// flips the parity of every boundary. One of these four lands mid-pair for
|
|
1386
|
-
// any chunk budget the encoder might pick, so the test does not depend on
|
|
1387
|
-
// knowing that budget.
|
|
1388
|
-
for (const pad of ["", "a", "ab", "abc"]) {
|
|
1389
|
-
const payload = { lastRecordSequence: 1, state: { k: `${pad}${"🙂".repeat(400_000)}` } };
|
|
1390
|
-
expect(chunked(payload).equals(encodeSnapshotBytesForTest(1, digest, payload))).toBe(true);
|
|
1391
|
-
}
|
|
1392
|
-
});
|
|
1393
|
-
it("rejects the same payloads the whole-buffer encoder rejected, before any chunk is emitted", () => {
|
|
1394
|
-
const bad = [
|
|
1395
|
-
["undefined property", { lastRecordSequence: 1, state: { a: undefined } }],
|
|
1396
|
-
["undefined array item", { lastRecordSequence: 1, state: { a: [1, undefined] } }],
|
|
1397
|
-
["non-finite number", { lastRecordSequence: 1, state: { a: Number.POSITIVE_INFINITY } }],
|
|
1398
|
-
["NaN", { lastRecordSequence: 1, state: { a: Number.NaN } }],
|
|
1399
|
-
["class instance", { lastRecordSequence: 1, state: { a: new (class {
|
|
1400
|
-
x = 1;
|
|
1401
|
-
})() } }],
|
|
1402
|
-
["null-prototype object", { lastRecordSequence: 1, state: { a: Object.create(null) } }],
|
|
1403
|
-
];
|
|
1404
|
-
for (const [name, payload] of bad) {
|
|
1405
|
-
expect(() => encodeSnapshotChunksForTest(1, digest, payload), name).toThrow(/state-store/);
|
|
1406
|
-
expect(() => encodeSnapshotBytesForTest(1, digest, payload), name).toThrow(/state-store/);
|
|
1407
|
-
}
|
|
1408
|
-
});
|
|
1409
|
-
it("rejects an array hole the whole-buffer encoder silently turned into invalid JSON", () => {
|
|
1410
|
-
// Deliberate behaviour change, pinned here so it is not mistaken for drift.
|
|
1411
|
-
// `Array.prototype.map` skips holes, so the old encoder emitted the literal
|
|
1412
|
-
// text `[1,,3]` — a snapshot that would fail its own next read. The chunked
|
|
1413
|
-
// encoder walks indices explicitly and refuses before anything is written.
|
|
1414
|
-
// Built by assignment rather than `[1, , 3]` so the fixture itself is not a
|
|
1415
|
-
// lint error; index 1 is a genuine hole either way.
|
|
1416
|
-
const holed = [];
|
|
1417
|
-
holed[0] = 1;
|
|
1418
|
-
holed[2] = 3;
|
|
1419
|
-
const payload = { lastRecordSequence: 1, state: { a: holed } };
|
|
1420
|
-
expect(encodeSnapshotBytesForTest(1, digest, payload).toString("utf8")).toContain("[1,,3]");
|
|
1421
|
-
expect(() => encodeSnapshotChunksForTest(1, digest, payload))
|
|
1422
|
-
.toThrow(/state-store: JSON arrays cannot be sparse or undefined/);
|
|
1423
|
-
});
|
|
1424
|
-
it("round-trips a state larger than one chunk through a real store", () => {
|
|
1425
|
-
const root = makeRoot();
|
|
1426
|
-
const store = openStore(root);
|
|
1427
|
-
const values = [];
|
|
1428
|
-
for (let index = 0; index < 20_000; index += 1) {
|
|
1429
|
-
// Multi-byte on purpose: the padding pushes emoji across chunk boundaries
|
|
1430
|
-
// at offsets nothing in the fixture chose by hand.
|
|
1431
|
-
values.push(`value-${index}-🙂${"x".repeat(240)}`);
|
|
1432
|
-
}
|
|
1433
|
-
// One snapshot write rather than 20,000 fsynced appends — the encoder is
|
|
1434
|
-
// what is under test here, not the WAL append path.
|
|
1435
|
-
store.replaceState({ values });
|
|
1436
|
-
const snapshotBytes = fs.readdirSync(store.stateDir)
|
|
1437
|
-
.filter((name) => name.startsWith("snapshot-"))
|
|
1438
|
-
.map((name) => fs.statSync(path.join(store.stateDir, name)).size);
|
|
1439
|
-
// Without this the test could pass on a state that never needed chunking.
|
|
1440
|
-
expect(Math.max(...snapshotBytes)).toBeGreaterThan(1_048_576);
|
|
1441
|
-
expect(openStore(root).getState().values).toEqual(values);
|
|
1442
|
-
});
|
|
1443
|
-
});
|
|
1444
|
-
describe("StateStore snapshot decoding", () => {
|
|
1445
|
-
it("hands an authenticated payload to an optional decoder in bounded chunks", () => {
|
|
1446
|
-
const root = makeRoot();
|
|
1447
|
-
const values = Array.from({ length: 24_000 }, (_, index) => `row-${index}-${"x".repeat(64)}`);
|
|
1448
|
-
const writer = StateStore.open({
|
|
1449
|
-
rootDir: root,
|
|
1450
|
-
scopeId: "streamed-snapshot",
|
|
1451
|
-
initialState: { values },
|
|
1452
|
-
reduce: (state, record) => ({ values: [...state.values, String(record.payload)] }),
|
|
1453
|
-
});
|
|
1454
|
-
const chunks = [];
|
|
1455
|
-
const recovered = StateStore.open({
|
|
1456
|
-
rootDir: root,
|
|
1457
|
-
scopeId: "streamed-snapshot",
|
|
1458
|
-
initialState: { values: [] },
|
|
1459
|
-
reduce: (state, record) => ({ values: [...state.values, String(record.payload)] }),
|
|
1460
|
-
snapshotDecoder: (payloadChunks) => {
|
|
1461
|
-
const payload = [...payloadChunks];
|
|
1462
|
-
chunks.push(...payload.map((chunk) => chunk.length));
|
|
1463
|
-
return JSON.parse(Buffer.concat(payload).toString("utf8"));
|
|
1464
|
-
},
|
|
1465
|
-
});
|
|
1466
|
-
expect(recovered.getState()).toEqual(writer.getState());
|
|
1467
|
-
expect(chunks.length).toBeGreaterThan(1);
|
|
1468
|
-
expect(Math.max(...chunks)).toBeLessThanOrEqual(1024 * 1024);
|
|
1469
|
-
});
|
|
1470
|
-
it("names HQSNAP4 as an unsupported format instead of state-store corruption", () => {
|
|
1471
|
-
const root = makeRoot();
|
|
1472
|
-
const writer = openStore(root);
|
|
1473
|
-
writer.append(RECORD, "kept");
|
|
1474
|
-
rewriteSnapshotMagic(writer, SNAPSHOT_V4_MAGIC);
|
|
1475
|
-
expectUnsupportedSnapshotFormat(() => openStore(root), "HQSNAP4", ["HQSNAP3"]);
|
|
1476
|
-
expect(fs.existsSync(snapshotPathFor(writer))).toBe(true);
|
|
1477
|
-
});
|
|
1478
|
-
it("does not fall back to an older generation when the newest magic is unrecognised", () => {
|
|
1479
|
-
const root = makeRoot();
|
|
1480
|
-
const writer = openStore(root);
|
|
1481
|
-
writer.append(RECORD, "last-good");
|
|
1482
|
-
writer.compact();
|
|
1483
|
-
writer.append(RECORD, "newer");
|
|
1484
|
-
expect(writer.getGeneration()).toBe(2);
|
|
1485
|
-
expect(fs.existsSync(path.join(writer.stateDir, "snapshot-1.bin"))).toBe(true);
|
|
1486
|
-
rewriteSnapshotMagic(writer, SNAPSHOT_V4_MAGIC, 2);
|
|
1487
|
-
expectUnsupportedSnapshotFormat(() => openStore(root), "HQSNAP4", ["HQSNAP3"]);
|
|
1488
|
-
});
|
|
1489
|
-
it("still opens a snapshot whose owner opted into the written magic", () => {
|
|
1490
|
-
const root = makeRoot();
|
|
1491
|
-
const writer = openStore(root);
|
|
1492
|
-
writer.append(RECORD, "kept");
|
|
1493
|
-
rewriteSnapshotMagic(writer, SNAPSHOT_V4_MAGIC);
|
|
1494
|
-
const recovered = StateStore.open({
|
|
1495
|
-
rootDir: root,
|
|
1496
|
-
scopeId: "prs_test_scope",
|
|
1497
|
-
initialState: { values: [] },
|
|
1498
|
-
snapshotMagics: [SNAPSHOT_V4_MAGIC],
|
|
1499
|
-
reduce: (state, record) => {
|
|
1500
|
-
if (record.type !== RECORD || typeof record.payload !== "string") {
|
|
1501
|
-
throw new Error("test reducer received an invalid record");
|
|
1502
|
-
}
|
|
1503
|
-
return { values: [...state.values, record.payload] };
|
|
1504
|
-
},
|
|
1505
|
-
});
|
|
1506
|
-
expect(recovered.getState()).toEqual({ values: ["kept"] });
|
|
1507
|
-
});
|
|
1508
|
-
it("hex-encodes a non-ASCII unrecognised magic and still refuses to call it corruption", () => {
|
|
1509
|
-
const root = makeRoot();
|
|
1510
|
-
const writer = openStore(root);
|
|
1511
|
-
const magic = Buffer.from([0x00, 0x01, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x00]);
|
|
1512
|
-
rewriteSnapshotMagic(writer, magic);
|
|
1513
|
-
expectUnsupportedSnapshotFormat(() => openStore(root), magic.toString("hex"), ["HQSNAP3"]);
|
|
1514
|
-
});
|
|
1515
|
-
it.each([
|
|
1516
|
-
["invalid JSON", (store) => {
|
|
1517
|
-
rewriteSnapshotPayload(store, Buffer.from('{"lastRecordSequence":0,"state":', "utf8"));
|
|
1518
|
-
}],
|
|
1519
|
-
["invalid state payload", (store) => {
|
|
1520
|
-
rewriteSnapshotPayload(store, Buffer.from('{"state":{}}', "utf8"));
|
|
1521
|
-
}],
|
|
1522
|
-
["torn payload", (store) => {
|
|
1523
|
-
const snapshotPath = snapshotPathFor(store);
|
|
1524
|
-
fs.truncateSync(snapshotPath, 53);
|
|
1525
|
-
}],
|
|
1526
|
-
["bad checksum", (store) => {
|
|
1527
|
-
const snapshotPath = snapshotPathFor(store);
|
|
1528
|
-
const bytes = fs.readFileSync(snapshotPath);
|
|
1529
|
-
bytes[bytes.length - 1] ^= 0xff;
|
|
1530
|
-
fs.writeFileSync(snapshotPath, bytes);
|
|
1531
|
-
}],
|
|
1532
|
-
["scope digest mismatch", (store) => {
|
|
1533
|
-
const snapshotPath = snapshotPathFor(store);
|
|
1534
|
-
const bytes = fs.readFileSync(snapshotPath);
|
|
1535
|
-
bytes[20] ^= 0xff;
|
|
1536
|
-
fs.writeFileSync(snapshotPath, bytes);
|
|
1537
|
-
}],
|
|
1538
|
-
])("keeps %s classified as StateStoreCorruptionError with a streaming decoder", (_name, corrupt) => {
|
|
1539
|
-
const root = makeRoot();
|
|
1540
|
-
const writer = StateStore.open({
|
|
1541
|
-
rootDir: root,
|
|
1542
|
-
scopeId: "streamed-corruption",
|
|
1543
|
-
initialState: { values: ["durable"] },
|
|
1544
|
-
reduce: (state, record) => ({ values: [...state.values, String(record.payload)] }),
|
|
1545
|
-
});
|
|
1546
|
-
corrupt(writer);
|
|
1547
|
-
expect(() => StateStore.open({
|
|
1548
|
-
rootDir: root,
|
|
1549
|
-
scopeId: "streamed-corruption",
|
|
1550
|
-
initialState: { values: [] },
|
|
1551
|
-
reduce: (state, record) => ({ values: [...state.values, String(record.payload)] }),
|
|
1552
|
-
snapshotDecoder,
|
|
1553
|
-
})).toThrow(StateStoreCorruptionError);
|
|
1554
|
-
});
|
|
1555
|
-
});
|
|
1556
|
-
//# sourceMappingURL=state-store.test.js.map
|