@opendatalabs/personal-server-ts-core 1.33.8 → 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/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,761 @@
|
|
|
1
|
+
import { ANTHROPIC_CONNECTOR_ID, CLAUDE_SOURCE_ID, } from "../legacy-projection/bindings.js";
|
|
2
|
+
import { hasAlternateLegacyProfile, LEGACY_SCOPE_BINDINGS, legacyBindingsForProfile, legacyScopeToPdppSelection, projectPdppRecordsToLegacyPayload, } from "../legacy-projection/index.js";
|
|
3
|
+
import { declaredBlobField, declaredBlobReference, decodeDeclaredBlob, decodeDeclaredRecordsForProjection, DeclaredTextError, } from "../legacy-projection/declared-text.js";
|
|
4
|
+
import { claudeBlobReference, ClaudeSourceRecordError, decodeClaudeSourceRecord, mergeClaudeSourceRecords, } from "../legacy-projection/claude-source-record.js";
|
|
5
|
+
import { CursorExpiredError, InvalidCursorError, } from "./pdpp-records/portable.js";
|
|
6
|
+
/**
|
|
7
|
+
* Read-time legacy view over canonical PDPP records.
|
|
8
|
+
*
|
|
9
|
+
* Reads ONE legacy scope from exactly one selected connection and returns the
|
|
10
|
+
* legacy `DataFileEnvelope`. Nothing is stored: every call recomputes the body
|
|
11
|
+
* from the live rows, so a repaired source is visible on the next read. The
|
|
12
|
+
* reader only calls read methods on the store.
|
|
13
|
+
*
|
|
14
|
+
* Out of scope here: manual/unbound scope files (no binding => `unbound_scope`),
|
|
15
|
+
* auth/grants, and the sync `DataStoragePort` listing surface.
|
|
16
|
+
*/
|
|
17
|
+
/** Bump when a change to this module (or the pure projector) can change bytes. */
|
|
18
|
+
export const CANONICAL_LEGACY_VIEW_CONTRACT = "canonical-legacy-view/7";
|
|
19
|
+
const DEFAULT_PAGE_SIZE = 500;
|
|
20
|
+
const MAX_BLOB_READ_BYTES = 64 * 1024 * 1024;
|
|
21
|
+
const SOURCE_ID_PREFIX = "https://registry.pdpp.dev/sources/";
|
|
22
|
+
const CONNECTOR_ID_PREFIX = "https://registry.pdpp.dev/connectors/";
|
|
23
|
+
/**
|
|
24
|
+
* Pick the single connection for a source. Several matches is an error, never
|
|
25
|
+
* a union: a legacy body is one account's data.
|
|
26
|
+
*/
|
|
27
|
+
export function resolveCanonicalConnection(store, query) {
|
|
28
|
+
const matches = store
|
|
29
|
+
.listConnections(query.sourceId)
|
|
30
|
+
.filter((binding) => binding.deletedAt === null &&
|
|
31
|
+
binding.method !== null &&
|
|
32
|
+
(query.method === undefined || binding.method === query.method) &&
|
|
33
|
+
(query.instance === undefined || binding.instance === query.instance));
|
|
34
|
+
const [only] = matches;
|
|
35
|
+
if (matches.length === 1 && only?.method) {
|
|
36
|
+
return {
|
|
37
|
+
ok: true,
|
|
38
|
+
selection: {
|
|
39
|
+
instance: only.instance,
|
|
40
|
+
sourceId: query.sourceId,
|
|
41
|
+
method: only.method,
|
|
42
|
+
},
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
return {
|
|
46
|
+
ok: false,
|
|
47
|
+
status: "unavailable",
|
|
48
|
+
reason: matches.length === 0 ? "no_connection" : "ambiguous_connection",
|
|
49
|
+
candidates: matches.length,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
export async function readCanonicalLegacyScope(input) {
|
|
53
|
+
const { store, scope, selection } = input;
|
|
54
|
+
const pageSize = Math.max(1, Math.floor(input.pageSize ?? DEFAULT_PAGE_SIZE));
|
|
55
|
+
const byMethod = input.profileBy === "method";
|
|
56
|
+
const profileKey = byMethod
|
|
57
|
+
? profileKeyForMethod(selection.method)
|
|
58
|
+
: profileKeyForSource(selection.sourceId);
|
|
59
|
+
const lookup = legacyScopeToPdppSelection(scope, { profileKey });
|
|
60
|
+
const binding = bindingFor(scope, profileKey);
|
|
61
|
+
if (!lookup.ok || !binding) {
|
|
62
|
+
return unavailable({
|
|
63
|
+
reason: "unbound_scope",
|
|
64
|
+
lookup: lookup.ok ? { kind: "unknown_scope", scope } : lookup.error,
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
if ((binding.pdppSource === ANTHROPIC_CONNECTOR_ID &&
|
|
68
|
+
selection.sourceId !== CLAUDE_SOURCE_ID) ||
|
|
69
|
+
(byMethod
|
|
70
|
+
? legacyConnectorKey(binding.pdppSource) !== selection.method
|
|
71
|
+
: canonicalSourceId(binding.pdppSource) !== selection.sourceId)) {
|
|
72
|
+
return unavailable({ reason: "scope_source_mismatch", scope });
|
|
73
|
+
}
|
|
74
|
+
const streams = lookup.selection.streams;
|
|
75
|
+
const snapshot = store.readSnapshot(() => input.confirmSelection && !input.confirmSelection()
|
|
76
|
+
? {
|
|
77
|
+
ok: false,
|
|
78
|
+
result: inconsistent("the connection selection changed"),
|
|
79
|
+
}
|
|
80
|
+
: collectSnapshot(store, selection, streams, binding.rowOrder === "arrival", pageSize, Number.isSafeInteger(input.blobByteBudget) &&
|
|
81
|
+
(input.blobByteBudget ?? -1) >= 0
|
|
82
|
+
? Math.min(MAX_BLOB_READ_BYTES, input.blobByteBudget)
|
|
83
|
+
: MAX_BLOB_READ_BYTES, input.requireCompletionReceipts === true, input.allowUnprovenEmptyStreams === true, input.declarations));
|
|
84
|
+
if (!snapshot.ok)
|
|
85
|
+
return snapshot.result;
|
|
86
|
+
return projectSnapshot({
|
|
87
|
+
scope,
|
|
88
|
+
selection,
|
|
89
|
+
streams,
|
|
90
|
+
binding,
|
|
91
|
+
profileKey,
|
|
92
|
+
includeStreamTimes: input.includeStreamTimes === true,
|
|
93
|
+
requireClaudeRawBlobs: input.requireClaudeRawBlobs === true,
|
|
94
|
+
...snapshot,
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
/** Everything the view needs from the store, read inside one snapshot. */
|
|
98
|
+
function collectSnapshot(store, selection, streams, arrivalOrder, pageSize, blobByteBudget, requireCompletionReceipts, allowUnprovenEmptyStreams, declarations) {
|
|
99
|
+
const { instance } = selection;
|
|
100
|
+
const binding = store.getInstanceBinding(instance);
|
|
101
|
+
const rejected = checkConnection(binding, selection);
|
|
102
|
+
if (rejected)
|
|
103
|
+
return { ok: false, result: unavailable(rejected) };
|
|
104
|
+
const listing = store.listStreams([instance]);
|
|
105
|
+
const streamSchemas = {};
|
|
106
|
+
for (const stream of streams) {
|
|
107
|
+
const schema = declarations?.forInstance(instance, stream)?.schema;
|
|
108
|
+
if (schema)
|
|
109
|
+
streamSchemas[stream] = structuredClone(schema);
|
|
110
|
+
}
|
|
111
|
+
const rowsByStream = new Map();
|
|
112
|
+
const orderGaps = [];
|
|
113
|
+
try {
|
|
114
|
+
for (const stream of streams) {
|
|
115
|
+
const rows = readAllRows(store, instance, stream, pageSize);
|
|
116
|
+
if (typeof rows === "string") {
|
|
117
|
+
return { ok: false, result: inconsistent(rows) };
|
|
118
|
+
}
|
|
119
|
+
// Inside one snapshot the listing and the pages must agree.
|
|
120
|
+
if (rows.length !== countFor(listing, stream)) {
|
|
121
|
+
return {
|
|
122
|
+
ok: false,
|
|
123
|
+
result: inconsistent(`${stream} pages disagree with the listing`),
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
const ordered = arrivalOrder
|
|
127
|
+
? inArrivalOrder(rows, store.listArrivalOrder(instance, stream))
|
|
128
|
+
: rows;
|
|
129
|
+
if (!ordered)
|
|
130
|
+
orderGaps.push(stream);
|
|
131
|
+
rowsByStream.set(stream, ordered ?? rows);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
catch (error) {
|
|
135
|
+
if (error instanceof CursorExpiredError ||
|
|
136
|
+
error instanceof InvalidCursorError) {
|
|
137
|
+
return { ok: false, result: inconsistent("store cursor rejected") };
|
|
138
|
+
}
|
|
139
|
+
throw error;
|
|
140
|
+
}
|
|
141
|
+
const completions = new Map();
|
|
142
|
+
for (const receipt of store.listStreamCompletions(instance)) {
|
|
143
|
+
if (streams.includes(receipt.stream) &&
|
|
144
|
+
receipt.method === binding.method &&
|
|
145
|
+
receipt.generation === binding.generation &&
|
|
146
|
+
receipt.liveCount === rowsByStream.get(receipt.stream)?.length) {
|
|
147
|
+
completions.set(receipt.stream, receipt);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
// Refuse missing source evidence before reading even one blob body.
|
|
151
|
+
// Binding, rows, receipts, metadata and the later copies share this snapshot.
|
|
152
|
+
if (completions.size === 0 &&
|
|
153
|
+
listing.every((entry) => entry.recordCount === 0)) {
|
|
154
|
+
return { ok: false, result: unavailable({ reason: "no_synced_data" }) };
|
|
155
|
+
}
|
|
156
|
+
const unprovenStreams = streams.filter((stream) => !completions.has(stream) &&
|
|
157
|
+
(requireCompletionReceipts ||
|
|
158
|
+
(!allowUnprovenEmptyStreams &&
|
|
159
|
+
(rowsByStream.get(stream)?.length ?? 0) === 0)));
|
|
160
|
+
if (unprovenStreams.length > 0) {
|
|
161
|
+
return {
|
|
162
|
+
ok: false,
|
|
163
|
+
result: unavailable({
|
|
164
|
+
reason: "stream_completion_unknown",
|
|
165
|
+
streams: unprovenStreams,
|
|
166
|
+
}),
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
// Preflight all unique references/metadata before copying ANY body. A legal
|
|
170
|
+
// per-blob size does not bound fan-in; excluded raw fields still cost bytes.
|
|
171
|
+
const blobContents = new Map();
|
|
172
|
+
let requiredBytes = 0;
|
|
173
|
+
const declaredBlobIds = new Set();
|
|
174
|
+
for (const stream of streams) {
|
|
175
|
+
const blobBacked = (rowsByStream.get(stream) ?? []).filter(hasBlobRef);
|
|
176
|
+
if (blobBacked.length === 0)
|
|
177
|
+
continue;
|
|
178
|
+
const projectedField = declaredBlobField(streamSchemas[stream]);
|
|
179
|
+
if (!projectedField &&
|
|
180
|
+
(selection.sourceId !== CLAUDE_SOURCE_ID ||
|
|
181
|
+
(stream !== "conversations" && stream !== "projects"))) {
|
|
182
|
+
return {
|
|
183
|
+
ok: false,
|
|
184
|
+
result: unavailable({
|
|
185
|
+
reason: "blob_backed_records_unsupported",
|
|
186
|
+
stream,
|
|
187
|
+
recordCount: blobBacked.length,
|
|
188
|
+
blocker: "This scope/stream has no verified mapping from blob bytes to its legacy fields.",
|
|
189
|
+
}),
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
for (const row of blobBacked) {
|
|
193
|
+
let ref;
|
|
194
|
+
try {
|
|
195
|
+
ref = projectedField
|
|
196
|
+
? declaredBlobReference(row.data, projectedField)
|
|
197
|
+
: claudeBlobReference(row.data);
|
|
198
|
+
}
|
|
199
|
+
catch (error) {
|
|
200
|
+
if (!projectedField && !(error instanceof ClaudeSourceRecordError))
|
|
201
|
+
throw error;
|
|
202
|
+
return {
|
|
203
|
+
ok: false,
|
|
204
|
+
result: unavailable({
|
|
205
|
+
reason: "blob_backed_record_invalid",
|
|
206
|
+
detail: projectedField
|
|
207
|
+
? "invalid_blob_reference"
|
|
208
|
+
: error.message,
|
|
209
|
+
}),
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
if (projectedField)
|
|
213
|
+
declaredBlobIds.add(ref.blobId);
|
|
214
|
+
const prior = blobContents.get(ref.blobId);
|
|
215
|
+
let meta;
|
|
216
|
+
try {
|
|
217
|
+
meta = prior?.meta ?? store.getBlobMeta?.(ref.blobId);
|
|
218
|
+
}
|
|
219
|
+
catch (error) {
|
|
220
|
+
if (!projectedField)
|
|
221
|
+
throw error;
|
|
222
|
+
return {
|
|
223
|
+
ok: false,
|
|
224
|
+
result: unavailable({ reason: "missing_raw_blob", recordCount: 1 }),
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
if (!meta && projectedField) {
|
|
228
|
+
return {
|
|
229
|
+
ok: false,
|
|
230
|
+
result: unavailable({ reason: "missing_raw_blob", recordCount: 1 }),
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
if (!meta)
|
|
234
|
+
return {
|
|
235
|
+
ok: false,
|
|
236
|
+
result: unavailable({
|
|
237
|
+
reason: "blob_backed_record_invalid",
|
|
238
|
+
detail: "blob_missing_or_corrupt",
|
|
239
|
+
}),
|
|
240
|
+
};
|
|
241
|
+
if (meta.blobId !== ref.blobId ||
|
|
242
|
+
meta.mimeType !== "application/json" ||
|
|
243
|
+
meta.sizeBytes !== ref.size ||
|
|
244
|
+
meta.sha256 !== ref.sha256) {
|
|
245
|
+
return {
|
|
246
|
+
ok: false,
|
|
247
|
+
result: unavailable({
|
|
248
|
+
reason: "blob_backed_record_invalid",
|
|
249
|
+
detail: "blob_metadata_mismatch",
|
|
250
|
+
}),
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
if (prior)
|
|
254
|
+
continue;
|
|
255
|
+
const sum = requiredBytes + ref.size;
|
|
256
|
+
// Never accumulate an inexact integer, even for a refused request.
|
|
257
|
+
if (!Number.isSafeInteger(sum)) {
|
|
258
|
+
return {
|
|
259
|
+
ok: false,
|
|
260
|
+
result: unavailable({
|
|
261
|
+
reason: "blob_read_budget_exceeded",
|
|
262
|
+
maxBytes: blobByteBudget,
|
|
263
|
+
}),
|
|
264
|
+
};
|
|
265
|
+
}
|
|
266
|
+
requiredBytes = sum;
|
|
267
|
+
blobContents.set(ref.blobId, { meta: { ...meta }, bytes: undefined });
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
if (requiredBytes > blobByteBudget) {
|
|
271
|
+
return {
|
|
272
|
+
ok: false,
|
|
273
|
+
result: unavailable({
|
|
274
|
+
reason: "blob_read_budget_exceeded",
|
|
275
|
+
requiredBytes,
|
|
276
|
+
maxBytes: blobByteBudget,
|
|
277
|
+
}),
|
|
278
|
+
};
|
|
279
|
+
}
|
|
280
|
+
for (const [blobId, contents] of blobContents) {
|
|
281
|
+
let bytes;
|
|
282
|
+
try {
|
|
283
|
+
bytes = store.getBlobBytes?.(blobId);
|
|
284
|
+
}
|
|
285
|
+
catch (error) {
|
|
286
|
+
if (!declaredBlobIds.has(blobId))
|
|
287
|
+
throw error;
|
|
288
|
+
return {
|
|
289
|
+
ok: false,
|
|
290
|
+
result: unavailable({ reason: "missing_raw_blob", recordCount: 1 }),
|
|
291
|
+
};
|
|
292
|
+
}
|
|
293
|
+
if (!bytes && declaredBlobIds.has(blobId)) {
|
|
294
|
+
return {
|
|
295
|
+
ok: false,
|
|
296
|
+
result: unavailable({ reason: "missing_raw_blob", recordCount: 1 }),
|
|
297
|
+
};
|
|
298
|
+
}
|
|
299
|
+
if (!bytes)
|
|
300
|
+
return {
|
|
301
|
+
ok: false,
|
|
302
|
+
result: unavailable({
|
|
303
|
+
reason: "blob_backed_record_invalid",
|
|
304
|
+
detail: "blob_missing_or_corrupt",
|
|
305
|
+
}),
|
|
306
|
+
};
|
|
307
|
+
if (bytes.byteLength !== contents.meta?.sizeBytes) {
|
|
308
|
+
return {
|
|
309
|
+
ok: false,
|
|
310
|
+
result: unavailable({
|
|
311
|
+
reason: "blob_backed_record_invalid",
|
|
312
|
+
detail: "blob_metadata_mismatch",
|
|
313
|
+
}),
|
|
314
|
+
};
|
|
315
|
+
}
|
|
316
|
+
contents.bytes = bytes.slice();
|
|
317
|
+
}
|
|
318
|
+
return {
|
|
319
|
+
ok: true,
|
|
320
|
+
generation: binding.generation,
|
|
321
|
+
listing,
|
|
322
|
+
rowsByStream,
|
|
323
|
+
orderGaps,
|
|
324
|
+
completions,
|
|
325
|
+
blobContents,
|
|
326
|
+
streamSchemas,
|
|
327
|
+
};
|
|
328
|
+
}
|
|
329
|
+
/**
|
|
330
|
+
* `rows` in the order their keys last arrived, or null when a row has no
|
|
331
|
+
* arrival to place it by. Zero or one row has only one order.
|
|
332
|
+
*/
|
|
333
|
+
function inArrivalOrder(rows, arrival) {
|
|
334
|
+
if (rows.length < 2)
|
|
335
|
+
return rows;
|
|
336
|
+
const position = new Map(arrival.map((key, index) => [key, index]));
|
|
337
|
+
const placed = rows.map((row) => ({ row, at: position.get(row.recordKey) }));
|
|
338
|
+
if (placed.some(({ at }) => at === undefined))
|
|
339
|
+
return null;
|
|
340
|
+
return placed.sort((a, b) => (a.at ?? 0) - (b.at ?? 0)).map(({ row }) => row);
|
|
341
|
+
}
|
|
342
|
+
async function projectSnapshot(input) {
|
|
343
|
+
const { scope, selection, streams, binding, profileKey, rowsByStream, orderGaps, completions, } = input;
|
|
344
|
+
const listingBefore = input.listing;
|
|
345
|
+
const sourceRowCounts = Object.fromEntries(streams.map((stream) => [stream, rowsByStream.get(stream)?.length ?? 0]));
|
|
346
|
+
const expanded = [];
|
|
347
|
+
const canExpandClaude = selection.sourceId === CLAUDE_SOURCE_ID &&
|
|
348
|
+
(scope === "claude.conversations" || scope === "claude.projects");
|
|
349
|
+
if (canExpandClaude && input.requireClaudeRawBlobs) {
|
|
350
|
+
const parentStream = scope === "claude.conversations" ? "conversations" : "projects";
|
|
351
|
+
const missingRawBlobCount = (rowsByStream.get(parentStream) ?? []).filter((row) => !hasBlobRef(row)).length;
|
|
352
|
+
if (missingRawBlobCount > 0) {
|
|
353
|
+
return unavailable({
|
|
354
|
+
reason: "missing_raw_blob",
|
|
355
|
+
recordCount: missingRawBlobCount,
|
|
356
|
+
});
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
for (const stream of streams) {
|
|
360
|
+
const blobBacked = (rowsByStream.get(stream) ?? []).filter(hasBlobRef);
|
|
361
|
+
if (canExpandClaude &&
|
|
362
|
+
!declaredBlobField(input.streamSchemas[stream]) &&
|
|
363
|
+
(stream === "conversations" || stream === "projects")) {
|
|
364
|
+
try {
|
|
365
|
+
for (const row of blobBacked) {
|
|
366
|
+
const ref = claudeBlobReference(row.data);
|
|
367
|
+
expanded.push(...(await decodeClaudeSourceRecord(row, input.blobContents.get(ref.blobId) ?? {
|
|
368
|
+
meta: undefined,
|
|
369
|
+
bytes: undefined,
|
|
370
|
+
})));
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
catch (error) {
|
|
374
|
+
if (!(error instanceof ClaudeSourceRecordError))
|
|
375
|
+
throw error;
|
|
376
|
+
return unavailable({
|
|
377
|
+
reason: "blob_backed_record_invalid",
|
|
378
|
+
detail: error.message,
|
|
379
|
+
});
|
|
380
|
+
}
|
|
381
|
+
continue;
|
|
382
|
+
}
|
|
383
|
+
if (blobBacked.length > 0 &&
|
|
384
|
+
!declaredBlobField(input.streamSchemas[stream])) {
|
|
385
|
+
return unavailable({
|
|
386
|
+
reason: "blob_backed_records_unsupported",
|
|
387
|
+
stream,
|
|
388
|
+
recordCount: blobBacked.length,
|
|
389
|
+
blocker: "This scope/stream has no verified mapping from blob bytes to its legacy fields.",
|
|
390
|
+
});
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
const collectedAt = latestCollectedAt(rowsByStream, completions, listingBefore);
|
|
394
|
+
if (collectedAt === null)
|
|
395
|
+
return unavailable({ reason: "invalid_collected_at" });
|
|
396
|
+
let records = streams.flatMap((stream) => (rowsByStream.get(stream) ?? []).map((row) => ({ stream, data: row.data })));
|
|
397
|
+
// Decode inline fields before inserting raw blob text; a JSON-string blob
|
|
398
|
+
// is already source text and must never be decoded a second time.
|
|
399
|
+
let decodeDiagnostics = [];
|
|
400
|
+
try {
|
|
401
|
+
const decoded = decodeDeclaredRecordsForProjection(records, input.streamSchemas, scope, true);
|
|
402
|
+
records = decoded.records;
|
|
403
|
+
decodeDiagnostics = decoded.diagnostics;
|
|
404
|
+
}
|
|
405
|
+
catch (error) {
|
|
406
|
+
if (!(error instanceof DeclaredTextError))
|
|
407
|
+
throw error;
|
|
408
|
+
return rejection(error.projectionError(scope), sourceRowCounts);
|
|
409
|
+
}
|
|
410
|
+
for (let i = 0; i < records.length; i++) {
|
|
411
|
+
const record = records[i];
|
|
412
|
+
const field = declaredBlobField(input.streamSchemas[record.stream]);
|
|
413
|
+
if (!field || !Object.hasOwn(record.data, "blob_ref"))
|
|
414
|
+
continue;
|
|
415
|
+
try {
|
|
416
|
+
const ref = declaredBlobReference(record.data, field);
|
|
417
|
+
records[i] = {
|
|
418
|
+
...record,
|
|
419
|
+
data: await decodeDeclaredBlob(record.data, field, input.blobContents.get(ref.blobId) ?? {
|
|
420
|
+
meta: undefined,
|
|
421
|
+
bytes: undefined,
|
|
422
|
+
}),
|
|
423
|
+
};
|
|
424
|
+
}
|
|
425
|
+
catch {
|
|
426
|
+
return unavailable({
|
|
427
|
+
reason: "blob_backed_record_invalid",
|
|
428
|
+
detail: "invalid_declared_blob",
|
|
429
|
+
});
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
if (expanded.length > 0) {
|
|
433
|
+
try {
|
|
434
|
+
records = mergeClaudeSourceRecords(records.filter((record) => !Object.hasOwn(record.data, "blob_ref")), expanded);
|
|
435
|
+
}
|
|
436
|
+
catch (error) {
|
|
437
|
+
if (!(error instanceof ClaudeSourceRecordError))
|
|
438
|
+
throw error;
|
|
439
|
+
return unavailable({
|
|
440
|
+
reason: "blob_backed_record_invalid",
|
|
441
|
+
detail: error.message,
|
|
442
|
+
});
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
const projected = projectPdppRecordsToLegacyPayload(scope, records, {
|
|
446
|
+
fetchedStreams: streams,
|
|
447
|
+
...(profileKey ? { profileKey } : {}),
|
|
448
|
+
now: collectedAt,
|
|
449
|
+
// Rows of an arrival-ordered scope go in as read. Without proof of that
|
|
450
|
+
// order the view falls back to key order and reports the gap below.
|
|
451
|
+
orderByPrimaryKey: binding.rowOrder !== "arrival" || orderGaps.length > 0,
|
|
452
|
+
});
|
|
453
|
+
if (!projected.ok) {
|
|
454
|
+
return rejection(projected.error, sourceRowCounts);
|
|
455
|
+
}
|
|
456
|
+
const omissions = [];
|
|
457
|
+
const partialRows = [];
|
|
458
|
+
const unproven = orderGaps.map((stream) => ({
|
|
459
|
+
stream,
|
|
460
|
+
aspect: "record_order",
|
|
461
|
+
reason: "some rows were stored before the store kept arrival order; they are served in primary-key order",
|
|
462
|
+
}));
|
|
463
|
+
for (const diagnostic of [
|
|
464
|
+
...decodeDiagnostics,
|
|
465
|
+
...(projected.diagnostics ?? []),
|
|
466
|
+
]) {
|
|
467
|
+
if (diagnostic.kind === "records_dropped" ||
|
|
468
|
+
diagnostic.kind === "records_partial") {
|
|
469
|
+
(diagnostic.kind === "records_dropped" ? omissions : partialRows).push({
|
|
470
|
+
stream: diagnostic.stream,
|
|
471
|
+
count: diagnostic.count,
|
|
472
|
+
reasons: diagnostic.reasons,
|
|
473
|
+
});
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
// Valid peers may deliver with omissions. If NOTHING of the item stream
|
|
477
|
+
// survived, a "successful empty" would read as an empty history.
|
|
478
|
+
const itemStream = streams[0];
|
|
479
|
+
const sourceRows = itemStream ? (sourceRowCounts[itemStream] ?? 0) : 0;
|
|
480
|
+
const itemOmissions = omissions.filter((entry) => entry.stream === itemStream);
|
|
481
|
+
const itemDropped = itemOmissions.reduce((sum, entry) => sum + entry.count, 0);
|
|
482
|
+
if (itemStream && sourceRows > 0 && itemDropped >= sourceRows) {
|
|
483
|
+
return {
|
|
484
|
+
ok: false,
|
|
485
|
+
status: "incomplete",
|
|
486
|
+
reason: "all_rows_rejected",
|
|
487
|
+
stream: itemStream,
|
|
488
|
+
sourceRows,
|
|
489
|
+
dropped: itemDropped,
|
|
490
|
+
reasons: [...new Set(itemOmissions.flatMap((entry) => entry.reasons))],
|
|
491
|
+
sourceRowCounts,
|
|
492
|
+
};
|
|
493
|
+
}
|
|
494
|
+
const revision = await viewRevision({
|
|
495
|
+
scope,
|
|
496
|
+
selection,
|
|
497
|
+
binding,
|
|
498
|
+
profileKey,
|
|
499
|
+
generation: input.generation,
|
|
500
|
+
streams,
|
|
501
|
+
rowsByStream,
|
|
502
|
+
orderGaps,
|
|
503
|
+
completions,
|
|
504
|
+
listing: listingBefore,
|
|
505
|
+
streamSchemas: input.streamSchemas,
|
|
506
|
+
});
|
|
507
|
+
return {
|
|
508
|
+
ok: true,
|
|
509
|
+
...(projected.projectionOmissions
|
|
510
|
+
? { projectionOmissions: projected.projectionOmissions }
|
|
511
|
+
: {}),
|
|
512
|
+
envelope: {
|
|
513
|
+
version: "1.0",
|
|
514
|
+
scope,
|
|
515
|
+
collectedAt,
|
|
516
|
+
data: projected.payload,
|
|
517
|
+
},
|
|
518
|
+
...(input.includeStreamTimes
|
|
519
|
+
? {
|
|
520
|
+
streamCollectionTimes: Object.fromEntries(streams.map((stream) => {
|
|
521
|
+
const receipt = completions.get(stream);
|
|
522
|
+
if (receipt)
|
|
523
|
+
return [stream, receipt.sourceCollectedAt];
|
|
524
|
+
const rows = rowsByStream.get(stream) ?? [];
|
|
525
|
+
const times = rows.map((row) => Date.parse(row.emittedAt));
|
|
526
|
+
if (times.length === 0 ||
|
|
527
|
+
times.some((time) => !Number.isFinite(time))) {
|
|
528
|
+
return [stream, null];
|
|
529
|
+
}
|
|
530
|
+
return [
|
|
531
|
+
stream,
|
|
532
|
+
new Date(times.reduce((oldest, time) => Math.min(oldest, time), Number.POSITIVE_INFINITY)).toISOString(),
|
|
533
|
+
];
|
|
534
|
+
})),
|
|
535
|
+
streamCompletionTimes: Object.fromEntries(streams.map((stream) => [
|
|
536
|
+
stream,
|
|
537
|
+
completions.get(stream)?.sourceCollectedAt ?? null,
|
|
538
|
+
])),
|
|
539
|
+
}
|
|
540
|
+
: {}),
|
|
541
|
+
revision,
|
|
542
|
+
complete: omissions.length === 0 &&
|
|
543
|
+
partialRows.length === 0 &&
|
|
544
|
+
unproven.length === 0,
|
|
545
|
+
diagnostics: {
|
|
546
|
+
sourceRowCounts,
|
|
547
|
+
omissions,
|
|
548
|
+
partialRows,
|
|
549
|
+
...(unproven.length > 0 ? { unproven } : {}),
|
|
550
|
+
},
|
|
551
|
+
};
|
|
552
|
+
}
|
|
553
|
+
function inconsistent(detail) {
|
|
554
|
+
return unavailable({ reason: "store_inconsistent_read", detail });
|
|
555
|
+
}
|
|
556
|
+
/** All live rows of one stream, or a detail string if the pages are not one clean listing. */
|
|
557
|
+
function readAllRows(store, instance, stream, limit) {
|
|
558
|
+
const rows = [];
|
|
559
|
+
const seen = new Set();
|
|
560
|
+
let cursor;
|
|
561
|
+
let horizon;
|
|
562
|
+
let first = true;
|
|
563
|
+
for (;;) {
|
|
564
|
+
const page = store.listRecords(stream, {
|
|
565
|
+
instanceIds: [instance],
|
|
566
|
+
limit,
|
|
567
|
+
cursor,
|
|
568
|
+
order: "asc",
|
|
569
|
+
});
|
|
570
|
+
if (first)
|
|
571
|
+
horizon = page.horizon;
|
|
572
|
+
else if (page.horizon !== horizon)
|
|
573
|
+
return `${stream} page horizon moved`;
|
|
574
|
+
first = false;
|
|
575
|
+
for (const row of page.data) {
|
|
576
|
+
const identity = `${row.instance}\u0000${row.recordKey}`;
|
|
577
|
+
if (row.instance !== instance ||
|
|
578
|
+
row.stream !== stream ||
|
|
579
|
+
seen.has(identity)) {
|
|
580
|
+
return `${stream} returned a foreign or repeated row`;
|
|
581
|
+
}
|
|
582
|
+
seen.add(identity);
|
|
583
|
+
rows.push(row);
|
|
584
|
+
}
|
|
585
|
+
if (!page.hasMore)
|
|
586
|
+
return rows;
|
|
587
|
+
if (!page.nextCursor || page.nextCursor === cursor) {
|
|
588
|
+
return `${stream} cursor did not advance`;
|
|
589
|
+
}
|
|
590
|
+
cursor = page.nextCursor;
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
function checkConnection(binding, selection) {
|
|
594
|
+
const detail = (value) => ({ reason: "connection_unavailable", detail: value });
|
|
595
|
+
if (binding.sourceId === null)
|
|
596
|
+
return detail("not_registered");
|
|
597
|
+
if (binding.deletedAt !== null)
|
|
598
|
+
return detail("deleted");
|
|
599
|
+
if (binding.sourceId !== selection.sourceId)
|
|
600
|
+
return detail("source_mismatch");
|
|
601
|
+
if (binding.method !== selection.method)
|
|
602
|
+
return detail("method_mismatch");
|
|
603
|
+
return null;
|
|
604
|
+
}
|
|
605
|
+
function rejection(error, sourceRowCounts) {
|
|
606
|
+
if (error.kind === "unknown_scope" ||
|
|
607
|
+
error.kind === "unsupported_profile" ||
|
|
608
|
+
error.kind === "gap") {
|
|
609
|
+
return unavailable({
|
|
610
|
+
reason: "unbound_scope",
|
|
611
|
+
lookup: error.kind === "gap"
|
|
612
|
+
? {
|
|
613
|
+
kind: "gap",
|
|
614
|
+
scope: error.scope,
|
|
615
|
+
reason: error.reason,
|
|
616
|
+
provenanceChecked: [],
|
|
617
|
+
}
|
|
618
|
+
: error,
|
|
619
|
+
});
|
|
620
|
+
}
|
|
621
|
+
return {
|
|
622
|
+
ok: false,
|
|
623
|
+
status: "incomplete",
|
|
624
|
+
reason: "projection_rejected",
|
|
625
|
+
error,
|
|
626
|
+
sourceRowCounts,
|
|
627
|
+
};
|
|
628
|
+
}
|
|
629
|
+
function unavailable(failure) {
|
|
630
|
+
return {
|
|
631
|
+
ok: false,
|
|
632
|
+
status: "unavailable",
|
|
633
|
+
...failure,
|
|
634
|
+
};
|
|
635
|
+
}
|
|
636
|
+
function hasBlobRef(row) {
|
|
637
|
+
return Object.hasOwn(row.data, "blob_ref");
|
|
638
|
+
}
|
|
639
|
+
function countFor(listing, stream) {
|
|
640
|
+
return listing.find((entry) => entry.stream === stream)?.recordCount ?? 0;
|
|
641
|
+
}
|
|
642
|
+
/**
|
|
643
|
+
* Newest collection time over the bound streams. A stream with a valid
|
|
644
|
+
* receipt was collected at its producer `sourceCollectedAt`, even when an
|
|
645
|
+
* unchanged replace left older rows. A stream without one falls back to its
|
|
646
|
+
* rows' `emittedAt` (the importer stamps it from the original envelope
|
|
647
|
+
* `collectedAt`). With neither, the newest stream horizon of the connection.
|
|
648
|
+
* Never the wall clock.
|
|
649
|
+
*/
|
|
650
|
+
function latestCollectedAt(rowsByStream, completions, listing) {
|
|
651
|
+
const stamps = [];
|
|
652
|
+
for (const [stream, rows] of rowsByStream) {
|
|
653
|
+
const receipt = completions.get(stream);
|
|
654
|
+
if (receipt)
|
|
655
|
+
stamps.push(receipt.sourceCollectedAt);
|
|
656
|
+
else
|
|
657
|
+
for (const row of rows)
|
|
658
|
+
stamps.push(row.emittedAt);
|
|
659
|
+
}
|
|
660
|
+
if (stamps.length === 0) {
|
|
661
|
+
for (const entry of listing) {
|
|
662
|
+
if (entry.lastUpdated !== null)
|
|
663
|
+
stamps.push(entry.lastUpdated);
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
let newest = Number.NEGATIVE_INFINITY;
|
|
667
|
+
for (const stamp of stamps) {
|
|
668
|
+
const ms = Date.parse(stamp);
|
|
669
|
+
if (Number.isNaN(ms))
|
|
670
|
+
return null;
|
|
671
|
+
newest = Math.max(newest, ms);
|
|
672
|
+
}
|
|
673
|
+
return Number.isFinite(newest) ? new Date(newest).toISOString() : null;
|
|
674
|
+
}
|
|
675
|
+
async function viewRevision(input) {
|
|
676
|
+
const horizons = [];
|
|
677
|
+
for (const stream of input.streams) {
|
|
678
|
+
const rows = input.rowsByStream.get(stream) ?? [];
|
|
679
|
+
horizons.push({
|
|
680
|
+
stream,
|
|
681
|
+
count: rows.length,
|
|
682
|
+
lastUpdated: input.listing.find((entry) => entry.stream === stream)?.lastUpdated ??
|
|
683
|
+
null,
|
|
684
|
+
rows: await sha256Hex(rows
|
|
685
|
+
.map((row) => `${row.recordKey}\u0000${row.version}\u0000${row.emittedAt}`)
|
|
686
|
+
.join("\n")),
|
|
687
|
+
});
|
|
688
|
+
}
|
|
689
|
+
const digest = await sha256Hex(JSON.stringify({
|
|
690
|
+
contract: CANONICAL_LEGACY_VIEW_CONTRACT,
|
|
691
|
+
streamSchemas: input.streamSchemas,
|
|
692
|
+
scope: input.scope,
|
|
693
|
+
instance: input.selection.instance,
|
|
694
|
+
sourceId: input.selection.sourceId,
|
|
695
|
+
method: input.selection.method,
|
|
696
|
+
generation: input.generation,
|
|
697
|
+
profileKey: input.profileKey ?? null,
|
|
698
|
+
declaration: {
|
|
699
|
+
pdppSource: input.binding.pdppSource,
|
|
700
|
+
streams: input.binding.pdppStreams,
|
|
701
|
+
primaryKey: input.binding.primaryKey,
|
|
702
|
+
fieldsRead: input.binding.fieldsRead,
|
|
703
|
+
provenance: input.binding.provenance,
|
|
704
|
+
},
|
|
705
|
+
// Rows hash in served order, so a re-delivery that moves a key moves
|
|
706
|
+
// the revision even though no row version changed.
|
|
707
|
+
horizons,
|
|
708
|
+
orderGaps: input.orderGaps,
|
|
709
|
+
// Not `writeSeq`: the write clock is shared by all connections, so an
|
|
710
|
+
// unchanged replay after another account's write would move it.
|
|
711
|
+
completions: input.streams.flatMap((stream) => {
|
|
712
|
+
const receipt = input.completions.get(stream);
|
|
713
|
+
return receipt
|
|
714
|
+
? [
|
|
715
|
+
{
|
|
716
|
+
stream,
|
|
717
|
+
method: receipt.method,
|
|
718
|
+
generation: receipt.generation,
|
|
719
|
+
sourceCollectedAt: receipt.sourceCollectedAt,
|
|
720
|
+
liveCount: receipt.liveCount,
|
|
721
|
+
},
|
|
722
|
+
]
|
|
723
|
+
: [];
|
|
724
|
+
}),
|
|
725
|
+
}));
|
|
726
|
+
return `v1:${digest}`;
|
|
727
|
+
}
|
|
728
|
+
async function sha256Hex(text) {
|
|
729
|
+
const bytes = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(text));
|
|
730
|
+
return Array.from(new Uint8Array(bytes), (b) => b.toString(16).padStart(2, "0")).join("");
|
|
731
|
+
}
|
|
732
|
+
function bindingFor(scope, profileKey) {
|
|
733
|
+
return profileKey
|
|
734
|
+
? legacyBindingsForProfile(profileKey).get(scope)
|
|
735
|
+
: LEGACY_SCOPE_BINDINGS.get(scope);
|
|
736
|
+
}
|
|
737
|
+
/** Alternate declaration profiles are keyed by the source's last segment. */
|
|
738
|
+
function profileKeyForSource(sourceId) {
|
|
739
|
+
const key = sourceId.slice(sourceId.lastIndexOf("/") + 1);
|
|
740
|
+
return hasAlternateLegacyProfile(key) ? key : undefined;
|
|
741
|
+
}
|
|
742
|
+
/** A method id names its alternate profile directly; others use the default. */
|
|
743
|
+
function profileKeyForMethod(method) {
|
|
744
|
+
return hasAlternateLegacyProfile(method) ? method : undefined;
|
|
745
|
+
}
|
|
746
|
+
/**
|
|
747
|
+
* The connector key a binding was reviewed for: the last segment of its
|
|
748
|
+
* `pdppSource`. It is a profile name, not a store source id.
|
|
749
|
+
*/
|
|
750
|
+
export function legacyConnectorKey(pdppSource) {
|
|
751
|
+
return pdppSource.slice(pdppSource.lastIndexOf("/") + 1);
|
|
752
|
+
}
|
|
753
|
+
/** Most connector keys match their public source; Anthropic collects Claude. */
|
|
754
|
+
function canonicalSourceId(pdppSource) {
|
|
755
|
+
if (pdppSource === ANTHROPIC_CONNECTOR_ID)
|
|
756
|
+
return CLAUDE_SOURCE_ID;
|
|
757
|
+
return pdppSource.startsWith(CONNECTOR_ID_PREFIX)
|
|
758
|
+
? SOURCE_ID_PREFIX + pdppSource.slice(CONNECTOR_ID_PREFIX.length)
|
|
759
|
+
: pdppSource;
|
|
760
|
+
}
|
|
761
|
+
//# sourceMappingURL=canonical-legacy-projection.js.map
|