@amalgm/shell 0.1.164-preview.36061234379 → 0.1.164-preview.36809057014
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/PURPOSE.md +642 -128
- package/README.md +10 -8
- package/dist/accepted-tail.d.ts +26 -0
- package/dist/accepted-tail.js +37 -0
- package/dist/accepted-tail.js.map +1 -0
- package/dist/agent-execution.d.ts +16 -1
- package/dist/agent-execution.js +13 -5
- package/dist/agent-execution.js.map +1 -1
- package/dist/apply-cleanup.d.ts +28 -0
- package/dist/apply-cleanup.js +166 -0
- package/dist/apply-cleanup.js.map +1 -0
- package/dist/apply-recovery.d.ts +47 -0
- package/dist/apply-recovery.js +99 -0
- package/dist/apply-recovery.js.map +1 -0
- package/dist/apply-rows.d.ts +72 -0
- package/dist/apply-rows.js +150 -0
- package/dist/apply-rows.js.map +1 -0
- package/dist/apply-saves.d.ts +85 -0
- package/dist/apply-saves.js +238 -0
- package/dist/apply-saves.js.map +1 -0
- package/dist/boot.js +2 -1
- package/dist/boot.js.map +1 -1
- package/dist/capture-work.d.ts +8 -0
- package/dist/capture-work.js +67 -0
- package/dist/capture-work.js.map +1 -0
- package/dist/cargo-group.d.ts +45 -0
- package/dist/cargo-group.js +88 -0
- package/dist/cargo-group.js.map +1 -0
- package/dist/channel-session.d.ts +4 -2
- package/dist/channel-session.js +4 -3
- package/dist/channel-session.js.map +1 -1
- package/dist/chat-composition.d.ts +2 -0
- package/dist/chat-composition.js +1 -0
- package/dist/chat-composition.js.map +1 -1
- package/dist/cli.js +20 -1
- package/dist/cli.js.map +1 -1
- package/dist/construct-apply-host.d.ts +4 -2
- package/dist/construct-apply-host.js +8 -3
- package/dist/construct-apply-host.js.map +1 -1
- package/dist/construct-door.js +1 -1
- package/dist/construct-door.js.map +1 -1
- package/dist/construct-ledger.d.ts +6 -1
- package/dist/construct-ledger.js +21 -33
- package/dist/construct-ledger.js.map +1 -1
- package/dist/content-cache-collection.d.ts +18 -0
- package/dist/content-cache-collection.js +99 -0
- package/dist/content-cache-collection.js.map +1 -0
- package/dist/content-cache-host.d.ts +83 -17
- package/dist/content-cache-host.js +335 -95
- package/dist/content-cache-host.js.map +1 -1
- package/dist/content-cache-namespace.d.ts +59 -0
- package/dist/content-cache-namespace.js +210 -0
- package/dist/content-cache-namespace.js.map +1 -0
- package/dist/content-cache-ownership.d.ts +13 -0
- package/dist/content-cache-ownership.js +75 -0
- package/dist/content-cache-ownership.js.map +1 -0
- package/dist/content-cache-retention.d.ts +23 -0
- package/dist/content-cache-retention.js +195 -0
- package/dist/content-cache-retention.js.map +1 -0
- package/dist/content-capture-pool.d.ts +287 -0
- package/dist/content-capture-pool.js +461 -0
- package/dist/content-capture-pool.js.map +1 -0
- package/dist/content-capture-worker.d.ts +8 -0
- package/dist/content-capture-worker.js +109 -0
- package/dist/content-capture-worker.js.map +1 -0
- package/dist/content-clock.d.ts +42 -0
- package/dist/content-clock.js +166 -0
- package/dist/content-clock.js.map +1 -0
- package/dist/content-direct.d.ts +2 -0
- package/dist/content-direct.js +12 -0
- package/dist/content-direct.js.map +1 -1
- package/dist/content-failure.d.ts +18 -0
- package/dist/content-failure.js +38 -0
- package/dist/content-failure.js.map +1 -0
- package/dist/content-stream-thread.d.ts +60 -0
- package/dist/content-stream-thread.js +95 -0
- package/dist/content-stream-thread.js.map +1 -0
- package/dist/content-stream-worker.d.ts +7 -0
- package/dist/content-stream-worker.js +52 -0
- package/dist/content-stream-worker.js.map +1 -0
- package/dist/declaration-absorption.d.ts +37 -0
- package/dist/declaration-absorption.js +119 -0
- package/dist/declaration-absorption.js.map +1 -0
- package/dist/declaration-chain.d.ts +36 -0
- package/dist/declaration-chain.js +96 -0
- package/dist/declaration-chain.js.map +1 -0
- package/dist/declaration-commit.d.ts +31 -0
- package/dist/declaration-commit.js +49 -0
- package/dist/declaration-commit.js.map +1 -0
- package/dist/declaration-hold.d.ts +22 -0
- package/dist/declaration-hold.js +24 -0
- package/dist/declaration-hold.js.map +1 -0
- package/dist/declaration-journal.d.ts +62 -0
- package/dist/declaration-journal.js +99 -0
- package/dist/declaration-journal.js.map +1 -0
- package/dist/declaration-save.d.ts +31 -0
- package/dist/declaration-save.js +134 -0
- package/dist/declaration-save.js.map +1 -0
- package/dist/detection/accepted-result.d.ts +19 -1
- package/dist/detection/accepted-result.js +36 -5
- package/dist/detection/accepted-result.js.map +1 -1
- package/dist/detection/admission-save.d.ts +55 -0
- package/dist/detection/admission-save.js +48 -0
- package/dist/detection/admission-save.js.map +1 -0
- package/dist/detection/admission-units.d.ts +66 -0
- package/dist/detection/admission-units.js +322 -0
- package/dist/detection/admission-units.js.map +1 -0
- package/dist/detection/alias-discovery.d.ts +12 -0
- package/dist/detection/alias-discovery.js +80 -0
- package/dist/detection/alias-discovery.js.map +1 -0
- package/dist/detection/alias-examination.d.ts +30 -0
- package/dist/detection/alias-examination.js +49 -0
- package/dist/detection/alias-examination.js.map +1 -0
- package/dist/detection/apply-ownership.d.ts +81 -0
- package/dist/detection/apply-ownership.js +232 -0
- package/dist/detection/apply-ownership.js.map +1 -0
- package/dist/detection/basis-advanced.d.ts +11 -0
- package/dist/detection/basis-advanced.js +26 -0
- package/dist/detection/basis-advanced.js.map +1 -0
- package/dist/detection/capture-outcome.d.ts +16 -0
- package/dist/detection/capture-outcome.js +92 -0
- package/dist/detection/capture-outcome.js.map +1 -0
- package/dist/detection/container-membership.d.ts +32 -0
- package/dist/detection/container-membership.js +49 -0
- package/dist/detection/container-membership.js.map +1 -0
- package/dist/detection/detect-cancelled.d.ts +6 -0
- package/dist/detection/detect-cancelled.js +10 -0
- package/dist/detection/detect-cancelled.js.map +1 -0
- package/dist/detection/directory-proof.d.ts +47 -0
- package/dist/detection/directory-proof.js +77 -0
- package/dist/detection/directory-proof.js.map +1 -0
- package/dist/detection/disposition-rows.d.ts +18 -0
- package/dist/detection/disposition-rows.js +29 -0
- package/dist/detection/disposition-rows.js.map +1 -0
- package/dist/detection/dispositions.d.ts +130 -0
- package/dist/detection/dispositions.js +248 -0
- package/dist/detection/dispositions.js.map +1 -0
- package/dist/detection/git-notice.d.ts +88 -0
- package/dist/detection/git-notice.js +444 -0
- package/dist/detection/git-notice.js.map +1 -0
- package/dist/detection/ground-proof.d.ts +130 -0
- package/dist/detection/ground-proof.js +274 -0
- package/dist/detection/ground-proof.js.map +1 -0
- package/dist/detection/ground-reader.d.ts +29 -0
- package/dist/detection/ground-reader.js +102 -0
- package/dist/detection/ground-reader.js.map +1 -0
- package/dist/detection/held-territory.d.ts +51 -0
- package/dist/detection/held-territory.js +205 -0
- package/dist/detection/held-territory.js.map +1 -0
- package/dist/detection/identity-discovery.d.ts +45 -0
- package/dist/detection/identity-discovery.js +287 -0
- package/dist/detection/identity-discovery.js.map +1 -0
- package/dist/detection/maintenance-sweep.d.ts +108 -0
- package/dist/detection/maintenance-sweep.js +325 -0
- package/dist/detection/maintenance-sweep.js.map +1 -0
- package/dist/detection/name-reach.d.ts +12 -0
- package/dist/detection/name-reach.js +28 -0
- package/dist/detection/name-reach.js.map +1 -0
- package/dist/detection/notebook-basis.d.ts +5 -7
- package/dist/detection/notebook-basis.js +6 -7
- package/dist/detection/notebook-basis.js.map +1 -1
- package/dist/detection/owed-selection.d.ts +51 -0
- package/dist/detection/owed-selection.js +169 -0
- package/dist/detection/owed-selection.js.map +1 -0
- package/dist/detection/portable.d.ts +34 -34
- package/dist/detection/portable.js +67 -93
- package/dist/detection/portable.js.map +1 -1
- package/dist/detection/reconciliation-coverage.d.ts +7 -0
- package/dist/detection/reconciliation-coverage.js +16 -0
- package/dist/detection/reconciliation-coverage.js.map +1 -0
- package/dist/detection/repository-publication.d.ts +20 -0
- package/dist/detection/repository-publication.js +79 -0
- package/dist/detection/repository-publication.js.map +1 -0
- package/dist/detection/repository-routes.d.ts +43 -0
- package/dist/detection/repository-routes.js +97 -0
- package/dist/detection/repository-routes.js.map +1 -0
- package/dist/detection/root-examination.d.ts +94 -0
- package/dist/detection/root-examination.js +523 -0
- package/dist/detection/root-examination.js.map +1 -0
- package/dist/detection/runtime.d.ts +131 -64
- package/dist/detection/runtime.js +808 -1103
- package/dist/detection/runtime.js.map +1 -1
- package/dist/detection/scoped-identity-port.d.ts +34 -0
- package/dist/detection/scoped-identity-port.js +81 -0
- package/dist/detection/scoped-identity-port.js.map +1 -0
- package/dist/detection/seal-law.d.ts +66 -0
- package/dist/detection/seal-law.js +116 -0
- package/dist/detection/seal-law.js.map +1 -0
- package/dist/detection/sharing-policy.d.ts +11 -0
- package/dist/detection/sharing-policy.js +32 -0
- package/dist/detection/sharing-policy.js.map +1 -0
- package/dist/detection/thread-database.d.ts +4 -0
- package/dist/detection/thread-database.js +30 -0
- package/dist/detection/thread-database.js.map +1 -0
- package/dist/detection/unreadable.d.ts +11 -0
- package/dist/detection/unreadable.js +9 -0
- package/dist/detection/unreadable.js.map +1 -0
- package/dist/detection/verified-content.d.ts +6 -0
- package/dist/detection/verified-content.js +12 -0
- package/dist/detection/verified-content.js.map +1 -0
- package/dist/detection/walk-law.d.ts +34 -0
- package/dist/detection/walk-law.js +55 -0
- package/dist/detection/walk-law.js.map +1 -0
- package/dist/development/fly-guest.js +2 -1
- package/dist/development/fly-guest.js.map +1 -1
- package/dist/entity-apply-host.d.ts +29 -13
- package/dist/entity-apply-host.js +1426 -461
- package/dist/entity-apply-host.js.map +1 -1
- package/dist/entity-apply-store.d.ts +77 -30
- package/dist/entity-apply-store.js +221 -516
- package/dist/entity-apply-store.js.map +1 -1
- package/dist/entity-record-observability.js +2 -0
- package/dist/entity-record-observability.js.map +1 -1
- package/dist/entity-record-saves.d.ts +20 -0
- package/dist/entity-record-saves.js +91 -0
- package/dist/entity-record-saves.js.map +1 -0
- package/dist/entity-record-store.d.ts +15 -7
- package/dist/entity-record-store.js +57 -117
- package/dist/entity-record-store.js.map +1 -1
- package/dist/files-register-host.d.ts +22 -3
- package/dist/files-register-host.js +70 -31
- package/dist/files-register-host.js.map +1 -1
- package/dist/filesystem.js +3 -2
- package/dist/filesystem.js.map +1 -1
- package/dist/git-binding.d.ts +65 -0
- package/dist/git-binding.js +220 -0
- package/dist/git-binding.js.map +1 -0
- package/dist/git-capture.d.ts +39 -0
- package/dist/git-capture.js +86 -0
- package/dist/git-capture.js.map +1 -0
- package/dist/git-card.d.ts +12 -0
- package/dist/git-card.js +123 -0
- package/dist/git-card.js.map +1 -0
- package/dist/git-checkpoint-construction.d.ts +41 -0
- package/dist/git-checkpoint-construction.js +96 -0
- package/dist/git-checkpoint-construction.js.map +1 -0
- package/dist/git-checkpoint-preparation.d.ts +37 -0
- package/dist/git-checkpoint-preparation.js +145 -0
- package/dist/git-checkpoint-preparation.js.map +1 -0
- package/dist/git-command.d.ts +61 -8
- package/dist/git-command.js +217 -63
- package/dist/git-command.js.map +1 -1
- package/dist/git-configuration.d.ts +40 -0
- package/dist/git-configuration.js +217 -0
- package/dist/git-configuration.js.map +1 -0
- package/dist/git-evidence.d.ts +61 -0
- package/dist/git-evidence.js +181 -0
- package/dist/git-evidence.js.map +1 -0
- package/dist/git-index-entries.d.ts +15 -0
- package/dist/git-index-entries.js +44 -0
- package/dist/git-index-entries.js.map +1 -0
- package/dist/git-index-format.d.ts +35 -0
- package/dist/git-index-format.js +293 -0
- package/dist/git-index-format.js.map +1 -0
- package/dist/git-index-witness.d.ts +33 -0
- package/dist/git-index-witness.js +69 -0
- package/dist/git-index-witness.js.map +1 -0
- package/dist/git-literal-host.d.ts +82 -0
- package/dist/git-literal-host.js +172 -0
- package/dist/git-literal-host.js.map +1 -0
- package/dist/git-literal-read.d.ts +88 -0
- package/dist/git-literal-read.js +170 -0
- package/dist/git-literal-read.js.map +1 -0
- package/dist/git-literal-replay.d.ts +33 -0
- package/dist/git-literal-replay.js +268 -0
- package/dist/git-literal-replay.js.map +1 -0
- package/dist/git-native-file.d.ts +42 -0
- package/dist/git-native-file.js +145 -0
- package/dist/git-native-file.js.map +1 -0
- package/dist/git-native-index.d.ts +21 -0
- package/dist/git-native-index.js +44 -0
- package/dist/git-native-index.js.map +1 -0
- package/dist/git-operation-host.d.ts +48 -0
- package/dist/git-operation-host.js +127 -0
- package/dist/git-operation-host.js.map +1 -0
- package/dist/git-private-index.d.ts +11 -0
- package/dist/git-private-index.js +42 -0
- package/dist/git-private-index.js.map +1 -0
- package/dist/git-private-repository.d.ts +4 -0
- package/dist/git-private-repository.js +69 -0
- package/dist/git-private-repository.js.map +1 -0
- package/dist/git-private-view.d.ts +39 -0
- package/dist/git-private-view.js +162 -0
- package/dist/git-private-view.js.map +1 -0
- package/dist/git-ref-store.d.ts +12 -0
- package/dist/git-ref-store.js +68 -0
- package/dist/git-ref-store.js.map +1 -0
- package/dist/git-registration-host.d.ts +20 -30
- package/dist/git-registration-host.js +86 -249
- package/dist/git-registration-host.js.map +1 -1
- package/dist/git-repository-host.d.ts +148 -22
- package/dist/git-repository-host.js +633 -650
- package/dist/git-repository-host.js.map +1 -1
- package/dist/git-worktree-host.d.ts +2 -0
- package/dist/git-worktree-host.js +16 -0
- package/dist/git-worktree-host.js.map +1 -0
- package/dist/graph-publication.d.ts +75 -0
- package/dist/graph-publication.js +203 -0
- package/dist/graph-publication.js.map +1 -0
- package/dist/ground-basis.d.ts +242 -0
- package/dist/ground-basis.js +1023 -0
- package/dist/ground-basis.js.map +1 -0
- package/dist/ground-checkpoint-worker.d.ts +1 -0
- package/dist/ground-checkpoint-worker.js +84 -0
- package/dist/ground-checkpoint-worker.js.map +1 -0
- package/dist/ground-checkpoints.d.ts +16 -0
- package/dist/ground-checkpoints.js +45 -0
- package/dist/ground-checkpoints.js.map +1 -0
- package/dist/ground-coordination.d.ts +107 -3
- package/dist/ground-coordination.js +249 -24
- package/dist/ground-coordination.js.map +1 -1
- package/dist/ground-database.d.ts +42 -0
- package/dist/ground-database.js +290 -0
- package/dist/ground-database.js.map +1 -0
- package/dist/ground-differences.d.ts +23 -0
- package/dist/ground-differences.js +47 -0
- package/dist/ground-differences.js.map +1 -0
- package/dist/ground-holders.d.ts +4 -0
- package/dist/ground-holders.js +15 -0
- package/dist/ground-holders.js.map +1 -0
- package/dist/ground-initialization-worker.d.ts +1 -0
- package/dist/ground-initialization-worker.js +20 -0
- package/dist/ground-initialization-worker.js.map +1 -0
- package/dist/ground-initialization.d.ts +35 -0
- package/dist/ground-initialization.js +102 -0
- package/dist/ground-initialization.js.map +1 -0
- package/dist/ground-install-saves.d.ts +63 -0
- package/dist/ground-install-saves.js +139 -0
- package/dist/ground-install-saves.js.map +1 -0
- package/dist/ground-inventory-schema.d.ts +18 -0
- package/dist/ground-inventory-schema.js +168 -0
- package/dist/ground-inventory-schema.js.map +1 -0
- package/dist/ground-profile.d.ts +7 -0
- package/dist/ground-profile.js +52 -0
- package/dist/ground-profile.js.map +1 -0
- package/dist/ground-root-index.d.ts +2 -1
- package/dist/ground-root-index.js +16 -14
- package/dist/ground-root-index.js.map +1 -1
- package/dist/ground-row.d.ts +62 -0
- package/dist/ground-row.js +70 -0
- package/dist/ground-row.js.map +1 -0
- package/dist/ground-save-ops.d.ts +44 -0
- package/dist/ground-save-ops.js +41 -0
- package/dist/ground-save-ops.js.map +1 -0
- package/dist/ground-saver-worker.d.ts +1 -0
- package/dist/ground-saver-worker.js +156 -0
- package/dist/ground-saver-worker.js.map +1 -0
- package/dist/ground-saver.d.ts +75 -0
- package/dist/ground-saver.js +216 -0
- package/dist/ground-saver.js.map +1 -0
- package/dist/ground-store.d.ts +84 -0
- package/dist/ground-store.js +473 -0
- package/dist/ground-store.js.map +1 -0
- package/dist/ground-territory.d.ts +130 -0
- package/dist/ground-territory.js +511 -0
- package/dist/ground-territory.js.map +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js.map +1 -1
- package/dist/installation/ownership.d.ts +0 -2
- package/dist/installation/ownership.js +3 -15
- package/dist/installation/ownership.js.map +1 -1
- package/dist/machine-host.d.ts +3 -2
- package/dist/machine-host.js +28 -16
- package/dist/machine-host.js.map +1 -1
- package/dist/materialized-graph.d.ts +7 -7
- package/dist/materialized-graph.js +11 -23
- package/dist/materialized-graph.js.map +1 -1
- package/dist/module-thread.d.ts +9 -0
- package/dist/module-thread.js +10 -0
- package/dist/module-thread.js.map +1 -0
- package/dist/native/fsevents-stream.node +0 -0
- package/dist/physical-path-witness.d.ts +64 -0
- package/dist/physical-path-witness.js +168 -0
- package/dist/physical-path-witness.js.map +1 -0
- package/dist/register-upload-fusion.d.ts +5 -4
- package/dist/register-upload-fusion.js +9 -6
- package/dist/register-upload-fusion.js.map +1 -1
- package/dist/registry-graph.d.ts +3 -2
- package/dist/registry-graph.js +9 -6
- package/dist/registry-graph.js.map +1 -1
- package/dist/registry-page-cache.d.ts +5 -2
- package/dist/registry-page-cache.js.map +1 -1
- package/dist/repository-identity-steps.d.ts +5 -0
- package/dist/repository-identity-steps.js +33 -0
- package/dist/repository-identity-steps.js.map +1 -0
- package/dist/runtime-node-flags.d.ts +10 -0
- package/dist/runtime-node-flags.js +11 -0
- package/dist/runtime-node-flags.js.map +1 -0
- package/dist/service/adapter.d.ts +1 -1
- package/dist/service/adapter.js +1 -1
- package/dist/service/adapter.js.map +1 -1
- package/dist/service/cli.js +16 -20
- package/dist/service/cli.js.map +1 -1
- package/dist/service/control.d.ts +2 -2
- package/dist/service/keepalive.d.ts +4 -11
- package/dist/service/keepalive.js +65 -118
- package/dist/service/keepalive.js.map +1 -1
- package/dist/service/policy.d.ts +36 -0
- package/dist/service/policy.js +25 -0
- package/dist/service/policy.js.map +1 -0
- package/dist/service/portable.d.ts +1 -1
- package/dist/service/portable.js +1 -1
- package/dist/service/portable.js.map +1 -1
- package/dist/service/recovery.d.ts +10 -9
- package/dist/service/recovery.js +29 -19
- package/dist/service/recovery.js.map +1 -1
- package/dist/service/windows-action.d.ts +5 -3
- package/dist/service/windows-action.js +6 -9
- package/dist/service/windows-action.js.map +1 -1
- package/dist/service/windows-task.js +5 -5
- package/dist/service/windows-task.js.map +1 -1
- package/dist/sharing-admission.d.ts +17 -0
- package/dist/sharing-admission.js +128 -0
- package/dist/sharing-admission.js.map +1 -0
- package/dist/snapshot-delivery-baseline.d.ts +12 -3
- package/dist/snapshot-delivery-baseline.js +41 -42
- package/dist/snapshot-delivery-baseline.js.map +1 -1
- package/dist/source-proof.d.ts +36 -0
- package/dist/source-proof.js +70 -0
- package/dist/source-proof.js.map +1 -0
- package/dist/source-signature.d.ts +56 -0
- package/dist/source-signature.js +56 -0
- package/dist/source-signature.js.map +1 -0
- package/dist/source-witness.d.ts +15 -0
- package/dist/source-witness.js +29 -0
- package/dist/source-witness.js.map +1 -0
- package/dist/sqlite-durability.d.ts +31 -0
- package/dist/sqlite-durability.js +28 -0
- package/dist/sqlite-durability.js.map +1 -0
- package/dist/sqlite.d.ts +8 -2
- package/dist/sqlite.js +27 -4
- package/dist/sqlite.js.map +1 -1
- package/dist/sync-address.d.ts +19 -0
- package/dist/sync-address.js +52 -0
- package/dist/sync-address.js.map +1 -0
- package/dist/sync-setting.d.ts +15 -0
- package/dist/sync-setting.js +13 -0
- package/dist/sync-setting.js.map +1 -1
- package/dist/turns.d.ts +42 -0
- package/dist/turns.js +112 -0
- package/dist/turns.js.map +1 -0
- package/dist/updates/bootstrap.d.ts +53 -7
- package/dist/updates/bootstrap.js +224 -137
- package/dist/updates/bootstrap.js.map +1 -1
- package/dist/updates/controller.d.ts +8 -0
- package/dist/updates/controller.js +27 -3
- package/dist/updates/controller.js.map +1 -1
- package/dist/updates/store.d.ts +3 -0
- package/dist/updates/store.js +12 -0
- package/dist/updates/store.js.map +1 -1
- package/dist/user-ground-host.d.ts +364 -69
- package/dist/user-ground-host.js +5231 -3366
- package/dist/user-ground-host.js.map +1 -1
- package/dist/watching/fsevents.d.ts +52 -1
- package/dist/watching/fsevents.js +60 -25
- package/dist/watching/fsevents.js.map +1 -1
- package/dist/watching/host.d.ts +129 -5
- package/dist/watching/host.js +433 -80
- package/dist/watching/host.js.map +1 -1
- package/dist/watching/index.d.ts +1 -1
- package/dist/wire-binary.d.ts +3 -2
- package/dist/wire-binary.js +10 -8
- package/dist/wire-binary.js.map +1 -1
- package/dist/wire-client.d.ts +3 -4
- package/dist/wire-client.js +18 -44
- package/dist/wire-client.js.map +1 -1
- package/dist/wire-outbox.d.ts +34 -0
- package/dist/wire-outbox.js +127 -0
- package/dist/wire-outbox.js.map +1 -0
- package/dist/workspace-host.d.ts +4 -0
- package/dist/workspace-host.js +12 -1
- package/dist/workspace-host.js.map +1 -1
- package/examinations/fixtures/private-wire-authority.js +133 -10
- package/npm-shrinkwrap.json +43 -56
- package/package.json +26 -13
package/dist/watching/host.js
CHANGED
|
@@ -1,10 +1,22 @@
|
|
|
1
1
|
import { EventEmitter } from "node:events";
|
|
2
|
+
import { randomUUID } from "node:crypto";
|
|
3
|
+
import { channel } from "node:diagnostics_channel";
|
|
2
4
|
import { lstatSync, readdirSync, realpathSync, watch as openNodeWatch } from "node:fs";
|
|
3
|
-
import { basename, dirname, isAbsolute, join,
|
|
5
|
+
import { basename, dirname, isAbsolute, join, resolve, sep } from "node:path";
|
|
4
6
|
import { createSuspicionScope, watchOwnerOf, } from "@amalgm/live";
|
|
5
7
|
import { openFsEvents } from "./fsevents.js";
|
|
6
8
|
const DEFAULT_RETRY_MINIMUM_MS = 250;
|
|
9
|
+
/** At most this many native notices are admitted per event-loop turn, and a
|
|
10
|
+
* turn's admissions stop once they have held the main thread for the budget.
|
|
11
|
+
* A burst of thousands (a Git checkout) is admitted in arrival order across
|
|
12
|
+
* turns, so no burst holds the main thread for more than one slice, while a
|
|
13
|
+
* quiet notice is admitted at once. Waiting notices stay owed (hasPending). */
|
|
14
|
+
const WATCH_ADMISSION_SLICE = 256;
|
|
15
|
+
const WATCH_ADMISSION_BUDGET_MS = 1.5;
|
|
7
16
|
const DEFAULT_RETRY_MAXIMUM_MS = 30_000;
|
|
17
|
+
/** Examiners hear each root's oldest unproved ring whenever it moves: a ring
|
|
18
|
+
* starts it, a proof advances or clears it, unbinding clears it. */
|
|
19
|
+
const obligations = channel("amalgm:watch-obligation");
|
|
8
20
|
/** macOS gets one FSEvents stream per handle. Node's own watcher serves every
|
|
9
21
|
* handle in the process from one stream that it recreates, resuming from the
|
|
10
22
|
* present, whenever a handle opens or closes, losing the events in between. */
|
|
@@ -16,13 +28,20 @@ function nativeOpenFor(platform) {
|
|
|
16
28
|
return (directory, options, listener) => openNodeWatch(realpathSync.native(directory), options, listener);
|
|
17
29
|
}
|
|
18
30
|
/** Linux has no recursive inotify primitive. This one logical handle expands
|
|
19
|
-
* to exactly one non-recursive fs.watch handle per directory, never per file.
|
|
31
|
+
* to exactly one non-recursive fs.watch handle per directory, never per file.
|
|
32
|
+
*
|
|
33
|
+
* A descendant the tree cannot enter is its parent's to hear, never a lost
|
|
34
|
+
* tree: one that vanished needs no handle, and one this process may not read
|
|
35
|
+
* is withheld, keeping any handle it already had, until an event on its name
|
|
36
|
+
* asks again. Detect names withheld ground unavailable meanwhile. Entering it
|
|
37
|
+
* later ends an interval no handle heard, so it suspects everything. */
|
|
20
38
|
class LinuxDirectoryTreeWatch extends EventEmitter {
|
|
21
39
|
root;
|
|
22
40
|
open;
|
|
23
41
|
listener;
|
|
24
42
|
excluded;
|
|
25
43
|
handles = new Map();
|
|
44
|
+
withheld = new Set();
|
|
26
45
|
closed = false;
|
|
27
46
|
constructor(root, open, listener, excluded) {
|
|
28
47
|
super();
|
|
@@ -50,30 +69,64 @@ class LinuxDirectoryTreeWatch extends EventEmitter {
|
|
|
50
69
|
this.closeAll();
|
|
51
70
|
this.emit("close");
|
|
52
71
|
}
|
|
53
|
-
|
|
72
|
+
/** Opens every directory the tree can enter at or below this one. `reached`
|
|
73
|
+
* collects each directory the walk stood on, entered or withheld. */
|
|
74
|
+
openTree(directory, reached) {
|
|
54
75
|
if (this.closed || this.isExcluded(directory) || !nativeDirectory(directory))
|
|
55
76
|
return;
|
|
56
|
-
|
|
57
|
-
|
|
77
|
+
let entries;
|
|
78
|
+
try {
|
|
79
|
+
this.openDirectory(directory);
|
|
80
|
+
entries = readdirSync(directory, { withFileTypes: true });
|
|
81
|
+
}
|
|
82
|
+
catch (error) {
|
|
83
|
+
if (!this.cannotEnter(directory, error))
|
|
84
|
+
throw error;
|
|
85
|
+
if (this.withheld.has(directory))
|
|
86
|
+
reached?.add(directory);
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
reached?.add(directory);
|
|
90
|
+
for (const entry of entries) {
|
|
58
91
|
if (!entry.isDirectory() || entry.isSymbolicLink())
|
|
59
92
|
continue;
|
|
60
|
-
this.openTree(join(directory, entry.name));
|
|
93
|
+
this.openTree(join(directory, entry.name), reached);
|
|
61
94
|
}
|
|
95
|
+
if (this.withheld.delete(directory))
|
|
96
|
+
this.listener("rename", null);
|
|
97
|
+
}
|
|
98
|
+
/** Whether a failure to enter this directory is ground its parent hears,
|
|
99
|
+
* rather than coverage the tree lost. The root is never its parent's. */
|
|
100
|
+
cannotEnter(directory, error) {
|
|
101
|
+
if (directory === this.root)
|
|
102
|
+
return false;
|
|
103
|
+
const code = error?.code;
|
|
104
|
+
if (code === "ENOENT" || code === "ENOTDIR") {
|
|
105
|
+
this.closeSubtree(directory);
|
|
106
|
+
return true;
|
|
107
|
+
}
|
|
108
|
+
if (code === "EACCES" || code === "EPERM") {
|
|
109
|
+
this.withheld.add(directory);
|
|
110
|
+
return true;
|
|
111
|
+
}
|
|
112
|
+
return false;
|
|
62
113
|
}
|
|
63
114
|
openDirectory(directory) {
|
|
64
115
|
if (this.handles.has(directory))
|
|
65
116
|
return;
|
|
66
|
-
const
|
|
117
|
+
const localDirectory = localPath(this.root, directory);
|
|
118
|
+
const handle = this.open(directory, { recursive: false }, (event, filename, evidence) => {
|
|
67
119
|
if (this.closed)
|
|
68
120
|
return;
|
|
69
|
-
const localDirectory = slash(relative(this.root, directory));
|
|
70
121
|
const hint = normalizedHint(filename);
|
|
71
|
-
const rootedHint = hint === null
|
|
122
|
+
const rootedHint = hint === null || localDirectory === null
|
|
72
123
|
? null
|
|
73
|
-
:
|
|
74
|
-
this.listener(event, rootedHint);
|
|
75
|
-
|
|
124
|
+
: localDirectory ? `${localDirectory}/${hint}` : hint;
|
|
125
|
+
this.listener(event, rootedHint, evidence);
|
|
126
|
+
// Access returning to withheld ground changes only its attributes.
|
|
127
|
+
if (event === "rename" || (hint !== null && this.withheld.has(below(directory, hint)))) {
|
|
76
128
|
this.refresh(directory, hint);
|
|
129
|
+
}
|
|
77
130
|
});
|
|
78
131
|
const record = { handle, intentional: false };
|
|
79
132
|
this.handles.set(directory, record);
|
|
@@ -89,7 +142,7 @@ class LinuxDirectoryTreeWatch extends EventEmitter {
|
|
|
89
142
|
this.reconcileTree();
|
|
90
143
|
return;
|
|
91
144
|
}
|
|
92
|
-
const candidate =
|
|
145
|
+
const candidate = below(directory, hint);
|
|
93
146
|
if (!within(this.root, candidate) || this.isExcluded(candidate))
|
|
94
147
|
return;
|
|
95
148
|
if (nativeDirectory(candidate))
|
|
@@ -102,23 +155,19 @@ class LinuxDirectoryTreeWatch extends EventEmitter {
|
|
|
102
155
|
}
|
|
103
156
|
});
|
|
104
157
|
}
|
|
158
|
+
/** Handles below withheld ground stay: no walk can prove them gone. */
|
|
105
159
|
reconcileTree() {
|
|
106
|
-
const
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
for (const directory of wanted)
|
|
118
|
-
this.openDirectory(directory);
|
|
119
|
-
for (const directory of this.handles.keys()) {
|
|
120
|
-
if (!wanted.has(directory))
|
|
121
|
-
this.closeOne(directory);
|
|
160
|
+
const reached = new Set();
|
|
161
|
+
this.openTree(this.root, reached);
|
|
162
|
+
for (const directory of [...this.withheld]) {
|
|
163
|
+
if (!reached.has(directory))
|
|
164
|
+
this.withheld.delete(directory);
|
|
165
|
+
}
|
|
166
|
+
const withheld = [...this.withheld];
|
|
167
|
+
for (const directory of [...this.handles.keys()]) {
|
|
168
|
+
if (reached.has(directory) || withheld.some((held) => within(held, directory)))
|
|
169
|
+
continue;
|
|
170
|
+
this.closeOne(directory);
|
|
122
171
|
}
|
|
123
172
|
}
|
|
124
173
|
closeSubtree(directory) {
|
|
@@ -126,6 +175,10 @@ class LinuxDirectoryTreeWatch extends EventEmitter {
|
|
|
126
175
|
if (within(directory, watched))
|
|
127
176
|
this.closeOne(watched);
|
|
128
177
|
}
|
|
178
|
+
for (const held of [...this.withheld]) {
|
|
179
|
+
if (within(directory, held))
|
|
180
|
+
this.withheld.delete(held);
|
|
181
|
+
}
|
|
129
182
|
}
|
|
130
183
|
closeOne(directory) {
|
|
131
184
|
const record = this.handles.get(directory);
|
|
@@ -174,9 +227,22 @@ export class NodeWatchHost {
|
|
|
174
227
|
stopErrors = [];
|
|
175
228
|
closeTask = null;
|
|
176
229
|
plan = emptyPlan();
|
|
230
|
+
domainOwner = randomUUID();
|
|
231
|
+
bindingGeneration = 0;
|
|
232
|
+
domainGeneration = 0;
|
|
233
|
+
resolvedDomainGeneration = 0;
|
|
234
|
+
coverageGap = false;
|
|
177
235
|
retryTimer = null;
|
|
178
236
|
retryAttempt = 0;
|
|
179
237
|
closed = false;
|
|
238
|
+
finalCutoff = false;
|
|
239
|
+
/** Native notices beyond this turn's slice, in arrival order. */
|
|
240
|
+
backlog = [];
|
|
241
|
+
backlogHead = 0;
|
|
242
|
+
sliceLeft = WATCH_ADMISSION_SLICE;
|
|
243
|
+
/** When this turn's budget ends; set by the turn's first admission. */
|
|
244
|
+
sliceEnds = Infinity;
|
|
245
|
+
turnScheduled = false;
|
|
180
246
|
currentHealth = {
|
|
181
247
|
state: "degraded",
|
|
182
248
|
logicalRoots: 0,
|
|
@@ -226,6 +292,8 @@ export class NodeWatchHost {
|
|
|
226
292
|
const next = buildPlan(grounds, previous.roots);
|
|
227
293
|
if (samePlan(previous, next))
|
|
228
294
|
return this.currentHealth;
|
|
295
|
+
this.bindingGeneration += 1;
|
|
296
|
+
const domainPending = this.domainGeneration > this.resolvedDomainGeneration;
|
|
229
297
|
this.cancelRetry();
|
|
230
298
|
const changed = changedRoots(previous, next);
|
|
231
299
|
const authoritativeBaselines = new Set(options.authoritativeBaselineRootIds ?? []);
|
|
@@ -239,17 +307,26 @@ export class NodeWatchHost {
|
|
|
239
307
|
if (!root.materialized) {
|
|
240
308
|
throw new Error(`authoritative Watch baseline is not materialized: ${rootId}`);
|
|
241
309
|
}
|
|
242
|
-
const baseline = root.suspicion.
|
|
310
|
+
const baseline = root.suspicion.freeze(null);
|
|
243
311
|
root.suspicion.settle(baseline);
|
|
244
|
-
root.
|
|
312
|
+
root.suspicion.release(baseline);
|
|
245
313
|
}
|
|
246
314
|
this.plan = next;
|
|
315
|
+
for (const root of previous.roots.values())
|
|
316
|
+
if (!next.roots.has(root.rootId))
|
|
317
|
+
this.observeObligation(root, false);
|
|
318
|
+
for (const root of next.roots.values())
|
|
319
|
+
this.observeObligation(root);
|
|
247
320
|
const health = this.establish(next, authoritativeBaselines);
|
|
248
321
|
const unknownChanges = changed.filter((rootId) => !authoritativeBaselines.has(rootId));
|
|
249
322
|
if (health.state === "healthy" && unknownChanges.length > 0) {
|
|
250
323
|
this.suspectRoots(unknownChanges);
|
|
251
324
|
this.signal();
|
|
252
325
|
}
|
|
326
|
+
// This is the caller's declared binding set, including temporarily missing
|
|
327
|
+
// ground. A changed set cannot inherit a proof against its predecessor.
|
|
328
|
+
if (domainPending)
|
|
329
|
+
this.suspectDomain();
|
|
253
330
|
return health;
|
|
254
331
|
}
|
|
255
332
|
/** Retry the retained desired topology immediately. Useful to supervisors and deterministic tests. */
|
|
@@ -272,7 +349,7 @@ export class NodeWatchHost {
|
|
|
272
349
|
rootId: root.rootId,
|
|
273
350
|
directory: root.directory,
|
|
274
351
|
materialized: root.materialized,
|
|
275
|
-
currency: root.suspicion.
|
|
352
|
+
currency: root.suspicion.owes ? "catching-up" : "current",
|
|
276
353
|
contentOwner: (() => {
|
|
277
354
|
const ownerId = this.plan.ownerByRoot.get(root.rootId);
|
|
278
355
|
return ownerId ? this.plan.roots.get(ownerId)?.directory ?? null : null;
|
|
@@ -287,38 +364,211 @@ export class NodeWatchHost {
|
|
|
287
364
|
.sort((left, right) => left.directory.localeCompare(right.directory)),
|
|
288
365
|
};
|
|
289
366
|
}
|
|
290
|
-
|
|
367
|
+
/** What every root still owes. Scheduling reads this; only a cut proves. */
|
|
368
|
+
pending() {
|
|
291
369
|
return [...this.plan.roots.values()]
|
|
292
|
-
.map(
|
|
293
|
-
rootId: root.rootId,
|
|
294
|
-
directory: root.directory,
|
|
295
|
-
suspicion: root.suspicion.snapshot(),
|
|
296
|
-
}))
|
|
370
|
+
.map(root => ({ rootId: root.rootId, directory: root.directory, pending: root.suspicion.pending() }))
|
|
297
371
|
.sort((left, right) => left.rootId.localeCompare(right.rootId));
|
|
298
372
|
}
|
|
373
|
+
/** Cut every root at what it owes now; an owed domain cuts all roots
|
|
374
|
+
* together. Native cutoffs are never widened by the passage of time. */
|
|
375
|
+
freeze() {
|
|
376
|
+
return this.domainGeneration === this.resolvedDomainGeneration
|
|
377
|
+
? this.freezeRoots(() => true, "owed")
|
|
378
|
+
: this.domainObservations(this.freezeRoots(() => true, "complete"));
|
|
379
|
+
}
|
|
380
|
+
/** Cut the selected roots alone. A partial cut never carries the domain.
|
|
381
|
+
* "escalated" cuts the rings a named proof escalated while no ring owes
|
|
382
|
+
* the complete root; otherwise the complete root. */
|
|
383
|
+
freezeRoots(select, scope) {
|
|
384
|
+
return [...this.plan.roots.values()]
|
|
385
|
+
.filter(root => select(root))
|
|
386
|
+
.sort((left, right) => left.rootId.localeCompare(right.rootId))
|
|
387
|
+
.map(root => {
|
|
388
|
+
const owed = root.suspicion.pending();
|
|
389
|
+
return this.cut(root, scope === "escalated" ? owed.escalated
|
|
390
|
+
: scope === "complete" || owed.complete ? null : owed.paths);
|
|
391
|
+
});
|
|
392
|
+
}
|
|
393
|
+
/** Cut one root at exactly these named rings. A lane that proves part of a
|
|
394
|
+
* root's names cuts only its own; the rest stay owed to their lane. */
|
|
395
|
+
freezePaths(rootId, paths) {
|
|
396
|
+
const root = this.plan.roots.get(rootId);
|
|
397
|
+
return root ? this.cut(root, paths) : null;
|
|
398
|
+
}
|
|
399
|
+
/** A ticket for exact addresses: each root waits only for the named
|
|
400
|
+
* addresses inside it. A root inside an address, or an address outside
|
|
401
|
+
* every root, needs that complete proof — the scope `suspect` rang. */
|
|
402
|
+
freezeAddresses(absolutePaths) {
|
|
403
|
+
const roots = [...this.plan.roots.values()].sort((left, right) => left.rootId.localeCompare(right.rootId));
|
|
404
|
+
const addresses = absolutePaths.map(path => resolve(path));
|
|
405
|
+
const unbound = addresses.length === 0 || addresses.some(address => !roots.some(root => localPath(root.directory, address) !== null));
|
|
406
|
+
return roots.flatMap(root => {
|
|
407
|
+
if (unbound || addresses.some(address => localPath(address, root.directory) !== null)) {
|
|
408
|
+
return [this.cut(root, null)];
|
|
409
|
+
}
|
|
410
|
+
const named = addresses.map(address => localPath(root.directory, address))
|
|
411
|
+
.filter((local) => local !== null);
|
|
412
|
+
return named.length ? [this.cut(root, named)] : [];
|
|
413
|
+
});
|
|
414
|
+
}
|
|
415
|
+
cut(root, paths) {
|
|
416
|
+
return Object.freeze({ rootId: root.rootId, directory: root.directory, suspicion: root.suspicion.freeze(paths) });
|
|
417
|
+
}
|
|
418
|
+
/** Every participant is one root's complete cut. */
|
|
419
|
+
domainObservations(complete) {
|
|
420
|
+
const domain = Object.freeze({
|
|
421
|
+
owner: this.domainOwner, generation: this.domainGeneration, bindings: this.bindingGeneration,
|
|
422
|
+
participants: Object.freeze(complete.map(observation => Object.freeze({
|
|
423
|
+
rootId: observation.rootId, directory: observation.directory, generation: observation.suspicion.generation,
|
|
424
|
+
}))),
|
|
425
|
+
});
|
|
426
|
+
return complete.map(observation => Object.freeze({ ...observation, domain }));
|
|
427
|
+
}
|
|
428
|
+
/** One finite complete-lane batch: an owed domain, else the roots owing a
|
|
429
|
+
* complete-root or escalated proof. Named rings are not complete work;
|
|
430
|
+
* their own lanes serve them. A root whose ground is `held` is not cut: it
|
|
431
|
+
* is `withheld`. A `parked` root is not cut either; its park's own cause
|
|
432
|
+
* wakes it. A domain, which cuts every root together, waits whole. */
|
|
433
|
+
selectComplete(held, parked) {
|
|
434
|
+
const roots = [...this.plan.roots.values()];
|
|
435
|
+
if (this.domainGeneration > this.resolvedDomainGeneration) {
|
|
436
|
+
const withheld = roots.map(root => root.directory).filter(held);
|
|
437
|
+
const waits = withheld.length > 0 || roots.some(root => parked(root.rootId));
|
|
438
|
+
return { cuts: waits ? [] : this.domainObservations(this.freezeRoots(() => true, "complete")), withheld };
|
|
439
|
+
}
|
|
440
|
+
const owing = new Set(roots.filter(root => root.suspicion.pending().complete && !parked(root.rootId))
|
|
441
|
+
.map(root => root.directory));
|
|
442
|
+
const withheld = [...owing].filter(held);
|
|
443
|
+
return { cuts: this.freezeRoots(root => owing.has(root.directory) && !withheld.includes(root.directory), "escalated"),
|
|
444
|
+
withheld };
|
|
445
|
+
}
|
|
446
|
+
/** The binding world a turn begins in; a later rebinding invalidates it. */
|
|
447
|
+
boundary() {
|
|
448
|
+
return Object.freeze({ owner: this.domainOwner, bindings: this.bindingGeneration });
|
|
449
|
+
}
|
|
450
|
+
boundaryCurrent(boundary) {
|
|
451
|
+
return boundary.owner === this.domainOwner && boundary.bindings === this.bindingGeneration;
|
|
452
|
+
}
|
|
453
|
+
/** A named cut widened: its rings keep their generations and are now owed
|
|
454
|
+
* a complete-root proof. The cut itself stays live until released. */
|
|
455
|
+
escalate(observation) {
|
|
456
|
+
const root = this.plan.roots.get(observation.rootId);
|
|
457
|
+
if (root?.suspicion.holds(observation.suspicion))
|
|
458
|
+
root.suspicion.escalate(observation.suspicion);
|
|
459
|
+
}
|
|
460
|
+
/** Names another examiner found (the sweep) that a named proof could not
|
|
461
|
+
* settle. Watch never rang them, so they are rung here and escalated at
|
|
462
|
+
* once: owed the same scoped wider proof as a widened native ring, never
|
|
463
|
+
* the complete root merely because Watch did not hear them. */
|
|
464
|
+
escalatePaths(rootId, paths) {
|
|
465
|
+
const root = this.plan.roots.get(rootId);
|
|
466
|
+
if (this.closed || !root || paths.length === 0)
|
|
467
|
+
return;
|
|
468
|
+
// The root itself is only ever owed its complete proof.
|
|
469
|
+
if (paths.includes("")) {
|
|
470
|
+
this.ring(root, null);
|
|
471
|
+
return;
|
|
472
|
+
}
|
|
473
|
+
for (const path of paths)
|
|
474
|
+
this.ring(root, path);
|
|
475
|
+
const cut = root.suspicion.freeze(paths);
|
|
476
|
+
root.suspicion.escalate(cut);
|
|
477
|
+
root.suspicion.release(cut);
|
|
478
|
+
}
|
|
479
|
+
/** Every cut must be released by its owner, proved or not. */
|
|
480
|
+
release(observations) {
|
|
481
|
+
for (const observation of observations) {
|
|
482
|
+
const root = this.plan.roots.get(observation.rootId);
|
|
483
|
+
if (root?.suspicion.holds(observation.suspicion))
|
|
484
|
+
root.suspicion.release(observation.suspicion);
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
completeDomain(observations) {
|
|
488
|
+
const domain = observations[0]?.domain;
|
|
489
|
+
if (!domain || observations.length !== this.plan.roots.size || observations.length === 0
|
|
490
|
+
|| domain.participants.length !== observations.length
|
|
491
|
+
|| new Set(domain.participants.map(item => item.rootId)).size !== observations.length
|
|
492
|
+
|| new Set(observations.map(item => item.rootId)).size !== observations.length
|
|
493
|
+
|| domain.owner !== this.domainOwner || domain.bindings !== this.bindingGeneration
|
|
494
|
+
|| !this.continuous())
|
|
495
|
+
return false;
|
|
496
|
+
return domain.participants.every(({ rootId, directory, generation }) => {
|
|
497
|
+
const root = this.plan.roots.get(rootId);
|
|
498
|
+
return root?.directory === directory && root.materialized
|
|
499
|
+
&& observations.some(item => item.rootId === rootId && item.directory === directory
|
|
500
|
+
&& item.domain === domain && item.suspicion.paths === null
|
|
501
|
+
&& item.suspicion.generation === generation
|
|
502
|
+
&& root.suspicion.holds(item.suspicion));
|
|
503
|
+
});
|
|
504
|
+
}
|
|
299
505
|
settle(observations) {
|
|
506
|
+
const domain = observations.find(observation => observation.domain)?.domain;
|
|
507
|
+
if (domain && !this.completeDomain(observations))
|
|
508
|
+
return;
|
|
300
509
|
for (const observation of observations) {
|
|
301
510
|
const root = this.plan.roots.get(observation.rootId);
|
|
302
|
-
if (root?.directory === observation.directory) {
|
|
511
|
+
if (root?.directory === observation.directory && root.suspicion.holds(observation.suspicion)) {
|
|
303
512
|
root.suspicion.settle(observation.suspicion);
|
|
304
|
-
|
|
513
|
+
this.observeObligation(root);
|
|
305
514
|
}
|
|
306
515
|
}
|
|
516
|
+
if (domain)
|
|
517
|
+
this.resolvedDomainGeneration = Math.max(this.resolvedDomainGeneration, domain.generation);
|
|
307
518
|
}
|
|
308
|
-
/** True only when
|
|
309
|
-
*
|
|
519
|
+
/** True only when everything each cut scoped at or before its generation
|
|
520
|
+
* has been proved, by any proof. Newer rings keep their own pass. A root
|
|
521
|
+
* no longer bound owes this caller nothing. */
|
|
310
522
|
settled(observations) {
|
|
311
523
|
return observations.every((observation) => {
|
|
312
524
|
const root = this.plan.roots.get(observation.rootId);
|
|
313
|
-
|
|
314
|
-
|
|
525
|
+
if (!root?.suspicion.holds(observation.suspicion))
|
|
526
|
+
return true;
|
|
527
|
+
return (!observation.domain || (observation.domain.owner === this.domainOwner
|
|
528
|
+
&& observation.domain.bindings === this.bindingGeneration
|
|
529
|
+
&& this.resolvedDomainGeneration >= observation.domain.generation))
|
|
530
|
+
&& root.suspicion.settled(observation.suspicion);
|
|
315
531
|
});
|
|
316
532
|
}
|
|
533
|
+
/** A capture may depend on another root's unfinished observation. Return
|
|
534
|
+
* scopes only; Apply ownership and recovery decisions remain with the host. */
|
|
535
|
+
dependencies(observations) {
|
|
536
|
+
const directories = new Set(observations.flatMap(observation => observation.domain?.participants.map(participant => participant.directory) ?? [observation.directory]));
|
|
537
|
+
if (this.domainGeneration > this.resolvedDomainGeneration) {
|
|
538
|
+
for (const root of this.plan.roots.values())
|
|
539
|
+
directories.add(root.directory);
|
|
540
|
+
}
|
|
541
|
+
return [...directories];
|
|
542
|
+
}
|
|
543
|
+
/** Coverage is continuous while healthy. A catch-up close is a final cutoff,
|
|
544
|
+
* not a gap: it suspects every root before retiring its handles, and every
|
|
545
|
+
* later change belongs to the next startup's blind interval. */
|
|
546
|
+
continuous() {
|
|
547
|
+
return !this.coverageGap && (this.currentHealth.state === "healthy" || this.finalCutoff);
|
|
548
|
+
}
|
|
549
|
+
bindingsCurrent(observations) {
|
|
550
|
+
return observations.every(observation => !observation.domain
|
|
551
|
+
|| (this.continuous()
|
|
552
|
+
&& observation.domain.owner === this.domainOwner
|
|
553
|
+
&& observation.domain.bindings === this.bindingGeneration));
|
|
554
|
+
}
|
|
317
555
|
suspectAll() {
|
|
318
556
|
if (this.closed)
|
|
319
557
|
return;
|
|
320
558
|
for (const root of this.plan.roots.values())
|
|
321
|
-
|
|
559
|
+
this.ring(root, null);
|
|
560
|
+
}
|
|
561
|
+
/** One ambiguous effect can reach another root through a historical alias.
|
|
562
|
+
* Keep one bounded obligation, not a history of native notifications. */
|
|
563
|
+
suspectDomain() {
|
|
564
|
+
if (this.closed)
|
|
565
|
+
return;
|
|
566
|
+
this.suspectAll();
|
|
567
|
+
this.domainGeneration += 1;
|
|
568
|
+
if (this.plan.roots.size === 0) {
|
|
569
|
+
this.resolvedDomainGeneration = this.domainGeneration;
|
|
570
|
+
}
|
|
571
|
+
this.signal();
|
|
322
572
|
}
|
|
323
573
|
/** A host effect that touched exact ground rings the same named suspicion a
|
|
324
574
|
* native callback would: each root containing the address suspects that
|
|
@@ -333,26 +583,27 @@ export class NodeWatchHost {
|
|
|
333
583
|
const local = localPath(root.directory, absolute);
|
|
334
584
|
if (local === null)
|
|
335
585
|
continue;
|
|
336
|
-
|
|
337
|
-
root.suspicion.suspectAll();
|
|
338
|
-
else
|
|
339
|
-
root.suspicion.ring(local);
|
|
586
|
+
this.ring(root, local === "" ? null : local);
|
|
340
587
|
named = true;
|
|
341
588
|
}
|
|
342
589
|
if (!named)
|
|
343
590
|
this.suspectRoots(this.plan.roots.keys());
|
|
344
591
|
}
|
|
345
592
|
get hasPending() {
|
|
346
|
-
return
|
|
593
|
+
return this.backlogHead < this.backlog.length
|
|
594
|
+
|| [...this.plan.roots.values()].some((root) => root.suspicion.owes);
|
|
347
595
|
}
|
|
348
596
|
close(options = {}) {
|
|
349
597
|
if (this.closeTask)
|
|
350
598
|
return this.closeTask;
|
|
351
599
|
if (options.catchUp !== false) {
|
|
352
600
|
for (const root of this.plan.roots.values())
|
|
353
|
-
|
|
601
|
+
this.ring(root, null);
|
|
602
|
+
this.finalCutoff = true;
|
|
354
603
|
}
|
|
355
604
|
this.closed = true;
|
|
605
|
+
this.backlog = [];
|
|
606
|
+
this.backlogHead = 0;
|
|
356
607
|
this.cancelRetry();
|
|
357
608
|
closeRecords(this.content);
|
|
358
609
|
closeRecords(this.addresses);
|
|
@@ -377,7 +628,7 @@ export class NodeWatchHost {
|
|
|
377
628
|
for (const directory of wantedContent) {
|
|
378
629
|
if (this.content.has(directory))
|
|
379
630
|
continue;
|
|
380
|
-
this.content.set(directory, this.openHandle("content", directory, true, (filename) => this.routeContent(directory, filename)));
|
|
631
|
+
this.content.set(directory, this.openHandle("content", directory, true, (filename, evidence) => this.routeContent(directory, filename, evidence)));
|
|
381
632
|
openedContent.push(directory);
|
|
382
633
|
}
|
|
383
634
|
for (const directory of wantedAddresses) {
|
|
@@ -390,9 +641,7 @@ export class NodeWatchHost {
|
|
|
390
641
|
catch (error) {
|
|
391
642
|
closeSelected(this.content, openedContent);
|
|
392
643
|
closeSelected(this.addresses, openedAddresses);
|
|
393
|
-
this.
|
|
394
|
-
const health = this.degrade(errorMessage(error));
|
|
395
|
-
this.signal();
|
|
644
|
+
const health = this.loseCoverage(errorMessage(error));
|
|
396
645
|
this.scheduleRetry();
|
|
397
646
|
return health;
|
|
398
647
|
}
|
|
@@ -420,15 +669,38 @@ export class NodeWatchHost {
|
|
|
420
669
|
contentHandles: this.content.size,
|
|
421
670
|
addressHandles: this.addresses.size,
|
|
422
671
|
};
|
|
672
|
+
const recovered = this.coverageGap;
|
|
673
|
+
if (recovered) {
|
|
674
|
+
this.coverageGap = false;
|
|
675
|
+
// An examination acquired inside the blind interval cannot prove
|
|
676
|
+
// recovery. The still-pending cohort must be read under new coverage.
|
|
677
|
+
this.bindingGeneration += 1;
|
|
678
|
+
}
|
|
423
679
|
this.publishHealth(health);
|
|
424
|
-
if (introducedSuspicion)
|
|
680
|
+
if (introducedSuspicion || recovered)
|
|
425
681
|
this.signal();
|
|
426
682
|
return health;
|
|
427
683
|
}
|
|
428
684
|
openHandle(kind, directory, recursive, route) {
|
|
685
|
+
const deliver = (event, filename, native) => {
|
|
686
|
+
if (this.closed)
|
|
687
|
+
return;
|
|
688
|
+
const at = Date.now();
|
|
689
|
+
this.admit(() => {
|
|
690
|
+
// Evidence is built within the admitted slice, dated at arrival.
|
|
691
|
+
this.onEvent?.({ kind, directory, path: normalizedHint(filename), event, ...(native ? { native } : {}), at });
|
|
692
|
+
// Preserve scope before ignored/control paths, repository routing, or
|
|
693
|
+
// current path lookup can discard the reason for wider examination.
|
|
694
|
+
if (native?.scope === "domain")
|
|
695
|
+
this.suspectDomain();
|
|
696
|
+
else
|
|
697
|
+
route(filename, native?.alias
|
|
698
|
+
? { alias: true, ...(native.file === null ? {} : { object: native.file }) } : undefined);
|
|
699
|
+
});
|
|
700
|
+
};
|
|
429
701
|
const handle = recursive && this.platform === "linux"
|
|
430
|
-
? new LinuxDirectoryTreeWatch(directory, this.open, (
|
|
431
|
-
: this.open(directory, { recursive }, (
|
|
702
|
+
? new LinuxDirectoryTreeWatch(directory, this.open, (event, filename, evidence) => deliver(event, filename, evidence), this.plan.exclusions.filter((excluded) => within(directory, excluded)))
|
|
703
|
+
: this.open(directory, { recursive }, (event, filename, evidence) => deliver(event, filename, evidence));
|
|
432
704
|
const record = {
|
|
433
705
|
handle,
|
|
434
706
|
identity: directoryIdentity(directory),
|
|
@@ -452,15 +724,12 @@ export class NodeWatchHost {
|
|
|
452
724
|
catch {
|
|
453
725
|
// The handle is already unusable; coverage loss is recorded below.
|
|
454
726
|
}
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
this.suspectRoots(roots ?? []);
|
|
459
|
-
this.degrade(`${kind} watcher ${directory}: ${reason}`);
|
|
460
|
-
this.signal();
|
|
727
|
+
// The lost interval could contain an edit through an alias in another
|
|
728
|
+
// root. Reopening this handle repairs coverage, not that attribution.
|
|
729
|
+
this.loseCoverage(`${kind} watcher ${directory}: ${reason}`);
|
|
461
730
|
this.scheduleRetry();
|
|
462
731
|
}
|
|
463
|
-
routeContent(directory, filename) {
|
|
732
|
+
routeContent(directory, filename, evidence) {
|
|
464
733
|
if (this.closed)
|
|
465
734
|
return;
|
|
466
735
|
const members = this.plan.membersByContent.get(directory);
|
|
@@ -470,9 +739,8 @@ export class NodeWatchHost {
|
|
|
470
739
|
return;
|
|
471
740
|
}
|
|
472
741
|
const hint = normalizedHint(filename);
|
|
473
|
-
this.onEvent?.({ kind: "content", directory, path: hint, at: Date.now() });
|
|
474
742
|
if (hint !== null) {
|
|
475
|
-
const absolute =
|
|
743
|
+
const absolute = below(directory, hint);
|
|
476
744
|
if (this.plan.exclusions.some((excluded) => within(excluded, absolute)))
|
|
477
745
|
return;
|
|
478
746
|
let signaled = false;
|
|
@@ -482,11 +750,11 @@ export class NodeWatchHost {
|
|
|
482
750
|
continue;
|
|
483
751
|
const local = localPath(root.directory, absolute);
|
|
484
752
|
if (local !== null && local !== "") {
|
|
485
|
-
|
|
753
|
+
this.ring(root, local, evidence);
|
|
486
754
|
signaled = true;
|
|
487
755
|
}
|
|
488
756
|
else if (local === "" || within(absolute, root.directory)) {
|
|
489
|
-
|
|
757
|
+
this.ring(root, null);
|
|
490
758
|
signaled = true;
|
|
491
759
|
}
|
|
492
760
|
}
|
|
@@ -507,7 +775,6 @@ export class NodeWatchHost {
|
|
|
507
775
|
return;
|
|
508
776
|
}
|
|
509
777
|
const hint = normalizedHint(filename);
|
|
510
|
-
this.onEvent?.({ kind: "address", directory, path: hint, at: Date.now() });
|
|
511
778
|
if (hint === null) {
|
|
512
779
|
this.suspectRoots(targets);
|
|
513
780
|
this.rearmAddressedContent(targets);
|
|
@@ -553,13 +820,46 @@ export class NodeWatchHost {
|
|
|
553
820
|
closeRecord(record);
|
|
554
821
|
this.content.delete(directory);
|
|
555
822
|
}
|
|
556
|
-
if (addressed)
|
|
823
|
+
if (addressed) {
|
|
824
|
+
this.loseCoverage("content directory identity changed");
|
|
557
825
|
this.establish(this.plan);
|
|
826
|
+
}
|
|
558
827
|
return suspects;
|
|
559
828
|
}
|
|
560
829
|
suspectRoots(rootIds) {
|
|
561
|
-
for (const rootId of rootIds)
|
|
562
|
-
this.plan.roots.get(rootId)
|
|
830
|
+
for (const rootId of rootIds) {
|
|
831
|
+
const root = this.plan.roots.get(rootId);
|
|
832
|
+
if (root)
|
|
833
|
+
this.ring(root, null);
|
|
834
|
+
}
|
|
835
|
+
}
|
|
836
|
+
/** Every doorbell enters here; null suspects the complete root. A ring can
|
|
837
|
+
* only start an obligation, so examiners hear it only when none was owed. */
|
|
838
|
+
ring(root, local, evidence) {
|
|
839
|
+
if (local === null)
|
|
840
|
+
root.suspicion.suspectAll();
|
|
841
|
+
else
|
|
842
|
+
root.suspicion.ring(local, evidence);
|
|
843
|
+
if (root.oldest === null)
|
|
844
|
+
this.observeObligation(root);
|
|
845
|
+
}
|
|
846
|
+
observeObligation(root, bound = true) {
|
|
847
|
+
if (!obligations.hasSubscribers)
|
|
848
|
+
return;
|
|
849
|
+
const oldest = bound ? root.suspicion.pending().oldest : null;
|
|
850
|
+
if (oldest === root.oldest)
|
|
851
|
+
return;
|
|
852
|
+
root.oldest = oldest;
|
|
853
|
+
obligations.publish({ rootId: root.rootId, oldest, at: performance.now() });
|
|
854
|
+
}
|
|
855
|
+
loseCoverage(reason) {
|
|
856
|
+
const health = this.degrade(reason);
|
|
857
|
+
if (!this.coverageGap) {
|
|
858
|
+
this.coverageGap = true;
|
|
859
|
+
this.bindingGeneration += 1;
|
|
860
|
+
this.suspectDomain();
|
|
861
|
+
}
|
|
862
|
+
return health;
|
|
563
863
|
}
|
|
564
864
|
degrade(reason) {
|
|
565
865
|
const health = {
|
|
@@ -581,6 +881,47 @@ export class NodeWatchHost {
|
|
|
581
881
|
signal() {
|
|
582
882
|
this.onSignal();
|
|
583
883
|
}
|
|
884
|
+
/** Admit a notice now while this turn's slice lasts and nothing earlier
|
|
885
|
+
* waits; otherwise after every earlier notice, in a later turn. */
|
|
886
|
+
admit(notice) {
|
|
887
|
+
this.nextTurn();
|
|
888
|
+
if (this.backlogHead === this.backlog.length && this.sliceLeft > 0) {
|
|
889
|
+
this.admitNow(notice);
|
|
890
|
+
return;
|
|
891
|
+
}
|
|
892
|
+
this.backlog.push(notice);
|
|
893
|
+
}
|
|
894
|
+
admitNow(notice) {
|
|
895
|
+
if (this.sliceEnds === Infinity)
|
|
896
|
+
this.sliceEnds = performance.now() + WATCH_ADMISSION_BUDGET_MS;
|
|
897
|
+
this.sliceLeft -= 1;
|
|
898
|
+
notice();
|
|
899
|
+
if (performance.now() >= this.sliceEnds)
|
|
900
|
+
this.sliceLeft = 0;
|
|
901
|
+
}
|
|
902
|
+
/** Each turn renews the slice and admits the oldest waiting notices. */
|
|
903
|
+
nextTurn() {
|
|
904
|
+
if (this.turnScheduled || this.closed)
|
|
905
|
+
return;
|
|
906
|
+
this.turnScheduled = true;
|
|
907
|
+
setImmediate(() => {
|
|
908
|
+
this.turnScheduled = false;
|
|
909
|
+
this.sliceLeft = WATCH_ADMISSION_SLICE;
|
|
910
|
+
this.sliceEnds = Infinity;
|
|
911
|
+
while (this.sliceLeft > 0 && this.backlogHead < this.backlog.length && !this.closed) {
|
|
912
|
+
const notice = this.backlog[this.backlogHead];
|
|
913
|
+
this.backlogHead += 1;
|
|
914
|
+
this.admitNow(notice);
|
|
915
|
+
}
|
|
916
|
+
if (this.backlogHead < this.backlog.length) {
|
|
917
|
+
this.nextTurn();
|
|
918
|
+
}
|
|
919
|
+
else {
|
|
920
|
+
this.backlog = [];
|
|
921
|
+
this.backlogHead = 0;
|
|
922
|
+
}
|
|
923
|
+
});
|
|
924
|
+
}
|
|
584
925
|
scheduleRetry() {
|
|
585
926
|
if (this.closed || this.retryTimer)
|
|
586
927
|
return;
|
|
@@ -614,7 +955,7 @@ function buildPlan(grounds, previous) {
|
|
|
614
955
|
paths.add(directory);
|
|
615
956
|
const excludedSubtrees = [...new Set((ground.excludedSubtrees ?? []).map((path) => resolve(path)))].sort();
|
|
616
957
|
const prior = previous.get(rootId);
|
|
617
|
-
const suspicion = prior?.suspicion ?? createSuspicionScope();
|
|
958
|
+
const suspicion = prior?.suspicion ?? createSuspicionScope(() => performance.now());
|
|
618
959
|
if (!prior || prior.directory !== directory)
|
|
619
960
|
suspicion.suspectAll();
|
|
620
961
|
roots.set(rootId, {
|
|
@@ -623,7 +964,7 @@ function buildPlan(grounds, previous) {
|
|
|
623
964
|
materialized: ground.materialized !== false,
|
|
624
965
|
excludedSubtrees,
|
|
625
966
|
suspicion,
|
|
626
|
-
|
|
967
|
+
oldest: prior?.oldest ?? null,
|
|
627
968
|
});
|
|
628
969
|
}
|
|
629
970
|
const exclusions = [...new Set([...roots.values()].flatMap((root) => root.excludedSubtrees))].sort();
|
|
@@ -735,13 +1076,25 @@ function normalizedHint(filename) {
|
|
|
735
1076
|
return null;
|
|
736
1077
|
return segments.join("/");
|
|
737
1078
|
}
|
|
1079
|
+
/** A canonical path's address below a canonical root: "" for the root itself,
|
|
1080
|
+
* null outside it. Plan paths are resolved once when the plan is built and
|
|
1081
|
+
* event paths are built from normalized hints, so routing an event costs one
|
|
1082
|
+
* prefix comparison, never a normalization. A path that is not canonical can
|
|
1083
|
+
* only fail to match, which widens its ring. Windows compares names without
|
|
1084
|
+
* case, as its path relation does. */
|
|
738
1085
|
function localPath(root, path) {
|
|
739
|
-
const
|
|
740
|
-
|
|
1086
|
+
const folded = sep === "\\";
|
|
1087
|
+
const base = folded ? root.toLowerCase() : root;
|
|
1088
|
+
const candidate = folded ? path.toLowerCase() : path;
|
|
1089
|
+
if (candidate === base)
|
|
741
1090
|
return "";
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
1091
|
+
const prefix = base.endsWith(sep) ? base : base + sep;
|
|
1092
|
+
return candidate.startsWith(prefix) ? slash(path.slice(prefix.length)) : null;
|
|
1093
|
+
}
|
|
1094
|
+
/** One canonical address from a canonical directory and a normalized hint. */
|
|
1095
|
+
function below(directory, hint) {
|
|
1096
|
+
const native = sep === "/" ? hint : hint.replaceAll("/", sep);
|
|
1097
|
+
return directory.endsWith(sep) ? directory + native : directory + sep + native;
|
|
745
1098
|
}
|
|
746
1099
|
function within(parent, child) {
|
|
747
1100
|
return localPath(parent, child) !== null;
|