@superbee/core 0.2.0-pre.3 → 0.2.0-pre.31
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 +1084 -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/recipe-set-preflight.d.ts +139 -0
- package/dist/recipe-set-preflight.d.ts.map +1 -0
- package/dist/recipe-set-preflight.js +536 -0
- package/dist/recipe-set-preflight.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 +107 -2
package/dist/remote-backend.js
CHANGED
|
@@ -2,12 +2,14 @@
|
|
|
2
2
|
* `RemoteBackend` — a {@link StorageBackend} implemented over the wire-protocol v0
|
|
3
3
|
* reference contract (`docs/WIRE-PROTOCOL.md`, `@superbee/server`).
|
|
4
4
|
*
|
|
5
|
-
* This is the client half of the seam over HTTP: every method maps directly to a wire endpoint
|
|
6
|
-
*
|
|
7
|
-
*
|
|
5
|
+
* This is the client half of the seam over HTTP: every method maps directly to a wire endpoint
|
|
6
|
+
* (a guarded document write may first ask `GET /v0/capabilities` whether the host records
|
|
7
|
+
* outcomes; see {@link RemoteBackendOptions.maxRetries}), and response versions remain the same
|
|
8
|
+
* content-addressed {@link Version} tokens local backends produce. Tri-backend contract tests pin
|
|
9
|
+
* that invariant.
|
|
8
10
|
*
|
|
9
11
|
* Zero new dependencies: it calls an injectable {@link FetchLike} transport
|
|
10
|
-
* (defaulting to the global `fetch`, available
|
|
12
|
+
* (defaulting to the global `fetch`, available in every supported Node runtime) with a constructed
|
|
11
13
|
* `Request`. That means the SAME adapter runs against a real HTTP server, or — in
|
|
12
14
|
* tests — directly against an in-process `createRouter(bundle)` function with no
|
|
13
15
|
* sockets: `createRouter` returns exactly the `(req: Request) => Promise<Response>`
|
|
@@ -42,30 +44,16 @@
|
|
|
42
44
|
*/
|
|
43
45
|
import { DEFAULT_BLOB_CONTENT_TYPE } from "./content-type.js";
|
|
44
46
|
import { InvalidInputError } from "./errors.js";
|
|
47
|
+
import { MalformedDocumentError } from "./frontmatter-contract.js";
|
|
45
48
|
import { encodeRemoteDocument } from "./remote-document-codec.js";
|
|
46
|
-
import {
|
|
49
|
+
import { RemoteError, malformed } from "./remote-error.js";
|
|
50
|
+
import { SNAPSHOT_TRUNCATED, parseHeadsAnswer, readSnapshotStream } from "./remote-parsers.js";
|
|
47
51
|
import { assertSafeBlobKey, assertSafeConceptId, assertSafeReservedDir, assertSafeReservedFilename, compareStorageKeys } from "./paths.js";
|
|
48
|
-
import { isRequestIdentity } from "./uncertain-write.js";
|
|
49
|
-
import { VersionConflict, stripETagWrapper } from "./version-transport.js";
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
* the raw HTTP `status`, so a caller can distinguish e.g.
|
|
54
|
-
* `AUTH_REQUIRED` (401, an unauthenticated/misconfigured `--remote`) from `RUNTIME` (5xx, a
|
|
55
|
-
* genuine server-side bug) instead of both collapsing into a generically-classified `Error`.
|
|
56
|
-
*/
|
|
57
|
-
export class RemoteError extends Error {
|
|
58
|
-
/** The envelope's `code` field, or a status-derived guess when the envelope is missing/unparseable. */
|
|
59
|
-
code;
|
|
60
|
-
/** The raw HTTP status that produced this error. */
|
|
61
|
-
status;
|
|
62
|
-
constructor(message, code, status, cause) {
|
|
63
|
-
super(message, cause === undefined ? undefined : { cause });
|
|
64
|
-
this.name = "RemoteError";
|
|
65
|
-
this.code = code;
|
|
66
|
-
this.status = status;
|
|
67
|
-
}
|
|
68
|
-
}
|
|
52
|
+
import { isRequestIdentity, mintRequestId } from "./uncertain-write.js";
|
|
53
|
+
import { VersionConflict, isContentVersion, stripETagWrapper } from "./version-transport.js";
|
|
54
|
+
// The typed rejection lives in `remote-error.ts`, shared with the heads and snapshot parsers this
|
|
55
|
+
// backend calls; it stays importable from here for every existing consumer.
|
|
56
|
+
export { RemoteError } from "./remote-error.js";
|
|
69
57
|
/** An ENOENT-shaped rejection so missing-document handling matches the local adapters. */
|
|
70
58
|
function notFound(id) {
|
|
71
59
|
const err = new Error(`no concept document '${id}'`);
|
|
@@ -112,6 +100,18 @@ function assertValidExpectedVersion(expectedVersion) {
|
|
|
112
100
|
}
|
|
113
101
|
/** The header that carries a write's durable request identity (`docs/WIRE-PROTOCOL.md`). */
|
|
114
102
|
const IDENTITY_HEADER = "Idempotency-Key";
|
|
103
|
+
/** The wire's `400 USAGE` message from a host that records no outcomes, answering any request that carries a key. */
|
|
104
|
+
const IDENTITY_UNSUPPORTED = "request identity is not supported by this host";
|
|
105
|
+
/** Whether `res` is a host's refusal of request identity itself, as opposed to a recorded or document refusal. */
|
|
106
|
+
async function refusesIdentity(res) {
|
|
107
|
+
try {
|
|
108
|
+
const envelope = (await res.json());
|
|
109
|
+
return envelope?.error?.code === "USAGE" && envelope.error.message === IDENTITY_UNSUPPORTED;
|
|
110
|
+
}
|
|
111
|
+
catch {
|
|
112
|
+
return false;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
115
|
/**
|
|
116
116
|
* Reject a request identity the wire would refuse before any request is sent, so a malformed
|
|
117
117
|
* key is a caller-side `InvalidInputError` rather than a `400` the caller might mistake for a
|
|
@@ -124,129 +124,10 @@ function assertRequestIdentity(requestId) {
|
|
|
124
124
|
}
|
|
125
125
|
/** The lookup route's `200` body: a recorded outcome, never `unknown`. */
|
|
126
126
|
const RECORDED_OUTCOME_KINDS = new Set(["committed", "conflict", "refused"]);
|
|
127
|
-
/** A snapshot body that ended, or failed, before its terminator arrived with the announced count. */
|
|
128
|
-
const SNAPSHOT_TRUNCATED = "SNAPSHOT_TRUNCATED";
|
|
129
|
-
/**
|
|
130
|
-
* A snapshot whose body arrived whole, with its terminator and the announced count, but whose
|
|
131
|
-
* rows digest to something other than the header announced. Not truncation: re-requesting will
|
|
132
|
-
* not necessarily repair it, since the authority contradicted itself. A consumer discards the
|
|
133
|
-
* documents' claim to the header digest either way.
|
|
134
|
-
*/
|
|
135
|
-
const SNAPSHOT_DIGEST_MISMATCH = "SNAPSHOT_DIGEST_MISMATCH";
|
|
136
|
-
function isDocumentHead(value) {
|
|
137
|
-
if (typeof value !== "object" || value === null)
|
|
138
|
-
return false;
|
|
139
|
-
const head = value;
|
|
140
|
-
return typeof head.id === "string" && typeof head.version === "string";
|
|
141
|
-
}
|
|
142
|
-
/** A wire payload the authority produced but the contract does not admit: not retried, not truncation. */
|
|
143
|
-
function malformed(message) {
|
|
144
|
-
return new RemoteError(message, "RUNTIME", 502);
|
|
145
|
-
}
|
|
146
127
|
/** Quote a bare digest for `If-None-Match`; an already quoted or weak form passes through. */
|
|
147
128
|
function etagForm(token) {
|
|
148
129
|
return token.startsWith('"') || token.startsWith("W/") ? token : `"${token}"`;
|
|
149
130
|
}
|
|
150
|
-
/**
|
|
151
|
-
* Split a response body into its NDJSON lines as they arrive, decoding UTF-8 across chunk
|
|
152
|
-
* boundaries. Only `TextDecoder` and the Web Streams reader are used, so the same code runs in a
|
|
153
|
-
* browser. A partial trailing line at the end of the body is a cut line and is not yielded; a
|
|
154
|
-
* transport failure while reading is reported as truncation, since either way the terminator
|
|
155
|
-
* never arrived. Leaving the loop early cancels the reader so the connection is released.
|
|
156
|
-
*/
|
|
157
|
-
async function* ndjsonLines(body, status) {
|
|
158
|
-
const reader = body.getReader();
|
|
159
|
-
const decoder = new TextDecoder();
|
|
160
|
-
let buffered = "";
|
|
161
|
-
try {
|
|
162
|
-
for (;;) {
|
|
163
|
-
let chunk;
|
|
164
|
-
try {
|
|
165
|
-
chunk = await reader.read();
|
|
166
|
-
}
|
|
167
|
-
catch (cause) {
|
|
168
|
-
throw new RemoteError("snapshot body failed before its end line arrived", SNAPSHOT_TRUNCATED, status, cause);
|
|
169
|
-
}
|
|
170
|
-
buffered += decoder.decode(chunk.value, { stream: !chunk.done });
|
|
171
|
-
let start = 0;
|
|
172
|
-
for (let newline = buffered.indexOf("\n"); newline !== -1; newline = buffered.indexOf("\n", start)) {
|
|
173
|
-
yield buffered.slice(start, newline);
|
|
174
|
-
start = newline + 1;
|
|
175
|
-
}
|
|
176
|
-
buffered = buffered.slice(start);
|
|
177
|
-
if (chunk.done)
|
|
178
|
-
return;
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
finally {
|
|
182
|
-
await reader.cancel().catch(() => undefined);
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
function parseSnapshotLine(line) {
|
|
186
|
-
let record;
|
|
187
|
-
try {
|
|
188
|
-
record = JSON.parse(line);
|
|
189
|
-
}
|
|
190
|
-
catch {
|
|
191
|
-
throw malformed("snapshot line is not JSON");
|
|
192
|
-
}
|
|
193
|
-
if (typeof record !== "object" || record === null)
|
|
194
|
-
throw malformed("snapshot line is not a JSON object");
|
|
195
|
-
return record;
|
|
196
|
-
}
|
|
197
|
-
function parseSnapshotHeader(line) {
|
|
198
|
-
const record = parseSnapshotLine(line);
|
|
199
|
-
if (record.kind !== "snapshot")
|
|
200
|
-
throw malformed("snapshot did not begin with its header line");
|
|
201
|
-
if (typeof record.count !== "number" || !Number.isInteger(record.count) || record.count < 0) {
|
|
202
|
-
throw malformed("snapshot header carries no document count");
|
|
203
|
-
}
|
|
204
|
-
if (!isHeadsDigest(record.digest))
|
|
205
|
-
throw malformed("snapshot header carries no well-formed digest");
|
|
206
|
-
return { count: record.count, digest: record.digest };
|
|
207
|
-
}
|
|
208
|
-
/**
|
|
209
|
-
* The document lines of a snapshot, ending normally only at an `end` line whose count equals
|
|
210
|
-
* both the header's announcement and the documents actually seen, and only when the digest
|
|
211
|
-
* recomputed over the received heads equals the header's. Anything else is a rejection: a body
|
|
212
|
-
* that ends first is `SNAPSHOT_TRUNCATED`; a whole body whose heads digest differently is
|
|
213
|
-
* `SNAPSHOT_DIGEST_MISMATCH`; a line the contract does not admit is malformed. The digest is the
|
|
214
|
-
* client's own check of the listing it is about to trust: a count and a terminator say the body
|
|
215
|
-
* is whole, only the recipe says it is the state the header named.
|
|
216
|
-
*/
|
|
217
|
-
async function* snapshotDocuments(lines, header, status) {
|
|
218
|
-
let seen = 0;
|
|
219
|
-
const received = [];
|
|
220
|
-
for await (const line of lines) {
|
|
221
|
-
const record = parseSnapshotLine(line);
|
|
222
|
-
if (record.kind === "doc") {
|
|
223
|
-
if (typeof record.id !== "string" || typeof record.version !== "string" || typeof record.body !== "string") {
|
|
224
|
-
throw malformed("snapshot doc line lacks id, version, or body");
|
|
225
|
-
}
|
|
226
|
-
if (typeof record.frontmatter !== "object" || record.frontmatter === null || Array.isArray(record.frontmatter)) {
|
|
227
|
-
throw malformed("snapshot doc line lacks a frontmatter object");
|
|
228
|
-
}
|
|
229
|
-
seen += 1;
|
|
230
|
-
if (seen > header.count)
|
|
231
|
-
throw malformed(`snapshot delivered more than the ${header.count} announced document(s)`);
|
|
232
|
-
received.push({ id: record.id, version: record.version });
|
|
233
|
-
yield { id: record.id, version: record.version, frontmatter: record.frontmatter, body: record.body };
|
|
234
|
-
continue;
|
|
235
|
-
}
|
|
236
|
-
if (record.kind === "end") {
|
|
237
|
-
if (record.count !== seen || seen !== header.count) {
|
|
238
|
-
throw new RemoteError(`snapshot end line counts ${String(record.count)} document(s), but ${seen} of ${header.count} announced arrived`, SNAPSHOT_TRUNCATED, status);
|
|
239
|
-
}
|
|
240
|
-
const recomputed = headsDigest(received);
|
|
241
|
-
if (recomputed !== header.digest) {
|
|
242
|
-
throw new RemoteError(`snapshot header announced digest ${header.digest}, but its ${seen} document(s) digest to ${recomputed}`, SNAPSHOT_DIGEST_MISMATCH, status);
|
|
243
|
-
}
|
|
244
|
-
return;
|
|
245
|
-
}
|
|
246
|
-
throw malformed(`snapshot line has unexpected kind ${JSON.stringify(record.kind)}`);
|
|
247
|
-
}
|
|
248
|
-
throw new RemoteError(`snapshot ended after ${seen} of ${header.count} document(s) without its end line`, SNAPSHOT_TRUNCATED, status);
|
|
249
|
-
}
|
|
250
131
|
/**
|
|
251
132
|
* Transient HTTP statuses worth retrying: 500 (a Cloudflare D1 cold-start surfaces as a 500
|
|
252
133
|
* "storage caused object to be reset" the first time a hibernated database is hit), plus the edge
|
|
@@ -254,6 +135,18 @@ async function* snapshotDocuments(lines, header, status) {
|
|
|
254
135
|
* REAL result, never retried; 501/505 are terminal server bugs, not transient, so also excluded.
|
|
255
136
|
*/
|
|
256
137
|
const RETRIABLE_STATUS = new Set([500, 502, 503, 504]);
|
|
138
|
+
/** A `500` naming a stored document that does not parse: a retry reads the same bytes again. */
|
|
139
|
+
async function namesMalformedDocument(res) {
|
|
140
|
+
if (res.status !== 500)
|
|
141
|
+
return false;
|
|
142
|
+
try {
|
|
143
|
+
const envelope = (await res.clone().json());
|
|
144
|
+
return typeof envelope?.error?.details?.malformed?.reason === "string";
|
|
145
|
+
}
|
|
146
|
+
catch {
|
|
147
|
+
return false;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
257
150
|
/** Retry-backoff timing: exponential base doubling, capped, with jitter to avoid a thundering herd. */
|
|
258
151
|
const RETRY_BASE_MS = 150;
|
|
259
152
|
const RETRY_CAP_MS = 2000;
|
|
@@ -299,6 +192,8 @@ export class RemoteBackend {
|
|
|
299
192
|
fetchImpl;
|
|
300
193
|
authToken;
|
|
301
194
|
maxRetries;
|
|
195
|
+
/** The pending or settled answer to whether this host records outcomes; see {@link recordsOutcomes}. */
|
|
196
|
+
operationsSupport;
|
|
302
197
|
constructor(options) {
|
|
303
198
|
this.baseUrl = trimTrailingSlashes(options.baseUrl);
|
|
304
199
|
this.bundle = options.bundle;
|
|
@@ -329,19 +224,23 @@ export class RemoteBackend {
|
|
|
329
224
|
// object reset" when a hibernated database is first hit; also 502/503/504 from the edge) or a
|
|
330
225
|
// network/transport error — with exponential backoff + jitter, so a hibernated-backend hiccup is
|
|
331
226
|
// transparent instead of a hard failure. A REAL result (2xx, or 4xx incl. 412 VersionConflict,
|
|
332
|
-
// or 401) returns/throws immediately, never retried.
|
|
333
|
-
//
|
|
334
|
-
//
|
|
335
|
-
//
|
|
336
|
-
// strings/bytes, so reusable — no consumed-stream hazard).
|
|
227
|
+
// or 401) returns/throws immediately, never retried. A retried read is idempotent. `send`
|
|
228
|
+
// rebuilds the Request per attempt from the same `init` (bodies are strings/bytes, so reusable —
|
|
229
|
+
// no consumed-stream hazard), so every attempt carries the same headers, `Idempotency-Key`
|
|
230
|
+
// included.
|
|
337
231
|
//
|
|
338
|
-
// A write that carries `Idempotency-Key` is
|
|
339
|
-
//
|
|
232
|
+
// A write that carries `Idempotency-Key` is therefore retried as a true replay, answered from
|
|
233
|
+
// the authority's recorded outcome while it keeps that record: a retry after a lost response
|
|
340
234
|
// can neither apply twice nor surface a spurious conflict against its own earlier application.
|
|
235
|
+
// `write` and `delete` attach one to guarded document writes on a host with `operations` (see
|
|
236
|
+
// `maxRetries` for which). A guarded write without one lands the same version or a conflict
|
|
237
|
+
// (possibly SPURIOUS — a prior attempt may have committed before its response was lost), except
|
|
238
|
+
// that a document returned in between to exactly the state its premise names (the same bytes
|
|
239
|
+
// hash to the same version) lets the retry apply again over that change.
|
|
341
240
|
for (let attempt = 0;; attempt++) {
|
|
342
241
|
try {
|
|
343
242
|
const res = await this.fetchImpl(new Request(url, init));
|
|
344
|
-
if (RETRIABLE_STATUS.has(res.status) && attempt < this.maxRetries) {
|
|
243
|
+
if (RETRIABLE_STATUS.has(res.status) && attempt < this.maxRetries && !(await namesMalformedDocument(res))) {
|
|
345
244
|
await delay(retryDelayMs(attempt));
|
|
346
245
|
continue;
|
|
347
246
|
}
|
|
@@ -370,6 +269,12 @@ export class RemoteBackend {
|
|
|
370
269
|
const actual = envelope?.error?.details?.actual ?? null;
|
|
371
270
|
return new VersionConflict(fallbackId, expected, actual);
|
|
372
271
|
}
|
|
272
|
+
const malformedDoc = envelope?.error?.details?.malformed;
|
|
273
|
+
if (malformedDoc && typeof malformedDoc.reason === "string") {
|
|
274
|
+
// The server could read the request but not the stored document: the same failure a local
|
|
275
|
+
// scan raises, so `onSkip` callers can set this one document aside.
|
|
276
|
+
return new MalformedDocumentError(typeof malformedDoc.id === "string" ? malformedDoc.id : fallbackId, new Error(malformedDoc.reason));
|
|
277
|
+
}
|
|
373
278
|
const message = envelope?.error?.message ?? `wire request failed with status ${res.status}`;
|
|
374
279
|
// The envelope's own `code` wins when present (every route in this repo's servers emits
|
|
375
280
|
// one); a status-derived guess covers a malformed/absent envelope or a conformant-but-
|
|
@@ -434,15 +339,19 @@ export class RemoteBackend {
|
|
|
434
339
|
headers["If-Match"] = options.expectedVersion;
|
|
435
340
|
if (options.actor)
|
|
436
341
|
headers["X-Actor"] = options.actor;
|
|
342
|
+
// Captured before the capability question can yield, so the payload is the document as it
|
|
343
|
+
// stood when `write` was called and an unencodable one is refused before any request.
|
|
344
|
+
const body = encodeRemoteDocument(doc.frontmatter, doc.body ?? "", options.bodyReplace);
|
|
345
|
+
let minted = false;
|
|
437
346
|
if (options.requestId !== undefined) {
|
|
438
347
|
assertRequestIdentity(options.requestId);
|
|
439
348
|
headers[IDENTITY_HEADER] = options.requestId;
|
|
440
349
|
}
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
});
|
|
350
|
+
else if (options.expectedVersion !== undefined && (await this.recordsOutcomes())) {
|
|
351
|
+
headers[IDENTITY_HEADER] = mintRequestId();
|
|
352
|
+
minted = true;
|
|
353
|
+
}
|
|
354
|
+
const res = await this.sendDocWrite(`/docs/${encodeId(id)}`, { method: "PUT", headers, body }, minted);
|
|
446
355
|
if (!res.ok)
|
|
447
356
|
throw await this.toError(res, id);
|
|
448
357
|
const payload = (await res.json());
|
|
@@ -450,10 +359,11 @@ export class RemoteBackend {
|
|
|
450
359
|
}
|
|
451
360
|
/**
|
|
452
361
|
* `GET /v0/capabilities`, deployment-scoped: what this authority implements. `operations` says
|
|
453
|
-
* whether it records outcomes by request identity.
|
|
454
|
-
* and
|
|
455
|
-
*
|
|
456
|
-
*
|
|
362
|
+
* whether it records outcomes by request identity. The reference router without it refuses any
|
|
363
|
+
* request carrying `Idempotency-Key`, and the lookup route, with `400 USAGE`; a host that lacks
|
|
364
|
+
* the lookup route altogether answers it with a route-miss `404`, which {@link lookupOperation}
|
|
365
|
+
* cannot tell from "never recorded". A consumer that relies on identity checks this once before
|
|
366
|
+
* it sends any intent. Missing booleans read as `false`.
|
|
457
367
|
*/
|
|
458
368
|
async wireCapabilities() {
|
|
459
369
|
const res = await this.send("/v0/capabilities", { method: "GET" }, "deployment");
|
|
@@ -472,16 +382,65 @@ export class RemoteBackend {
|
|
|
472
382
|
snapshot: flag("snapshot"),
|
|
473
383
|
};
|
|
474
384
|
}
|
|
385
|
+
/**
|
|
386
|
+
* Whether this host records outcomes by request identity, asked through `GET /v0/capabilities`
|
|
387
|
+
* and shared by concurrent callers. A host that answers anything but a `2xx` with
|
|
388
|
+
* `operations: true` is treated as not recording them, since a host that does not record them
|
|
389
|
+
* refuses a key with `400`. That answer is kept for the backend's lifetime, except that a
|
|
390
|
+
* transient status still returned after `send`'s retries, or a transport failure, is not kept:
|
|
391
|
+
* the former sends this write unidentified, and the latter rejects the write before it is sent.
|
|
392
|
+
* A kept yes is also dropped when a minted key meets the host's identity refusal; see
|
|
393
|
+
* {@link sendDocWrite}.
|
|
394
|
+
*/
|
|
395
|
+
recordsOutcomes() {
|
|
396
|
+
this.operationsSupport ??= this.probeOperations();
|
|
397
|
+
return this.operationsSupport;
|
|
398
|
+
}
|
|
399
|
+
async probeOperations() {
|
|
400
|
+
let res;
|
|
401
|
+
try {
|
|
402
|
+
res = await this.send("/v0/capabilities", { method: "GET" }, "deployment");
|
|
403
|
+
}
|
|
404
|
+
catch (err) {
|
|
405
|
+
this.operationsSupport = undefined;
|
|
406
|
+
throw err;
|
|
407
|
+
}
|
|
408
|
+
if (RETRIABLE_STATUS.has(res.status))
|
|
409
|
+
this.operationsSupport = undefined;
|
|
410
|
+
if (!res.ok)
|
|
411
|
+
return false;
|
|
412
|
+
try {
|
|
413
|
+
const payload = (await res.json());
|
|
414
|
+
return payload?.operations === true;
|
|
415
|
+
}
|
|
416
|
+
catch {
|
|
417
|
+
return false;
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
/**
|
|
421
|
+
* Send a document `PUT` or `DELETE`. When its `Idempotency-Key` was minted here, a kept
|
|
422
|
+
* `operations` answer may be stale: a host that has since lost its outcome store refuses every
|
|
423
|
+
* key with the wire's `400` {@link IDENTITY_UNSUPPORTED} before applying anything. That answer
|
|
424
|
+
* is dropped and the write is sent once more without a key, as to any host without
|
|
425
|
+
* `operations`. A caller's own `requestId` is never dropped, so its refusal is the answer.
|
|
426
|
+
*/
|
|
427
|
+
async sendDocWrite(path, init, minted) {
|
|
428
|
+
const res = await this.send(path, init);
|
|
429
|
+
if (!minted || res.status !== 400 || !(await refusesIdentity(res.clone())))
|
|
430
|
+
return res;
|
|
431
|
+
this.operationsSupport = undefined;
|
|
432
|
+
const { [IDENTITY_HEADER]: _refused, ...headers } = init.headers;
|
|
433
|
+
return this.send(path, { ...init, headers });
|
|
434
|
+
}
|
|
475
435
|
/**
|
|
476
436
|
* `GET /heads`: every document id and version the authority holds, under one digest, or
|
|
477
437
|
* `null` when `ifNoneMatch` named the digest it still holds (a `304`: nothing changed). A
|
|
478
438
|
* `200` is diffed against the caller's own copy: an id missing from `heads` was deleted, a
|
|
479
439
|
* differing version changed. A `200` without a well-formed digest, whose `count` and rows
|
|
480
440
|
* disagree, or whose rows digest by the documented recipe to something other than the served
|
|
481
|
-
* digest, is rejected rather than trusted
|
|
482
|
-
* `
|
|
483
|
-
*
|
|
484
|
-
* diffed as a mass deletion. Not part of the {@link StorageBackend} seam.
|
|
441
|
+
* digest, is rejected rather than trusted ({@link parseHeadsAnswer} owns that admission), as
|
|
442
|
+
* is a `304` to a request that sent no `ifNoneMatch`. Not part of the {@link StorageBackend}
|
|
443
|
+
* seam.
|
|
485
444
|
*/
|
|
486
445
|
async heads(options = {}) {
|
|
487
446
|
const headers = {};
|
|
@@ -497,21 +456,7 @@ export class RemoteBackend {
|
|
|
497
456
|
}
|
|
498
457
|
if (!res.ok)
|
|
499
458
|
throw await this.toError(res, "heads");
|
|
500
|
-
|
|
501
|
-
if (!isHeadsDigest(payload?.digest))
|
|
502
|
-
throw malformed("wire heads answered without a well-formed digest");
|
|
503
|
-
if (!Array.isArray(payload.heads) || !payload.heads.every(isDocumentHead)) {
|
|
504
|
-
throw malformed("wire heads answered without a heads array of { id, version } rows");
|
|
505
|
-
}
|
|
506
|
-
if (payload.count !== payload.heads.length) {
|
|
507
|
-
throw malformed(`wire heads count ${String(payload.count)} disagrees with its ${payload.heads.length} row(s)`);
|
|
508
|
-
}
|
|
509
|
-
const heads = payload.heads.map(({ id, version }) => ({ id, version }));
|
|
510
|
-
const recomputed = headsDigest(heads);
|
|
511
|
-
if (recomputed !== payload.digest) {
|
|
512
|
-
throw malformed(`wire heads served digest ${payload.digest}, but its ${heads.length} row(s) digest to ${recomputed}`);
|
|
513
|
-
}
|
|
514
|
-
return { digest: payload.digest, heads };
|
|
459
|
+
return parseHeadsAnswer(await res.json());
|
|
515
460
|
}
|
|
516
461
|
/**
|
|
517
462
|
* `GET /snapshot`: the whole bundle in one response. The header line is parsed before this
|
|
@@ -519,8 +464,9 @@ export class RemoteBackend {
|
|
|
519
464
|
* the documents then stream through {@link RemoteSnapshot.docs}. Transient retry applies only
|
|
520
465
|
* to obtaining the response: a body cut mid-stream is reported to the consumer as
|
|
521
466
|
* `SNAPSHOT_TRUNCATED`, a whole body whose heads do not digest to the header's announcement as
|
|
522
|
-
* `SNAPSHOT_DIGEST_MISMATCH
|
|
523
|
-
* not part of a snapshot. Not part of the
|
|
467
|
+
* `SNAPSHOT_DIGEST_MISMATCH` ({@link readSnapshotStream} owns the grammar), and re-requesting
|
|
468
|
+
* is the consumer's decision. Reserved files are not part of a snapshot. Not part of the
|
|
469
|
+
* {@link StorageBackend} seam.
|
|
524
470
|
*/
|
|
525
471
|
async snapshot() {
|
|
526
472
|
const res = await this.send("/snapshot", { method: "GET" });
|
|
@@ -528,19 +474,7 @@ export class RemoteBackend {
|
|
|
528
474
|
throw await this.toError(res, "snapshot");
|
|
529
475
|
if (!res.body)
|
|
530
476
|
throw new RemoteError("snapshot response carried no body", SNAPSHOT_TRUNCATED, res.status);
|
|
531
|
-
|
|
532
|
-
const first = await lines.next();
|
|
533
|
-
if (first.done)
|
|
534
|
-
throw new RemoteError("snapshot ended before its header line", SNAPSHOT_TRUNCATED, res.status);
|
|
535
|
-
let header;
|
|
536
|
-
try {
|
|
537
|
-
header = parseSnapshotHeader(first.value);
|
|
538
|
-
}
|
|
539
|
-
catch (err) {
|
|
540
|
-
await lines.return(undefined);
|
|
541
|
-
throw err;
|
|
542
|
-
}
|
|
543
|
-
return { header, docs: snapshotDocuments(lines, header, res.status) };
|
|
477
|
+
return readSnapshotStream(res.body, { status: res.status });
|
|
544
478
|
}
|
|
545
479
|
/**
|
|
546
480
|
* `GET /operations/{requestId}`: the authority's recorded outcome for an identified write, or
|
|
@@ -576,11 +510,14 @@ export class RemoteBackend {
|
|
|
576
510
|
* {@link RemoteBackend.queryHeads} are both thin row-mappings over it, so a wire
|
|
577
511
|
* pagination change cannot make the two scans silently paginate differently.
|
|
578
512
|
*/
|
|
579
|
-
async pageDocs(baseParams, mapRow, errorContext) {
|
|
513
|
+
async pageDocs(baseParams, mapRow, errorContext, skipped) {
|
|
580
514
|
const rows = [];
|
|
581
515
|
let cursor;
|
|
582
516
|
for (;;) {
|
|
583
517
|
const params = new URLSearchParams(baseParams);
|
|
518
|
+
// Ask the server to leave an unparsable document out and name it, rather than fail the page.
|
|
519
|
+
if (skipped)
|
|
520
|
+
params.set("malformed", "skip");
|
|
584
521
|
if (cursor)
|
|
585
522
|
params.set("cursor", cursor);
|
|
586
523
|
const qs = params.toString();
|
|
@@ -590,6 +527,13 @@ export class RemoteBackend {
|
|
|
590
527
|
const payload = (await res.json());
|
|
591
528
|
for (const row of payload.docs)
|
|
592
529
|
rows.push(mapRow(row));
|
|
530
|
+
if (skipped && Array.isArray(payload.skipped)) {
|
|
531
|
+
for (const entry of payload.skipped) {
|
|
532
|
+
if (typeof entry?.id !== "string")
|
|
533
|
+
continue;
|
|
534
|
+
skipped.set(entry.id, typeof entry.reason === "string" ? entry.reason : "malformed frontmatter");
|
|
535
|
+
}
|
|
536
|
+
}
|
|
593
537
|
if (!payload.next_cursor)
|
|
594
538
|
break;
|
|
595
539
|
cursor = payload.next_cursor;
|
|
@@ -600,7 +544,11 @@ export class RemoteBackend {
|
|
|
600
544
|
const params = new URLSearchParams();
|
|
601
545
|
if (prefix)
|
|
602
546
|
params.set("prefix", prefix);
|
|
603
|
-
|
|
547
|
+
// A document the server could not parse still exists: it is listed, and reading it raises
|
|
548
|
+
// the same MalformedDocumentError a local read does.
|
|
549
|
+
const skipped = new Map();
|
|
550
|
+
const ids = await this.pageDocs(params, (row) => row.id, prefix ?? "", skipped);
|
|
551
|
+
return [...new Set([...ids, ...skipped.keys()])].sort(compareStorageKeys);
|
|
604
552
|
}
|
|
605
553
|
/**
|
|
606
554
|
* The seam's OPTIONAL head-projection push-down, over the SAME `GET /docs` route
|
|
@@ -613,7 +561,7 @@ export class RemoteBackend {
|
|
|
613
561
|
* `docs/WIRE-PROTOCOL.md`); the engine's `queryHeads` re-filter covers it, per the
|
|
614
562
|
* seam contract (over-returning is fine; semantics live in core).
|
|
615
563
|
*/
|
|
616
|
-
async queryHeads(filter = {}) {
|
|
564
|
+
async queryHeads(filter = {}, options = {}) {
|
|
617
565
|
const params = new URLSearchParams();
|
|
618
566
|
params.set("fields", "frontmatter");
|
|
619
567
|
if (filter.prefix)
|
|
@@ -622,7 +570,17 @@ export class RemoteBackend {
|
|
|
622
570
|
params.set("type", filter.type);
|
|
623
571
|
for (const tag of filter.tags ?? [])
|
|
624
572
|
params.append("tag", tag);
|
|
625
|
-
|
|
573
|
+
const skipped = new Map();
|
|
574
|
+
const rows = await this.pageDocs(params, (row) => ({ id: row.id, frontmatter: row.frontmatter, version: row.version }), filter.prefix ?? "", skipped);
|
|
575
|
+
// The server leaves a malformed document out of the listing and names it. A caller that
|
|
576
|
+
// asked for no skip report keeps the scan's fail-loud contract.
|
|
577
|
+
const named = [...skipped].sort(([a], [b]) => compareStorageKeys(a, b));
|
|
578
|
+
if (named.length > 0 && !options.onSkip) {
|
|
579
|
+
throw new MalformedDocumentError(named[0][0], new Error(named[0][1]));
|
|
580
|
+
}
|
|
581
|
+
for (const [id, reason] of named)
|
|
582
|
+
options.onSkip?.({ id, reason });
|
|
583
|
+
return rows;
|
|
626
584
|
}
|
|
627
585
|
async versions(id) {
|
|
628
586
|
assertSafeConceptId(id);
|
|
@@ -688,11 +646,20 @@ export class RemoteBackend {
|
|
|
688
646
|
const headers = {};
|
|
689
647
|
if (options.expectedVersion !== undefined)
|
|
690
648
|
headers["If-Match"] = options.expectedVersion;
|
|
649
|
+
let minted = false;
|
|
691
650
|
if (options.requestId !== undefined) {
|
|
692
651
|
assertRequestIdentity(options.requestId);
|
|
693
652
|
headers[IDENTITY_HEADER] = options.requestId;
|
|
694
653
|
}
|
|
695
|
-
|
|
654
|
+
else if (options.expectedVersion !== undefined &&
|
|
655
|
+
isContentVersion(stripETagWrapper(options.expectedVersion)) &&
|
|
656
|
+
(await this.recordsOutcomes())) {
|
|
657
|
+
// The wire identifies a delete only under a content-version premise; any other premise
|
|
658
|
+
// stays an unidentified request rather than becoming a `400`.
|
|
659
|
+
headers[IDENTITY_HEADER] = mintRequestId();
|
|
660
|
+
minted = true;
|
|
661
|
+
}
|
|
662
|
+
const res = await this.sendDocWrite(`/docs/${encodeId(id)}`, { method: "DELETE", headers }, minted);
|
|
696
663
|
if (!res.ok)
|
|
697
664
|
throw await this.toError(res, id);
|
|
698
665
|
const payload = (await res.json());
|