@opendatalabs/personal-server-ts-core 1.33.7 → 1.34.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/additions/budget.d.ts +28 -0
- package/dist/additions/budget.d.ts.map +1 -0
- package/dist/additions/budget.js +119 -0
- package/dist/additions/budget.js.map +1 -0
- package/dist/additions/durable.d.ts +64 -0
- package/dist/additions/durable.d.ts.map +1 -0
- package/dist/additions/durable.js +591 -0
- package/dist/additions/durable.js.map +1 -0
- package/dist/additions/first-added.d.ts +32 -48
- package/dist/additions/first-added.d.ts.map +1 -1
- package/dist/additions/first-added.js +68 -123
- package/dist/additions/first-added.js.map +1 -1
- package/dist/additions/index.d.ts +3 -0
- package/dist/additions/index.d.ts.map +1 -1
- package/dist/additions/index.js +3 -0
- package/dist/additions/index.js.map +1 -1
- package/dist/additions/ledger-store.js +1 -1
- package/dist/additions/record-rules.d.ts.map +1 -1
- package/dist/additions/record-rules.js +7 -0
- package/dist/additions/record-rules.js.map +1 -1
- package/dist/additions/summary.d.ts +88 -1
- package/dist/additions/summary.d.ts.map +1 -1
- package/dist/additions/summary.js +173 -8
- package/dist/additions/summary.js.map +1 -1
- package/dist/additions/version-inventory.d.ts +80 -0
- package/dist/additions/version-inventory.d.ts.map +1 -0
- package/dist/additions/version-inventory.js +232 -0
- package/dist/additions/version-inventory.js.map +1 -0
- package/dist/api/index.d.ts +70 -2
- package/dist/api/index.d.ts.map +1 -1
- package/dist/api/index.js +506 -79
- package/dist/api/index.js.map +1 -1
- package/dist/auth/index.d.ts +1 -1
- package/dist/auth/index.d.ts.map +1 -1
- package/dist/auth/index.js +1 -1
- package/dist/auth/index.js.map +1 -1
- package/dist/auth/request.d.ts +6 -0
- package/dist/auth/request.d.ts.map +1 -1
- package/dist/auth/request.js +66 -4
- package/dist/auth/request.js.map +1 -1
- package/dist/contracts/data.d.ts +54 -8
- package/dist/contracts/data.d.ts.map +1 -1
- package/dist/contracts/data.js +178 -35
- package/dist/contracts/data.js.map +1 -1
- package/dist/contracts/index.d.ts +1 -1
- package/dist/contracts/index.d.ts.map +1 -1
- package/dist/derivatives/registration.d.ts +0 -5
- package/dist/derivatives/registration.d.ts.map +1 -1
- package/dist/derivatives/registration.js +2 -1
- package/dist/derivatives/registration.js.map +1 -1
- package/dist/errors/pdpp-catalog.d.ts +36 -0
- package/dist/errors/pdpp-catalog.d.ts.map +1 -0
- package/dist/errors/pdpp-catalog.js +86 -0
- package/dist/errors/pdpp-catalog.js.map +1 -0
- package/dist/grants/index.d.ts +2 -0
- package/dist/grants/index.d.ts.map +1 -1
- package/dist/grants/index.js +4 -0
- package/dist/grants/index.js.map +1 -1
- package/dist/grants/pdpp-binding-store.d.ts +89 -0
- package/dist/grants/pdpp-binding-store.d.ts.map +1 -0
- package/dist/grants/pdpp-binding-store.js +112 -0
- package/dist/grants/pdpp-binding-store.js.map +1 -0
- package/dist/grants/pdpp-binding.d.ts +169 -0
- package/dist/grants/pdpp-binding.d.ts.map +1 -0
- package/dist/grants/pdpp-binding.js +236 -0
- package/dist/grants/pdpp-binding.js.map +1 -0
- package/dist/legacy-projection/bindings.d.ts +28 -1
- package/dist/legacy-projection/bindings.d.ts.map +1 -1
- package/dist/legacy-projection/bindings.js +622 -189
- package/dist/legacy-projection/bindings.js.map +1 -1
- package/dist/legacy-projection/claude-source-record.d.ts +22 -0
- package/dist/legacy-projection/claude-source-record.d.ts.map +1 -0
- package/dist/legacy-projection/claude-source-record.js +360 -0
- package/dist/legacy-projection/claude-source-record.js.map +1 -0
- package/dist/legacy-projection/declared-text.d.ts +41 -0
- package/dist/legacy-projection/declared-text.d.ts.map +1 -0
- package/dist/legacy-projection/declared-text.js +199 -0
- package/dist/legacy-projection/declared-text.js.map +1 -0
- package/dist/legacy-projection/github-browser-bindings.d.ts.map +1 -1
- package/dist/legacy-projection/github-browser-bindings.js +89 -5
- package/dist/legacy-projection/github-browser-bindings.js.map +1 -1
- package/dist/legacy-projection/github-legacy-fields.d.ts +6 -0
- package/dist/legacy-projection/github-legacy-fields.d.ts.map +1 -0
- package/dist/legacy-projection/github-legacy-fields.js +202 -0
- package/dist/legacy-projection/github-legacy-fields.js.map +1 -0
- package/dist/legacy-projection/index.d.ts +3 -1
- package/dist/legacy-projection/index.d.ts.map +1 -1
- package/dist/legacy-projection/index.js +49 -1
- package/dist/legacy-projection/index.js.map +1 -1
- package/dist/legacy-projection/oura-browser-bindings.d.ts.map +1 -1
- package/dist/legacy-projection/oura-browser-bindings.js +43 -41
- package/dist/legacy-projection/oura-browser-bindings.js.map +1 -1
- package/dist/legacy-projection/types.d.ts +38 -2
- package/dist/legacy-projection/types.d.ts.map +1 -1
- package/dist/mcp/connection-api.d.ts.map +1 -1
- package/dist/mcp/connection-api.js +4 -0
- package/dist/mcp/connection-api.js.map +1 -1
- package/dist/mcp/index.d.ts +1 -0
- package/dist/mcp/index.d.ts.map +1 -1
- package/dist/mcp/index.js +1 -0
- package/dist/mcp/index.js.map +1 -1
- package/dist/mcp/oauth-redirect.d.ts +14 -0
- package/dist/mcp/oauth-redirect.d.ts.map +1 -0
- package/dist/mcp/oauth-redirect.js +51 -0
- package/dist/mcp/oauth-redirect.js.map +1 -0
- package/dist/mcp/resources.d.ts.map +1 -1
- package/dist/mcp/resources.js +3 -0
- package/dist/mcp/resources.js.map +1 -1
- package/dist/mcp/tools.d.ts.map +1 -1
- package/dist/mcp/tools.js +11 -2
- package/dist/mcp/tools.js.map +1 -1
- package/dist/pdpp/approval.d.ts +271 -0
- package/dist/pdpp/approval.d.ts.map +1 -0
- package/dist/pdpp/approval.js +467 -0
- package/dist/pdpp/approval.js.map +1 -0
- package/dist/pdpp/client-identity.d.ts +88 -0
- package/dist/pdpp/client-identity.d.ts.map +1 -0
- package/dist/pdpp/client-identity.js +196 -0
- package/dist/pdpp/client-identity.js.map +1 -0
- package/dist/pdpp/client-metadata.d.ts +95 -0
- package/dist/pdpp/client-metadata.d.ts.map +1 -0
- package/dist/pdpp/client-metadata.js +96 -0
- package/dist/pdpp/client-metadata.js.map +1 -0
- package/dist/pdpp/commitments.d.ts +75 -0
- package/dist/pdpp/commitments.d.ts.map +1 -0
- package/dist/pdpp/commitments.js +113 -0
- package/dist/pdpp/commitments.js.map +1 -0
- package/dist/pdpp/declaration.d.ts +118 -0
- package/dist/pdpp/declaration.d.ts.map +1 -0
- package/dist/pdpp/declaration.js +836 -0
- package/dist/pdpp/declaration.js.map +1 -0
- package/dist/pdpp/index.d.ts +37 -0
- package/dist/pdpp/index.d.ts.map +1 -0
- package/dist/pdpp/index.js +23 -0
- package/dist/pdpp/index.js.map +1 -0
- package/dist/pdpp/issuance.d.ts +126 -0
- package/dist/pdpp/issuance.d.ts.map +1 -0
- package/dist/pdpp/issuance.js +180 -0
- package/dist/pdpp/issuance.js.map +1 -0
- package/dist/pdpp/pkce.d.ts +53 -0
- package/dist/pdpp/pkce.d.ts.map +1 -0
- package/dist/pdpp/pkce.js +109 -0
- package/dist/pdpp/pkce.js.map +1 -0
- package/dist/pdpp/redirect.d.ts +60 -0
- package/dist/pdpp/redirect.d.ts.map +1 -0
- package/dist/pdpp/redirect.js +125 -0
- package/dist/pdpp/redirect.js.map +1 -0
- package/dist/pdpp/resolve.d.ts +109 -0
- package/dist/pdpp/resolve.d.ts.map +1 -0
- package/dist/pdpp/resolve.js +393 -0
- package/dist/pdpp/resolve.js.map +1 -0
- package/dist/pdpp/review.d.ts +280 -0
- package/dist/pdpp/review.d.ts.map +1 -0
- package/dist/pdpp/review.js +205 -0
- package/dist/pdpp/review.js.map +1 -0
- package/dist/pdpp/selection.d.ts +50 -0
- package/dist/pdpp/selection.d.ts.map +1 -0
- package/dist/pdpp/selection.js +442 -0
- package/dist/pdpp/selection.js.map +1 -0
- package/dist/pdpp/store.d.ts +228 -0
- package/dist/pdpp/store.d.ts.map +1 -0
- package/dist/pdpp/store.js +542 -0
- package/dist/pdpp/store.js.map +1 -0
- package/dist/pdpp/tokens.d.ts +181 -0
- package/dist/pdpp/tokens.d.ts.map +1 -0
- package/dist/pdpp/tokens.js +419 -0
- package/dist/pdpp/tokens.js.map +1 -0
- package/dist/pdpp/types.d.ts +441 -0
- package/dist/pdpp/types.d.ts.map +1 -0
- package/dist/pdpp/types.js +61 -0
- package/dist/pdpp/types.js.map +1 -0
- package/dist/pdpp-version.d.ts +28 -0
- package/dist/pdpp-version.d.ts.map +1 -0
- package/dist/pdpp-version.js +28 -0
- package/dist/pdpp-version.js.map +1 -0
- package/dist/policy/data-read.d.ts +24 -0
- package/dist/policy/data-read.d.ts.map +1 -1
- package/dist/policy/data-read.js +60 -3
- package/dist/policy/data-read.js.map +1 -1
- package/dist/policy/data-write.d.ts.map +1 -1
- package/dist/policy/data-write.js +3 -0
- package/dist/policy/data-write.js.map +1 -1
- package/dist/policy/index.d.ts +1 -0
- package/dist/policy/index.d.ts.map +1 -1
- package/dist/policy/index.js +1 -0
- package/dist/policy/index.js.map +1 -1
- package/dist/policy/reserved-scopes.d.ts +3 -0
- package/dist/policy/reserved-scopes.d.ts.map +1 -0
- package/dist/policy/reserved-scopes.js +5 -0
- package/dist/policy/reserved-scopes.js.map +1 -0
- package/dist/ports/index.d.ts +63 -0
- package/dist/ports/index.d.ts.map +1 -1
- package/dist/ports/index.js.map +1 -1
- package/dist/ports/pdpp-auth.d.ts +129 -0
- package/dist/ports/pdpp-auth.d.ts.map +1 -0
- package/dist/ports/pdpp-auth.js +60 -0
- package/dist/ports/pdpp-auth.js.map +1 -0
- package/dist/ports/pdpp-auth.test-utils.d.ts +9 -0
- package/dist/ports/pdpp-auth.test-utils.d.ts.map +1 -0
- package/dist/ports/pdpp-auth.test-utils.js +18 -0
- package/dist/ports/pdpp-auth.test-utils.js.map +1 -0
- package/dist/schemas/server-config.d.ts +44 -0
- package/dist/schemas/server-config.d.ts.map +1 -1
- package/dist/schemas/server-config.js +91 -0
- package/dist/schemas/server-config.js.map +1 -1
- package/dist/signing/request-signer.d.ts +2 -0
- package/dist/signing/request-signer.d.ts.map +1 -1
- package/dist/signing/request-signer.js +1 -0
- package/dist/signing/request-signer.js.map +1 -1
- package/dist/storage/adapters/interface.d.ts +3 -0
- package/dist/storage/adapters/interface.d.ts.map +1 -1
- package/dist/storage/adapters/vana-audience.d.ts +12 -0
- package/dist/storage/adapters/vana-audience.d.ts.map +1 -0
- package/dist/storage/adapters/vana-audience.js +72 -0
- package/dist/storage/adapters/vana-audience.js.map +1 -0
- package/dist/storage/adapters/vana.d.ts +2 -0
- package/dist/storage/adapters/vana.d.ts.map +1 -1
- package/dist/storage/adapters/vana.js +53 -20
- package/dist/storage/adapters/vana.js.map +1 -1
- package/dist/storage/canonical-legacy-projection.d.ts +262 -0
- package/dist/storage/canonical-legacy-projection.d.ts.map +1 -0
- package/dist/storage/canonical-legacy-projection.js +761 -0
- package/dist/storage/canonical-legacy-projection.js.map +1 -0
- package/dist/storage/index/manager.d.ts +29 -2
- package/dist/storage/index/manager.d.ts.map +1 -1
- package/dist/storage/index/schema.d.ts +1 -1
- package/dist/storage/index/schema.js +1 -1
- package/dist/storage/index/types.d.ts +7 -1
- package/dist/storage/index/types.d.ts.map +1 -1
- package/dist/storage/legacy-projection.d.ts +4 -3
- package/dist/storage/legacy-projection.d.ts.map +1 -1
- package/dist/storage/legacy-projection.js +145 -42
- package/dist/storage/legacy-projection.js.map +1 -1
- package/dist/storage/legacy-scope-coverage-exceptions.d.ts +8 -0
- package/dist/storage/legacy-scope-coverage-exceptions.d.ts.map +1 -0
- package/dist/storage/legacy-scope-coverage-exceptions.js +10 -0
- package/dist/storage/legacy-scope-coverage-exceptions.js.map +1 -0
- package/dist/storage/legacy-scope-coverage.d.ts +53 -0
- package/dist/storage/legacy-scope-coverage.d.ts.map +1 -0
- package/dist/storage/legacy-scope-coverage.js +403 -0
- package/dist/storage/legacy-scope-coverage.js.map +1 -0
- package/dist/storage/legacy-scope-entity-keys.d.ts +4 -0
- package/dist/storage/legacy-scope-entity-keys.d.ts.map +1 -0
- package/dist/storage/legacy-scope-entity-keys.js +92 -0
- package/dist/storage/legacy-scope-entity-keys.js.map +1 -0
- package/dist/storage/legacy-scope-view.d.ts +154 -0
- package/dist/storage/legacy-scope-view.d.ts.map +1 -0
- package/dist/storage/legacy-scope-view.js +735 -0
- package/dist/storage/legacy-scope-view.js.map +1 -0
- package/dist/storage/pdpp-records/completion.d.ts +91 -0
- package/dist/storage/pdpp-records/completion.d.ts.map +1 -0
- package/dist/storage/pdpp-records/completion.js +170 -0
- package/dist/storage/pdpp-records/completion.js.map +1 -0
- package/dist/storage/pdpp-records/cursor.d.ts +41 -0
- package/dist/storage/pdpp-records/cursor.d.ts.map +1 -0
- package/dist/storage/pdpp-records/cursor.js +49 -0
- package/dist/storage/pdpp-records/cursor.js.map +1 -0
- package/dist/storage/pdpp-records/declaration-witness.d.ts +58 -0
- package/dist/storage/pdpp-records/declaration-witness.d.ts.map +1 -0
- package/dist/storage/pdpp-records/declaration-witness.js +230 -0
- package/dist/storage/pdpp-records/declaration-witness.js.map +1 -0
- package/dist/storage/pdpp-records/enforcement.d.ts +52 -0
- package/dist/storage/pdpp-records/enforcement.d.ts.map +1 -0
- package/dist/storage/pdpp-records/enforcement.js +116 -0
- package/dist/storage/pdpp-records/enforcement.js.map +1 -0
- package/dist/storage/pdpp-records/index.d.ts +5 -0
- package/dist/storage/pdpp-records/index.d.ts.map +1 -0
- package/dist/storage/pdpp-records/index.js +5 -0
- package/dist/storage/pdpp-records/index.js.map +1 -0
- package/dist/storage/pdpp-records/ingest-plan.d.ts +39 -0
- package/dist/storage/pdpp-records/ingest-plan.d.ts.map +1 -0
- package/dist/storage/pdpp-records/ingest-plan.js +103 -0
- package/dist/storage/pdpp-records/ingest-plan.js.map +1 -0
- package/dist/storage/pdpp-records/interface.d.ts +104 -0
- package/dist/storage/pdpp-records/interface.d.ts.map +1 -0
- package/dist/storage/pdpp-records/interface.js +2 -0
- package/dist/storage/pdpp-records/interface.js.map +1 -0
- package/dist/storage/pdpp-records/memory-store.d.ts +9 -0
- package/dist/storage/pdpp-records/memory-store.d.ts.map +1 -0
- package/dist/storage/pdpp-records/memory-store.js +466 -0
- package/dist/storage/pdpp-records/memory-store.js.map +1 -0
- package/dist/storage/pdpp-records/portable.d.ts +9 -0
- package/dist/storage/pdpp-records/portable.d.ts.map +1 -0
- package/dist/storage/pdpp-records/portable.js +8 -0
- package/dist/storage/pdpp-records/portable.js.map +1 -0
- package/dist/storage/pdpp-records/read-port.d.ts +44 -0
- package/dist/storage/pdpp-records/read-port.d.ts.map +1 -0
- package/dist/storage/pdpp-records/read-port.js +22 -0
- package/dist/storage/pdpp-records/read-port.js.map +1 -0
- package/dist/storage/pdpp-records/record-key.d.ts +52 -0
- package/dist/storage/pdpp-records/record-key.d.ts.map +1 -0
- package/dist/storage/pdpp-records/record-key.js +97 -0
- package/dist/storage/pdpp-records/record-key.js.map +1 -0
- package/dist/storage/pdpp-records/schema.d.ts +41 -0
- package/dist/storage/pdpp-records/schema.d.ts.map +1 -0
- package/dist/storage/pdpp-records/schema.js +153 -0
- package/dist/storage/pdpp-records/schema.js.map +1 -0
- package/dist/storage/pdpp-records/stream-declaration.d.ts +88 -0
- package/dist/storage/pdpp-records/stream-declaration.d.ts.map +1 -0
- package/dist/storage/pdpp-records/stream-declaration.js +57 -0
- package/dist/storage/pdpp-records/stream-declaration.js.map +1 -0
- package/dist/storage/pdpp-records/types.d.ts +138 -0
- package/dist/storage/pdpp-records/types.d.ts.map +1 -0
- package/dist/storage/pdpp-records/types.js +38 -0
- package/dist/storage/pdpp-records/types.js.map +1 -0
- package/dist/sync/data-point-feed.d.ts +3 -0
- package/dist/sync/data-point-feed.d.ts.map +1 -1
- package/dist/sync/data-point-feed.js +17 -1
- package/dist/sync/data-point-feed.js.map +1 -1
- package/dist/sync/delete-data-port.d.ts +2 -0
- package/dist/sync/delete-data-port.d.ts.map +1 -1
- package/dist/sync/delete-data-port.js +1 -1
- package/dist/sync/delete-data-port.js.map +1 -1
- package/dist/sync/engine/sync-manager.d.ts.map +1 -1
- package/dist/sync/engine/sync-manager.js +5 -2
- package/dist/sync/engine/sync-manager.js.map +1 -1
- package/dist/sync/index.d.ts +3 -1
- package/dist/sync/index.d.ts.map +1 -1
- package/dist/sync/index.js +4 -0
- package/dist/sync/index.js.map +1 -1
- package/dist/sync/pdpp-import.d.ts +192 -0
- package/dist/sync/pdpp-import.d.ts.map +1 -0
- package/dist/sync/pdpp-import.js +441 -0
- package/dist/sync/pdpp-import.js.map +1 -0
- package/dist/sync/scope-deletions.d.ts +10 -0
- package/dist/sync/scope-deletions.d.ts.map +1 -1
- package/dist/sync/scope-deletions.js +40 -7
- package/dist/sync/scope-deletions.js.map +1 -1
- package/dist/sync/workers/download.d.ts +14 -0
- package/dist/sync/workers/download.d.ts.map +1 -1
- package/dist/sync/workers/download.js +149 -13
- package/dist/sync/workers/download.js.map +1 -1
- package/dist/sync/workers/upload.d.ts +2 -0
- package/dist/sync/workers/upload.d.ts.map +1 -1
- package/dist/sync/workers/upload.js +7 -0
- package/dist/sync/workers/upload.js.map +1 -1
- package/dist/test-utils/memory-storage.d.ts +1 -1
- package/dist/test-utils/memory-storage.d.ts.map +1 -1
- package/dist/test-utils/memory-storage.js +11 -5
- package/dist/test-utils/memory-storage.js.map +1 -1
- package/package.json +54 -4
|
@@ -0,0 +1,735 @@
|
|
|
1
|
+
import { compareLegacyCoverage, } from "./legacy-scope-coverage.js";
|
|
2
|
+
import { LEGACY_SCOPE_ENTITY_KEYS } from "./legacy-scope-entity-keys.js";
|
|
3
|
+
import { GITHUB_BROWSER_BINDINGS, LEGACY_SCOPE_BINDINGS, legacyBindingsForProfile, META_040_BINDINGS, OURA_BROWSER_BINDINGS, } from "../legacy-projection/index.js";
|
|
4
|
+
import { legacyConnectorKey, readCanonicalLegacyScope, CANONICAL_LEGACY_VIEW_CONTRACT, } from "./canonical-legacy-projection.js";
|
|
5
|
+
/**
|
|
6
|
+
* Decides, per legacy scope, whether the latest body comes from the
|
|
7
|
+
* canonical rows or from the existing legacy file, and reads it.
|
|
8
|
+
*
|
|
9
|
+
* Only a bound scope can move. Its binding names the connector key it was
|
|
10
|
+
* reviewed for (`oura`, `oura-browser`, `github-browser`, `meta`, ...), and
|
|
11
|
+
* that key is the method id a connection is registered with: the operator's
|
|
12
|
+
* trusted method configuration names methods the same way. The store source
|
|
13
|
+
* id is never derived from the key; it is the connection's registered
|
|
14
|
+
* source, and that (source, method) pair must be configured on this boot.
|
|
15
|
+
*
|
|
16
|
+
* A bound scope is canonical-owned for write/refusal safety when a connection
|
|
17
|
+
* of one of its methods has held a row of, or a completion receipt for, one
|
|
18
|
+
* of the scope's streams, or was reset or deleted (both erase the rows that
|
|
19
|
+
* would show this). With a stored legacy head, the first eligible GET checks
|
|
20
|
+
* the projected body against the latest legacy body and records the verdict.
|
|
21
|
+
* A newer legacy version is checked again; a mismatch explicitly returns
|
|
22
|
+
* that file. A regression against an already covered version refuses.
|
|
23
|
+
* Every other scope, unbound ones included, keeps the file.
|
|
24
|
+
*
|
|
25
|
+
* The connection comes only from the registry: the single live connection
|
|
26
|
+
* of the scope's methods that has held any data. A request cannot name one.
|
|
27
|
+
*/
|
|
28
|
+
/** Every declaration profile the pure module serves a scope from. */
|
|
29
|
+
const BINDING_PROFILES = [
|
|
30
|
+
LEGACY_SCOPE_BINDINGS,
|
|
31
|
+
GITHUB_BROWSER_BINDINGS,
|
|
32
|
+
META_040_BINDINGS,
|
|
33
|
+
OURA_BROWSER_BINDINGS,
|
|
34
|
+
];
|
|
35
|
+
const COVERAGE_RULE_VERSION = 5;
|
|
36
|
+
function scopeDeclarationCursor(scope) {
|
|
37
|
+
return JSON.stringify(BINDING_PROFILES.flatMap((profile) => [...profile.values()])
|
|
38
|
+
.filter((binding) => binding.scope === scope)
|
|
39
|
+
.map((binding) => ({
|
|
40
|
+
pdppSource: binding.pdppSource,
|
|
41
|
+
pdppStreams: binding.pdppStreams,
|
|
42
|
+
primaryKey: binding.primaryKey,
|
|
43
|
+
fieldsRead: binding.fieldsRead,
|
|
44
|
+
provenance: binding.provenance,
|
|
45
|
+
})));
|
|
46
|
+
}
|
|
47
|
+
export function createLegacyScopeView(input) {
|
|
48
|
+
const { authority, records } = input;
|
|
49
|
+
const coveredProjection = new Map();
|
|
50
|
+
function decide(scope) {
|
|
51
|
+
const streamsByMethod = scopeMethods(scope);
|
|
52
|
+
if (streamsByMethod.size === 0) {
|
|
53
|
+
return authority.hasTakeover(scope)
|
|
54
|
+
? refused({
|
|
55
|
+
status: "unavailable",
|
|
56
|
+
reason: "connection_unavailable",
|
|
57
|
+
detail: "method_not_configured",
|
|
58
|
+
})
|
|
59
|
+
: { kind: "file" };
|
|
60
|
+
}
|
|
61
|
+
const connections = authority.listMethodConnections([
|
|
62
|
+
...streamsByMethod.keys(),
|
|
63
|
+
]);
|
|
64
|
+
const owned = authority.hasTakeover(scope) ||
|
|
65
|
+
connections.some((connection) => connection.deletedAt !== null ||
|
|
66
|
+
connection.generation > 1 ||
|
|
67
|
+
authority.hasHeldData(connection.instance, streamsByMethod.get(connection.method ?? "") ?? []));
|
|
68
|
+
if (!owned)
|
|
69
|
+
return { kind: "file" };
|
|
70
|
+
// A connection that never held anything (boot registers one per source)
|
|
71
|
+
// is not a candidate; any other live one is, whatever stream it holds,
|
|
72
|
+
// so two accounts never split one namespace between them.
|
|
73
|
+
const candidates = connections.filter((connection) => connection.deletedAt === null &&
|
|
74
|
+
authority.hasHeldData(connection.instance, null));
|
|
75
|
+
const [only] = candidates;
|
|
76
|
+
if (!only) {
|
|
77
|
+
return refused({
|
|
78
|
+
status: "unavailable",
|
|
79
|
+
reason: "no_connection",
|
|
80
|
+
candidates: 0,
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
if (candidates.length > 1) {
|
|
84
|
+
return refused({
|
|
85
|
+
status: "unavailable",
|
|
86
|
+
reason: "ambiguous_connection",
|
|
87
|
+
candidates: candidates.length,
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
const { instance, sourceId, method } = only;
|
|
91
|
+
if (!records ||
|
|
92
|
+
sourceId === null ||
|
|
93
|
+
method === null ||
|
|
94
|
+
!input.configuredMethods(sourceId).includes(method)) {
|
|
95
|
+
return refused({
|
|
96
|
+
status: "unavailable",
|
|
97
|
+
reason: "connection_unavailable",
|
|
98
|
+
detail: "method_not_configured",
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
return {
|
|
102
|
+
kind: "canonical",
|
|
103
|
+
records,
|
|
104
|
+
selection: { instance, sourceId, method },
|
|
105
|
+
generation: only.generation,
|
|
106
|
+
streams: streamsByMethod.get(method) ?? [],
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
function canServeLegacyBeforeTakeover(scope) {
|
|
110
|
+
const methods = [...scopeMethods(scope).keys()];
|
|
111
|
+
const connections = authority.listMethodConnections(methods);
|
|
112
|
+
if (!connections.length ||
|
|
113
|
+
connections.some((connection) => connection.deletedAt !== null)) {
|
|
114
|
+
return false;
|
|
115
|
+
}
|
|
116
|
+
const current = decide(scope);
|
|
117
|
+
if (current.kind === "refused" &&
|
|
118
|
+
!(current.refusal.status === "unavailable" &&
|
|
119
|
+
current.refusal.reason === "no_connection")) {
|
|
120
|
+
return false;
|
|
121
|
+
}
|
|
122
|
+
return (authority.eligibleLegacyHead(scope) !== null &&
|
|
123
|
+
!authority.hasTakeover(scope, current.kind === "canonical" ? current.selection.instance : undefined, current.kind === "canonical" ? current.generation : undefined));
|
|
124
|
+
}
|
|
125
|
+
function listingFor(scope) {
|
|
126
|
+
return authority.readSnapshot(() => {
|
|
127
|
+
if (authority.isReset(scope))
|
|
128
|
+
return { kind: "file" };
|
|
129
|
+
const decision = decide(scope);
|
|
130
|
+
const legacyHead = authority.eligibleLegacyHead(scope);
|
|
131
|
+
const hasTakeover = authority.hasTakeover(scope, decision.kind === "canonical" ? decision.selection.instance : undefined, decision.kind === "canonical" ? decision.generation : undefined);
|
|
132
|
+
if (decision.kind === "refused" &&
|
|
133
|
+
decision.refusal.status === "unavailable" &&
|
|
134
|
+
decision.refusal.reason === "no_connection" &&
|
|
135
|
+
canServeLegacyBeforeTakeover(scope)) {
|
|
136
|
+
return { kind: "file" };
|
|
137
|
+
}
|
|
138
|
+
if (decision.kind !== "canonical")
|
|
139
|
+
return { kind: decision.kind };
|
|
140
|
+
const hasLegacyHead = legacyHead !== null;
|
|
141
|
+
if (hasLegacyHead && input.readLegacyHead) {
|
|
142
|
+
const verdict = authority
|
|
143
|
+
.verdictFor(scope)
|
|
144
|
+
.find((row) => row.instance === decision.selection.instance &&
|
|
145
|
+
row.generation === decision.generation &&
|
|
146
|
+
row.legacyVersionId === legacyHead.id);
|
|
147
|
+
const coveredHead = authority.hasCoveredHead(scope, legacyHead);
|
|
148
|
+
if (verdict?.verdict !== "covered") {
|
|
149
|
+
return { kind: coveredHead ? "refused" : "file" };
|
|
150
|
+
}
|
|
151
|
+
if (!verdict.canonicalCursor.startsWith(`${authority.writeClock()}:${authority.arrivalRevision()}:${receiptCursor(decision, input.declarations)}:`)) {
|
|
152
|
+
return { kind: "refused" };
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
else if (hasLegacyHead && !hasTakeover)
|
|
156
|
+
return { kind: "file" };
|
|
157
|
+
if (!input.readLegacyHead &&
|
|
158
|
+
hasLegacyHead &&
|
|
159
|
+
hasTakeover &&
|
|
160
|
+
missingCompletionReceipts(decision).length > 0) {
|
|
161
|
+
return { kind: "refused" };
|
|
162
|
+
}
|
|
163
|
+
const collectedAt = listedCollectedAt(decision.records, decision.selection.instance, decision.streams);
|
|
164
|
+
return collectedAt === null
|
|
165
|
+
? { kind: "refused" }
|
|
166
|
+
: { kind: "canonical", collectedAt };
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
return {
|
|
170
|
+
async readLatest(scope) {
|
|
171
|
+
if (authority.isReset(scope))
|
|
172
|
+
return { kind: "file" };
|
|
173
|
+
const initial = authority.readSnapshot(() => {
|
|
174
|
+
const decision = decide(scope);
|
|
175
|
+
return {
|
|
176
|
+
decision,
|
|
177
|
+
legacyHead: authority.eligibleLegacyHead(scope),
|
|
178
|
+
hasTakeover: authority.hasTakeover(scope, decision.kind === "canonical"
|
|
179
|
+
? decision.selection.instance
|
|
180
|
+
: undefined, decision.kind === "canonical" ? decision.generation : undefined),
|
|
181
|
+
canServeLegacy: canServeLegacyBeforeTakeover(scope),
|
|
182
|
+
writeClock: authority.writeClock(),
|
|
183
|
+
arrivalRevision: authority.arrivalRevision(),
|
|
184
|
+
receiptCursor: decision.kind === "canonical"
|
|
185
|
+
? receiptCursor(decision, input.declarations)
|
|
186
|
+
: "",
|
|
187
|
+
};
|
|
188
|
+
});
|
|
189
|
+
const { decision, legacyHead, hasTakeover, canServeLegacy } = initial;
|
|
190
|
+
if (decision.kind === "refused" &&
|
|
191
|
+
decision.refusal.status === "unavailable" &&
|
|
192
|
+
decision.refusal.reason === "no_connection" &&
|
|
193
|
+
canServeLegacy) {
|
|
194
|
+
return { kind: "file" };
|
|
195
|
+
}
|
|
196
|
+
if (decision.kind !== "canonical")
|
|
197
|
+
return decision;
|
|
198
|
+
const canonicalCursor = legacyHead
|
|
199
|
+
? [
|
|
200
|
+
initial.writeClock,
|
|
201
|
+
initial.arrivalRevision,
|
|
202
|
+
initial.receiptCursor,
|
|
203
|
+
decision.generation,
|
|
204
|
+
CANONICAL_LEGACY_VIEW_CONTRACT,
|
|
205
|
+
scopeDeclarationCursor(scope),
|
|
206
|
+
COVERAGE_RULE_VERSION,
|
|
207
|
+
input.coverageThresholds?.maxPayloadRatio ?? 1.5,
|
|
208
|
+
input.coverageThresholds?.maxExtraBytes ?? 256 * 1024,
|
|
209
|
+
].join(":")
|
|
210
|
+
: "";
|
|
211
|
+
const projectionKey = legacyHead
|
|
212
|
+
? JSON.stringify([
|
|
213
|
+
decision.selection.instance,
|
|
214
|
+
decision.generation,
|
|
215
|
+
legacyHead.id,
|
|
216
|
+
canonicalCursor,
|
|
217
|
+
])
|
|
218
|
+
: "";
|
|
219
|
+
const projectionOwner = scope;
|
|
220
|
+
const cachedCoverage = legacyHead !== null && input.readLegacyHead
|
|
221
|
+
? authority.cachedVerdict(scope, decision.selection.instance, decision.generation, legacyHead.id, canonicalCursor)
|
|
222
|
+
: null;
|
|
223
|
+
const cachedProjection = coveredProjection.get(projectionOwner);
|
|
224
|
+
if (cachedCoverage?.covered && cachedProjection?.key === projectionKey) {
|
|
225
|
+
const confirmed = authority.confirmRead(scope, legacyHead, () => sameSelection(decide(scope), decision) &&
|
|
226
|
+
receiptCursor(decision, input.declarations) ===
|
|
227
|
+
initial.receiptCursor, {
|
|
228
|
+
instance: decision.selection.instance,
|
|
229
|
+
generation: decision.generation,
|
|
230
|
+
writeClock: initial.writeClock,
|
|
231
|
+
arrivalRevision: initial.arrivalRevision,
|
|
232
|
+
canonicalCursor,
|
|
233
|
+
coverage: cachedCoverage,
|
|
234
|
+
cached: true,
|
|
235
|
+
});
|
|
236
|
+
if (!confirmed) {
|
|
237
|
+
if (authority.isReset(scope))
|
|
238
|
+
return { kind: "file" };
|
|
239
|
+
return {
|
|
240
|
+
kind: "refused",
|
|
241
|
+
refusal: {
|
|
242
|
+
status: "unavailable",
|
|
243
|
+
reason: "store_inconsistent_read",
|
|
244
|
+
detail: "legacy head or canonical cursor changed during parity evaluation",
|
|
245
|
+
},
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
return {
|
|
249
|
+
kind: "canonical",
|
|
250
|
+
envelope: cachedProjection.envelope,
|
|
251
|
+
revision: cachedProjection.revision,
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
if (cachedCoverage && !cachedCoverage.covered && legacyHead) {
|
|
255
|
+
let legacy;
|
|
256
|
+
try {
|
|
257
|
+
legacy = await input.readLegacyHead(legacyHead);
|
|
258
|
+
}
|
|
259
|
+
catch {
|
|
260
|
+
return hasTakeover
|
|
261
|
+
? {
|
|
262
|
+
kind: "refused",
|
|
263
|
+
refusal: {
|
|
264
|
+
status: "unavailable",
|
|
265
|
+
reason: "store_inconsistent_read",
|
|
266
|
+
detail: "indexed legacy head could not be read",
|
|
267
|
+
},
|
|
268
|
+
}
|
|
269
|
+
: { kind: "file" };
|
|
270
|
+
}
|
|
271
|
+
if (legacy.scope !== scope) {
|
|
272
|
+
return {
|
|
273
|
+
kind: "refused",
|
|
274
|
+
refusal: {
|
|
275
|
+
status: "unavailable",
|
|
276
|
+
reason: "store_inconsistent_read",
|
|
277
|
+
detail: "indexed legacy head has another scope",
|
|
278
|
+
},
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
const alreadyCovered = authority.hasCoveredHead(scope, legacyHead);
|
|
282
|
+
const confirmed = authority.confirmRead(scope, legacyHead, () => sameSelection(decide(scope), decision) &&
|
|
283
|
+
receiptCursor(decision, input.declarations) ===
|
|
284
|
+
initial.receiptCursor, {
|
|
285
|
+
instance: decision.selection.instance,
|
|
286
|
+
generation: decision.generation,
|
|
287
|
+
writeClock: initial.writeClock,
|
|
288
|
+
arrivalRevision: initial.arrivalRevision,
|
|
289
|
+
canonicalCursor,
|
|
290
|
+
coverage: cachedCoverage,
|
|
291
|
+
cached: true,
|
|
292
|
+
});
|
|
293
|
+
if (!confirmed) {
|
|
294
|
+
if (authority.isReset(scope))
|
|
295
|
+
return { kind: "file" };
|
|
296
|
+
return {
|
|
297
|
+
kind: "refused",
|
|
298
|
+
refusal: {
|
|
299
|
+
status: "unavailable",
|
|
300
|
+
reason: "store_inconsistent_read",
|
|
301
|
+
detail: "legacy head or canonical cursor changed during parity evaluation",
|
|
302
|
+
},
|
|
303
|
+
};
|
|
304
|
+
}
|
|
305
|
+
return alreadyCovered
|
|
306
|
+
? {
|
|
307
|
+
kind: "refused",
|
|
308
|
+
refusal: {
|
|
309
|
+
status: "incomplete",
|
|
310
|
+
reason: "canonical_view_incomplete",
|
|
311
|
+
diagnostics: [],
|
|
312
|
+
},
|
|
313
|
+
}
|
|
314
|
+
: { kind: "file" };
|
|
315
|
+
}
|
|
316
|
+
const read = await readCanonicalLegacyScope({
|
|
317
|
+
store: decision.records,
|
|
318
|
+
declarations: input.declarations,
|
|
319
|
+
scope,
|
|
320
|
+
selection: decision.selection,
|
|
321
|
+
profileBy: "method",
|
|
322
|
+
requireCompletionReceipts: legacyHead !== null && !input.readLegacyHead,
|
|
323
|
+
allowUnprovenEmptyStreams: legacyHead !== null && !!input.readLegacyHead,
|
|
324
|
+
includeStreamTimes: legacyHead !== null && !!input.readLegacyHead,
|
|
325
|
+
requireClaudeRawBlobs: legacyHead !== null && !!input.readLegacyHead,
|
|
326
|
+
// The rows must come from a state in which this is still the one
|
|
327
|
+
// connection: a second account appearing between the decision and
|
|
328
|
+
// the rows refuses instead of serving a body chosen while ambiguous.
|
|
329
|
+
confirmSelection: () => sameSelection(decide(scope), decision),
|
|
330
|
+
});
|
|
331
|
+
if (!read.ok) {
|
|
332
|
+
if (legacyHead !== null &&
|
|
333
|
+
input.readLegacyHead &&
|
|
334
|
+
isProjectionDataFailure(read.reason)) {
|
|
335
|
+
let legacy;
|
|
336
|
+
try {
|
|
337
|
+
legacy = await input.readLegacyHead(legacyHead);
|
|
338
|
+
}
|
|
339
|
+
catch {
|
|
340
|
+
const { ok: _ok, ...refusal } = read;
|
|
341
|
+
return { kind: "refused", refusal };
|
|
342
|
+
}
|
|
343
|
+
if (legacy.scope !== scope) {
|
|
344
|
+
return {
|
|
345
|
+
kind: "refused",
|
|
346
|
+
refusal: {
|
|
347
|
+
status: "unavailable",
|
|
348
|
+
reason: "store_inconsistent_read",
|
|
349
|
+
detail: "indexed legacy head has another scope",
|
|
350
|
+
},
|
|
351
|
+
};
|
|
352
|
+
}
|
|
353
|
+
const coveredHead = authority.hasCoveredHead(scope, legacyHead);
|
|
354
|
+
const coverage = projectionUnavailableCoverage(legacy.data, read.reason === "missing_raw_blob" ? read.recordCount : 0);
|
|
355
|
+
const confirmed = authority.confirmRead(scope, legacyHead, () => sameSelection(decide(scope), decision) &&
|
|
356
|
+
receiptCursor(decision, input.declarations) ===
|
|
357
|
+
initial.receiptCursor, {
|
|
358
|
+
instance: decision.selection.instance,
|
|
359
|
+
generation: decision.generation,
|
|
360
|
+
writeClock: initial.writeClock,
|
|
361
|
+
arrivalRevision: initial.arrivalRevision,
|
|
362
|
+
canonicalCursor: `${initial.writeClock}:${initial.arrivalRevision}:${initial.receiptCursor}:unavailable:${read.reason}:${COVERAGE_RULE_VERSION}`,
|
|
363
|
+
coverage,
|
|
364
|
+
cached: false,
|
|
365
|
+
});
|
|
366
|
+
if (!confirmed) {
|
|
367
|
+
if (authority.isReset(scope))
|
|
368
|
+
return { kind: "file" };
|
|
369
|
+
return {
|
|
370
|
+
kind: "refused",
|
|
371
|
+
refusal: {
|
|
372
|
+
status: "unavailable",
|
|
373
|
+
reason: "store_inconsistent_read",
|
|
374
|
+
detail: "legacy head or canonical cursor changed during parity evaluation",
|
|
375
|
+
},
|
|
376
|
+
};
|
|
377
|
+
}
|
|
378
|
+
if (coveredHead) {
|
|
379
|
+
const { ok: _ok, ...refusal } = read;
|
|
380
|
+
return { kind: "refused", refusal };
|
|
381
|
+
}
|
|
382
|
+
return { kind: "file" };
|
|
383
|
+
}
|
|
384
|
+
if (legacyHead !== null &&
|
|
385
|
+
!hasTakeover &&
|
|
386
|
+
(read.reason === "projection_rejected" ||
|
|
387
|
+
read.reason === "all_rows_rejected" ||
|
|
388
|
+
read.reason === "no_synced_data" ||
|
|
389
|
+
read.reason === "stream_completion_unknown" ||
|
|
390
|
+
read.reason === "blob_read_budget_exceeded" ||
|
|
391
|
+
read.reason === "blob_backed_record_invalid" ||
|
|
392
|
+
read.reason === "missing_raw_blob" ||
|
|
393
|
+
read.reason === "blob_backed_records_unsupported")) {
|
|
394
|
+
const stillLegacyOwned = authority.readSnapshot(() => canServeLegacyBeforeTakeover(scope));
|
|
395
|
+
if (stillLegacyOwned)
|
|
396
|
+
return { kind: "file" };
|
|
397
|
+
}
|
|
398
|
+
const { ok: _ok, ...refusal } = read;
|
|
399
|
+
return { kind: "refused", refusal };
|
|
400
|
+
}
|
|
401
|
+
if (legacyHead !== null && input.readLegacyHead) {
|
|
402
|
+
let coverage = cachedCoverage;
|
|
403
|
+
const cached = coverage !== null;
|
|
404
|
+
if (!coverage) {
|
|
405
|
+
let legacy;
|
|
406
|
+
try {
|
|
407
|
+
legacy = await input.readLegacyHead(legacyHead);
|
|
408
|
+
}
|
|
409
|
+
catch {
|
|
410
|
+
return hasTakeover
|
|
411
|
+
? {
|
|
412
|
+
kind: "refused",
|
|
413
|
+
refusal: {
|
|
414
|
+
status: "unavailable",
|
|
415
|
+
reason: "store_inconsistent_read",
|
|
416
|
+
detail: "indexed legacy head could not be read",
|
|
417
|
+
},
|
|
418
|
+
}
|
|
419
|
+
: { kind: "file" };
|
|
420
|
+
}
|
|
421
|
+
if (legacy.scope !== scope) {
|
|
422
|
+
return {
|
|
423
|
+
kind: "refused",
|
|
424
|
+
refusal: {
|
|
425
|
+
status: "unavailable",
|
|
426
|
+
reason: "store_inconsistent_read",
|
|
427
|
+
detail: "indexed legacy head has another scope",
|
|
428
|
+
},
|
|
429
|
+
};
|
|
430
|
+
}
|
|
431
|
+
coverage = compareLegacyCoverage(legacy.data, read.envelope.data, {
|
|
432
|
+
scope,
|
|
433
|
+
entityKeys: LEGACY_SCOPE_ENTITY_KEYS[scope],
|
|
434
|
+
projectionOmissions: read.projectionOmissions,
|
|
435
|
+
canonicalNewer: allStreamsNewerThan(read, decision.streams, legacyHead.collectedAt),
|
|
436
|
+
allowProvenDeletion: read.complete &&
|
|
437
|
+
allStreamsProvenAfter(read, decision.streams, legacyHead.collectedAt),
|
|
438
|
+
...input.coverageThresholds,
|
|
439
|
+
});
|
|
440
|
+
}
|
|
441
|
+
const alreadyCovered = authority.hasCoveredHead(scope, legacyHead);
|
|
442
|
+
const confirmed = authority.confirmRead(scope, legacyHead, () => sameSelection(decide(scope), decision) &&
|
|
443
|
+
receiptCursor(decision, input.declarations) ===
|
|
444
|
+
initial.receiptCursor, {
|
|
445
|
+
instance: decision.selection.instance,
|
|
446
|
+
generation: decision.generation,
|
|
447
|
+
writeClock: initial.writeClock,
|
|
448
|
+
arrivalRevision: initial.arrivalRevision,
|
|
449
|
+
canonicalCursor,
|
|
450
|
+
coverage,
|
|
451
|
+
cached,
|
|
452
|
+
});
|
|
453
|
+
if (!confirmed) {
|
|
454
|
+
if (authority.isReset(scope))
|
|
455
|
+
return { kind: "file" };
|
|
456
|
+
return {
|
|
457
|
+
kind: "refused",
|
|
458
|
+
refusal: {
|
|
459
|
+
status: "unavailable",
|
|
460
|
+
reason: "store_inconsistent_read",
|
|
461
|
+
detail: "legacy head or canonical cursor changed during parity evaluation",
|
|
462
|
+
},
|
|
463
|
+
};
|
|
464
|
+
}
|
|
465
|
+
if (!coverage.covered) {
|
|
466
|
+
// A newer file is an explicit, recorded rollback. A canonical
|
|
467
|
+
// regression against the same file must still refuse after takeover.
|
|
468
|
+
return alreadyCovered
|
|
469
|
+
? {
|
|
470
|
+
kind: "refused",
|
|
471
|
+
refusal: {
|
|
472
|
+
status: "incomplete",
|
|
473
|
+
reason: "canonical_view_incomplete",
|
|
474
|
+
diagnostics: [],
|
|
475
|
+
},
|
|
476
|
+
}
|
|
477
|
+
: { kind: "file" };
|
|
478
|
+
}
|
|
479
|
+
coveredProjection.set(projectionOwner, {
|
|
480
|
+
key: projectionKey,
|
|
481
|
+
envelope: read.envelope,
|
|
482
|
+
revision: read.revision,
|
|
483
|
+
});
|
|
484
|
+
return {
|
|
485
|
+
kind: "canonical",
|
|
486
|
+
envelope: read.envelope,
|
|
487
|
+
revision: read.revision,
|
|
488
|
+
};
|
|
489
|
+
}
|
|
490
|
+
if (!read.complete) {
|
|
491
|
+
if (legacyHead !== null && !hasTakeover) {
|
|
492
|
+
const stillLegacyOwned = authority.readSnapshot(() => canServeLegacyBeforeTakeover(scope));
|
|
493
|
+
if (stillLegacyOwned)
|
|
494
|
+
return { kind: "file" };
|
|
495
|
+
}
|
|
496
|
+
return {
|
|
497
|
+
kind: "refused",
|
|
498
|
+
refusal: {
|
|
499
|
+
status: "incomplete",
|
|
500
|
+
reason: "canonical_view_incomplete",
|
|
501
|
+
diagnostics: incompleteness(read),
|
|
502
|
+
},
|
|
503
|
+
};
|
|
504
|
+
}
|
|
505
|
+
const confirmed = authority.confirmRead(scope, legacyHead, () => sameSelection(decide(scope), decision));
|
|
506
|
+
if (!confirmed) {
|
|
507
|
+
const current = authority.readSnapshot(() => ({
|
|
508
|
+
canServeLegacy: canServeLegacyBeforeTakeover(scope),
|
|
509
|
+
hasTakeover: authority.hasTakeover(scope, decision.selection.instance, decision.generation),
|
|
510
|
+
}));
|
|
511
|
+
if (current.canServeLegacy && !current.hasTakeover)
|
|
512
|
+
return { kind: "file" };
|
|
513
|
+
return {
|
|
514
|
+
kind: "refused",
|
|
515
|
+
refusal: {
|
|
516
|
+
status: "unavailable",
|
|
517
|
+
reason: "store_inconsistent_read",
|
|
518
|
+
detail: "legacy head or connection selection changed during canonical projection",
|
|
519
|
+
},
|
|
520
|
+
};
|
|
521
|
+
}
|
|
522
|
+
return {
|
|
523
|
+
kind: "canonical",
|
|
524
|
+
envelope: read.envelope,
|
|
525
|
+
revision: read.revision,
|
|
526
|
+
};
|
|
527
|
+
},
|
|
528
|
+
listingFor,
|
|
529
|
+
listCanonicalScopes(scopePrefix) {
|
|
530
|
+
const listed = [];
|
|
531
|
+
for (const scope of boundScopes()) {
|
|
532
|
+
if (scopePrefix !== undefined && !scope.startsWith(scopePrefix)) {
|
|
533
|
+
continue;
|
|
534
|
+
}
|
|
535
|
+
const listing = listingFor(scope);
|
|
536
|
+
if (listing.kind === "canonical") {
|
|
537
|
+
listed.push({ scope, collectedAt: listing.collectedAt });
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
return listed;
|
|
541
|
+
},
|
|
542
|
+
ownsScope(scope) {
|
|
543
|
+
return authority.readSnapshot(() => decide(scope)).kind !== "file";
|
|
544
|
+
},
|
|
545
|
+
verdictFor: (scope) => authority.verdictFor(scope),
|
|
546
|
+
resetTakeover: (scope) => authority.resetTakeover(scope),
|
|
547
|
+
isReset: (scope) => authority.isReset(scope),
|
|
548
|
+
};
|
|
549
|
+
}
|
|
550
|
+
function missingCompletionReceipts(decision) {
|
|
551
|
+
const binding = decision.records.getInstanceBinding(decision.selection.instance);
|
|
552
|
+
const listings = decision.records.listStreams([decision.selection.instance]);
|
|
553
|
+
const receipts = decision.records.listStreamCompletions(decision.selection.instance);
|
|
554
|
+
return decision.streams.filter((stream) => {
|
|
555
|
+
const liveCount = listings.find((entry) => entry.stream === stream)?.recordCount ?? 0;
|
|
556
|
+
return !receipts.some((receipt) => receipt.stream === stream &&
|
|
557
|
+
receipt.method === binding.method &&
|
|
558
|
+
receipt.generation === binding.generation &&
|
|
559
|
+
receipt.liveCount === liveCount);
|
|
560
|
+
});
|
|
561
|
+
}
|
|
562
|
+
function isProjectionDataFailure(reason) {
|
|
563
|
+
return [
|
|
564
|
+
"projection_rejected",
|
|
565
|
+
"all_rows_rejected",
|
|
566
|
+
"no_synced_data",
|
|
567
|
+
"stream_completion_unknown",
|
|
568
|
+
"invalid_collected_at",
|
|
569
|
+
"blob_read_budget_exceeded",
|
|
570
|
+
"blob_backed_record_invalid",
|
|
571
|
+
"missing_raw_blob",
|
|
572
|
+
"blob_backed_records_unsupported",
|
|
573
|
+
].includes(reason);
|
|
574
|
+
}
|
|
575
|
+
function receiptCursor(decision, declarations) {
|
|
576
|
+
const receipts = decision.records
|
|
577
|
+
.listStreamCompletions(decision.selection.instance)
|
|
578
|
+
.filter((receipt) => decision.streams.includes(receipt.stream))
|
|
579
|
+
.map((receipt) => [
|
|
580
|
+
receipt.stream,
|
|
581
|
+
receipt.method,
|
|
582
|
+
receipt.generation,
|
|
583
|
+
receipt.sourceCollectedAt,
|
|
584
|
+
receipt.writeSeq,
|
|
585
|
+
receipt.liveCount,
|
|
586
|
+
])
|
|
587
|
+
.sort((a, b) => String(a[0]).localeCompare(String(b[0])));
|
|
588
|
+
// A declaration-only change must invalidate covered-body and verdict caches,
|
|
589
|
+
// including while an asynchronous blob digest or legacy file read is pending.
|
|
590
|
+
if (!declarations)
|
|
591
|
+
return JSON.stringify(receipts);
|
|
592
|
+
return JSON.stringify({
|
|
593
|
+
receipts,
|
|
594
|
+
schemas: decision.streams.map((stream) => [
|
|
595
|
+
stream,
|
|
596
|
+
declarations.forInstance(decision.selection.instance, stream)?.schema ??
|
|
597
|
+
null,
|
|
598
|
+
]),
|
|
599
|
+
});
|
|
600
|
+
}
|
|
601
|
+
function projectionUnavailableCoverage(legacyPayload, missingRawBlobCount = 0) {
|
|
602
|
+
const legacyBytes = new TextEncoder().encode(JSON.stringify(legacyPayload)).length;
|
|
603
|
+
return {
|
|
604
|
+
comparisonCase: "same_time",
|
|
605
|
+
covered: false,
|
|
606
|
+
reason: missingRawBlobCount > 0 ? "missing_raw_blob" : "projection_unavailable",
|
|
607
|
+
reasonCounts: {
|
|
608
|
+
no_stable_key: 0,
|
|
609
|
+
missing_entity: 0,
|
|
610
|
+
missing_field: 0,
|
|
611
|
+
field_mismatch: 0,
|
|
612
|
+
type_mismatch: 0,
|
|
613
|
+
timestamp_format: 0,
|
|
614
|
+
identity_mismatch: 0,
|
|
615
|
+
creation_mismatch: 0,
|
|
616
|
+
array_order: 0,
|
|
617
|
+
payload_growth: 0,
|
|
618
|
+
missing_raw_blob: missingRawBlobCount,
|
|
619
|
+
projection_unavailable: missingRawBlobCount > 0 ? 0 : 1,
|
|
620
|
+
},
|
|
621
|
+
appliedExceptions: [],
|
|
622
|
+
missingEntityCount: 0,
|
|
623
|
+
mismatchedFieldPaths: [],
|
|
624
|
+
legacyBytes,
|
|
625
|
+
canonicalBytes: 0,
|
|
626
|
+
byteDelta: -legacyBytes,
|
|
627
|
+
};
|
|
628
|
+
}
|
|
629
|
+
/** A newer scope snapshot requires a newer collection horizon for each input stream. */
|
|
630
|
+
function allStreamsNewerThan(read, streams, legacyCollectedAt) {
|
|
631
|
+
const legacyTime = Date.parse(legacyCollectedAt);
|
|
632
|
+
if (!Number.isFinite(legacyTime))
|
|
633
|
+
return false;
|
|
634
|
+
return streams.every((stream) => {
|
|
635
|
+
const time = Date.parse(read.streamCollectionTimes?.[stream] ?? "");
|
|
636
|
+
return Number.isFinite(time) && time > legacyTime;
|
|
637
|
+
});
|
|
638
|
+
}
|
|
639
|
+
/** Only current P7 receipts from every input stream prove a later deletion. */
|
|
640
|
+
function allStreamsProvenAfter(read, streams, legacyCollectedAt) {
|
|
641
|
+
const legacyTime = Date.parse(legacyCollectedAt);
|
|
642
|
+
if (!Number.isFinite(legacyTime))
|
|
643
|
+
return false;
|
|
644
|
+
return streams.every((stream) => {
|
|
645
|
+
const time = Date.parse(read.streamCompletionTimes?.[stream] ?? "");
|
|
646
|
+
return Number.isFinite(time) && time > legacyTime;
|
|
647
|
+
});
|
|
648
|
+
}
|
|
649
|
+
function refused(refusal) {
|
|
650
|
+
return { kind: "refused", refusal };
|
|
651
|
+
}
|
|
652
|
+
function sameSelection(again, decision) {
|
|
653
|
+
return (again.kind === "canonical" &&
|
|
654
|
+
again.selection.instance === decision.selection.instance &&
|
|
655
|
+
again.selection.sourceId === decision.selection.sourceId &&
|
|
656
|
+
again.selection.method === decision.selection.method &&
|
|
657
|
+
again.generation === decision.generation);
|
|
658
|
+
}
|
|
659
|
+
/**
|
|
660
|
+
* The methods that may serve a scope, each with the streams its binding
|
|
661
|
+
* reads. A method id is a binding's connector key; its binding is the one
|
|
662
|
+
* the reader will use for that method.
|
|
663
|
+
*/
|
|
664
|
+
function scopeMethods(scope) {
|
|
665
|
+
const methods = new Map();
|
|
666
|
+
for (const profile of BINDING_PROFILES) {
|
|
667
|
+
const bound = profile.get(scope);
|
|
668
|
+
if (!bound)
|
|
669
|
+
continue;
|
|
670
|
+
const method = legacyConnectorKey(bound.pdppSource);
|
|
671
|
+
const binding = legacyBindingsForProfile(method).get(scope);
|
|
672
|
+
if (binding)
|
|
673
|
+
methods.set(method, binding.pdppStreams);
|
|
674
|
+
}
|
|
675
|
+
return methods;
|
|
676
|
+
}
|
|
677
|
+
function boundScopes() {
|
|
678
|
+
const scopes = new Set();
|
|
679
|
+
for (const profile of BINDING_PROFILES) {
|
|
680
|
+
for (const scope of profile.keys())
|
|
681
|
+
scopes.add(scope);
|
|
682
|
+
}
|
|
683
|
+
return [...scopes].sort();
|
|
684
|
+
}
|
|
685
|
+
/**
|
|
686
|
+
* The `collectedAt` a read would return, from listing counts and receipts:
|
|
687
|
+
* a stream's valid receipt time, else its newest row time. Null when a bound
|
|
688
|
+
* stream is empty without a valid receipt, which a read refuses.
|
|
689
|
+
*/
|
|
690
|
+
function listedCollectedAt(store, instance, streams) {
|
|
691
|
+
const binding = store.getInstanceBinding(instance);
|
|
692
|
+
const listing = store.listStreams([instance]);
|
|
693
|
+
const receipts = store.listStreamCompletions(instance);
|
|
694
|
+
let newest = Number.NEGATIVE_INFINITY;
|
|
695
|
+
for (const stream of streams) {
|
|
696
|
+
const entry = listing.find((candidate) => candidate.stream === stream);
|
|
697
|
+
const count = entry?.recordCount ?? 0;
|
|
698
|
+
const receipt = receipts.find((candidate) => candidate.stream === stream &&
|
|
699
|
+
candidate.method === binding.method &&
|
|
700
|
+
candidate.generation === binding.generation &&
|
|
701
|
+
candidate.liveCount === count);
|
|
702
|
+
const stamp = receipt?.sourceCollectedAt ?? entry?.lastUpdated ?? null;
|
|
703
|
+
if (count === 0 && !receipt)
|
|
704
|
+
return null;
|
|
705
|
+
if (stamp === null)
|
|
706
|
+
return null;
|
|
707
|
+
const ms = Date.parse(stamp);
|
|
708
|
+
if (Number.isNaN(ms))
|
|
709
|
+
return null;
|
|
710
|
+
newest = Math.max(newest, ms);
|
|
711
|
+
}
|
|
712
|
+
return Number.isFinite(newest) ? new Date(newest).toISOString() : null;
|
|
713
|
+
}
|
|
714
|
+
function incompleteness(read) {
|
|
715
|
+
const { omissions, partialRows, unproven } = read.diagnostics;
|
|
716
|
+
return [
|
|
717
|
+
...omissions.map((entry) => ({
|
|
718
|
+
stream: entry.stream,
|
|
719
|
+
kind: "omitted",
|
|
720
|
+
detail: `${entry.count} rows: ${entry.reasons.join("; ")}`,
|
|
721
|
+
})),
|
|
722
|
+
...partialRows.map((entry) => ({
|
|
723
|
+
stream: entry.stream,
|
|
724
|
+
kind: "partial",
|
|
725
|
+
detail: `${entry.count} rows: ${entry.reasons.join("; ")}`,
|
|
726
|
+
})),
|
|
727
|
+
// Optional on the canonical-legacy-view/4 contract: omitted when empty.
|
|
728
|
+
...(unproven ?? []).map((entry) => ({
|
|
729
|
+
stream: entry.stream,
|
|
730
|
+
kind: "unproven",
|
|
731
|
+
detail: `${entry.aspect}: ${entry.reason}`,
|
|
732
|
+
})),
|
|
733
|
+
];
|
|
734
|
+
}
|
|
735
|
+
//# sourceMappingURL=legacy-scope-view.js.map
|