@superbee/core 0.2.0-pre.3 → 0.2.0-pre.30
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/README.md +1082 -0
- package/dist/artifact-contract.d.ts +90 -0
- package/dist/artifact-contract.d.ts.map +1 -0
- package/dist/artifact-contract.js +205 -0
- package/dist/artifact-contract.js.map +1 -0
- package/dist/backend.d.ts +19 -1
- package/dist/backend.d.ts.map +1 -1
- package/dist/backend.js +83 -36
- package/dist/backend.js.map +1 -1
- package/dist/blob-contract.d.ts +80 -0
- package/dist/blob-contract.d.ts.map +1 -0
- package/dist/blob-contract.js +131 -0
- package/dist/blob-contract.js.map +1 -0
- package/dist/blob-journal.d.ts +302 -0
- package/dist/blob-journal.d.ts.map +1 -0
- package/dist/blob-journal.js +540 -0
- package/dist/blob-journal.js.map +1 -0
- package/dist/body-replace-guards.d.ts +52 -0
- package/dist/body-replace-guards.d.ts.map +1 -0
- package/dist/body-replace-guards.js +176 -0
- package/dist/body-replace-guards.js.map +1 -0
- package/dist/builtin-recipes/content.d.ts +93 -0
- package/dist/builtin-recipes/content.d.ts.map +1 -0
- package/dist/builtin-recipes/content.js +288 -0
- package/dist/builtin-recipes/content.js.map +1 -0
- package/dist/builtin-recipes.d.ts +10 -0
- package/dist/builtin-recipes.d.ts.map +1 -0
- package/dist/builtin-recipes.js +97 -0
- package/dist/builtin-recipes.js.map +1 -0
- package/dist/bundle-health/index.d.ts +14 -0
- package/dist/bundle-health/index.d.ts.map +1 -0
- package/dist/bundle-health/index.js +12 -0
- package/dist/bundle-health/index.js.map +1 -0
- package/dist/bundle-health/kind-findings.d.ts +44 -0
- package/dist/bundle-health/kind-findings.d.ts.map +1 -0
- package/dist/bundle-health/kind-findings.js +68 -0
- package/dist/bundle-health/kind-findings.js.map +1 -0
- package/dist/bundle-health/legacy-page.d.ts +39 -0
- package/dist/bundle-health/legacy-page.d.ts.map +1 -0
- package/dist/bundle-health/legacy-page.js +50 -0
- package/dist/bundle-health/legacy-page.js.map +1 -0
- package/dist/bundle-health/report.d.ts +202 -0
- package/dist/bundle-health/report.d.ts.map +1 -0
- package/dist/bundle-health/report.js +438 -0
- package/dist/bundle-health/report.js.map +1 -0
- package/dist/bundle-ops.d.ts +1 -1
- package/dist/bundle-ops.d.ts.map +1 -1
- package/dist/bundle-ops.js +2 -2
- package/dist/bundle-ops.js.map +1 -1
- package/dist/bundle.d.ts +2 -1
- package/dist/bundle.d.ts.map +1 -1
- package/dist/bundle.js +3 -3
- package/dist/bundle.js.map +1 -1
- package/dist/code-point-order.d.ts +8 -0
- package/dist/code-point-order.d.ts.map +1 -0
- package/dist/code-point-order.js +20 -0
- package/dist/code-point-order.js.map +1 -0
- package/dist/connector-request.d.ts +87 -0
- package/dist/connector-request.d.ts.map +1 -0
- package/dist/connector-request.js +370 -0
- package/dist/connector-request.js.map +1 -0
- package/dist/connectors.d.ts +262 -0
- package/dist/connectors.d.ts.map +1 -0
- package/dist/connectors.js +1179 -0
- package/dist/connectors.js.map +1 -0
- package/dist/cross-bundle.d.ts +135 -0
- package/dist/cross-bundle.d.ts.map +1 -0
- package/dist/cross-bundle.js +186 -0
- package/dist/cross-bundle.js.map +1 -0
- package/dist/dependencies.d.ts +94 -0
- package/dist/dependencies.d.ts.map +1 -0
- package/dist/dependencies.js +180 -0
- package/dist/dependencies.js.map +1 -0
- package/dist/document-codec.d.ts +5 -1
- package/dist/document-codec.d.ts.map +1 -1
- package/dist/document-codec.js +7 -4
- package/dist/document-codec.js.map +1 -1
- package/dist/document-field-actions.d.ts +31 -1
- package/dist/document-field-actions.d.ts.map +1 -1
- package/dist/document-field-actions.js +137 -34
- package/dist/document-field-actions.js.map +1 -1
- package/dist/document-mutation.d.ts +22 -10
- package/dist/document-mutation.d.ts.map +1 -1
- package/dist/document-mutation.js +61 -27
- package/dist/document-mutation.js.map +1 -1
- package/dist/document-write-policy.d.ts.map +1 -1
- package/dist/document-write-policy.js +4 -2
- package/dist/document-write-policy.js.map +1 -1
- package/dist/domain-model.d.ts +73 -0
- package/dist/domain-model.d.ts.map +1 -0
- package/dist/domain-model.js +169 -0
- package/dist/domain-model.js.map +1 -0
- package/dist/engine.d.ts +16 -8
- package/dist/engine.d.ts.map +1 -1
- package/dist/engine.js +42 -4
- package/dist/engine.js.map +1 -1
- package/dist/file-journaled-backend-internal.d.ts +21 -0
- package/dist/file-journaled-backend-internal.d.ts.map +1 -0
- package/dist/file-journaled-backend-internal.js +12 -0
- package/dist/file-journaled-backend-internal.js.map +1 -0
- package/dist/file-journaled-backend.d.ts +160 -0
- package/dist/file-journaled-backend.d.ts.map +1 -0
- package/dist/file-journaled-backend.js +1345 -0
- package/dist/file-journaled-backend.js.map +1 -0
- package/dist/filesystem-host.d.ts +15 -0
- package/dist/filesystem-host.d.ts.map +1 -0
- package/dist/filesystem-host.js +20 -0
- package/dist/filesystem-host.js.map +1 -0
- package/dist/filesystem-identity.d.ts +86 -4
- package/dist/filesystem-identity.d.ts.map +1 -1
- package/dist/filesystem-identity.js +338 -102
- package/dist/filesystem-identity.js.map +1 -1
- package/dist/filesystem-lock.d.ts +16 -3
- package/dist/filesystem-lock.d.ts.map +1 -1
- package/dist/filesystem-lock.js +323 -77
- package/dist/filesystem-lock.js.map +1 -1
- package/dist/filesystem-push-role.d.ts +77 -0
- package/dist/filesystem-push-role.d.ts.map +1 -0
- package/dist/filesystem-push-role.js +165 -0
- package/dist/filesystem-push-role.js.map +1 -0
- package/dist/filesystem.d.ts +17 -0
- package/dist/filesystem.d.ts.map +1 -0
- package/dist/filesystem.js +18 -0
- package/dist/filesystem.js.map +1 -0
- package/dist/governed-body-write.d.ts +106 -0
- package/dist/governed-body-write.d.ts.map +1 -0
- package/dist/governed-body-write.js +251 -0
- package/dist/governed-body-write.js.map +1 -0
- package/dist/host-policy-snapshot.d.ts +9 -0
- package/dist/host-policy-snapshot.d.ts.map +1 -0
- package/dist/host-policy-snapshot.js +33 -0
- package/dist/host-policy-snapshot.js.map +1 -0
- package/dist/host-text.d.ts +31 -0
- package/dist/host-text.d.ts.map +1 -0
- package/dist/host-text.js +77 -0
- package/dist/host-text.js.map +1 -0
- package/dist/hosted-client/binding.d.ts +7 -0
- package/dist/hosted-client/binding.d.ts.map +1 -0
- package/dist/hosted-client/binding.js +10 -0
- package/dist/hosted-client/binding.js.map +1 -0
- package/dist/hosted-client/blob-delivery.d.ts +67 -0
- package/dist/hosted-client/blob-delivery.d.ts.map +1 -0
- package/dist/hosted-client/blob-delivery.js +169 -0
- package/dist/hosted-client/blob-delivery.js.map +1 -0
- package/dist/hosted-client/blob-write.d.ts +386 -0
- package/dist/hosted-client/blob-write.d.ts.map +1 -0
- package/dist/hosted-client/blob-write.js +770 -0
- package/dist/hosted-client/blob-write.js.map +1 -0
- package/dist/hosted-client/bundle-listing.d.ts +46 -0
- package/dist/hosted-client/bundle-listing.d.ts.map +1 -0
- package/dist/hosted-client/bundle-listing.js +36 -0
- package/dist/hosted-client/bundle-listing.js.map +1 -0
- package/dist/hosted-client/client.d.ts +219 -0
- package/dist/hosted-client/client.d.ts.map +1 -0
- package/dist/hosted-client/client.js +309 -0
- package/dist/hosted-client/client.js.map +1 -0
- package/dist/hosted-client/directory.d.ts +46 -0
- package/dist/hosted-client/directory.d.ts.map +1 -0
- package/dist/hosted-client/directory.js +74 -0
- package/dist/hosted-client/directory.js.map +1 -0
- package/dist/hosted-client/errors.d.ts +26 -0
- package/dist/hosted-client/errors.d.ts.map +1 -0
- package/dist/hosted-client/errors.js +35 -0
- package/dist/hosted-client/errors.js.map +1 -0
- package/dist/hosted-client/inbox.d.ts +320 -0
- package/dist/hosted-client/inbox.d.ts.map +1 -0
- package/dist/hosted-client/inbox.js +379 -0
- package/dist/hosted-client/inbox.js.map +1 -0
- package/dist/hosted-client/index.d.ts +30 -0
- package/dist/hosted-client/index.d.ts.map +1 -0
- package/dist/hosted-client/index.js +32 -0
- package/dist/hosted-client/index.js.map +1 -0
- package/dist/hosted-client/journal.d.ts +103 -0
- package/dist/hosted-client/journal.d.ts.map +1 -0
- package/dist/hosted-client/journal.js +464 -0
- package/dist/hosted-client/journal.js.map +1 -0
- package/dist/hosted-client/node.d.ts +74 -0
- package/dist/hosted-client/node.d.ts.map +1 -0
- package/dist/hosted-client/node.js +219 -0
- package/dist/hosted-client/node.js.map +1 -0
- package/dist/hosted-client/reference.d.ts +12 -0
- package/dist/hosted-client/reference.d.ts.map +1 -0
- package/dist/hosted-client/reference.js +29 -0
- package/dist/hosted-client/reference.js.map +1 -0
- package/dist/hosted-client/write.d.ts +217 -0
- package/dist/hosted-client/write.d.ts.map +1 -0
- package/dist/hosted-client/write.js +418 -0
- package/dist/hosted-client/write.js.map +1 -0
- package/dist/hosted-transport/answer-rows.d.ts +243 -0
- package/dist/hosted-transport/answer-rows.d.ts.map +1 -0
- package/dist/hosted-transport/answer-rows.js +454 -0
- package/dist/hosted-transport/answer-rows.js.map +1 -0
- package/dist/hosted-transport/blob-reads.d.ts +35 -0
- package/dist/hosted-transport/blob-reads.d.ts.map +1 -0
- package/dist/hosted-transport/blob-reads.js +179 -0
- package/dist/hosted-transport/blob-reads.js.map +1 -0
- package/dist/hosted-transport/blob-write.d.ts +34 -0
- package/dist/hosted-transport/blob-write.d.ts.map +1 -0
- package/dist/hosted-transport/blob-write.js +115 -0
- package/dist/hosted-transport/blob-write.js.map +1 -0
- package/dist/hosted-transport/carrier.d.ts +151 -0
- package/dist/hosted-transport/carrier.d.ts.map +1 -0
- package/dist/hosted-transport/carrier.js +260 -0
- package/dist/hosted-transport/carrier.js.map +1 -0
- package/dist/hosted-transport/history-pages.d.ts +55 -0
- package/dist/hosted-transport/history-pages.d.ts.map +1 -0
- package/dist/hosted-transport/history-pages.js +69 -0
- package/dist/hosted-transport/history-pages.js.map +1 -0
- package/dist/hosted-transport/index.d.ts +18 -0
- package/dist/hosted-transport/index.d.ts.map +1 -0
- package/dist/hosted-transport/index.js +18 -0
- package/dist/hosted-transport/index.js.map +1 -0
- package/dist/hosted-transport/operations.d.ts +85 -0
- package/dist/hosted-transport/operations.d.ts.map +1 -0
- package/dist/hosted-transport/operations.js +128 -0
- package/dist/hosted-transport/operations.js.map +1 -0
- package/dist/hosted-transport/paged-reads.d.ts +87 -0
- package/dist/hosted-transport/paged-reads.d.ts.map +1 -0
- package/dist/hosted-transport/paged-reads.js +238 -0
- package/dist/hosted-transport/paged-reads.js.map +1 -0
- package/dist/hosted-transport/read-adapter.d.ts +256 -0
- package/dist/hosted-transport/read-adapter.d.ts.map +1 -0
- package/dist/hosted-transport/read-adapter.js +568 -0
- package/dist/hosted-transport/read-adapter.js.map +1 -0
- package/dist/hosted-transport/root-write.d.ts +114 -0
- package/dist/hosted-transport/root-write.d.ts.map +1 -0
- package/dist/hosted-transport/root-write.js +171 -0
- package/dist/hosted-transport/root-write.js.map +1 -0
- package/dist/hosted-transport/whole-document-transport.d.ts +204 -0
- package/dist/hosted-transport/whole-document-transport.d.ts.map +1 -0
- package/dist/hosted-transport/whole-document-transport.js +447 -0
- package/dist/hosted-transport/whole-document-transport.js.map +1 -0
- package/dist/index.d.ts +34 -10
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +26 -8
- package/dist/index.js.map +1 -1
- package/dist/indexeddb-backend.d.ts +28 -37
- package/dist/indexeddb-backend.d.ts.map +1 -1
- package/dist/indexeddb-backend.js +467 -113
- package/dist/indexeddb-backend.js.map +1 -1
- package/dist/journaled-backend.d.ts +232 -15
- package/dist/journaled-backend.d.ts.map +1 -1
- package/dist/journaled-backend.js +254 -55
- package/dist/journaled-backend.js.map +1 -1
- package/dist/kind-authoring.d.ts +170 -0
- package/dist/kind-authoring.d.ts.map +1 -0
- package/dist/kind-authoring.js +451 -0
- package/dist/kind-authoring.js.map +1 -0
- package/dist/kind-conformance-error.d.ts +15 -0
- package/dist/kind-conformance-error.d.ts.map +1 -0
- package/dist/kind-conformance-error.js +20 -0
- package/dist/kind-conformance-error.js.map +1 -0
- package/dist/kind-coverage.d.ts +99 -0
- package/dist/kind-coverage.d.ts.map +1 -0
- package/dist/kind-coverage.js +186 -0
- package/dist/kind-coverage.js.map +1 -0
- package/dist/kind-field-mutation.d.ts +4 -0
- package/dist/kind-field-mutation.d.ts.map +1 -1
- package/dist/kind-field-mutation.js +28 -3
- package/dist/kind-field-mutation.js.map +1 -1
- package/dist/kinds-load.js +4 -4
- package/dist/kinds-load.js.map +1 -1
- package/dist/kinds.d.ts +65 -2
- package/dist/kinds.d.ts.map +1 -1
- package/dist/kinds.js +341 -21
- package/dist/kinds.js.map +1 -1
- package/dist/link-types.d.ts +26 -0
- package/dist/link-types.d.ts.map +1 -0
- package/dist/link-types.js +57 -0
- package/dist/link-types.js.map +1 -0
- package/dist/links.d.ts +21 -3
- package/dist/links.d.ts.map +1 -1
- package/dist/links.js +213 -14
- package/dist/links.js.map +1 -1
- package/dist/managed-document-metadata.d.ts +2 -0
- package/dist/managed-document-metadata.d.ts.map +1 -0
- package/dist/managed-document-metadata.js +9 -0
- package/dist/managed-document-metadata.js.map +1 -0
- package/dist/memory-backend.d.ts.map +1 -1
- package/dist/memory-backend.js +6 -2
- package/dist/memory-backend.js.map +1 -1
- package/dist/paths.d.ts +16 -0
- package/dist/paths.d.ts.map +1 -1
- package/dist/paths.js +25 -0
- package/dist/paths.js.map +1 -1
- package/dist/platform.d.ts +13 -2
- package/dist/platform.d.ts.map +1 -1
- package/dist/platform.js +16 -0
- package/dist/platform.js.map +1 -1
- package/dist/portable-export.d.ts +119 -0
- package/dist/portable-export.d.ts.map +1 -0
- package/dist/portable-export.js +501 -0
- package/dist/portable-export.js.map +1 -0
- package/dist/publication-filesystem.d.ts +1 -1
- package/dist/publication-filesystem.d.ts.map +1 -1
- package/dist/publication-filesystem.js +1 -1
- package/dist/publication-filesystem.js.map +1 -1
- package/dist/qualified-links.d.ts +67 -0
- package/dist/qualified-links.d.ts.map +1 -0
- package/dist/qualified-links.js +117 -0
- package/dist/qualified-links.js.map +1 -0
- package/dist/query-filter.d.ts +16 -2
- package/dist/query-filter.d.ts.map +1 -1
- package/dist/query-filter.js +40 -1
- package/dist/query-filter.js.map +1 -1
- package/dist/query-order.d.ts +22 -0
- package/dist/query-order.d.ts.map +1 -0
- package/dist/query-order.js +42 -0
- package/dist/query-order.js.map +1 -0
- package/dist/recipe-lifecycle/errors.d.ts +96 -0
- package/dist/recipe-lifecycle/errors.d.ts.map +1 -0
- package/dist/recipe-lifecycle/errors.js +122 -0
- package/dist/recipe-lifecycle/errors.js.map +1 -0
- package/dist/recipe-lifecycle/evolution.d.ts +112 -0
- package/dist/recipe-lifecycle/evolution.d.ts.map +1 -0
- package/dist/recipe-lifecycle/evolution.js +941 -0
- package/dist/recipe-lifecycle/evolution.js.map +1 -0
- package/dist/recipe-lifecycle/index.d.ts +30 -0
- package/dist/recipe-lifecycle/index.d.ts.map +1 -0
- package/dist/recipe-lifecycle/index.js +26 -0
- package/dist/recipe-lifecycle/index.js.map +1 -0
- package/dist/recipe-lifecycle/install.d.ts +155 -0
- package/dist/recipe-lifecycle/install.d.ts.map +1 -0
- package/dist/recipe-lifecycle/install.js +594 -0
- package/dist/recipe-lifecycle/install.js.map +1 -0
- package/dist/recipe-lifecycle/legacy-aliases.d.ts +16 -0
- package/dist/recipe-lifecycle/legacy-aliases.d.ts.map +1 -0
- package/dist/recipe-lifecycle/legacy-aliases.js +51 -0
- package/dist/recipe-lifecycle/legacy-aliases.js.map +1 -0
- package/dist/recipe-lifecycle/stamp.d.ts +151 -0
- package/dist/recipe-lifecycle/stamp.d.ts.map +1 -0
- package/dist/recipe-lifecycle/stamp.js +364 -0
- package/dist/recipe-lifecycle/stamp.js.map +1 -0
- package/dist/recipes.d.ts +29 -1
- package/dist/recipes.d.ts.map +1 -1
- package/dist/recipes.js +137 -1
- package/dist/recipes.js.map +1 -1
- package/dist/record-overlay/apply.d.ts +43 -0
- package/dist/record-overlay/apply.d.ts.map +1 -0
- package/dist/record-overlay/apply.js +527 -0
- package/dist/record-overlay/apply.js.map +1 -0
- package/dist/record-overlay/canonical.d.ts +22 -0
- package/dist/record-overlay/canonical.d.ts.map +1 -0
- package/dist/record-overlay/canonical.js +47 -0
- package/dist/record-overlay/canonical.js.map +1 -0
- package/dist/record-overlay/digest.d.ts +4 -0
- package/dist/record-overlay/digest.d.ts.map +1 -0
- package/dist/record-overlay/digest.js +11 -0
- package/dist/record-overlay/digest.js.map +1 -0
- package/dist/record-overlay/errors.d.ts +44 -0
- package/dist/record-overlay/errors.d.ts.map +1 -0
- package/dist/record-overlay/errors.js +55 -0
- package/dist/record-overlay/errors.js.map +1 -0
- package/dist/record-overlay/findings.d.ts +5 -0
- package/dist/record-overlay/findings.d.ts.map +1 -0
- package/dist/record-overlay/findings.js +83 -0
- package/dist/record-overlay/findings.js.map +1 -0
- package/dist/record-overlay/index.d.ts +23 -0
- package/dist/record-overlay/index.d.ts.map +1 -0
- package/dist/record-overlay/index.js +21 -0
- package/dist/record-overlay/index.js.map +1 -0
- package/dist/record-overlay/plan.d.ts +12 -0
- package/dist/record-overlay/plan.d.ts.map +1 -0
- package/dist/record-overlay/plan.js +285 -0
- package/dist/record-overlay/plan.js.map +1 -0
- package/dist/record-overlay/policy.d.ts +69 -0
- package/dist/record-overlay/policy.d.ts.map +1 -0
- package/dist/record-overlay/policy.js +228 -0
- package/dist/record-overlay/policy.js.map +1 -0
- package/dist/record-overlay/redact.d.ts +3 -0
- package/dist/record-overlay/redact.d.ts.map +1 -0
- package/dist/record-overlay/redact.js +27 -0
- package/dist/record-overlay/redact.js.map +1 -0
- package/dist/record-overlay/signal-kind.d.ts +30 -0
- package/dist/record-overlay/signal-kind.d.ts.map +1 -0
- package/dist/record-overlay/signal-kind.js +38 -0
- package/dist/record-overlay/signal-kind.js.map +1 -0
- package/dist/record-overlay/signal-text.d.ts +45 -0
- package/dist/record-overlay/signal-text.d.ts.map +1 -0
- package/dist/record-overlay/signal-text.js +116 -0
- package/dist/record-overlay/signal-text.js.map +1 -0
- package/dist/record-overlay/source-index.d.ts +19 -0
- package/dist/record-overlay/source-index.d.ts.map +1 -0
- package/dist/record-overlay/source-index.js +102 -0
- package/dist/record-overlay/source-index.js.map +1 -0
- package/dist/record-overlay/types.d.ts +291 -0
- package/dist/record-overlay/types.d.ts.map +1 -0
- package/dist/record-overlay/types.js +2 -0
- package/dist/record-overlay/types.js.map +1 -0
- package/dist/remote-backend.d.ts +67 -67
- package/dist/remote-backend.d.ts.map +1 -1
- package/dist/remote-backend.js +168 -201
- package/dist/remote-backend.js.map +1 -1
- package/dist/remote-document-codec.d.ts +2 -1
- package/dist/remote-document-codec.d.ts.map +1 -1
- package/dist/remote-document-codec.js +3 -2
- package/dist/remote-document-codec.js.map +1 -1
- package/dist/remote-error.d.ts +38 -0
- package/dist/remote-error.d.ts.map +1 -0
- package/dist/remote-error.js +59 -0
- package/dist/remote-error.js.map +1 -0
- package/dist/remote-operations.d.ts +0 -40
- package/dist/remote-operations.d.ts.map +1 -1
- package/dist/remote-operations.js +26 -6
- package/dist/remote-operations.js.map +1 -1
- package/dist/remote-parsers.d.ts +86 -0
- package/dist/remote-parsers.d.ts.map +1 -0
- package/dist/remote-parsers.js +181 -0
- package/dist/remote-parsers.js.map +1 -0
- package/dist/remote.d.ts +5 -2
- package/dist/remote.d.ts.map +1 -1
- package/dist/remote.js +5 -1
- package/dist/remote.js.map +1 -1
- package/dist/search/backend-source.d.ts +6 -0
- package/dist/search/backend-source.d.ts.map +1 -0
- package/dist/search/backend-source.js +33 -0
- package/dist/search/backend-source.js.map +1 -0
- package/dist/search/contract.d.ts +151 -0
- package/dist/search/contract.d.ts.map +1 -0
- package/dist/search/contract.js +78 -0
- package/dist/search/contract.js.map +1 -0
- package/dist/search/errors.d.ts +30 -0
- package/dist/search/errors.d.ts.map +1 -0
- package/dist/search/errors.js +50 -0
- package/dist/search/errors.js.map +1 -0
- package/dist/search/identity.d.ts +12 -0
- package/dist/search/identity.d.ts.map +1 -0
- package/dist/search/identity.js +90 -0
- package/dist/search/identity.js.map +1 -0
- package/dist/search/index.d.ts +16 -0
- package/dist/search/index.d.ts.map +1 -0
- package/dist/search/index.js +15 -0
- package/dist/search/index.js.map +1 -0
- package/dist/search/memory-source.d.ts +36 -0
- package/dist/search/memory-source.d.ts.map +1 -0
- package/dist/search/memory-source.js +235 -0
- package/dist/search/memory-source.js.map +1 -0
- package/dist/search/rank.d.ts +34 -0
- package/dist/search/rank.d.ts.map +1 -0
- package/dist/search/rank.js +129 -0
- package/dist/search/rank.js.map +1 -0
- package/dist/search/row-text.d.ts +38 -0
- package/dist/search/row-text.d.ts.map +1 -0
- package/dist/search/row-text.js +88 -0
- package/dist/search/row-text.js.map +1 -0
- package/dist/search/search.d.ts +28 -0
- package/dist/search/search.d.ts.map +1 -0
- package/dist/search/search.js +257 -0
- package/dist/search/search.js.map +1 -0
- package/dist/search/source.d.ts +67 -0
- package/dist/search/source.d.ts.map +1 -0
- package/dist/search/source.js +2 -0
- package/dist/search/source.js.map +1 -0
- package/dist/search/text.d.ts +47 -0
- package/dist/search/text.d.ts.map +1 -0
- package/dist/search/text.js +150 -0
- package/dist/search/text.js.map +1 -0
- package/dist/site-capabilities.d.ts +345 -0
- package/dist/site-capabilities.d.ts.map +1 -0
- package/dist/site-capabilities.js +724 -0
- package/dist/site-capabilities.js.map +1 -0
- package/dist/source-engine/guard.d.ts +78 -0
- package/dist/source-engine/guard.d.ts.map +1 -0
- package/dist/source-engine/guard.js +423 -0
- package/dist/source-engine/guard.js.map +1 -0
- package/dist/source-engine/index.d.ts +26 -0
- package/dist/source-engine/index.d.ts.map +1 -0
- package/dist/source-engine/index.js +21 -0
- package/dist/source-engine/index.js.map +1 -0
- package/dist/source-engine/machine-state.d.ts +74 -0
- package/dist/source-engine/machine-state.d.ts.map +1 -0
- package/dist/source-engine/machine-state.js +48 -0
- package/dist/source-engine/machine-state.js.map +1 -0
- package/dist/source-engine/observe.d.ts +18 -0
- package/dist/source-engine/observe.d.ts.map +1 -0
- package/dist/source-engine/observe.js +620 -0
- package/dist/source-engine/observe.js.map +1 -0
- package/dist/source-engine/overlay.d.ts +49 -0
- package/dist/source-engine/overlay.d.ts.map +1 -0
- package/dist/source-engine/overlay.js +114 -0
- package/dist/source-engine/overlay.js.map +1 -0
- package/dist/source-engine/robots.d.ts +53 -0
- package/dist/source-engine/robots.d.ts.map +1 -0
- package/dist/source-engine/robots.js +158 -0
- package/dist/source-engine/robots.js.map +1 -0
- package/dist/source-engine/types.d.ts +287 -0
- package/dist/source-engine/types.d.ts.map +1 -0
- package/dist/source-engine/types.js +2 -0
- package/dist/source-engine/types.js.map +1 -0
- package/dist/source-harness/conformance.d.ts +16 -0
- package/dist/source-harness/conformance.d.ts.map +1 -0
- package/dist/source-harness/conformance.js +266 -0
- package/dist/source-harness/conformance.js.map +1 -0
- package/dist/source-harness/fixture.d.ts +95 -0
- package/dist/source-harness/fixture.d.ts.map +1 -0
- package/dist/source-harness/fixture.js +273 -0
- package/dist/source-harness/fixture.js.map +1 -0
- package/dist/source-harness/index.d.ts +22 -0
- package/dist/source-harness/index.d.ts.map +1 -0
- package/dist/source-harness/index.js +19 -0
- package/dist/source-harness/index.js.map +1 -0
- package/dist/source-harness/peer.d.ts +74 -0
- package/dist/source-harness/peer.d.ts.map +1 -0
- package/dist/source-harness/peer.js +106 -0
- package/dist/source-harness/peer.js.map +1 -0
- package/dist/source-profiles/index.d.ts +31 -0
- package/dist/source-profiles/index.d.ts.map +1 -0
- package/dist/source-profiles/index.js +206 -0
- package/dist/source-profiles/index.js.map +1 -0
- package/dist/source-profiles/link-header.d.ts +6 -0
- package/dist/source-profiles/link-header.d.ts.map +1 -0
- package/dist/source-profiles/link-header.js +131 -0
- package/dist/source-profiles/link-header.js.map +1 -0
- package/dist/storage.d.ts +6 -2
- package/dist/storage.d.ts.map +1 -1
- package/dist/storage.js +4 -1
- package/dist/storage.js.map +1 -1
- package/dist/types.d.ts +38 -5
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +0 -10
- package/dist/types.js.map +1 -1
- package/dist/uncertain-write.d.ts +161 -19
- package/dist/uncertain-write.d.ts.map +1 -1
- package/dist/uncertain-write.js +90 -31
- package/dist/uncertain-write.js.map +1 -1
- package/dist/versioning.d.ts +9 -4
- package/dist/versioning.d.ts.map +1 -1
- package/dist/versioning.js +9 -4
- package/dist/versioning.js.map +1 -1
- package/dist/write-identity.d.ts +5 -0
- package/dist/write-identity.d.ts.map +1 -0
- package/dist/write-identity.js +9 -0
- package/dist/write-identity.js.map +1 -0
- package/package.json +103 -2
package/dist/filesystem-lock.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { promises as fs, realpathSync } from "node:fs";
|
|
2
|
-
import { homedir, hostname
|
|
2
|
+
import { homedir, hostname } from "node:os";
|
|
3
|
+
import { captureFilesystemHostPolicy } from "./filesystem-host.js";
|
|
3
4
|
import path from "node:path";
|
|
4
5
|
import { createHash, randomUUID } from "node:crypto";
|
|
5
6
|
const OWNER_FILE = "owner.json";
|
|
@@ -19,19 +20,6 @@ export class FilesystemMutationLockError extends Error {
|
|
|
19
20
|
this.malformed = details.malformed;
|
|
20
21
|
}
|
|
21
22
|
}
|
|
22
|
-
function runtimeOwnerKey() {
|
|
23
|
-
const uid = process.getuid?.();
|
|
24
|
-
if (uid !== undefined)
|
|
25
|
-
return `uid-${uid}`;
|
|
26
|
-
let username = "unknown";
|
|
27
|
-
try {
|
|
28
|
-
username = userInfo().username;
|
|
29
|
-
}
|
|
30
|
-
catch {
|
|
31
|
-
// Windows temp directories are already user-scoped; this is only a stable path segment.
|
|
32
|
-
}
|
|
33
|
-
return `user-${createHash("sha256").update(username).digest("hex").slice(0, 16)}`;
|
|
34
|
-
}
|
|
35
23
|
function canonicalExistingPath(value) {
|
|
36
24
|
try {
|
|
37
25
|
return realpathSync(value);
|
|
@@ -45,17 +33,13 @@ function pathContains(root, candidate) {
|
|
|
45
33
|
return rel === "" || (!rel.startsWith("..") && !path.isAbsolute(rel));
|
|
46
34
|
}
|
|
47
35
|
/** Stable per-user runtime namespace outside `portableRoot`; refuses an impossible root bundle. */
|
|
48
|
-
export function filesystemMutationLockRoot(portableRoot) {
|
|
49
|
-
|
|
50
|
-
//
|
|
51
|
-
|
|
52
|
-
// of tmpdir(); otherwise two shells can derive different locks for the same portable target.
|
|
53
|
-
const runtimeParent = process.platform === "win32"
|
|
54
|
-
? path.join(homedir(), "AppData", "Local")
|
|
55
|
-
: "/tmp";
|
|
36
|
+
export function filesystemMutationLockRoot(portableRoot, hostPolicy) {
|
|
37
|
+
const policy = captureFilesystemHostPolicy(hostPolicy);
|
|
38
|
+
// A stable host parent keeps different process/session temp settings in one namespace.
|
|
39
|
+
const runtimeParent = policy.runtimeLockParent();
|
|
56
40
|
const tempParent = canonicalExistingPath(runtimeParent);
|
|
57
41
|
const homeParent = canonicalExistingPath(homedir());
|
|
58
|
-
const ownerKey = runtimeOwnerKey();
|
|
42
|
+
const ownerKey = policy.runtimeOwnerKey();
|
|
59
43
|
const candidates = [
|
|
60
44
|
path.join(tempParent, `agentstate-lite-mutation-locks-${ownerKey}`),
|
|
61
45
|
path.join(homeParent, ".agentstate", `mutation-locks-${ownerKey}`),
|
|
@@ -78,8 +62,8 @@ function explicitFilesystemMutationLockRoot(root, portableRoot) {
|
|
|
78
62
|
throw new FilesystemMutationLockError(`cannot place filesystem mutation locks outside portable root '${portable}'`, { lockPath: portable, owner: null, stale: false, malformed: true });
|
|
79
63
|
}
|
|
80
64
|
/** Runtime lock directory for one already-canonical physical target. */
|
|
81
|
-
export function filesystemMutationLockPath(target, portableRoot) {
|
|
82
|
-
return filesystemMutationLockPathInRoot(target, filesystemMutationLockRoot(portableRoot));
|
|
65
|
+
export function filesystemMutationLockPath(target, portableRoot, hostPolicy) {
|
|
66
|
+
return filesystemMutationLockPathInRoot(target, filesystemMutationLockRoot(portableRoot, hostPolicy));
|
|
83
67
|
}
|
|
84
68
|
function filesystemMutationLockPathInRoot(target, lockRoot) {
|
|
85
69
|
const digest = createHash("sha256").update(path.resolve(target)).digest("hex");
|
|
@@ -113,14 +97,44 @@ export function parseFilesystemMutationLockOwner(value) {
|
|
|
113
97
|
target: value.target,
|
|
114
98
|
};
|
|
115
99
|
}
|
|
116
|
-
|
|
100
|
+
/** A missing path or malformed content is `absent`; every other read failure is `unreadable`. */
|
|
101
|
+
async function readOwnerRecord(lockPath) {
|
|
102
|
+
let raw;
|
|
103
|
+
try {
|
|
104
|
+
raw = await fs.readFile(path.join(lockPath, OWNER_FILE), "utf8");
|
|
105
|
+
}
|
|
106
|
+
catch (err) {
|
|
107
|
+
const code = err.code;
|
|
108
|
+
return code === "ENOENT" || code === "ENOTDIR" ? { state: "absent" } : { state: "unreadable", error: err };
|
|
109
|
+
}
|
|
110
|
+
let owner = null;
|
|
117
111
|
try {
|
|
118
|
-
|
|
112
|
+
owner = parseFilesystemMutationLockOwner(JSON.parse(raw));
|
|
119
113
|
}
|
|
120
114
|
catch {
|
|
121
|
-
|
|
115
|
+
owner = null;
|
|
116
|
+
}
|
|
117
|
+
return owner === null ? { state: "absent" } : { state: "record", owner };
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Resolve the owner record to a definitive answer, polling out an indeterminate read inside the
|
|
121
|
+
* caller's remaining budget. `EMFILE`/`ENFILE` under concurrency, and the `EBUSY`/`EACCES`/`EPERM`
|
|
122
|
+
* open transients that Windows indexing and antivirus software produce, otherwise read as "no
|
|
123
|
+
* record" and let a caller conclude that a lock it still owns has changed hands. An unreadable
|
|
124
|
+
* record never authorizes the destructive step: the caller fails closed when the budget expires.
|
|
125
|
+
*/
|
|
126
|
+
async function resolveOwnerRecord(lockPath, started, waitMs, pollMs) {
|
|
127
|
+
while (true) {
|
|
128
|
+
const record = await readOwnerRecord(lockPath);
|
|
129
|
+
if (record.state !== "unreadable" || Date.now() - started >= waitMs)
|
|
130
|
+
return record;
|
|
131
|
+
await delay(pollMs);
|
|
122
132
|
}
|
|
123
133
|
}
|
|
134
|
+
async function readOwner(lockPath) {
|
|
135
|
+
const record = await readOwnerRecord(lockPath);
|
|
136
|
+
return record.state === "record" ? record.owner : null;
|
|
137
|
+
}
|
|
124
138
|
function processExists(pid) {
|
|
125
139
|
try {
|
|
126
140
|
process.kill(pid, 0);
|
|
@@ -130,6 +144,27 @@ function processExists(pid) {
|
|
|
130
144
|
return err.code !== "ESRCH";
|
|
131
145
|
}
|
|
132
146
|
}
|
|
147
|
+
/**
|
|
148
|
+
* Tokens of the claims held in this process, from just before each owner record is written until
|
|
149
|
+
* its release. The set lives on the global object so every copy of this module one process loads
|
|
150
|
+
* (each published bundle carries its own) shares it: the start-time test below cannot protect
|
|
151
|
+
* such a copy's claims, because a suspend or a forward clock step moves the start it computes past
|
|
152
|
+
* them.
|
|
153
|
+
*/
|
|
154
|
+
const HELD_TOKENS = Symbol.for("superbee.filesystem-lock.held-tokens");
|
|
155
|
+
const heldTokens = (globalThis[HELD_TOKENS] ??= new Set());
|
|
156
|
+
/**
|
|
157
|
+
* Whether a same-host owner is demonstrably gone. `kill(pid, 0)` answers that for another process,
|
|
158
|
+
* but for this process's own id it always answers "alive", so a record left by an earlier process
|
|
159
|
+
* that had this id (a container entry point that is PID 1 again after a restart) would hold its
|
|
160
|
+
* lock forever. Such a record is gone only when no claim in this process carries its token and it
|
|
161
|
+
* was written before this process started.
|
|
162
|
+
*/
|
|
163
|
+
function ownerIsGone(owner) {
|
|
164
|
+
if (owner.pid !== process.pid)
|
|
165
|
+
return !processExists(owner.pid);
|
|
166
|
+
return !heldTokens.has(owner.token) && owner.created_at_ms < Date.now() - process.uptime() * 1000;
|
|
167
|
+
}
|
|
133
168
|
function staleLockQuarantinePath(lockPath, owner) {
|
|
134
169
|
const tokenHash = createHash("sha256").update(owner.token).digest("hex");
|
|
135
170
|
return `${lockPath}.stale-${tokenHash}`;
|
|
@@ -148,10 +183,15 @@ async function pathExists(candidate) {
|
|
|
148
183
|
/**
|
|
149
184
|
* Move one demonstrably dead same-host lock aside without deleting its evidence. The destination
|
|
150
185
|
* is stable for the dead owner's token and remains non-empty, so a delayed competing reclaimer
|
|
151
|
-
* cannot rename a replacement live lock over it
|
|
186
|
+
* cannot rename a replacement live lock over it across supported filesystems.
|
|
152
187
|
*/
|
|
153
|
-
async function quarantineStaleLock(lockPath, owner) {
|
|
154
|
-
if (owner.hostname !== hostname() ||
|
|
188
|
+
async function quarantineStaleLock(lockPath, owner, policy) {
|
|
189
|
+
if (owner.hostname !== hostname() || !ownerIsGone(owner))
|
|
190
|
+
return false;
|
|
191
|
+
// Death was established for this owner's snapshot. If the path no longer carries that owner's
|
|
192
|
+
// record, the lock changed hands after the snapshot and the rename would move a live lock.
|
|
193
|
+
const current = await readOwner(lockPath);
|
|
194
|
+
if (current === null || current.token !== owner.token)
|
|
155
195
|
return false;
|
|
156
196
|
const quarantinePath = staleLockQuarantinePath(lockPath, owner);
|
|
157
197
|
try {
|
|
@@ -164,7 +204,7 @@ async function quarantineStaleLock(lockPath, owner) {
|
|
|
164
204
|
return false;
|
|
165
205
|
if (await pathExists(quarantinePath))
|
|
166
206
|
return false;
|
|
167
|
-
if (
|
|
207
|
+
if (policy.isDirectoryContentionError(err))
|
|
168
208
|
return false;
|
|
169
209
|
throw err;
|
|
170
210
|
}
|
|
@@ -185,7 +225,7 @@ export function isPrivateFilesystemMutationLockRoot(facts) {
|
|
|
185
225
|
const unsafeMode = facts.enforcePrivateMode && (facts.mode & 0o777) !== 0o700;
|
|
186
226
|
return facts.directory && !facts.symbolicLink && !wrongOwner && !unsafeMode;
|
|
187
227
|
}
|
|
188
|
-
async function ensurePrivateLockRoot(root) {
|
|
228
|
+
async function ensurePrivateLockRoot(root, policy) {
|
|
189
229
|
try {
|
|
190
230
|
await fs.mkdir(root, { recursive: true, mode: 0o700 });
|
|
191
231
|
}
|
|
@@ -201,7 +241,7 @@ async function ensurePrivateLockRoot(root) {
|
|
|
201
241
|
ownerUid: stat.uid,
|
|
202
242
|
expectedUid: uid,
|
|
203
243
|
mode: stat.mode,
|
|
204
|
-
enforcePrivateMode:
|
|
244
|
+
enforcePrivateMode: policy.enforcePrivateMode,
|
|
205
245
|
})) {
|
|
206
246
|
throw new FilesystemMutationLockError(`refusing unsafe filesystem mutation lock root '${root}'; it must be a private directory owned by this user`, { lockPath: root, owner: null, stale: false, malformed: true });
|
|
207
247
|
}
|
|
@@ -231,7 +271,7 @@ async function canonicalTargetInDirectory(directory, requestedBasename) {
|
|
|
231
271
|
candidateStat = await fs.lstat(candidate);
|
|
232
272
|
}
|
|
233
273
|
catch {
|
|
234
|
-
// A sibling may disappear during the scan, or
|
|
274
|
+
// A sibling may disappear during the scan, or the host may deny metadata for a protected
|
|
235
275
|
// system entry in an otherwise-readable directory. Neither sibling can be the successfully
|
|
236
276
|
// witnessed requested entry, so it contributes no alias evidence and must not block locking.
|
|
237
277
|
continue;
|
|
@@ -250,10 +290,24 @@ async function canonicalTargetInDirectory(directory, requestedBasename) {
|
|
|
250
290
|
* a lock reached through its own fold key holds the same identity by construction, but nothing
|
|
251
291
|
* here re-derives that from an arbitrary `owner.json`, so it must not assert the equivalence.
|
|
252
292
|
*/
|
|
253
|
-
function timeoutError(lockPath,
|
|
293
|
+
async function timeoutError(lockPath, snapshot, guarded) {
|
|
294
|
+
// A dead-owner answer is only about the snapshot; report it as stale only while the path still
|
|
295
|
+
// carries that owner's record. Otherwise the lock was released and possibly claimed since, so
|
|
296
|
+
// diagnose the record there now, never the dead snapshot as its holder: a caller that checks the
|
|
297
|
+
// named PID (the push role's PID-reuse check, or a person running `ps`) would otherwise judge a
|
|
298
|
+
// live replacement by a process that no longer holds anything.
|
|
299
|
+
let diagnosed = snapshot;
|
|
300
|
+
let stale = false;
|
|
301
|
+
if (snapshot !== null && snapshot.hostname === hostname() && ownerIsGone(snapshot)) {
|
|
302
|
+
const current = await readOwner(lockPath);
|
|
303
|
+
if (current?.token === snapshot.token)
|
|
304
|
+
stale = true;
|
|
305
|
+
else
|
|
306
|
+
diagnosed = current;
|
|
307
|
+
}
|
|
308
|
+
const owner = diagnosed;
|
|
254
309
|
const malformed = owner === null;
|
|
255
310
|
const sameHost = owner?.hostname === hostname();
|
|
256
|
-
const stale = owner !== null && sameHost && !processExists(owner.pid);
|
|
257
311
|
let message;
|
|
258
312
|
if (malformed) {
|
|
259
313
|
message =
|
|
@@ -265,9 +319,21 @@ function timeoutError(lockPath, owner, guarded) {
|
|
|
265
319
|
`stale filesystem mutation lock '${lockPath}' belongs to absent PID ${owner.pid} on ${owner.hostname}. ` +
|
|
266
320
|
`Inspect and remove the lock, then retry.`;
|
|
267
321
|
}
|
|
322
|
+
else if (!sameHost) {
|
|
323
|
+
// Another host's process ids cannot be probed from here, and a holder that crashed before this
|
|
324
|
+
// host was renamed looks exactly like a live holder elsewhere, so no retry can clear it.
|
|
325
|
+
message =
|
|
326
|
+
`filesystem mutation lock '${lockPath}' is held by PID ${owner.pid} on ${owner.hostname}, which is not this host (${hostname()}); ` +
|
|
327
|
+
`whether its holder is alive cannot be checked from here, so the lock is never reclaimed automatically. ` +
|
|
328
|
+
`A person must check it: once no process on ${owner.hostname} is mutating the target (this host may have been renamed since the lock was taken), remove the lock, then retry.`;
|
|
329
|
+
}
|
|
268
330
|
else {
|
|
331
|
+
// A live process id is not proof of the holder: the holder may have exited and its id been
|
|
332
|
+
// reused, which this module cannot tell apart from a long mutation.
|
|
269
333
|
message =
|
|
270
|
-
`timed out waiting for filesystem mutation lock '${lockPath}' held by PID ${owner.pid} on ${owner.hostname}; retry the mutation
|
|
334
|
+
`timed out waiting for filesystem mutation lock '${lockPath}' held by PID ${owner.pid} on ${owner.hostname}; retry the mutation. ` +
|
|
335
|
+
`If it stays held, PID ${owner.pid} may no longer be the process that claimed it, because a process id is reused after its process exits: ` +
|
|
336
|
+
`remove the lock only after confirming no process is mutating the target.`;
|
|
271
337
|
}
|
|
272
338
|
const heldFor = owner !== null && owner.target !== guarded
|
|
273
339
|
? ` Its holder recorded '${owner.target}' for the same lock key.`
|
|
@@ -280,22 +346,21 @@ async function resolvedPortableRoot(portableRoot) {
|
|
|
280
346
|
return undefined;
|
|
281
347
|
return fs.realpath(portableRoot).catch(() => path.resolve(portableRoot));
|
|
282
348
|
}
|
|
283
|
-
async function selectLockRoot(options) {
|
|
349
|
+
async function selectLockRoot(options, policy) {
|
|
284
350
|
const portableRoot = await resolvedPortableRoot(options.portableRoot);
|
|
285
351
|
const lockRoot = options.lockRoot !== undefined
|
|
286
352
|
? explicitFilesystemMutationLockRoot(options.lockRoot, portableRoot)
|
|
287
|
-
: filesystemMutationLockRoot(portableRoot);
|
|
288
|
-
await ensurePrivateLockRoot(lockRoot);
|
|
353
|
+
: filesystemMutationLockRoot(portableRoot, policy);
|
|
354
|
+
await ensurePrivateLockRoot(lockRoot, policy);
|
|
289
355
|
return lockRoot;
|
|
290
356
|
}
|
|
291
|
-
|
|
292
|
-
async function classifyLockClaimFailure(error, lockPath) {
|
|
357
|
+
async function classifyLockClaimFailure(error, lockPath, policy) {
|
|
293
358
|
const code = error.code;
|
|
294
359
|
if (code === "EEXIST")
|
|
295
360
|
return "contention";
|
|
296
|
-
if (
|
|
361
|
+
if (!policy.isDirectoryContentionError(error))
|
|
297
362
|
return "terminal";
|
|
298
|
-
//
|
|
363
|
+
// A host may report a contention-shaped error while another claimer creates or removes this exact
|
|
299
364
|
// directory. A witnessed path is contention. An absent path is ambiguous: permit one bounded
|
|
300
365
|
// retry in case the competing directory operation just completed, but never turn a durable
|
|
301
366
|
// create denial into a malformed-lock timeout. A denied probe remains terminal immediately.
|
|
@@ -306,7 +371,7 @@ async function classifyLockClaimFailure(error, lockPath) {
|
|
|
306
371
|
catch (probeError) {
|
|
307
372
|
const probeCode = probeError.code;
|
|
308
373
|
return probeCode === "ENOENT" || probeCode === "ENOTDIR"
|
|
309
|
-
? "unwitnessed-
|
|
374
|
+
? "unwitnessed-contention-error"
|
|
310
375
|
: "terminal";
|
|
311
376
|
}
|
|
312
377
|
}
|
|
@@ -315,31 +380,31 @@ async function classifyLockClaimFailure(error, lockPath) {
|
|
|
315
380
|
* owner record makes a crash leftover diagnosable, and release is token-checked so a caller can
|
|
316
381
|
* never remove a lock it no longer owns. Waits up to `waitMs`, polling every `pollMs`.
|
|
317
382
|
*/
|
|
318
|
-
async function claimLockPath(lockPath, owner, waitMs, pollMs) {
|
|
383
|
+
async function claimLockPath(lockPath, owner, waitMs, pollMs, policy) {
|
|
319
384
|
const started = owner.created_at_ms;
|
|
320
|
-
let
|
|
385
|
+
let unwitnessedRetryUsed = false;
|
|
321
386
|
while (true) {
|
|
322
387
|
try {
|
|
323
388
|
await fs.mkdir(lockPath, { mode: 0o700 });
|
|
324
389
|
}
|
|
325
390
|
catch (err) {
|
|
326
|
-
const failure = await classifyLockClaimFailure(err, lockPath);
|
|
391
|
+
const failure = await classifyLockClaimFailure(err, lockPath, policy);
|
|
327
392
|
if (failure === "terminal")
|
|
328
393
|
throw err;
|
|
329
|
-
if (failure === "unwitnessed-
|
|
330
|
-
if (
|
|
394
|
+
if (failure === "unwitnessed-contention-error") {
|
|
395
|
+
if (unwitnessedRetryUsed)
|
|
331
396
|
throw err;
|
|
332
|
-
|
|
397
|
+
unwitnessedRetryUsed = true;
|
|
333
398
|
// This is one immediate re-attempt, not a wait. It is permitted even with waitMs: 0 so
|
|
334
399
|
// the next result can distinguish a one-shot sharing race from a durable create denial.
|
|
335
400
|
continue;
|
|
336
401
|
}
|
|
337
402
|
else {
|
|
338
|
-
|
|
403
|
+
unwitnessedRetryUsed = false;
|
|
339
404
|
}
|
|
340
405
|
let existingOwner = await readOwner(lockPath);
|
|
341
406
|
if (existingOwner !== null) {
|
|
342
|
-
if (await quarantineStaleLock(lockPath, existingOwner))
|
|
407
|
+
if (await quarantineStaleLock(lockPath, existingOwner, policy))
|
|
343
408
|
continue;
|
|
344
409
|
// A failed quarantine attempt is non-progress: another reclaimer may have moved the stale
|
|
345
410
|
// lock and installed a live replacement while this caller was delayed in rename. Diagnose
|
|
@@ -347,12 +412,18 @@ async function claimLockPath(lockPath, owner, waitMs, pollMs) {
|
|
|
347
412
|
existingOwner = await readOwner(lockPath);
|
|
348
413
|
}
|
|
349
414
|
if (Date.now() - started >= waitMs)
|
|
350
|
-
throw timeoutError(lockPath, existingOwner, owner.target);
|
|
415
|
+
throw await timeoutError(lockPath, existingOwner, owner.target);
|
|
351
416
|
await delay(pollMs);
|
|
352
417
|
continue;
|
|
353
418
|
}
|
|
354
|
-
// The claim
|
|
355
|
-
|
|
419
|
+
// The directory this claim made, to recognize it later even if the owner record never lands.
|
|
420
|
+
const claimed = await fs.lstat(lockPath, { bigint: true }).catch(() => null);
|
|
421
|
+
// The claim is ours now, unless this claimer was suspended long enough for the owner-less
|
|
422
|
+
// directory to be removed as orphaned and claimed again. Owner initialization and rollback
|
|
423
|
+
// failures are not claim contention, even when the host reports a contention-shaped error, and
|
|
424
|
+
// must propagate unchanged. A lost claim is contention: a record this claim never wrote
|
|
425
|
+
// (EEXIST), or no directory to write into (ENOENT, ENOTDIR).
|
|
426
|
+
heldTokens.add(owner.token);
|
|
356
427
|
try {
|
|
357
428
|
await fs.writeFile(path.join(lockPath, OWNER_FILE), `${JSON.stringify(owner)}\n`, {
|
|
358
429
|
encoding: "utf8",
|
|
@@ -361,22 +432,172 @@ async function claimLockPath(lockPath, owner, waitMs, pollMs) {
|
|
|
361
432
|
});
|
|
362
433
|
}
|
|
363
434
|
catch (err) {
|
|
364
|
-
|
|
435
|
+
const code = err.code;
|
|
436
|
+
// `wx` fails with EEXIST only when another claimer's owner.json was already there, possibly
|
|
437
|
+
// opened but not yet written. The directory is that claimer's now: never roll it back.
|
|
438
|
+
// A lost claim re-enters the loop, whose next `mkdir` either claims or reaches the timeout check.
|
|
439
|
+
if (code === "EEXIST") {
|
|
440
|
+
heldTokens.delete(owner.token);
|
|
441
|
+
continue;
|
|
442
|
+
}
|
|
443
|
+
await rollBackOwnClaim(lockPath, owner, claimed, waitMs, pollMs, policy).catch(() => { });
|
|
444
|
+
heldTokens.delete(owner.token);
|
|
445
|
+
if (code === "ENOENT" || code === "ENOTDIR")
|
|
446
|
+
continue;
|
|
365
447
|
throw err;
|
|
366
448
|
}
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
449
|
+
// One release in flight per claim: a concurrent second invocation shares the first outcome
|
|
450
|
+
// instead of racing it, so two removers can never act on one claim. A later invocation
|
|
451
|
+
// re-verifies the record and refuses, as it always did.
|
|
452
|
+
let inFlight;
|
|
453
|
+
return () => {
|
|
454
|
+
if (inFlight)
|
|
455
|
+
return inFlight;
|
|
456
|
+
inFlight = (async () => {
|
|
457
|
+
// One budget spans ownership resolution and removal, so the worst-case hold stays the
|
|
458
|
+
// mutation plus waitMs however the two steps divide it.
|
|
459
|
+
const started = Date.now();
|
|
460
|
+
const current = await resolveOwnerRecord(lockPath, started, waitMs, pollMs);
|
|
461
|
+
if (current.state === "unreadable")
|
|
462
|
+
throw unreadableOwnerRefusal(lockPath, current.error);
|
|
463
|
+
if (current.state !== "record" || current.owner.token !== owner.token) {
|
|
464
|
+
throw changedOwnerRefusal(lockPath, current.state === "record" ? current.owner : null);
|
|
465
|
+
}
|
|
466
|
+
await removeReleasedLock(lockPath, owner, started, waitMs, pollMs, policy);
|
|
467
|
+
heldTokens.delete(owner.token);
|
|
468
|
+
})();
|
|
469
|
+
return inFlight.finally(() => {
|
|
470
|
+
inFlight = undefined;
|
|
471
|
+
});
|
|
472
|
+
};
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
/**
|
|
476
|
+
* Undo a claim whose owner record could not be written, removing the lock directory only when it
|
|
477
|
+
* is provably this claim's: a compare-and-release. The directory is this claim's when it carries
|
|
478
|
+
* this claim's owner record, or when it carries no record and is still the very directory this
|
|
479
|
+
* claim made (same device, inode and birth time). Any other record is another process's lock, and
|
|
480
|
+
* a directory that is not the one this claim made may be another claim in progress; both are left
|
|
481
|
+
* alone. So is a record this process still cannot read once the wait budget is spent (a transient
|
|
482
|
+
* read failure is polled out first), and a host that reports no birth time (Linux on NFS, say), where the
|
|
483
|
+
* directory cannot be told from a successor that reused its inode: an owner-less lock left behind
|
|
484
|
+
* is reported as orphaned once the claim grace passes, while a deleted live lock breaks exclusion.
|
|
485
|
+
*/
|
|
486
|
+
async function rollBackOwnClaim(lockPath, owner, claimed, waitMs, pollMs, policy) {
|
|
487
|
+
const started = Date.now();
|
|
488
|
+
const record = await resolveOwnerRecord(lockPath, started, waitMs, pollMs);
|
|
489
|
+
if (record.state === "record") {
|
|
490
|
+
if (record.owner.token === owner.token)
|
|
491
|
+
await removeReleasedLock(lockPath, owner, started, waitMs, pollMs, policy);
|
|
492
|
+
return;
|
|
493
|
+
}
|
|
494
|
+
if (record.state === "unreadable" || !(await isClaimedDirectory(lockPath, claimed)))
|
|
495
|
+
return;
|
|
496
|
+
// Move exactly the verified directory aside, under a name only this claim can produce, then remove it.
|
|
497
|
+
const remnant = releasedLockRemnantPath(lockPath, owner);
|
|
498
|
+
await fs.rename(lockPath, remnant);
|
|
499
|
+
await fs.rm(remnant, { recursive: true, force: true });
|
|
500
|
+
}
|
|
501
|
+
async function isClaimedDirectory(lockPath, claimed) {
|
|
502
|
+
if (claimed === null || claimed.birthtimeNs <= 0n)
|
|
503
|
+
return false;
|
|
504
|
+
const current = await fs.lstat(lockPath, { bigint: true }).catch(() => null);
|
|
505
|
+
return current !== null && current.isDirectory() && current.dev === claimed.dev && current.ino === claimed.ino && current.birthtimeNs === claimed.birthtimeNs;
|
|
506
|
+
}
|
|
507
|
+
function changedOwnerRefusal(lockPath, current) {
|
|
508
|
+
return new FilesystemMutationLockError(`refusing to release filesystem mutation lock '${lockPath}' because its owner token changed; the mutation may have completed, inspect the lock before retrying.`, { lockPath, owner: current, stale: false, malformed: current === null });
|
|
509
|
+
}
|
|
510
|
+
/**
|
|
511
|
+
* A record this process could never read is not a record that changed; say which one happened.
|
|
512
|
+
* `malformed` stays true because the outcome for a consumer is the same one it already handled,
|
|
513
|
+
* no usable owner record, and the message carries the distinction the flag cannot.
|
|
514
|
+
*/
|
|
515
|
+
function unreadableOwnerRefusal(lockPath, error) {
|
|
516
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
517
|
+
return new FilesystemMutationLockError(`refusing to release filesystem mutation lock '${lockPath}' because its owner record could not be read within the wait budget (${message}); the mutation may have completed, inspect the lock before retrying.`, { lockPath, owner: null, stale: false, malformed: true });
|
|
518
|
+
}
|
|
519
|
+
/** Token-derived sibling name that only this claim can produce; a competitor can never claim it. */
|
|
520
|
+
function releasedLockRemnantPath(lockPath, owner) {
|
|
521
|
+
const tokenHash = createHash("sha256").update(owner.token).digest("hex");
|
|
522
|
+
return `${lockPath}.released-${tokenHash}`;
|
|
523
|
+
}
|
|
524
|
+
function removalFailure(inspectPath, owner, err, attempts, detail) {
|
|
525
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
526
|
+
const suffix = attempts > 1 ? ` after ${attempts} bounded attempts` : "";
|
|
527
|
+
return new FilesystemMutationLockError(`mutation completed but ${detail}${suffix} (${message}); inspect the lock before retrying.`, { lockPath: inspectPath, owner, stale: false, malformed: false });
|
|
528
|
+
}
|
|
529
|
+
/**
|
|
530
|
+
* Release a lock directory whose owner record was just verified as this caller's, in two steps
|
|
531
|
+
* that keep the destructive action fenced to this claim.
|
|
532
|
+
*
|
|
533
|
+
* Step one renames the directory to a token-derived sibling. Rename is atomic and moves exactly
|
|
534
|
+
* the directory whose record was verified; the lock key is free the instant it succeeds, and a
|
|
535
|
+
* competitor's later claim lands on a fresh directory that step two never names. Step two removes
|
|
536
|
+
* the renamed remnant, which only this claim can produce. What remains is the check-then-act gap
|
|
537
|
+
* between a record read and the next rename syscall, reachable only through an external actor
|
|
538
|
+
* that removes the verified directory and a completed competitor claim inside that gap: the same
|
|
539
|
+
* class the stale-lock quarantine rename accepts.
|
|
540
|
+
*
|
|
541
|
+
* A host may report a contention-shaped error while another claimer still holds a handle inside
|
|
542
|
+
* the directory: Windows unlink through older libuv-backed Node releases marks a file delete-on-close, so a
|
|
543
|
+
* competitor's poll of `owner.json` briefly blocks both the directory rename and the removal of
|
|
544
|
+
* its unlinked record. Retry only what the host policy classifies as directory contention, inside
|
|
545
|
+
* the budget `started` at release entry and bounded by the claim's `waitMs`, polling every
|
|
546
|
+
* `pollMs`, so the worst-case hold is the mutation plus `waitMs`. The default supported policy
|
|
547
|
+
* classifies nothing, so its release stays single-shot. Before each rename retry the record is
|
|
548
|
+
* re-read: rename never removes the record, so anything but this claim's own record means the
|
|
549
|
+
* directory changed hands and the release refuses, unless the directory itself is gone. A record
|
|
550
|
+
* this process cannot read is neither, and is polled out rather than acted on. A directory gone
|
|
551
|
+
* between attempts is the requested outcome, and so is an absent remnant, whose token-derived
|
|
552
|
+
* name no competitor can produce.
|
|
553
|
+
*/
|
|
554
|
+
async function removeReleasedLock(lockPath, owner, started, waitMs, pollMs, policy) {
|
|
555
|
+
const remnant = releasedLockRemnantPath(lockPath, owner);
|
|
556
|
+
let attempts = 0;
|
|
557
|
+
while (true) {
|
|
558
|
+
try {
|
|
559
|
+
await fs.rename(lockPath, remnant);
|
|
560
|
+
break;
|
|
561
|
+
}
|
|
562
|
+
catch (err) {
|
|
563
|
+
attempts += 1;
|
|
564
|
+
if (attempts > 1 && err.code === "ENOENT")
|
|
565
|
+
return;
|
|
566
|
+
if (!policy.isDirectoryContentionError(err) || Date.now() - started >= waitMs) {
|
|
567
|
+
throw removalFailure(lockPath, owner, err, attempts, `filesystem lock '${lockPath}' could not be removed`);
|
|
374
568
|
}
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
569
|
+
}
|
|
570
|
+
await delay(pollMs);
|
|
571
|
+
// Only a definitive record authorizes another rename, so an indeterminate read delays this
|
|
572
|
+
// claim's own cleanup instead of ever pointing the rename at a directory it did not verify.
|
|
573
|
+
const current = await resolveOwnerRecord(lockPath, started, waitMs, pollMs);
|
|
574
|
+
if (current.state === "record" && current.owner.token === owner.token)
|
|
575
|
+
continue;
|
|
576
|
+
if (current.state === "unreadable")
|
|
577
|
+
throw unreadableOwnerRefusal(lockPath, current.error);
|
|
578
|
+
// Rename never removes the record, so a record-less directory here is a competitor's claim in
|
|
579
|
+
// progress, never this caller's leftover; only an absent directory means already released.
|
|
580
|
+
if (current.state === "absent" && !(await pathExists(lockPath)))
|
|
581
|
+
return;
|
|
582
|
+
throw changedOwnerRefusal(lockPath, current.state === "record" ? current.owner : null);
|
|
583
|
+
}
|
|
584
|
+
attempts = 0;
|
|
585
|
+
while (true) {
|
|
586
|
+
try {
|
|
587
|
+
await fs.rm(remnant, { recursive: true, force: false });
|
|
588
|
+
return;
|
|
589
|
+
}
|
|
590
|
+
catch (err) {
|
|
591
|
+
attempts += 1;
|
|
592
|
+
// No competitor can produce this token-derived name, so an absent remnant on any attempt,
|
|
593
|
+
// the first included, means the release already reached its requested end state.
|
|
594
|
+
if (err.code === "ENOENT")
|
|
595
|
+
return;
|
|
596
|
+
if (!policy.isDirectoryContentionError(err) || Date.now() - started >= waitMs) {
|
|
597
|
+
throw removalFailure(remnant, owner, err, attempts, `filesystem lock '${lockPath}' was released yet its remnant '${remnant}' could not be removed`);
|
|
378
598
|
}
|
|
379
|
-
}
|
|
599
|
+
}
|
|
600
|
+
await delay(pollMs);
|
|
380
601
|
}
|
|
381
602
|
}
|
|
382
603
|
function newOwner(target) {
|
|
@@ -398,13 +619,14 @@ function newOwner(target) {
|
|
|
398
619
|
* fail-closed.
|
|
399
620
|
*/
|
|
400
621
|
export async function acquireFilesystemMutationLock(target, options = {}) {
|
|
622
|
+
const policy = captureFilesystemHostPolicy(options.hostPolicy);
|
|
401
623
|
const waitMs = positiveOption(options.waitMs, DEFAULT_WAIT_MS, "waitMs");
|
|
402
624
|
const pollMs = positiveOption(options.pollMs, DEFAULT_POLL_MS, "pollMs");
|
|
403
625
|
const targetResolved = path.resolve(target);
|
|
404
626
|
const targetDir = path.dirname(targetResolved);
|
|
405
627
|
const owner = newOwner(targetResolved);
|
|
406
|
-
//
|
|
407
|
-
//
|
|
628
|
+
// Existing filesystem roots can reject even a recursive mkdir. Resolve first and create
|
|
629
|
+
// only when the parent is genuinely absent. This preserves the
|
|
408
630
|
// create-on-demand behavior without mutating an already-existing filesystem root.
|
|
409
631
|
let canonicalDir;
|
|
410
632
|
try {
|
|
@@ -419,9 +641,9 @@ export async function acquireFilesystemMutationLock(target, options = {}) {
|
|
|
419
641
|
// Two callers may spell the same bundle through real and symlinked parent paths. Canonicalize
|
|
420
642
|
// the now-existing parent so both claim the same runtime lock for the physical target.
|
|
421
643
|
const targetCanonical = await canonicalTargetInDirectory(canonicalDir, path.basename(targetResolved));
|
|
422
|
-
const lockRoot = await selectLockRoot(options);
|
|
644
|
+
const lockRoot = await selectLockRoot(options, policy);
|
|
423
645
|
const lockPath = filesystemMutationLockPathInRoot(targetCanonical, lockRoot);
|
|
424
|
-
return claimLockPath(lockPath, { ...owner, target: targetCanonical }, waitMs, pollMs);
|
|
646
|
+
return claimLockPath(lockPath, { ...owner, target: targetCanonical }, waitMs, pollMs, policy);
|
|
425
647
|
}
|
|
426
648
|
const IDENTITY_KEY_SHAPE = /^[0-9a-f]{64}$/;
|
|
427
649
|
function assertIdentityKey(key) {
|
|
@@ -429,9 +651,9 @@ function assertIdentityKey(key) {
|
|
|
429
651
|
throw new TypeError("identity key must be a lowercase hex sha256 digest");
|
|
430
652
|
}
|
|
431
653
|
/** @internal Runtime lock directory for one identity key; the key is the whole path component. */
|
|
432
|
-
export function filesystemIdentityLockPath(key, portableRoot) {
|
|
654
|
+
export function filesystemIdentityLockPath(key, portableRoot, hostPolicy) {
|
|
433
655
|
assertIdentityKey(key);
|
|
434
|
-
return path.join(filesystemMutationLockRoot(portableRoot), `${key}.lock`);
|
|
656
|
+
return path.join(filesystemMutationLockRoot(portableRoot, hostPolicy), `${key}.lock`);
|
|
435
657
|
}
|
|
436
658
|
/**
|
|
437
659
|
* @internal Claim the same-user cross-process lock for an identity key that the caller derived
|
|
@@ -441,11 +663,12 @@ export function filesystemIdentityLockPath(key, portableRoot) {
|
|
|
441
663
|
*/
|
|
442
664
|
export async function acquireFilesystemIdentityLock(key, identity, options = {}) {
|
|
443
665
|
assertIdentityKey(key);
|
|
666
|
+
const policy = captureFilesystemHostPolicy(options.hostPolicy);
|
|
444
667
|
const waitMs = positiveOption(options.waitMs, DEFAULT_WAIT_MS, "waitMs");
|
|
445
668
|
const pollMs = positiveOption(options.pollMs, DEFAULT_POLL_MS, "pollMs");
|
|
446
669
|
const owner = newOwner(identity);
|
|
447
|
-
const lockRoot = await selectLockRoot(options);
|
|
448
|
-
return claimLockPath(path.join(lockRoot, `${key}.lock`), owner, waitMs, pollMs);
|
|
670
|
+
const lockRoot = await selectLockRoot(options, policy);
|
|
671
|
+
return claimLockPath(path.join(lockRoot, `${key}.lock`), owner, waitMs, pollMs, policy);
|
|
449
672
|
}
|
|
450
673
|
/** Run `fn` while holding the same-user cross-process mutation lock for `target`. */
|
|
451
674
|
export async function withFilesystemMutationLock(target, fn, options = {}) {
|
|
@@ -457,4 +680,27 @@ export async function withFilesystemMutationLock(target, fn, options = {}) {
|
|
|
457
680
|
await release();
|
|
458
681
|
}
|
|
459
682
|
}
|
|
683
|
+
/**
|
|
684
|
+
* How long ago a lock directory last changed (its owner record written or removed), or null when
|
|
685
|
+
* it is gone. Lets a caller tell a claim or release in progress from an orphaned lock.
|
|
686
|
+
*/
|
|
687
|
+
export async function filesystemLockAgeMs(lockPath) {
|
|
688
|
+
try {
|
|
689
|
+
const info = await fs.stat(lockPath);
|
|
690
|
+
return Math.max(0, Date.now() - info.mtimeMs);
|
|
691
|
+
}
|
|
692
|
+
catch {
|
|
693
|
+
return null;
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
/**
|
|
697
|
+
* Whether the lock at `lockPath` still carries the owner record with `token`: a definite answer
|
|
698
|
+
* when a record or its absence was read, `null` when this process could not read it.
|
|
699
|
+
*/
|
|
700
|
+
export async function filesystemLockHeldBy(lockPath, token) {
|
|
701
|
+
const record = await readOwnerRecord(lockPath);
|
|
702
|
+
if (record.state === "unreadable")
|
|
703
|
+
return null;
|
|
704
|
+
return record.state === "record" && record.owner.token === token;
|
|
705
|
+
}
|
|
460
706
|
//# sourceMappingURL=filesystem-lock.js.map
|