@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
|
@@ -0,0 +1,620 @@
|
|
|
1
|
+
import { validateConnectorRequest, declarationRequestRules, placeholders, providerRequestRules } from "../connector-request.js";
|
|
2
|
+
/**
|
|
3
|
+
* The observation loop: run a profile page by page behind the guarded transport, diff each page
|
|
4
|
+
* against the stored records into record-overlay changes, hand them to the caller's commit, and
|
|
5
|
+
* only then store the cursor and the records. A change the commit asks to retry stays pending in
|
|
6
|
+
* the machine state and is planned again before the next run fetches anything, so the cursor
|
|
7
|
+
* never waits on one record. A pass counts as a complete observation only when it ran in complete
|
|
8
|
+
* mode from no cursor to its last page within this run, the profile claimed it complete and the
|
|
9
|
+
* source is declared complete; only a complete observation counts a stored record as missing, and
|
|
10
|
+
* a record is `disappeared` after `missing_threshold` of them.
|
|
11
|
+
*/
|
|
12
|
+
import { changeBetween, findingsFor, mapRecord } from "../connectors.js";
|
|
13
|
+
import { overlayCanonicalJson } from "../record-overlay/canonical.js";
|
|
14
|
+
import { DEFAULT_SOURCE_BUDGET, DEFAULT_STATUS_RULES, guardTransport, raceAbort, sourceHostSet, SourceStop } from "./guard.js";
|
|
15
|
+
import { isSourceState } from "./machine-state.js";
|
|
16
|
+
/** Complete observations without a record before it is `disappeared`, when the feed names none. */
|
|
17
|
+
export const DEFAULT_MISSING_THRESHOLD = 2;
|
|
18
|
+
/** The largest stored cursor, as canonical JSON characters. */
|
|
19
|
+
export const MAX_CURSOR_CHARS = 8192;
|
|
20
|
+
/**
|
|
21
|
+
* Replays of a pending change whose commit threw, after which the change is dropped with a finding:
|
|
22
|
+
* the record is planned again when it is next observed.
|
|
23
|
+
*/
|
|
24
|
+
export const MAX_REPLAY_FAILURES = 5;
|
|
25
|
+
const PROFILE_ID = /^[a-z][a-z0-9-]*$/;
|
|
26
|
+
const UNSUPPORTED_CONTENT = "SOURCE_UNSUPPORTED_CONTENT";
|
|
27
|
+
/** Thrown by a profile that cannot read a provider's body; the run ends `unsupported_content`. */
|
|
28
|
+
export function unsupportedContent(detail) {
|
|
29
|
+
throw Object.assign(new Error(`unsupported content: ${detail}`), { code: UNSUPPORTED_CONTENT, detail });
|
|
30
|
+
}
|
|
31
|
+
const record = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
|
|
32
|
+
const clip = (text, n) => (text.length > n ? text.slice(0, n - 1) + "…" : text);
|
|
33
|
+
/** JSON data only: finite numbers, plain objects and arrays, no undefined members. */
|
|
34
|
+
function isJsonData(value, depth = 0) {
|
|
35
|
+
if (depth > 32)
|
|
36
|
+
return false;
|
|
37
|
+
if (value === null || typeof value === "string" || typeof value === "boolean")
|
|
38
|
+
return true;
|
|
39
|
+
if (typeof value === "number")
|
|
40
|
+
return Number.isFinite(value);
|
|
41
|
+
if (Array.isArray(value))
|
|
42
|
+
return value.every((item) => isJsonData(item, depth + 1));
|
|
43
|
+
if (!record(value))
|
|
44
|
+
return false;
|
|
45
|
+
// A plain object from any realm: its prototype is null or a prototype that itself has none.
|
|
46
|
+
const proto = Object.getPrototypeOf(value);
|
|
47
|
+
if (proto !== null && Object.getPrototypeOf(proto) !== null)
|
|
48
|
+
return false;
|
|
49
|
+
return Object.values(value).every((item) => item !== undefined && isJsonData(item, depth + 1));
|
|
50
|
+
}
|
|
51
|
+
function budgetOf(given) {
|
|
52
|
+
const budget = { ...DEFAULT_SOURCE_BUDGET, ...given };
|
|
53
|
+
// Timer delays above 2^31 - 1 milliseconds overflow to an immediate timeout.
|
|
54
|
+
const ok = ["requests", "maxBytes", "redirects", "pages", "deadlineMs", "requestTimeoutMs"].every((key) => Number.isInteger(budget[key])
|
|
55
|
+
&& budget[key] >= (key === "redirects" ? 0 : 1) && budget[key] <= 2 ** 31 - 1);
|
|
56
|
+
return ok ? budget : undefined;
|
|
57
|
+
}
|
|
58
|
+
function statusesOf(given) {
|
|
59
|
+
const statuses = { ...DEFAULT_STATUS_RULES, ...given };
|
|
60
|
+
const ok = Object.values(statuses).every((list) => Array.isArray(list) && list.every((s) => Number.isInteger(s) && s >= 400 && s <= 599));
|
|
61
|
+
return ok ? statuses : undefined;
|
|
62
|
+
}
|
|
63
|
+
function profileProblem(profile) {
|
|
64
|
+
if (!record(profile))
|
|
65
|
+
return "not a profile";
|
|
66
|
+
if (typeof profile.id !== "string" || !PROFILE_ID.test(profile.id))
|
|
67
|
+
return "id is a slug";
|
|
68
|
+
if (!Number.isInteger(profile.version) || profile.version < 1)
|
|
69
|
+
return "version is a positive integer";
|
|
70
|
+
if (profile.kind !== "generic" && profile.kind !== "extension")
|
|
71
|
+
return "kind is generic or extension";
|
|
72
|
+
if (typeof profile.validateSpec !== "function" || typeof profile.hostsFor !== "function" || typeof profile.observe !== "function")
|
|
73
|
+
return "missing a method";
|
|
74
|
+
return undefined;
|
|
75
|
+
}
|
|
76
|
+
/** The page as the engine accepts it, or why not. */
|
|
77
|
+
function pageProblem(page) {
|
|
78
|
+
if (!record(page))
|
|
79
|
+
return "page_invalid";
|
|
80
|
+
if (!Array.isArray(page.records) || typeof page.more !== "boolean" || typeof page.complete !== "boolean")
|
|
81
|
+
return "page_invalid";
|
|
82
|
+
if (page.diagnostics !== undefined && !(Array.isArray(page.diagnostics) && page.diagnostics.every((d) => record(d) && typeof d.code === "string" && typeof d.message === "string")))
|
|
83
|
+
return "page_invalid";
|
|
84
|
+
if (page.validators !== undefined && !(record(page.validators) && Object.values(page.validators).every((v) => typeof v === "string")))
|
|
85
|
+
return "page_invalid";
|
|
86
|
+
const cursor = page.nextCursor === undefined ? null : page.nextCursor;
|
|
87
|
+
if (!isJsonData(cursor) || (overlayCanonicalJson(cursor) ?? "").length > MAX_CURSOR_CHARS)
|
|
88
|
+
return "cursor_invalid";
|
|
89
|
+
return undefined;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Diff one page against the stored records. Within a page the last occurrence of an id wins and is
|
|
93
|
+
* diffed against the stored entry, so every change's `before` is what the overlay last accepted.
|
|
94
|
+
*/
|
|
95
|
+
function diffPage(state, page, bound, observedAt) {
|
|
96
|
+
const diagnostics = [];
|
|
97
|
+
const profileDiagnostics = [...(page.diagnostics ?? [])].map((d) => ({ ...d }));
|
|
98
|
+
const last = new Map();
|
|
99
|
+
const seen = new Set();
|
|
100
|
+
let notObjects = 0;
|
|
101
|
+
let duplicates = 0;
|
|
102
|
+
for (const raw of page.records) {
|
|
103
|
+
if (!record(raw)) {
|
|
104
|
+
notObjects += 1;
|
|
105
|
+
continue;
|
|
106
|
+
}
|
|
107
|
+
const mapped = mapRecord(bound.source, raw, bound.kind);
|
|
108
|
+
diagnostics.push(...mapped.diagnostics);
|
|
109
|
+
const id = mapped.external_id;
|
|
110
|
+
if (id === null)
|
|
111
|
+
continue;
|
|
112
|
+
seen.add(id);
|
|
113
|
+
if (last.has(id))
|
|
114
|
+
duplicates += 1;
|
|
115
|
+
last.set(id, mapped);
|
|
116
|
+
}
|
|
117
|
+
if (notObjects)
|
|
118
|
+
profileDiagnostics.push({ code: "RECORD_NOT_OBJECT", message: "records that are not JSON objects were skipped", count: notObjects });
|
|
119
|
+
if (duplicates)
|
|
120
|
+
profileDiagnostics.push({ code: "RECORD_DUPLICATE", message: "a page listed the same record more than once; the last one was used", count: duplicates });
|
|
121
|
+
const changes = [];
|
|
122
|
+
const entries = new Map();
|
|
123
|
+
for (const [id, mapped] of last) {
|
|
124
|
+
const stored = Object.hasOwn(state.records, id) ? state.records[id] : undefined;
|
|
125
|
+
if (mapped.kind === "refused") {
|
|
126
|
+
// Refused but present: still observed, so never missing; its accepted values stay.
|
|
127
|
+
if (stored)
|
|
128
|
+
entries.set(id, { ...stored, missing: 0, seen_at: observedAt, first_missing_at: undefined });
|
|
129
|
+
continue;
|
|
130
|
+
}
|
|
131
|
+
const previous = stored && !stored.gone ? stored.after : null;
|
|
132
|
+
const change = changeBetween(previous, mapped, { external_id: id, change_kind: previous === null ? "new" : "changed" });
|
|
133
|
+
entries.set(id, { after: change.after, digest: change.content_digest, missing: 0, seen_at: observedAt });
|
|
134
|
+
if (previous === null || change.changed_fields.length > 0)
|
|
135
|
+
changes.push(change);
|
|
136
|
+
}
|
|
137
|
+
return { changes, entries, seen, diagnostics, profileDiagnostics };
|
|
138
|
+
}
|
|
139
|
+
/** Disappearances at the end of a complete observation: every present record the pass did not list. */
|
|
140
|
+
function missingAfterComplete(records, seen, threshold, observedAt) {
|
|
141
|
+
const changes = [];
|
|
142
|
+
const entries = new Map();
|
|
143
|
+
for (const [id, entry] of Object.entries(records)) {
|
|
144
|
+
if (entry.gone || seen.has(id))
|
|
145
|
+
continue;
|
|
146
|
+
const missing = entry.missing + 1;
|
|
147
|
+
const firstMissingAt = entry.first_missing_at ?? observedAt;
|
|
148
|
+
if (missing >= threshold) {
|
|
149
|
+
entries.set(id, { ...entry, missing, first_missing_at: firstMissingAt, gone: true });
|
|
150
|
+
changes.push({
|
|
151
|
+
external_id: id, change_kind: "disappeared", changed_fields: [], before: { ...entry.after }, after: null,
|
|
152
|
+
content_digest: entry.digest, missing_count: missing, first_missing_at: firstMissingAt,
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
else {
|
|
156
|
+
entries.set(id, { ...entry, missing, first_missing_at: firstMissingAt });
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
return { changes, entries };
|
|
160
|
+
}
|
|
161
|
+
/** Drop `undefined` members so the stored state is plain JSON. */
|
|
162
|
+
function entryJson(entry) {
|
|
163
|
+
const { first_missing_at: firstMissingAt, gone, ...rest } = entry;
|
|
164
|
+
return { ...rest, ...(firstMissingAt !== undefined ? { first_missing_at: firstMissingAt } : {}), ...(gone ? { gone } : {}) };
|
|
165
|
+
}
|
|
166
|
+
function emptyState(profile) {
|
|
167
|
+
return { v: 1, profile: { id: profile.id, version: profile.version }, cursor: null, open: false, records: {}, complete_observations: 0 };
|
|
168
|
+
}
|
|
169
|
+
const OUTCOME_TEXT = {
|
|
170
|
+
deferred: "the provider asked to slow down; the run stopped and resumes from the last committed page",
|
|
171
|
+
credential_refused: "the provider refused the credential; runs stay stopped until the credential changes",
|
|
172
|
+
provider_unavailable: "the provider did not answer usefully; nothing past the last committed page was read",
|
|
173
|
+
not_found: "the provider says the resource does not exist",
|
|
174
|
+
unsupported_content: "the provider sent a body the profile cannot read; nothing from it was written",
|
|
175
|
+
too_large: "the provider sent a body over the byte bound; nothing from it was written",
|
|
176
|
+
refused: "a request was refused before it was sent",
|
|
177
|
+
failed: "the run failed before its page was stored",
|
|
178
|
+
};
|
|
179
|
+
/** One finding for a run that did not end as observed, keyed so a repeat collapses into one Signal. */
|
|
180
|
+
function outcomeFinding(source, stop, url) {
|
|
181
|
+
if (stop.outcome === "observed" || (stop.outcome === "deferred" && (stop.reason === "budget" || stop.reason === "deadline")))
|
|
182
|
+
return null;
|
|
183
|
+
const where = url ? ` (${clip(url, 160)})` : "";
|
|
184
|
+
const status = stop.status !== undefined ? ` Status ${stop.status}.` : "";
|
|
185
|
+
return {
|
|
186
|
+
key: `source:${source}:${stop.outcome}${stop.reason ? `:${stop.reason}` : ""}`,
|
|
187
|
+
title: clip(`Source ${source}: ${stop.outcome.replaceAll("_", " ")}${stop.reason ? ` (${stop.reason.replaceAll("_", " ")})` : ""}`, 140),
|
|
188
|
+
summary: clip(`${OUTCOME_TEXT[stop.outcome] ?? stop.outcome}.${status}${where}`, 600),
|
|
189
|
+
priority: stop.outcome === "credential_refused" || stop.outcome === "refused" || stop.outcome === "failed" ? "normal" : "low",
|
|
190
|
+
facts: { outcome: stop.outcome, reason: stop.reason ?? null, status: stop.status ?? null },
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
/** A URL as a finding shows it: scheme, host and path, never the query. */
|
|
194
|
+
function findingUrl(url) {
|
|
195
|
+
if (!url)
|
|
196
|
+
return undefined;
|
|
197
|
+
try {
|
|
198
|
+
const parsed = new URL(url);
|
|
199
|
+
return `${parsed.protocol}//${parsed.host}${parsed.pathname}`;
|
|
200
|
+
}
|
|
201
|
+
catch {
|
|
202
|
+
return undefined;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
/** The ids a commit did not write, read defensively. */
|
|
206
|
+
function commitResult(result) {
|
|
207
|
+
const ids = (value) => (Array.isArray(value) ? value.filter((id) => typeof id === "string") : []);
|
|
208
|
+
const retry = new Set(record(result) ? ids(result.retry) : []);
|
|
209
|
+
const hold = new Set((record(result) ? ids(result.hold) : []).filter((id) => !retry.has(id)));
|
|
210
|
+
return { hold, retry };
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Run one observation of a bound Source Feed. Returns a receipt; never throws for a provider,
|
|
214
|
+
* profile, guard or commit problem, which all end the run with an outcome and a finding.
|
|
215
|
+
*/
|
|
216
|
+
export async function observeSource(options) {
|
|
217
|
+
const now = options.now ?? (() => new Date());
|
|
218
|
+
const observedAt = now().toISOString();
|
|
219
|
+
// Authority is private to this run: callback views cannot rewrite the checked declaration.
|
|
220
|
+
const { bound, connector } = structuredClone({ bound: options.bound, connector: options.connector });
|
|
221
|
+
const profile = options.profile;
|
|
222
|
+
const requestedMode = options.mode ?? "incremental";
|
|
223
|
+
const receipt = {
|
|
224
|
+
feed: bound.policy.feed,
|
|
225
|
+
provider: bound.policy.provider,
|
|
226
|
+
source: bound.source.name,
|
|
227
|
+
profile: { id: String(profile?.id), version: Number(profile?.version) },
|
|
228
|
+
mode: requestedMode,
|
|
229
|
+
observed_at: observedAt,
|
|
230
|
+
outcome: "observed",
|
|
231
|
+
retry: true,
|
|
232
|
+
pages: 0,
|
|
233
|
+
held: 0,
|
|
234
|
+
pending: 0,
|
|
235
|
+
requests: [],
|
|
236
|
+
pass: { resumed: false, ended: false, complete: false },
|
|
237
|
+
changes: { new: 0, changed: 0, disappeared: 0 },
|
|
238
|
+
cursor: null,
|
|
239
|
+
diagnostics: [],
|
|
240
|
+
profile_diagnostics: [],
|
|
241
|
+
findings: [],
|
|
242
|
+
};
|
|
243
|
+
let stop;
|
|
244
|
+
let stopUrl;
|
|
245
|
+
const extraFindings = [];
|
|
246
|
+
const finish = () => {
|
|
247
|
+
if (stop) {
|
|
248
|
+
receipt.outcome = stop.outcome;
|
|
249
|
+
receipt.retry = stop.retry;
|
|
250
|
+
if (stop.reason !== undefined)
|
|
251
|
+
receipt.reason = stop.reason;
|
|
252
|
+
if (stop.detail !== undefined)
|
|
253
|
+
receipt.detail = clip(stop.detail, 300);
|
|
254
|
+
if (stop.status !== undefined)
|
|
255
|
+
receipt.status = stop.status;
|
|
256
|
+
if (stop.retry_after !== undefined)
|
|
257
|
+
receipt.retry_after = stop.retry_after;
|
|
258
|
+
}
|
|
259
|
+
const mapping = findingsFor(receipt.diagnostics, { source: bound.source.name });
|
|
260
|
+
const outcome = stop ? outcomeFinding(bound.source.name, stop, findingUrl(stopUrl)) : null;
|
|
261
|
+
receipt.findings = [...(outcome ? [outcome] : []), ...extraFindings, ...mapping];
|
|
262
|
+
return receipt;
|
|
263
|
+
};
|
|
264
|
+
const refuse = (reason, detail) => {
|
|
265
|
+
stop = { outcome: "refused", reason, retry: false, ...(detail !== undefined ? { detail } : {}) };
|
|
266
|
+
return finish();
|
|
267
|
+
};
|
|
268
|
+
const errorText = (error) => (error instanceof Error ? error.message : String(error));
|
|
269
|
+
const problem = profileProblem(profile);
|
|
270
|
+
if (problem)
|
|
271
|
+
return refuse("profile_invalid", problem);
|
|
272
|
+
if (profile.id !== bound.source.profile)
|
|
273
|
+
return refuse("profile_mismatch", `the source names ${bound.source.profile}`);
|
|
274
|
+
if (requestedMode !== "incremental" && requestedMode !== "complete")
|
|
275
|
+
return refuse("mode_invalid");
|
|
276
|
+
const declaredRequest = bound.source.request;
|
|
277
|
+
if (declaredRequest !== undefined) {
|
|
278
|
+
const problems = validateConnectorRequest(bound.source, connector);
|
|
279
|
+
if (problems.length)
|
|
280
|
+
return refuse("declaration_invalid", problems.map(p => `${p.path} ${p.message}`).join("; "));
|
|
281
|
+
}
|
|
282
|
+
if (declaredRequest && placeholders(bound.source.identity.resource).some(name => typeof bound.parameters[name] !== "string" || bound.parameters[name] === "" || [".", ".."].includes(bound.parameters[name])))
|
|
283
|
+
return refuse("binding_invalid", "request requires every resource binding value");
|
|
284
|
+
const budget = budgetOf(options.budget);
|
|
285
|
+
if (!budget)
|
|
286
|
+
return refuse("budget_invalid");
|
|
287
|
+
if (declaredRequest?.rate)
|
|
288
|
+
budget.requests = Math.min(budget.requests, declaredRequest.rate.requests);
|
|
289
|
+
const statuses = statusesOf(declaredRequest?.errors ?? options.statuses);
|
|
290
|
+
if (!statuses)
|
|
291
|
+
return refuse("statuses_invalid");
|
|
292
|
+
const identity = structuredClone(options.identity);
|
|
293
|
+
if (!record(identity) || typeof identity.userAgent !== "string" || !/^[\x20-\x7e]{1,512}$/.test(identity.userAgent)
|
|
294
|
+
|| typeof identity.productToken !== "string" || !/^[A-Za-z_-]{1,64}$/.test(identity.productToken))
|
|
295
|
+
return refuse("identity_invalid");
|
|
296
|
+
let spec;
|
|
297
|
+
let hosts;
|
|
298
|
+
try {
|
|
299
|
+
const checked = profile.validateSpec(options.spec);
|
|
300
|
+
if (!checked.ok)
|
|
301
|
+
return refuse("spec_invalid", checked.errors.map((e) => `${e.path} ${e.message}`).join("; "));
|
|
302
|
+
spec = checked.spec;
|
|
303
|
+
hosts = profile.hostsFor(spec, structuredClone(bound));
|
|
304
|
+
}
|
|
305
|
+
catch (error) {
|
|
306
|
+
stop = { outcome: "failed", reason: "profile_error", retry: false, detail: errorText(error) };
|
|
307
|
+
return finish();
|
|
308
|
+
}
|
|
309
|
+
if (!Array.isArray(hosts) || !hosts.every((host) => typeof host === "string"))
|
|
310
|
+
return refuse("hosts_invalid");
|
|
311
|
+
const hostSet = sourceHostSet(connector, hosts);
|
|
312
|
+
if (!hostSet.ok)
|
|
313
|
+
return refuse("hosts_undeclared", hostSet.undeclared.join(", "));
|
|
314
|
+
if (hostSet.hosts.credential.size && !declaredRequest)
|
|
315
|
+
return refuse("declaration_required", "credentialed sources require a binding-scoped request declaration");
|
|
316
|
+
const key = bound.policy.feed;
|
|
317
|
+
let version;
|
|
318
|
+
let state;
|
|
319
|
+
try {
|
|
320
|
+
const stored = await options.state.read(key);
|
|
321
|
+
if (stored && !isSourceState(stored.state)) {
|
|
322
|
+
stop = { outcome: "failed", reason: "state_invalid", retry: false };
|
|
323
|
+
return finish();
|
|
324
|
+
}
|
|
325
|
+
version = stored?.version ?? null;
|
|
326
|
+
state = stored?.state ?? emptyState(profile);
|
|
327
|
+
}
|
|
328
|
+
catch (error) {
|
|
329
|
+
stop = { outcome: "failed", reason: "state_unreadable", retry: true, detail: errorText(error) };
|
|
330
|
+
return finish();
|
|
331
|
+
}
|
|
332
|
+
const baseline = version === null;
|
|
333
|
+
receipt.cursor = state.cursor;
|
|
334
|
+
receipt.pending = Object.keys(state.pending ?? {}).length;
|
|
335
|
+
let mode = requestedMode;
|
|
336
|
+
if (state.profile.id !== profile.id || state.profile.version !== profile.version) {
|
|
337
|
+
// A cursor is the profile's own format, so another profile's is never handed back.
|
|
338
|
+
if (!baseline)
|
|
339
|
+
receipt.cursor_reset = "profile_version";
|
|
340
|
+
state = { ...state, profile: { id: profile.id, version: profile.version }, cursor: null, open: false };
|
|
341
|
+
delete state.validators;
|
|
342
|
+
if (!baseline)
|
|
343
|
+
mode = "complete";
|
|
344
|
+
}
|
|
345
|
+
receipt.mode = mode;
|
|
346
|
+
// A complete observation always lists from the start; an incremental one continues the stored cursor.
|
|
347
|
+
const resumed = mode === "incremental" && state.open;
|
|
348
|
+
receipt.pass.resumed = resumed;
|
|
349
|
+
let cursor = mode === "complete" ? null : state.cursor;
|
|
350
|
+
const threshold = bound.policy.missing_threshold ?? DEFAULT_MISSING_THRESHOLD;
|
|
351
|
+
const sourceComplete = bound.source.completeness === "complete";
|
|
352
|
+
// Without the caller's deadline the run has its own; every request has its own timeout either way.
|
|
353
|
+
const ownDeadline = options.signal ? undefined : new AbortController();
|
|
354
|
+
const timer = ownDeadline ? setTimeout(() => ownDeadline.abort(), budget.deadlineMs) : undefined;
|
|
355
|
+
const signal = options.signal ?? ownDeadline.signal;
|
|
356
|
+
const declarationRules = declaredRequest ? declarationRequestRules(connector, bound) : undefined;
|
|
357
|
+
const customRules = options.rules;
|
|
358
|
+
const rules = declarationRules ? {
|
|
359
|
+
url: (url, hop) => declarationRules.url(url, hop) && (!customRules?.url || customRules.url(url, hop)),
|
|
360
|
+
post: (url, body) => declarationRules.post(url, body) && (!customRules?.post || customRules.post(url, body)),
|
|
361
|
+
header: (name, url) => declarationRules.header(name, url) && (!customRules?.header || customRules.header(name, url)),
|
|
362
|
+
response: declarationRules.response,
|
|
363
|
+
} : customRules ?? {};
|
|
364
|
+
const guard = guardTransport({
|
|
365
|
+
hosts: hostSet.hosts, transport: options.transport, identity, budget, rules, statuses, now, conditional: mode === "incremental",
|
|
366
|
+
signal, requestTimeoutMs: budget.requestTimeoutMs, authority: declarationRules, graphqlPaths: providerRequestRules(connector.auth)?.read_posts.filter(p => p.graphql).map(p => p.path),
|
|
367
|
+
});
|
|
368
|
+
receipt.requests = guard.requests;
|
|
369
|
+
/** Store `next` after a commit; false when the store refused it and the run has stopped. */
|
|
370
|
+
const store = async (next) => {
|
|
371
|
+
try {
|
|
372
|
+
version = await options.state.write(key, next, version);
|
|
373
|
+
state = next;
|
|
374
|
+
receipt.pending = Object.keys(next.pending ?? {}).length;
|
|
375
|
+
return true;
|
|
376
|
+
}
|
|
377
|
+
catch (error) {
|
|
378
|
+
const conflict = record(error) && error.code === "MACHINE_STATE_CONFLICT";
|
|
379
|
+
stop = { outcome: "failed", reason: conflict ? "state_conflict" : "state_unwritable", retry: true };
|
|
380
|
+
return false;
|
|
381
|
+
}
|
|
382
|
+
};
|
|
383
|
+
try {
|
|
384
|
+
// Changes an earlier commit asked to retry are planned again before anything is fetched. A
|
|
385
|
+
// record that has since disappeared has nothing left to write, unless the held change is the
|
|
386
|
+
// record coming back.
|
|
387
|
+
const pendingIds = signal.aborted ? [] : Object.keys(state.pending ?? {}).sort();
|
|
388
|
+
if (pendingIds.length) {
|
|
389
|
+
const pending = state.pending;
|
|
390
|
+
const live = pendingIds.filter((id) => !state.records[id]?.gone || pending[id].change.change_kind === "new");
|
|
391
|
+
const changes = live.map((id) => pending[id].change);
|
|
392
|
+
const records = {};
|
|
393
|
+
for (const change of changes)
|
|
394
|
+
records[change.external_id] = { fields: { ...(change.after ?? change.before ?? {}) } };
|
|
395
|
+
let result;
|
|
396
|
+
let failure;
|
|
397
|
+
if (live.length) {
|
|
398
|
+
try {
|
|
399
|
+
result = commitResult(await options.commit({
|
|
400
|
+
feed: key, provider: bound.policy.provider, observed_at: observedAt, page: 0, replay: true, changes, records, baseline: false, complete: false,
|
|
401
|
+
}));
|
|
402
|
+
}
|
|
403
|
+
catch (error) {
|
|
404
|
+
failure = errorText(error);
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
const nextRecords = { ...state.records };
|
|
408
|
+
const nextPending = {};
|
|
409
|
+
const dropped = [];
|
|
410
|
+
for (const id of live) {
|
|
411
|
+
const entry = pending[id];
|
|
412
|
+
if (failure !== undefined) {
|
|
413
|
+
// A replay the commit cannot take stays pending, so a later sighting can still supersede
|
|
414
|
+
// it, until it has failed too often.
|
|
415
|
+
const failures = (entry.replay_failures ?? 0) + 1;
|
|
416
|
+
if (failures >= MAX_REPLAY_FAILURES)
|
|
417
|
+
dropped.push(id);
|
|
418
|
+
else
|
|
419
|
+
nextPending[id] = { ...entry, replay_failures: failures };
|
|
420
|
+
}
|
|
421
|
+
else if (result.retry.has(id)) {
|
|
422
|
+
nextPending[id] = entry;
|
|
423
|
+
}
|
|
424
|
+
else if (!result.hold.has(id)) {
|
|
425
|
+
// The record's absence since the hold is newer than the change, so its count stays; a
|
|
426
|
+
// record that comes back starts afresh.
|
|
427
|
+
const current = state.records[id];
|
|
428
|
+
nextRecords[id] = entryJson(current?.gone ? entry.entry : { ...entry.entry, missing: current?.missing ?? 0, first_missing_at: current?.first_missing_at });
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
receipt.held += live.filter((id) => failure !== undefined || result.retry.has(id) || result.hold.has(id)).length;
|
|
432
|
+
if (failure !== undefined) {
|
|
433
|
+
extraFindings.push({
|
|
434
|
+
key: `source:${bound.source.name}:replay_failed`,
|
|
435
|
+
title: clip(`Source ${bound.source.name}: a held change could not be planned again`, 140),
|
|
436
|
+
summary: clip(`Replaying ${live.length} pending change${live.length === 1 ? "" : "s"} failed (${failure}); they stay pending and the run went on to read the provider.`, 600),
|
|
437
|
+
priority: "normal",
|
|
438
|
+
facts: { outcome: "failed", reason: "replay_failed" },
|
|
439
|
+
});
|
|
440
|
+
}
|
|
441
|
+
if (dropped.length) {
|
|
442
|
+
extraFindings.push({
|
|
443
|
+
key: `source:${bound.source.name}:replay_dropped`,
|
|
444
|
+
title: clip(`Source ${bound.source.name}: held changes dropped after ${MAX_REPLAY_FAILURES} failed replays`, 140),
|
|
445
|
+
summary: clip(`Dropped ${dropped.length} pending change${dropped.length === 1 ? "" : "s"} (${dropped.slice(0, 3).map((id) => clip(id, 60)).join(", ")}${dropped.length > 3 ? ", …" : ""}); each record is planned again when it is next observed.`, 600),
|
|
446
|
+
priority: "normal",
|
|
447
|
+
facts: { outcome: "failed", reason: "replay_dropped", count: dropped.length },
|
|
448
|
+
});
|
|
449
|
+
}
|
|
450
|
+
const next = { ...state, records: nextRecords };
|
|
451
|
+
if (Object.keys(nextPending).length)
|
|
452
|
+
next.pending = nextPending;
|
|
453
|
+
else
|
|
454
|
+
delete next.pending;
|
|
455
|
+
if (!(await store(next)))
|
|
456
|
+
return finish();
|
|
457
|
+
}
|
|
458
|
+
const seen = new Set();
|
|
459
|
+
/** The cursors this run has asked for; a page handing one back again has looped. */
|
|
460
|
+
const asked = new Set();
|
|
461
|
+
const lastRequestUrl = () => guard.requests.at(-1)?.url;
|
|
462
|
+
for (let first = true;; first = false) {
|
|
463
|
+
if (signal.aborted) {
|
|
464
|
+
stop = { outcome: "deferred", reason: "deadline", retry: true };
|
|
465
|
+
break;
|
|
466
|
+
}
|
|
467
|
+
// A resumed pass may restart from its first page, so the cursor it resumes from is not a loop.
|
|
468
|
+
if (cursor !== null && !(first && resumed))
|
|
469
|
+
asked.add(overlayCanonicalJson(cursor));
|
|
470
|
+
let raw;
|
|
471
|
+
try {
|
|
472
|
+
// The deadline bounds profile code too: a page still pending when it passes is discarded.
|
|
473
|
+
raw = await raceAbort(profile.observe({
|
|
474
|
+
spec, bound: structuredClone(bound), cursor, resumed: first && resumed, mode, transport: guard.transport,
|
|
475
|
+
budget: { requests: guard.remaining(), maxBytes: budget.maxBytes }, now: now(),
|
|
476
|
+
validators: mode === "complete" ? null : state.validators ?? null,
|
|
477
|
+
}), signal);
|
|
478
|
+
}
|
|
479
|
+
catch (error) {
|
|
480
|
+
stop = guard.stopped() ?? (error instanceof SourceStop ? error.stop : undefined);
|
|
481
|
+
if (!stop && signal.aborted)
|
|
482
|
+
stop = { outcome: "deferred", reason: "deadline", retry: true };
|
|
483
|
+
if (!stop) {
|
|
484
|
+
const code = record(error) ? error.code : undefined;
|
|
485
|
+
stop = code === UNSUPPORTED_CONTENT
|
|
486
|
+
? { outcome: "unsupported_content", retry: false, detail: String(error.detail ?? "") }
|
|
487
|
+
: { outcome: "failed", reason: "profile_error", retry: false, detail: errorText(error) };
|
|
488
|
+
}
|
|
489
|
+
stopUrl = lastRequestUrl();
|
|
490
|
+
break;
|
|
491
|
+
}
|
|
492
|
+
// A stop the profile caught and swallowed still ends the run, and its page is discarded.
|
|
493
|
+
const guardStop = guard.stopped();
|
|
494
|
+
if (guardStop) {
|
|
495
|
+
stop = guardStop;
|
|
496
|
+
stopUrl = lastRequestUrl();
|
|
497
|
+
break;
|
|
498
|
+
}
|
|
499
|
+
let page;
|
|
500
|
+
try {
|
|
501
|
+
const invalid = pageProblem(raw);
|
|
502
|
+
if (invalid) {
|
|
503
|
+
stop = { outcome: "failed", reason: invalid, retry: false };
|
|
504
|
+
break;
|
|
505
|
+
}
|
|
506
|
+
// One read of the profile's page, as JSON data, so no getter or later mutation reaches the diff.
|
|
507
|
+
page = JSON.parse(JSON.stringify(raw));
|
|
508
|
+
}
|
|
509
|
+
catch (error) {
|
|
510
|
+
stop = { outcome: "failed", reason: "page_invalid", retry: false, detail: errorText(error) };
|
|
511
|
+
break;
|
|
512
|
+
}
|
|
513
|
+
const nextCursor = page.nextCursor ?? null;
|
|
514
|
+
// A page that promises more but hands back no cursor, or one this run already asked for, would
|
|
515
|
+
// read the same pages until the budget runs out.
|
|
516
|
+
if (page.more && (nextCursor === null || asked.has(overlayCanonicalJson(nextCursor)))) {
|
|
517
|
+
stop = { outcome: "failed", reason: "cursor_stuck", retry: false };
|
|
518
|
+
break;
|
|
519
|
+
}
|
|
520
|
+
const diff = diffPage(state, page, bound, observedAt);
|
|
521
|
+
for (const id of diff.seen)
|
|
522
|
+
seen.add(id);
|
|
523
|
+
const ended = !page.more;
|
|
524
|
+
const complete = ended && mode === "complete" && page.complete && sourceComplete;
|
|
525
|
+
const merged = { ...state.records };
|
|
526
|
+
for (const [id, entry] of diff.entries)
|
|
527
|
+
merged[id] = entry;
|
|
528
|
+
const missing = complete ? missingAfterComplete(merged, seen, threshold, observedAt) : { changes: [], entries: new Map() };
|
|
529
|
+
const changes = [...diff.changes, ...missing.changes];
|
|
530
|
+
const byId = new Map(changes.map((change) => [change.external_id, change]));
|
|
531
|
+
// A newer observation of a record, its listing or its absence from a complete pass, supersedes
|
|
532
|
+
// its pending change, whether or not the record was ever stored. Those entries are dropped and
|
|
533
|
+
// stored before the commit, so a crash after the commit never replays a change older than what
|
|
534
|
+
// the commit wrote.
|
|
535
|
+
const unlisted = complete ? Object.keys(state.pending ?? {}).filter((id) => !seen.has(id)) : [];
|
|
536
|
+
const superseded = [...new Set([...diff.seen, ...missing.entries.keys(), ...unlisted])].filter((id) => state.pending !== undefined && Object.hasOwn(state.pending, id));
|
|
537
|
+
if (superseded.length) {
|
|
538
|
+
const remaining = { ...state.pending };
|
|
539
|
+
for (const id of superseded)
|
|
540
|
+
delete remaining[id];
|
|
541
|
+
const pre = { ...state };
|
|
542
|
+
if (Object.keys(remaining).length)
|
|
543
|
+
pre.pending = remaining;
|
|
544
|
+
else
|
|
545
|
+
delete pre.pending;
|
|
546
|
+
if (!(await store(pre)))
|
|
547
|
+
break;
|
|
548
|
+
}
|
|
549
|
+
const records = {};
|
|
550
|
+
for (const change of changes)
|
|
551
|
+
records[change.external_id] = { fields: { ...(change.after ?? change.before ?? {}) } };
|
|
552
|
+
let result;
|
|
553
|
+
try {
|
|
554
|
+
result = commitResult(await options.commit({
|
|
555
|
+
feed: key, provider: bound.policy.provider, observed_at: observedAt, page: receipt.pages + 1, replay: false,
|
|
556
|
+
changes, records, baseline, complete,
|
|
557
|
+
}));
|
|
558
|
+
}
|
|
559
|
+
catch (error) {
|
|
560
|
+
stop = { outcome: "failed", reason: "commit_failed", retry: true, detail: errorText(error) };
|
|
561
|
+
break;
|
|
562
|
+
}
|
|
563
|
+
const notWritten = (id) => byId.has(id) && (result.hold.has(id) || result.retry.has(id));
|
|
564
|
+
// The page committed: now, and only now, its records and cursor are stored.
|
|
565
|
+
const nextRecords = { ...state.records };
|
|
566
|
+
const nextPending = { ...state.pending };
|
|
567
|
+
for (const [id, entry] of [...diff.entries, ...missing.entries]) {
|
|
568
|
+
if (byId.has(id) && result.retry.has(id))
|
|
569
|
+
nextPending[id] = { change: byId.get(id), entry: entryJson(entry), held_at: observedAt };
|
|
570
|
+
else if (!notWritten(id))
|
|
571
|
+
nextRecords[id] = entryJson(entry);
|
|
572
|
+
}
|
|
573
|
+
const next = {
|
|
574
|
+
...state,
|
|
575
|
+
cursor: nextCursor,
|
|
576
|
+
open: page.more,
|
|
577
|
+
records: nextRecords,
|
|
578
|
+
complete_observations: state.complete_observations + (complete ? 1 : 0),
|
|
579
|
+
...(complete ? { last_complete_at: observedAt } : {}),
|
|
580
|
+
...(page.validators ? { validators: { ...page.validators } } : {}),
|
|
581
|
+
};
|
|
582
|
+
if (Object.keys(nextPending).length)
|
|
583
|
+
next.pending = nextPending;
|
|
584
|
+
else
|
|
585
|
+
delete next.pending;
|
|
586
|
+
if (!(await store(next)))
|
|
587
|
+
break;
|
|
588
|
+
receipt.pages += 1;
|
|
589
|
+
receipt.cursor = nextCursor;
|
|
590
|
+
receipt.diagnostics.push(...diff.diagnostics);
|
|
591
|
+
receipt.profile_diagnostics.push(...diff.profileDiagnostics);
|
|
592
|
+
for (const change of changes) {
|
|
593
|
+
if (notWritten(change.external_id)) {
|
|
594
|
+
receipt.held += 1;
|
|
595
|
+
continue;
|
|
596
|
+
}
|
|
597
|
+
const kind = change.change_kind;
|
|
598
|
+
if (Object.hasOwn(receipt.changes, kind))
|
|
599
|
+
receipt.changes[kind] += 1;
|
|
600
|
+
}
|
|
601
|
+
if (ended) {
|
|
602
|
+
receipt.pass.ended = true;
|
|
603
|
+
receipt.pass.complete = complete;
|
|
604
|
+
break;
|
|
605
|
+
}
|
|
606
|
+
if (receipt.pages >= budget.pages) {
|
|
607
|
+
stop = { outcome: "deferred", reason: "budget", retry: true };
|
|
608
|
+
break;
|
|
609
|
+
}
|
|
610
|
+
cursor = nextCursor;
|
|
611
|
+
}
|
|
612
|
+
return finish();
|
|
613
|
+
}
|
|
614
|
+
finally {
|
|
615
|
+
if (timer !== undefined)
|
|
616
|
+
clearTimeout(timer);
|
|
617
|
+
guard.close();
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
//# sourceMappingURL=observe.js.map
|