@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,230 @@
|
|
|
1
|
+
import { createCipheriv, createDecipheriv, createHash, randomBytes, } from "node:crypto";
|
|
2
|
+
import { decodeCursor, InvalidCursorSyntaxError } from "./cursor.js";
|
|
3
|
+
import { compileRecordSchema, redactedPath } from "./schema.js";
|
|
4
|
+
import { CursorExpiredError, InvalidCursorError, } from "./types.js";
|
|
5
|
+
export class DeclarationWitnessInputError extends Error {
|
|
6
|
+
constructor() {
|
|
7
|
+
super("Declaration witness requires a source, stream and positive safe-integer limit");
|
|
8
|
+
this.name = "DeclarationWitnessInputError";
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
export class DeclarationWitnessSchemaError extends Error {
|
|
12
|
+
constructor() {
|
|
13
|
+
// Do not attach Ajv errors or schema text to a public error.
|
|
14
|
+
super("Declaration witness schema cannot be compiled synchronously");
|
|
15
|
+
this.name = "DeclarationWitnessSchemaError";
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
export class DeclarationWitnessCursorError extends InvalidCursorError {
|
|
19
|
+
constructor() {
|
|
20
|
+
super("Declaration witness cursor is invalid, expired or belongs to another walk");
|
|
21
|
+
this.name = "DeclarationWitnessCursorError";
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
export class DeclarationWitnessReadError extends Error {
|
|
25
|
+
constructor() {
|
|
26
|
+
super("Declaration witness read port returned an invalid page");
|
|
27
|
+
this.name = "DeclarationWitnessReadError";
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
// The ordinary list cursor is unsigned and not instance/schema-bound.
|
|
31
|
+
// Encrypt/authenticate it without changing its bytes or retaining one entry
|
|
32
|
+
// per page. Weak keys allow ports and their secrets to be garbage-collected.
|
|
33
|
+
// Tokens resume only with the same port object in the same process.
|
|
34
|
+
const cursorKeys = new WeakMap();
|
|
35
|
+
const prefix = "dw1.";
|
|
36
|
+
function sealCursor(port, cursor, context) {
|
|
37
|
+
let key = cursorKeys.get(port);
|
|
38
|
+
if (!key) {
|
|
39
|
+
key = randomBytes(32);
|
|
40
|
+
cursorKeys.set(port, key);
|
|
41
|
+
}
|
|
42
|
+
const iv = randomBytes(12);
|
|
43
|
+
const cipher = createCipheriv("aes-256-gcm", key, iv);
|
|
44
|
+
cipher.setAAD(Buffer.from(context, "utf8"));
|
|
45
|
+
const encrypted = Buffer.concat([
|
|
46
|
+
cipher.update(cursor, "utf8"),
|
|
47
|
+
cipher.final(),
|
|
48
|
+
]);
|
|
49
|
+
return (prefix +
|
|
50
|
+
Buffer.concat([iv, cipher.getAuthTag(), encrypted]).toString("base64url"));
|
|
51
|
+
}
|
|
52
|
+
function openCursor(port, token, context) {
|
|
53
|
+
try {
|
|
54
|
+
const key = cursorKeys.get(port);
|
|
55
|
+
if (!key || !token.startsWith(prefix))
|
|
56
|
+
throw new DeclarationWitnessCursorError();
|
|
57
|
+
const encoded = token.slice(prefix.length);
|
|
58
|
+
if (!/^[A-Za-z0-9_-]+$/.test(encoded))
|
|
59
|
+
throw new DeclarationWitnessCursorError();
|
|
60
|
+
const bytes = Buffer.from(encoded, "base64url");
|
|
61
|
+
if (bytes.length <= 28 || bytes.toString("base64url") !== encoded) {
|
|
62
|
+
throw new DeclarationWitnessCursorError();
|
|
63
|
+
}
|
|
64
|
+
const decipher = createDecipheriv("aes-256-gcm", key, bytes.subarray(0, 12));
|
|
65
|
+
decipher.setAAD(Buffer.from(context, "utf8"));
|
|
66
|
+
decipher.setAuthTag(bytes.subarray(12, 28));
|
|
67
|
+
return Buffer.concat([
|
|
68
|
+
decipher.update(bytes.subarray(28)),
|
|
69
|
+
decipher.final(),
|
|
70
|
+
]).toString("utf8");
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
throw new DeclarationWitnessCursorError();
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
/** Check the store's existing cursor contract; never manufacture a position. */
|
|
77
|
+
function storePosition(cursor, stream) {
|
|
78
|
+
const payload = decodeCursor(cursor);
|
|
79
|
+
if (payload.kind !== "list" ||
|
|
80
|
+
payload.stream !== stream ||
|
|
81
|
+
typeof payload.epoch !== "string" ||
|
|
82
|
+
payload.order !== "asc" ||
|
|
83
|
+
(payload.sortValue !== null && typeof payload.sortValue !== "string") ||
|
|
84
|
+
typeof payload.recordKey !== "string" ||
|
|
85
|
+
(payload.horizon !== undefined && typeof payload.horizon !== "string")) {
|
|
86
|
+
throw new DeclarationWitnessCursorError();
|
|
87
|
+
}
|
|
88
|
+
return payload;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Validate one bounded page of a declaration-upgrade walk.
|
|
92
|
+
*
|
|
93
|
+
* A partial result certifies only the rows already visited. A pass means the
|
|
94
|
+
* read port reported its terminal page and all visited live rows validated.
|
|
95
|
+
* The read port is NOT a snapshot: inserts behind the cursor, updates and
|
|
96
|
+
* deletes can change what the walk observes. The caller MUST keep writes and
|
|
97
|
+
* resets quiescent (or supply a snapshot-backed port) for the entire walk
|
|
98
|
+
* through the upgrade decision. This API supplies no mutation fence.
|
|
99
|
+
*
|
|
100
|
+
* sourceKey is exactly one instance ID, avoiding cross-instance tuple ties.
|
|
101
|
+
* Continuations are opaque, encrypted/authenticated, and scoped to the same
|
|
102
|
+
* port object/process, source, stream and JSON-serialized nextSchema. Restart
|
|
103
|
+
* the walk after recreating a port, restarting the process or changing the
|
|
104
|
+
* schema. A retry may reuse a cursor; the port's live-read caveat still applies.
|
|
105
|
+
* No record data, raw keys, schema contents or validator messages are logged
|
|
106
|
+
* or returned. Array indices and schema-declared property names may appear
|
|
107
|
+
* in the redacted failure pointer.
|
|
108
|
+
*/
|
|
109
|
+
export async function witnessDeclarationUpgrade({ readPort, sourceKey, stream, nextSchema, cursor, limit, }) {
|
|
110
|
+
if (!Number.isSafeInteger(limit) ||
|
|
111
|
+
limit <= 0 ||
|
|
112
|
+
typeof sourceKey !== "string" ||
|
|
113
|
+
sourceKey.length === 0 ||
|
|
114
|
+
typeof stream !== "string" ||
|
|
115
|
+
stream.length === 0) {
|
|
116
|
+
throw new DeclarationWitnessInputError();
|
|
117
|
+
}
|
|
118
|
+
let schema;
|
|
119
|
+
let validate;
|
|
120
|
+
let schemaDigest;
|
|
121
|
+
try {
|
|
122
|
+
// Keep schema compilation, fingerprinting and path redaction on the same
|
|
123
|
+
// object even if the caller mutates its schema while listRecords awaits.
|
|
124
|
+
schema = structuredClone(nextSchema);
|
|
125
|
+
validate = compileRecordSchema(schema);
|
|
126
|
+
if ("$async" in validate && validate.$async)
|
|
127
|
+
throw new DeclarationWitnessSchemaError();
|
|
128
|
+
schemaDigest = createHash("sha256")
|
|
129
|
+
.update(JSON.stringify(schema))
|
|
130
|
+
.digest("hex");
|
|
131
|
+
}
|
|
132
|
+
catch {
|
|
133
|
+
throw new DeclarationWitnessSchemaError();
|
|
134
|
+
}
|
|
135
|
+
const context = JSON.stringify([prefix, sourceKey, stream, schemaDigest]);
|
|
136
|
+
let storeCursor;
|
|
137
|
+
if (cursor !== undefined) {
|
|
138
|
+
storeCursor = openCursor(readPort, cursor, context);
|
|
139
|
+
try {
|
|
140
|
+
storePosition(storeCursor, stream);
|
|
141
|
+
}
|
|
142
|
+
catch {
|
|
143
|
+
throw new DeclarationWitnessCursorError();
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
let page;
|
|
147
|
+
try {
|
|
148
|
+
page = await readPort.listRecords(stream, {
|
|
149
|
+
instanceIds: [sourceKey],
|
|
150
|
+
order: "asc",
|
|
151
|
+
limit,
|
|
152
|
+
...(storeCursor === undefined ? {} : { cursor: storeCursor }),
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
catch (error) {
|
|
156
|
+
if (error instanceof InvalidCursorError ||
|
|
157
|
+
error instanceof InvalidCursorSyntaxError ||
|
|
158
|
+
error instanceof CursorExpiredError) {
|
|
159
|
+
throw new DeclarationWitnessCursorError();
|
|
160
|
+
}
|
|
161
|
+
// A bridge may include row contents in an error. Do not expose its cause.
|
|
162
|
+
throw new DeclarationWitnessReadError();
|
|
163
|
+
}
|
|
164
|
+
if (!page ||
|
|
165
|
+
!Array.isArray(page.data) ||
|
|
166
|
+
page.data.length > limit ||
|
|
167
|
+
typeof page.hasMore !== "boolean" ||
|
|
168
|
+
(!page.hasMore && page.nextCursor !== undefined)) {
|
|
169
|
+
throw new DeclarationWitnessReadError();
|
|
170
|
+
}
|
|
171
|
+
if (page.hasMore) {
|
|
172
|
+
try {
|
|
173
|
+
if (!page.nextCursor ||
|
|
174
|
+
page.nextCursor === storeCursor ||
|
|
175
|
+
page.data.length === 0) {
|
|
176
|
+
throw new DeclarationWitnessReadError();
|
|
177
|
+
}
|
|
178
|
+
const position = storePosition(page.nextCursor, stream);
|
|
179
|
+
const last = page.data[page.data.length - 1];
|
|
180
|
+
if (!last ||
|
|
181
|
+
position.recordKey !== last.recordKey ||
|
|
182
|
+
position.sortValue !== last.emittedAt) {
|
|
183
|
+
throw new DeclarationWitnessReadError();
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
catch {
|
|
187
|
+
throw new DeclarationWitnessReadError();
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
let checked = 0;
|
|
191
|
+
for (const row of page.data) {
|
|
192
|
+
if (!row ||
|
|
193
|
+
row.instance !== sourceKey ||
|
|
194
|
+
row.stream !== stream ||
|
|
195
|
+
typeof row.recordKey !== "string" ||
|
|
196
|
+
(row.deleted !== true && row.deleted !== false)) {
|
|
197
|
+
throw new DeclarationWitnessReadError();
|
|
198
|
+
}
|
|
199
|
+
// listRecords excludes tombstones by contract; defend against adapters
|
|
200
|
+
// that return them anyway without validating their absent data.
|
|
201
|
+
if (row.deleted)
|
|
202
|
+
continue;
|
|
203
|
+
checked += 1;
|
|
204
|
+
if (!validate(row.data)) {
|
|
205
|
+
const [error] = validate.errors ?? [];
|
|
206
|
+
return {
|
|
207
|
+
verdict: "fail",
|
|
208
|
+
checked,
|
|
209
|
+
nextCursor: null,
|
|
210
|
+
failure: {
|
|
211
|
+
recordKeyDigest: createHash("sha256")
|
|
212
|
+
.update(row.recordKey)
|
|
213
|
+
.digest("hex"),
|
|
214
|
+
instancePath: error?.instancePath
|
|
215
|
+
? redactedPath(schema, error.instancePath, row.data)
|
|
216
|
+
: "",
|
|
217
|
+
keyword: error?.keyword ?? "schema",
|
|
218
|
+
},
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
return page.hasMore
|
|
223
|
+
? {
|
|
224
|
+
verdict: "partial",
|
|
225
|
+
checked,
|
|
226
|
+
nextCursor: sealCursor(readPort, page.nextCursor, context),
|
|
227
|
+
}
|
|
228
|
+
: { verdict: "pass", checked, nextCursor: null };
|
|
229
|
+
}
|
|
230
|
+
//# sourceMappingURL=declaration-witness.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"declaration-witness.js","sourceRoot":"","sources":["../../../src/storage/pdpp-records/declaration-witness.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { PdppError } from "../../errors/pdpp-catalog.js";
|
|
2
|
+
import { type PdppTokenContext, type StreamGrant } from "../../ports/pdpp-auth.js";
|
|
3
|
+
import { type StreamDeclaration } from "./stream-declaration.js";
|
|
4
|
+
/**
|
|
5
|
+
* Resolves a request-time authorization context to the effective read
|
|
6
|
+
* parameters this lane's PdppRecordStore calls need — `instanceIds`,
|
|
7
|
+
* `fields` — and validates the request against the grant/subject scope. This
|
|
8
|
+
* is the single narrow chokepoint every §8 read endpoint calls through, so
|
|
9
|
+
* enforcement logic (and any future grant-shape change) lives in one place.
|
|
10
|
+
*
|
|
11
|
+
* Throws PdppError for every documented spec §8 failure mode. Never returns
|
|
12
|
+
* a widened scope: on ambiguity, the narrower (deny) reading wins.
|
|
13
|
+
*/
|
|
14
|
+
export interface ResolvedReadScope {
|
|
15
|
+
instanceIds: string[];
|
|
16
|
+
fields?: string[];
|
|
17
|
+
streamGrant?: StreamGrant;
|
|
18
|
+
/**
|
|
19
|
+
* Whether the declaration's schema-required fields are part of this scope's
|
|
20
|
+
* disclosure floor.
|
|
21
|
+
*
|
|
22
|
+
* True under a v0.1 grant, where §8's per-stream consent floor keeps
|
|
23
|
+
* schema-required fields in every projection. False under a v0.2 grant,
|
|
24
|
+
* where the disclosed members are exactly what the grant approved and a
|
|
25
|
+
* field must not be added "merely because the schema requires it".
|
|
26
|
+
*
|
|
27
|
+
* Carried on the scope rather than recomputed at each call site so the two
|
|
28
|
+
* revisions cannot drift apart between the list read, the single-record
|
|
29
|
+
* read, and the stream-metadata projection — three places that must agree
|
|
30
|
+
* about what a grant discloses.
|
|
31
|
+
*/
|
|
32
|
+
schemaRequiredFloor: boolean;
|
|
33
|
+
}
|
|
34
|
+
export declare function resolveReadScope(context: PdppTokenContext, stream: string, declaration: StreamDeclaration | undefined): ResolvedReadScope;
|
|
35
|
+
/**
|
|
36
|
+
* Validates that a record's time field satisfies the grant's frozen
|
|
37
|
+
* time_constraint, if any. Callers filter store results through this before
|
|
38
|
+
* returning them (the store itself is time_constraint-agnostic).
|
|
39
|
+
*/
|
|
40
|
+
export declare function recordWithinGrantTimeConstraint(data: Record<string, unknown>, streamGrant: StreamGrant | undefined): boolean;
|
|
41
|
+
/** Validates a canonical record_key is within the grant's `resources` allowlist, if constrained. */
|
|
42
|
+
export declare function recordKeyWithinGrantResources(recordKey: string, streamGrant: StreamGrant | undefined): boolean;
|
|
43
|
+
/**
|
|
44
|
+
* The PdppError an inactive token maps to.
|
|
45
|
+
*
|
|
46
|
+
* Exported so every route answers an inactive token with the SAME reason.
|
|
47
|
+
* `/v1/streams` previously inlined a flat `authentication_error` while record
|
|
48
|
+
* reads used this mapping, so one revoked grant produced a 401 on one endpoint
|
|
49
|
+
* and a 403 `grant_revoked` on another.
|
|
50
|
+
*/
|
|
51
|
+
export declare function mapInactiveToError(context: PdppTokenContext): PdppError;
|
|
52
|
+
//# sourceMappingURL=enforcement.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"enforcement.d.ts","sourceRoot":"","sources":["../../../src/storage/pdpp-records/enforcement.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,8BAA8B,CAAC;AACzD,OAAO,EAIL,KAAK,gBAAgB,EACrB,KAAK,WAAW,EACjB,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAEL,KAAK,iBAAiB,EACvB,MAAM,yBAAyB,CAAC;AAEjC;;;;;;;;;GASG;AACH,MAAM,WAAW,iBAAiB;IAChC,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B;;;;;;;;;;;;;OAaG;IACH,mBAAmB,EAAE,OAAO,CAAC;CAC9B;AAED,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,gBAAgB,EACzB,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,iBAAiB,GAAG,SAAS,GACzC,iBAAiB,CA4FnB;AAED;;;;GAIG;AACH,wBAAgB,+BAA+B,CAC7C,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,WAAW,EAAE,WAAW,GAAG,SAAS,GACnC,OAAO,CAOT;AAED,oGAAoG;AACpG,wBAAgB,6BAA6B,CAC3C,SAAS,EAAE,MAAM,EACjB,WAAW,EAAE,WAAW,GAAG,SAAS,GACnC,OAAO,CAGT;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,gBAAgB,GAAG,SAAS,CAevE"}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { PdppError } from "../../errors/pdpp-catalog.js";
|
|
2
|
+
import { findStreamGrant, grantIsV02, withinTimeConstraint, } from "../../ports/pdpp-auth.js";
|
|
3
|
+
import { withRequiredFields, } from "./stream-declaration.js";
|
|
4
|
+
export function resolveReadScope(context, stream, declaration) {
|
|
5
|
+
if (!context.active) {
|
|
6
|
+
throw mapInactiveToError(context);
|
|
7
|
+
}
|
|
8
|
+
if (!declaration) {
|
|
9
|
+
throw new PdppError("not_found", `Stream '${stream}' not found`);
|
|
10
|
+
}
|
|
11
|
+
if (context.tokenKind === "owner") {
|
|
12
|
+
// Owner tokens carry no grant: full current-capability read, but still
|
|
13
|
+
// scoped to the owner's own subject — enforced by the caller passing
|
|
14
|
+
// only that subject's instance_ids into the store query, which for the
|
|
15
|
+
// owner-token case is "every instance belonging to subjectId". This
|
|
16
|
+
// module doesn't own instance-to-subject resolution (that's the RS
|
|
17
|
+
// route layer's data-store lookup); it returns `undefined` fields (no
|
|
18
|
+
// projection restriction) since an owner token has no grant field list.
|
|
19
|
+
// An owner token has no grant and so no projection to floor: the owner
|
|
20
|
+
// reads their own records whole.
|
|
21
|
+
return { instanceIds: [], fields: undefined, schemaRequiredFloor: false };
|
|
22
|
+
}
|
|
23
|
+
// Client token: requires an active resolved grant.
|
|
24
|
+
if (!context.grant) {
|
|
25
|
+
throw new PdppError("grant_invalid", "Client token has no resolved grant");
|
|
26
|
+
}
|
|
27
|
+
const streamGrant = findStreamGrant(context.grant, stream);
|
|
28
|
+
if (!streamGrant) {
|
|
29
|
+
throw new PdppError("grant_stream_not_allowed", `Grant does not include stream '${stream}'`);
|
|
30
|
+
}
|
|
31
|
+
if (streamGrant.fields.length === 0 ||
|
|
32
|
+
streamGrant.instance_ids.length === 0) {
|
|
33
|
+
// Per the AS contract: "If the RS finds ... an empty `fields`, or an
|
|
34
|
+
// empty `instance_ids`, that is an AS bug — fail closed."
|
|
35
|
+
throw new PdppError("grant_invalid", "Resolved grant is malformed (empty fields or instance_ids)");
|
|
36
|
+
}
|
|
37
|
+
// v0.2 reverses v0.1's consent floor. Under v0.1 the declaration's
|
|
38
|
+
// required fields are re-added here, so a record is never disclosed in a
|
|
39
|
+
// shape its own schema would reject. Under v0.2 that is a disclosure bug:
|
|
40
|
+
// the AS deliberately resolves a grant WITHOUT a schema-required field the
|
|
41
|
+
// owner did not approve, and re-adding it from the declaration undoes that
|
|
42
|
+
// narrowing one layer below the decision the owner actually made.
|
|
43
|
+
const schemaRequiredFloor = !grantIsV02(context.grant);
|
|
44
|
+
// v0.2 only: can this RS actually serve the projection the grant approved?
|
|
45
|
+
//
|
|
46
|
+
// The retained declaration is the RS's only authority for what a record of
|
|
47
|
+
// this stream means. A granted member the declaration does not declare has
|
|
48
|
+
// no meaning the RS can stand behind: omitting it asserts "no such value"
|
|
49
|
+
// (unknown), nulling it is forbidden outright by `v0.2-4-3`, and serving
|
|
50
|
+
// whatever the record carries under that key discloses an undeclared member
|
|
51
|
+
// — which is repairing the projection with unauthorized data.
|
|
52
|
+
//
|
|
53
|
+
// So refuse, with the code the spec reserves for exactly this. The
|
|
54
|
+
// distinction from `grant_invalid` is the client's remedy: the grant is
|
|
55
|
+
// well-formed and this RS cannot serve it, so the answer is to reauthorize
|
|
56
|
+
// against the current declaration, not to treat the grant as corrupt.
|
|
57
|
+
//
|
|
58
|
+
// Checked here rather than at each endpoint because all three client read
|
|
59
|
+
// surfaces (list, single record, stream metadata) resolve through this
|
|
60
|
+
// function, and a refusal one of them forgot would be a surface that
|
|
61
|
+
// discloses what the other two refuse.
|
|
62
|
+
if (!schemaRequiredFloor && declaration.declaredFields) {
|
|
63
|
+
const declared = new Set(declaration.declaredFields);
|
|
64
|
+
const undeclared = streamGrant.fields.filter((f) => !declared.has(f));
|
|
65
|
+
if (undeclared.length > 0) {
|
|
66
|
+
throw new PdppError("disclosure_unavailable", `Grant authorizes ${undeclared.join(", ")} on stream '${stream}', which the retained declaration does not declare; the projection cannot be served without disclosing an undeclared member`);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return {
|
|
70
|
+
instanceIds: streamGrant.instance_ids,
|
|
71
|
+
fields: schemaRequiredFloor
|
|
72
|
+
? withRequiredFields(streamGrant.fields, declaration.requiredFields)
|
|
73
|
+
: [...streamGrant.fields],
|
|
74
|
+
streamGrant,
|
|
75
|
+
schemaRequiredFloor,
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Validates that a record's time field satisfies the grant's frozen
|
|
80
|
+
* time_constraint, if any. Callers filter store results through this before
|
|
81
|
+
* returning them (the store itself is time_constraint-agnostic).
|
|
82
|
+
*/
|
|
83
|
+
export function recordWithinGrantTimeConstraint(data, streamGrant) {
|
|
84
|
+
if (!streamGrant?.time_constraint)
|
|
85
|
+
return true;
|
|
86
|
+
const value = data[streamGrant.time_constraint.field];
|
|
87
|
+
return withinTimeConstraint(typeof value === "string" ? value : undefined, streamGrant.time_constraint);
|
|
88
|
+
}
|
|
89
|
+
/** Validates a canonical record_key is within the grant's `resources` allowlist, if constrained. */
|
|
90
|
+
export function recordKeyWithinGrantResources(recordKey, streamGrant) {
|
|
91
|
+
if (!streamGrant?.resources)
|
|
92
|
+
return true; // absent = all records
|
|
93
|
+
return streamGrant.resources.includes(recordKey);
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* The PdppError an inactive token maps to.
|
|
97
|
+
*
|
|
98
|
+
* Exported so every route answers an inactive token with the SAME reason.
|
|
99
|
+
* `/v1/streams` previously inlined a flat `authentication_error` while record
|
|
100
|
+
* reads used this mapping, so one revoked grant produced a 401 on one endpoint
|
|
101
|
+
* and a 403 `grant_revoked` on another.
|
|
102
|
+
*/
|
|
103
|
+
export function mapInactiveToError(context) {
|
|
104
|
+
switch (context.inactiveReason) {
|
|
105
|
+
case "grant_revoked":
|
|
106
|
+
return new PdppError("grant_revoked", "Grant has been revoked");
|
|
107
|
+
case "grant_expired":
|
|
108
|
+
return new PdppError("grant_expired", "Grant has expired");
|
|
109
|
+
case "expired":
|
|
110
|
+
case "revoked":
|
|
111
|
+
case "unknown":
|
|
112
|
+
default:
|
|
113
|
+
return new PdppError("authentication_error", "Missing or invalid access token");
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
//# sourceMappingURL=enforcement.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"enforcement.js","sourceRoot":"","sources":["../../../src/storage/pdpp-records/enforcement.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/storage/pdpp-records/index.ts"],"names":[],"mappings":"AAAA,cAAc,eAAe,CAAC;AAC9B,cAAc,mBAAmB,CAAC;AAClC,cAAc,0BAA0B,CAAC;AACzC,cAAc,gBAAgB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/storage/pdpp-records/index.ts"],"names":[],"mappings":"AAAA,cAAc,eAAe,CAAC;AAC9B,cAAc,mBAAmB,CAAC;AAClC,cAAc,0BAA0B,CAAC;AACzC,cAAc,gBAAgB,CAAC"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { IngestOutcome, IngestResult, PdppRecordEnvelopeInput, StreamSemantics } from "./types.js";
|
|
2
|
+
/** The stored state of a key that decides an envelope's outcome. */
|
|
3
|
+
export interface CurrentContent {
|
|
4
|
+
deleted: boolean;
|
|
5
|
+
data: Record<string, unknown> | null;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* What one envelope should do to the store. Both backends call this so they
|
|
9
|
+
* cannot disagree about outcomes; each only performs the write.
|
|
10
|
+
*
|
|
11
|
+
* Content is `deleted` plus the canonical JSON of `data`. `emitted_at` is
|
|
12
|
+
* version metadata, not content: re-emitting equal content is `unchanged`
|
|
13
|
+
* and keeps the stored `emitted_at`, so a full refresh of unchanged data
|
|
14
|
+
* writes no history and wakes no `changes_since` reader.
|
|
15
|
+
*/
|
|
16
|
+
export type IngestPlan = {
|
|
17
|
+
outcome: IngestOutcome;
|
|
18
|
+
write?: undefined;
|
|
19
|
+
} | {
|
|
20
|
+
outcome: {
|
|
21
|
+
index: number;
|
|
22
|
+
outcome: "accepted";
|
|
23
|
+
};
|
|
24
|
+
write: {
|
|
25
|
+
recordKey: string;
|
|
26
|
+
data: Record<string, unknown> | null;
|
|
27
|
+
};
|
|
28
|
+
};
|
|
29
|
+
/**
|
|
30
|
+
* Checks upsert `data` against the stream's declared schema (P5). Returns the
|
|
31
|
+
* rejection reason, or null when the data conforms.
|
|
32
|
+
*/
|
|
33
|
+
export type RecordDataValidator = (stream: string, instance: string, data: Record<string, unknown>) => string | null;
|
|
34
|
+
export declare function planIngest(index: number, envelope: PdppRecordEnvelopeInput, semantics: StreamSemantics, primaryKey: string[], current: (recordKey: string) => CurrentContent | undefined, validateData?: RecordDataValidator): IngestPlan;
|
|
35
|
+
/** JSON with object keys sorted at every depth. */
|
|
36
|
+
export declare function canonicalJson(value: unknown): string;
|
|
37
|
+
/** The per-index outcomes plus the counts callers already read. */
|
|
38
|
+
export declare function summarizeIngest(results: IngestOutcome[]): IngestResult;
|
|
39
|
+
//# sourceMappingURL=ingest-plan.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ingest-plan.d.ts","sourceRoot":"","sources":["../../../src/storage/pdpp-records/ingest-plan.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EACV,aAAa,EACb,YAAY,EACZ,uBAAuB,EACvB,eAAe,EAChB,MAAM,YAAY,CAAC;AAEpB,oEAAoE;AACpE,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;CACtC;AAED;;;;;;;;GAQG;AACH,MAAM,MAAM,UAAU,GAClB;IAAE,OAAO,EAAE,aAAa,CAAC;IAAC,KAAK,CAAC,EAAE,SAAS,CAAA;CAAE,GAC7C;IACE,OAAO,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,UAAU,CAAA;KAAE,CAAC;IAChD,KAAK,EAAE;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAA;KAAE,CAAC;CACpE,CAAC;AAEN;;;GAGG;AACH,MAAM,MAAM,mBAAmB,GAAG,CAChC,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAC1B,MAAM,GAAG,IAAI,CAAC;AAEnB,wBAAgB,UAAU,CACxB,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,uBAAuB,EACjC,SAAS,EAAE,eAAe,EAC1B,UAAU,EAAE,MAAM,EAAE,EACpB,OAAO,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,cAAc,GAAG,SAAS,EAC1D,YAAY,CAAC,EAAE,mBAAmB,GACjC,UAAU,CAyEZ;AAED,mDAAmD;AACnD,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAEpD;AAcD,mEAAmE;AACnE,wBAAgB,eAAe,CAAC,OAAO,EAAE,aAAa,EAAE,GAAG,YAAY,CAUtE"}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { encodeRecordKey, keyMatchesData, RecordKeyError, } from "./record-key.js";
|
|
2
|
+
export function planIngest(index, envelope, semantics, primaryKey, current, validateData) {
|
|
3
|
+
const reject = (reason) => ({
|
|
4
|
+
outcome: { index, outcome: "rejected", reason },
|
|
5
|
+
});
|
|
6
|
+
const unchanged = (flag) => ({
|
|
7
|
+
outcome: { index, outcome: "unchanged", ...(flag && { flag }) },
|
|
8
|
+
});
|
|
9
|
+
if (typeof envelope.emitted_at !== "string" || !envelope.emitted_at) {
|
|
10
|
+
return reject("emitted_at must be a non-empty string");
|
|
11
|
+
}
|
|
12
|
+
// JSON `null` means absent, like a missing field: Desktop's blob host
|
|
13
|
+
// serializes `Option::None` as `"op": null`. Absent `op` is an upsert.
|
|
14
|
+
if (envelope.op != null &&
|
|
15
|
+
envelope.op !== "upsert" &&
|
|
16
|
+
envelope.op !== "delete") {
|
|
17
|
+
return reject("op must be 'upsert' or 'delete'");
|
|
18
|
+
}
|
|
19
|
+
let recordKey;
|
|
20
|
+
try {
|
|
21
|
+
recordKey = encodeRecordKey(envelope.key);
|
|
22
|
+
}
|
|
23
|
+
catch (err) {
|
|
24
|
+
if (err instanceof RecordKeyError)
|
|
25
|
+
return reject(err.message);
|
|
26
|
+
throw err;
|
|
27
|
+
}
|
|
28
|
+
const existing = current(recordKey);
|
|
29
|
+
if (envelope.op === "delete") {
|
|
30
|
+
if (semantics === "append_only") {
|
|
31
|
+
return reject("append_only streams do not support delete directives");
|
|
32
|
+
}
|
|
33
|
+
if (!existing || existing.deleted)
|
|
34
|
+
return unchanged();
|
|
35
|
+
return {
|
|
36
|
+
outcome: { index, outcome: "accepted" },
|
|
37
|
+
write: { recordKey, data: null },
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
const data = envelope.data;
|
|
41
|
+
if (data === null || typeof data !== "object" || Array.isArray(data)) {
|
|
42
|
+
return reject("data must be a JSON object for upsert");
|
|
43
|
+
}
|
|
44
|
+
try {
|
|
45
|
+
if (!keyMatchesData(envelope.key, data, primaryKey)) {
|
|
46
|
+
return reject("envelope key does not match data's declared primary_key fields");
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
catch (err) {
|
|
50
|
+
if (err instanceof RecordKeyError)
|
|
51
|
+
return reject(err.message);
|
|
52
|
+
throw err;
|
|
53
|
+
}
|
|
54
|
+
// Before the equality check, so the outcome depends only on the data and
|
|
55
|
+
// the declared schema, never on what happens to be stored already.
|
|
56
|
+
const violation = validateData?.(envelope.stream, envelope.instance, data);
|
|
57
|
+
if (violation)
|
|
58
|
+
return reject(violation);
|
|
59
|
+
if (existing && !existing.deleted) {
|
|
60
|
+
if (canonicalJson(existing.data) === canonicalJson(data)) {
|
|
61
|
+
return unchanged();
|
|
62
|
+
}
|
|
63
|
+
// Core: append_only records are immutable and duplicate keys are
|
|
64
|
+
// idempotent, so the first write stands. The flag lets the producer
|
|
65
|
+
// count a source that changed a record it declared immutable.
|
|
66
|
+
if (semantics === "append_only")
|
|
67
|
+
return unchanged("append_only_conflict");
|
|
68
|
+
}
|
|
69
|
+
return {
|
|
70
|
+
outcome: { index, outcome: "accepted" },
|
|
71
|
+
write: { recordKey, data },
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
/** JSON with object keys sorted at every depth. */
|
|
75
|
+
export function canonicalJson(value) {
|
|
76
|
+
return JSON.stringify(sortKeys(value));
|
|
77
|
+
}
|
|
78
|
+
function sortKeys(value) {
|
|
79
|
+
if (Array.isArray(value))
|
|
80
|
+
return value.map(sortKeys);
|
|
81
|
+
if (value !== null && typeof value === "object") {
|
|
82
|
+
return Object.fromEntries(Object.keys(value)
|
|
83
|
+
.sort()
|
|
84
|
+
.map((key) => [key, sortKeys(value[key])]));
|
|
85
|
+
}
|
|
86
|
+
return value;
|
|
87
|
+
}
|
|
88
|
+
/** The per-index outcomes plus the counts callers already read. */
|
|
89
|
+
export function summarizeIngest(results) {
|
|
90
|
+
let accepted = 0;
|
|
91
|
+
let unchanged = 0;
|
|
92
|
+
const rejected = [];
|
|
93
|
+
for (const result of results) {
|
|
94
|
+
if (result.outcome === "accepted")
|
|
95
|
+
accepted += 1;
|
|
96
|
+
else if (result.outcome === "unchanged")
|
|
97
|
+
unchanged += 1;
|
|
98
|
+
else
|
|
99
|
+
rejected.push({ index: result.index, reason: result.reason });
|
|
100
|
+
}
|
|
101
|
+
return { accepted, unchanged, rejected, results };
|
|
102
|
+
}
|
|
103
|
+
//# sourceMappingURL=ingest-plan.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ingest-plan.js","sourceRoot":"","sources":["../../../src/storage/pdpp-records/ingest-plan.ts"],"names":[],"mappings":"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"}
|