@opendatalabs/personal-server-ts-core 1.34.2 → 1.35.1
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/durable.d.ts +32 -2
- package/dist/additions/durable.d.ts.map +1 -1
- package/dist/additions/durable.js +237 -88
- package/dist/additions/durable.js.map +1 -1
- package/dist/additions/version-inventory.d.ts +4 -0
- package/dist/additions/version-inventory.d.ts.map +1 -1
- package/dist/additions/version-inventory.js +11 -1
- package/dist/additions/version-inventory.js.map +1 -1
- package/dist/api/index.d.ts +18 -1
- package/dist/api/index.d.ts.map +1 -1
- package/dist/api/index.js +172 -4
- package/dist/api/index.js.map +1 -1
- package/dist/api/oura-collection-requirements.d.ts +16 -0
- package/dist/api/oura-collection-requirements.d.ts.map +1 -0
- package/dist/api/oura-collection-requirements.js +70 -0
- package/dist/api/oura-collection-requirements.js.map +1 -0
- package/dist/api/scope-upload-batches.d.ts +18 -0
- package/dist/api/scope-upload-batches.d.ts.map +1 -0
- package/dist/api/scope-upload-batches.js +71 -0
- package/dist/api/scope-upload-batches.js.map +1 -0
- package/dist/api/scope-uploads.d.ts +112 -0
- package/dist/api/scope-uploads.d.ts.map +1 -0
- package/dist/api/scope-uploads.js +369 -0
- package/dist/api/scope-uploads.js.map +1 -0
- package/dist/client.d.ts +70 -0
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +1 -0
- package/dist/client.js.map +1 -1
- package/dist/contracts/data.d.ts +8 -0
- package/dist/contracts/data.d.ts.map +1 -1
- package/dist/contracts/data.js +31 -1
- package/dist/contracts/data.js.map +1 -1
- package/dist/contracts/grants.d.ts.map +1 -1
- package/dist/contracts/grants.js +3 -0
- package/dist/contracts/grants.js.map +1 -1
- package/dist/contracts/index.d.ts +3 -1
- package/dist/contracts/index.d.ts.map +1 -1
- package/dist/contracts/index.js +3 -1
- package/dist/contracts/index.js.map +1 -1
- package/dist/contracts/oura-collection-requirements.d.ts +17 -0
- package/dist/contracts/oura-collection-requirements.d.ts.map +1 -0
- package/dist/contracts/oura-collection-requirements.js +49 -0
- package/dist/contracts/oura-collection-requirements.js.map +1 -0
- package/dist/legacy-projection/bindings.d.ts +1 -7
- package/dist/legacy-projection/bindings.d.ts.map +1 -1
- package/dist/legacy-projection/bindings.js +162 -28
- package/dist/legacy-projection/bindings.js.map +1 -1
- package/dist/legacy-projection/index.d.ts +6 -5
- package/dist/legacy-projection/index.d.ts.map +1 -1
- package/dist/legacy-projection/index.js +9 -21
- package/dist/legacy-projection/index.js.map +1 -1
- package/dist/legacy-projection/oura-browser-bindings.d.ts +2 -0
- package/dist/legacy-projection/oura-browser-bindings.d.ts.map +1 -1
- package/dist/legacy-projection/oura-browser-bindings.js +2 -0
- package/dist/legacy-projection/oura-browser-bindings.js.map +1 -1
- package/dist/legacy-projection/strava-browser-bindings.d.ts +3 -0
- package/dist/legacy-projection/strava-browser-bindings.d.ts.map +1 -0
- package/dist/legacy-projection/strava-browser-bindings.js +32 -0
- package/dist/legacy-projection/strava-browser-bindings.js.map +1 -0
- package/dist/legacy-projection/types.d.ts +44 -12
- package/dist/legacy-projection/types.d.ts.map +1 -1
- package/dist/mcp/connection-api.d.ts +11 -1
- package/dist/mcp/connection-api.d.ts.map +1 -1
- package/dist/mcp/connection-api.js +94 -9
- package/dist/mcp/connection-api.js.map +1 -1
- package/dist/mcp/index.d.ts +1 -0
- package/dist/mcp/index.d.ts.map +1 -1
- package/dist/mcp/index.js +1 -0
- package/dist/mcp/index.js.map +1 -1
- package/dist/mcp/server.d.ts +9 -0
- package/dist/mcp/server.d.ts.map +1 -1
- package/dist/mcp/server.js +19 -6
- package/dist/mcp/server.js.map +1 -1
- package/dist/mcp/store.d.ts +10 -1
- package/dist/mcp/store.d.ts.map +1 -1
- package/dist/mcp/store.js +51 -0
- package/dist/mcp/store.js.map +1 -1
- package/dist/mcp/tools.d.ts +52 -2
- package/dist/mcp/tools.d.ts.map +1 -1
- package/dist/mcp/tools.js +395 -11
- package/dist/mcp/tools.js.map +1 -1
- package/dist/mcp/types.d.ts +36 -2
- package/dist/mcp/types.d.ts.map +1 -1
- package/dist/mcp/types.js +12 -0
- package/dist/mcp/types.js.map +1 -1
- package/dist/mcp/write-client.d.ts +81 -0
- package/dist/mcp/write-client.d.ts.map +1 -0
- package/dist/mcp/write-client.js +140 -0
- package/dist/mcp/write-client.js.map +1 -0
- package/dist/oura/history-policy.d.ts +12 -0
- package/dist/oura/history-policy.d.ts.map +1 -0
- package/dist/oura/history-policy.js +54 -0
- package/dist/oura/history-policy.js.map +1 -0
- package/dist/oura/index.d.ts +51 -0
- package/dist/oura/index.d.ts.map +1 -0
- package/dist/oura/index.js +315 -0
- package/dist/oura/index.js.map +1 -0
- package/dist/pdpp/approval.d.ts +1 -1
- package/dist/pdpp/approval.d.ts.map +1 -1
- package/dist/pdpp/approval.js +29 -10
- package/dist/pdpp/approval.js.map +1 -1
- package/dist/pdpp/declaration-limits.d.ts +7 -0
- package/dist/pdpp/declaration-limits.d.ts.map +1 -0
- package/dist/pdpp/declaration-limits.js +7 -0
- package/dist/pdpp/declaration-limits.js.map +1 -0
- package/dist/pdpp/declaration.d.ts +2 -1
- package/dist/pdpp/declaration.d.ts.map +1 -1
- package/dist/pdpp/declaration.js +2 -1
- package/dist/pdpp/declaration.js.map +1 -1
- package/dist/pdpp/index.d.ts +3 -2
- package/dist/pdpp/index.d.ts.map +1 -1
- package/dist/pdpp/index.js +3 -2
- package/dist/pdpp/index.js.map +1 -1
- package/dist/pdpp/issuance.d.ts.map +1 -1
- package/dist/pdpp/issuance.js +6 -2
- package/dist/pdpp/issuance.js.map +1 -1
- package/dist/pdpp/resolve.d.ts +12 -1
- package/dist/pdpp/resolve.d.ts.map +1 -1
- package/dist/pdpp/resolve.js +156 -58
- package/dist/pdpp/resolve.js.map +1 -1
- package/dist/pdpp/selection.d.ts +30 -1
- package/dist/pdpp/selection.d.ts.map +1 -1
- package/dist/pdpp/selection.js +77 -26
- package/dist/pdpp/selection.js.map +1 -1
- package/dist/pdpp/time.d.ts +39 -0
- package/dist/pdpp/time.d.ts.map +1 -0
- package/dist/pdpp/time.js +173 -0
- package/dist/pdpp/time.js.map +1 -0
- package/dist/pdpp/tokens.d.ts.map +1 -1
- package/dist/pdpp/tokens.js +4 -2
- package/dist/pdpp/tokens.js.map +1 -1
- package/dist/pdpp/types.d.ts +18 -2
- package/dist/pdpp/types.d.ts.map +1 -1
- package/dist/pdpp/types.js +7 -0
- package/dist/pdpp/types.js.map +1 -1
- package/dist/policy/reserved-scopes.d.ts +7 -0
- package/dist/policy/reserved-scopes.d.ts.map +1 -1
- package/dist/policy/reserved-scopes.js +12 -1
- package/dist/policy/reserved-scopes.js.map +1 -1
- package/dist/ports/index.d.ts +85 -1
- package/dist/ports/index.d.ts.map +1 -1
- package/dist/ports/index.js.map +1 -1
- package/dist/ports/pdpp-auth.d.ts +17 -27
- package/dist/ports/pdpp-auth.d.ts.map +1 -1
- package/dist/ports/pdpp-auth.js +14 -28
- package/dist/ports/pdpp-auth.js.map +1 -1
- package/dist/publication/publish.d.ts +96 -0
- package/dist/publication/publish.d.ts.map +1 -0
- package/dist/publication/publish.js +261 -0
- package/dist/publication/publish.js.map +1 -0
- package/dist/replication/completion.d.ts +20 -0
- package/dist/replication/completion.d.ts.map +1 -0
- package/dist/replication/completion.js +73 -0
- package/dist/replication/completion.js.map +1 -0
- package/dist/replication/coordinator-state.d.ts +29 -0
- package/dist/replication/coordinator-state.d.ts.map +1 -0
- package/dist/replication/coordinator-state.js +88 -0
- package/dist/replication/coordinator-state.js.map +1 -0
- package/dist/replication/coordinator-state.test-utils.d.ts +32 -0
- package/dist/replication/coordinator-state.test-utils.d.ts.map +1 -0
- package/dist/replication/coordinator-state.test-utils.js +113 -0
- package/dist/replication/coordinator-state.test-utils.js.map +1 -0
- package/dist/replication/coordinator.d.ts +264 -0
- package/dist/replication/coordinator.d.ts.map +1 -0
- package/dist/replication/coordinator.js +499 -0
- package/dist/replication/coordinator.js.map +1 -0
- package/dist/replication/copies.d.ts +126 -0
- package/dist/replication/copies.d.ts.map +1 -0
- package/dist/replication/copies.js +437 -0
- package/dist/replication/copies.js.map +1 -0
- package/dist/replication/copy-promotion.d.ts +132 -0
- package/dist/replication/copy-promotion.d.ts.map +1 -0
- package/dist/replication/copy-promotion.js +126 -0
- package/dist/replication/copy-promotion.js.map +1 -0
- package/dist/replication/door.d.ts +33 -0
- package/dist/replication/door.d.ts.map +1 -0
- package/dist/replication/door.js +10 -0
- package/dist/replication/door.js.map +1 -0
- package/dist/replication/dpv2.d.ts +141 -0
- package/dist/replication/dpv2.d.ts.map +1 -0
- package/dist/replication/dpv2.js +320 -0
- package/dist/replication/dpv2.js.map +1 -0
- package/dist/replication/effects.d.ts +68 -0
- package/dist/replication/effects.d.ts.map +1 -0
- package/dist/replication/effects.js +89 -0
- package/dist/replication/effects.js.map +1 -0
- package/dist/replication/engine.d.ts +378 -0
- package/dist/replication/engine.d.ts.map +1 -0
- package/dist/replication/engine.js +3197 -0
- package/dist/replication/engine.js.map +1 -0
- package/dist/replication/envelope.d.ts +481 -0
- package/dist/replication/envelope.d.ts.map +1 -0
- package/dist/replication/envelope.js +192 -0
- package/dist/replication/envelope.js.map +1 -0
- package/dist/replication/evidence.d.ts +118 -0
- package/dist/replication/evidence.d.ts.map +1 -0
- package/dist/replication/evidence.js +198 -0
- package/dist/replication/evidence.js.map +1 -0
- package/dist/replication/finality.d.ts +16 -0
- package/dist/replication/finality.d.ts.map +1 -0
- package/dist/replication/finality.js +11 -0
- package/dist/replication/finality.js.map +1 -0
- package/dist/replication/fixtures.d.ts +16 -0
- package/dist/replication/fixtures.d.ts.map +1 -0
- package/dist/replication/fixtures.js +47 -0
- package/dist/replication/fixtures.js.map +1 -0
- package/dist/replication/host-config.d.ts +52 -0
- package/dist/replication/host-config.d.ts.map +1 -0
- package/dist/replication/host-config.js +111 -0
- package/dist/replication/host-config.js.map +1 -0
- package/dist/replication/host-driver.d.ts +95 -0
- package/dist/replication/host-driver.d.ts.map +1 -0
- package/dist/replication/host-driver.js +202 -0
- package/dist/replication/host-driver.js.map +1 -0
- package/dist/replication/host-ports.d.ts +48 -0
- package/dist/replication/host-ports.d.ts.map +1 -0
- package/dist/replication/host-ports.js +69 -0
- package/dist/replication/host-ports.js.map +1 -0
- package/dist/replication/identity.d.ts +13 -0
- package/dist/replication/identity.d.ts.map +1 -0
- package/dist/replication/identity.js +22 -0
- package/dist/replication/identity.js.map +1 -0
- package/dist/replication/limits.d.ts +124 -0
- package/dist/replication/limits.d.ts.map +1 -0
- package/dist/replication/limits.js +182 -0
- package/dist/replication/limits.js.map +1 -0
- package/dist/replication/merge-plan.d.ts +92 -0
- package/dist/replication/merge-plan.d.ts.map +1 -0
- package/dist/replication/merge-plan.js +229 -0
- package/dist/replication/merge-plan.js.map +1 -0
- package/dist/replication/namespace.d.ts +4 -0
- package/dist/replication/namespace.d.ts.map +1 -0
- package/dist/replication/namespace.js +7 -0
- package/dist/replication/namespace.js.map +1 -0
- package/dist/replication/outcome.d.ts +178 -0
- package/dist/replication/outcome.d.ts.map +1 -0
- package/dist/replication/outcome.js +83 -0
- package/dist/replication/outcome.js.map +1 -0
- package/dist/replication/payload.d.ts +39 -0
- package/dist/replication/payload.d.ts.map +1 -0
- package/dist/replication/payload.js +137 -0
- package/dist/replication/payload.js.map +1 -0
- package/dist/replication/preparation.d.ts +42 -0
- package/dist/replication/preparation.d.ts.map +1 -0
- package/dist/replication/preparation.js +118 -0
- package/dist/replication/preparation.js.map +1 -0
- package/dist/replication/rebaseline.d.ts +33 -0
- package/dist/replication/rebaseline.d.ts.map +1 -0
- package/dist/replication/rebaseline.js +40 -0
- package/dist/replication/rebaseline.js.map +1 -0
- package/dist/replication/rebinding.d.ts +43 -0
- package/dist/replication/rebinding.d.ts.map +1 -0
- package/dist/replication/rebinding.js +52 -0
- package/dist/replication/rebinding.js.map +1 -0
- package/dist/replication/receive.d.ts +57 -0
- package/dist/replication/receive.d.ts.map +1 -0
- package/dist/replication/receive.js +114 -0
- package/dist/replication/receive.js.map +1 -0
- package/dist/replication/record-reducer.d.ts +16 -0
- package/dist/replication/record-reducer.d.ts.map +1 -0
- package/dist/replication/record-reducer.js +52 -0
- package/dist/replication/record-reducer.js.map +1 -0
- package/dist/replication/revision.d.ts +24 -0
- package/dist/replication/revision.d.ts.map +1 -0
- package/dist/replication/revision.js +35 -0
- package/dist/replication/revision.js.map +1 -0
- package/dist/replication/roster.d.ts +53 -0
- package/dist/replication/roster.d.ts.map +1 -0
- package/dist/replication/roster.js +115 -0
- package/dist/replication/roster.js.map +1 -0
- package/dist/replication/schedule.d.ts +73 -0
- package/dist/replication/schedule.d.ts.map +1 -0
- package/dist/replication/schedule.js +182 -0
- package/dist/replication/schedule.js.map +1 -0
- package/dist/replication/stream.d.ts +73 -0
- package/dist/replication/stream.d.ts.map +1 -0
- package/dist/replication/stream.js +542 -0
- package/dist/replication/stream.js.map +1 -0
- package/dist/replication/transport.d.ts +236 -0
- package/dist/replication/transport.d.ts.map +1 -0
- package/dist/replication/transport.js +440 -0
- package/dist/replication/transport.js.map +1 -0
- package/dist/replication/visibility-engine.d.ts +337 -0
- package/dist/replication/visibility-engine.d.ts.map +1 -0
- package/dist/replication/visibility-engine.js +602 -0
- package/dist/replication/visibility-engine.js.map +1 -0
- package/dist/replication/visibility-policy.d.ts +26 -0
- package/dist/replication/visibility-policy.d.ts.map +1 -0
- package/dist/replication/visibility-policy.js +33 -0
- package/dist/replication/visibility-policy.js.map +1 -0
- package/dist/replication/writer-identity-store.d.ts +100 -0
- package/dist/replication/writer-identity-store.d.ts.map +1 -0
- package/dist/replication/writer-identity-store.js +218 -0
- package/dist/replication/writer-identity-store.js.map +1 -0
- package/dist/replication/writer-identity.d.ts +252 -0
- package/dist/replication/writer-identity.d.ts.map +1 -0
- package/dist/replication/writer-identity.js +281 -0
- package/dist/replication/writer-identity.js.map +1 -0
- package/dist/replication/writer-identity.test-utils.d.ts +24 -0
- package/dist/replication/writer-identity.test-utils.d.ts.map +1 -0
- package/dist/replication/writer-identity.test-utils.js +86 -0
- package/dist/replication/writer-identity.test-utils.js.map +1 -0
- package/dist/schemas/server-config.d.ts +13 -0
- package/dist/schemas/server-config.d.ts.map +1 -1
- package/dist/schemas/server-config.js +24 -0
- package/dist/schemas/server-config.js.map +1 -1
- package/dist/storage/adapters/index.d.ts +3 -1
- package/dist/storage/adapters/index.d.ts.map +1 -1
- package/dist/storage/adapters/index.js +2 -0
- package/dist/storage/adapters/index.js.map +1 -1
- package/dist/storage/adapters/interface.d.ts +18 -1
- package/dist/storage/adapters/interface.d.ts.map +1 -1
- package/dist/storage/adapters/interface.js +13 -1
- package/dist/storage/adapters/interface.js.map +1 -1
- package/dist/storage/adapters/sdk.d.ts +2 -1
- package/dist/storage/adapters/sdk.d.ts.map +1 -1
- package/dist/storage/adapters/sdk.js +21 -3
- package/dist/storage/adapters/sdk.js.map +1 -1
- package/dist/storage/adapters/vana-audience.d.ts +17 -5
- package/dist/storage/adapters/vana-audience.d.ts.map +1 -1
- package/dist/storage/adapters/vana-audience.js +24 -9
- package/dist/storage/adapters/vana-audience.js.map +1 -1
- package/dist/storage/adapters/vana.d.ts +2 -2
- package/dist/storage/adapters/vana.d.ts.map +1 -1
- package/dist/storage/adapters/vana.js +30 -22
- package/dist/storage/adapters/vana.js.map +1 -1
- package/dist/storage/canonical-legacy-projection.d.ts +1 -1
- package/dist/storage/canonical-legacy-projection.d.ts.map +1 -1
- package/dist/storage/canonical-legacy-projection.js +13 -15
- package/dist/storage/canonical-legacy-projection.js.map +1 -1
- package/dist/storage/legacy-projection.d.ts.map +1 -1
- package/dist/storage/legacy-projection.js +16 -1
- package/dist/storage/legacy-projection.js.map +1 -1
- package/dist/storage/legacy-scope-coverage-exceptions.d.ts +5 -0
- package/dist/storage/legacy-scope-coverage-exceptions.d.ts.map +1 -1
- package/dist/storage/legacy-scope-coverage-exceptions.js +4 -0
- package/dist/storage/legacy-scope-coverage-exceptions.js.map +1 -1
- package/dist/storage/legacy-scope-coverage.d.ts.map +1 -1
- package/dist/storage/legacy-scope-coverage.js +14 -1
- package/dist/storage/legacy-scope-coverage.js.map +1 -1
- package/dist/storage/legacy-scope-entity-keys.d.ts +14 -1
- package/dist/storage/legacy-scope-entity-keys.d.ts.map +1 -1
- package/dist/storage/legacy-scope-entity-keys.js +18 -1
- package/dist/storage/legacy-scope-entity-keys.js.map +1 -1
- package/dist/storage/legacy-scope-view.d.ts +57 -5
- package/dist/storage/legacy-scope-view.d.ts.map +1 -1
- package/dist/storage/legacy-scope-view.js +644 -318
- package/dist/storage/legacy-scope-view.js.map +1 -1
- package/dist/storage/pdpp-records/cursor.d.ts +47 -2
- package/dist/storage/pdpp-records/cursor.d.ts.map +1 -1
- package/dist/storage/pdpp-records/cursor.js +7 -0
- package/dist/storage/pdpp-records/cursor.js.map +1 -1
- package/dist/storage/pdpp-records/enforcement.d.ts +3 -16
- package/dist/storage/pdpp-records/enforcement.d.ts.map +1 -1
- package/dist/storage/pdpp-records/enforcement.js +40 -13
- package/dist/storage/pdpp-records/enforcement.js.map +1 -1
- package/dist/storage/pdpp-records/index.d.ts +2 -0
- package/dist/storage/pdpp-records/index.d.ts.map +1 -1
- package/dist/storage/pdpp-records/index.js +1 -0
- package/dist/storage/pdpp-records/index.js.map +1 -1
- package/dist/storage/pdpp-records/interface.d.ts +8 -1
- package/dist/storage/pdpp-records/interface.d.ts.map +1 -1
- package/dist/storage/pdpp-records/list-page.d.ts +35 -0
- package/dist/storage/pdpp-records/list-page.d.ts.map +1 -0
- package/dist/storage/pdpp-records/list-page.js +106 -0
- package/dist/storage/pdpp-records/list-page.js.map +1 -0
- package/dist/storage/pdpp-records/memory-store.d.ts.map +1 -1
- package/dist/storage/pdpp-records/memory-store.js +95 -99
- package/dist/storage/pdpp-records/memory-store.js.map +1 -1
- package/dist/storage/pdpp-records/portable.d.ts +3 -0
- package/dist/storage/pdpp-records/portable.d.ts.map +1 -1
- package/dist/storage/pdpp-records/portable.js +3 -0
- package/dist/storage/pdpp-records/portable.js.map +1 -1
- package/dist/storage/pdpp-records/replication-journal.d.ts +20 -0
- package/dist/storage/pdpp-records/replication-journal.d.ts.map +1 -0
- package/dist/storage/pdpp-records/replication-journal.js +2 -0
- package/dist/storage/pdpp-records/replication-journal.js.map +1 -0
- package/dist/storage/pdpp-records/stream-declaration.d.ts +0 -2
- package/dist/storage/pdpp-records/stream-declaration.d.ts.map +1 -1
- package/dist/storage/pdpp-records/stream-declaration.js +0 -9
- package/dist/storage/pdpp-records/stream-declaration.js.map +1 -1
- package/dist/storage/pdpp-records/sync-selection.d.ts +4 -0
- package/dist/storage/pdpp-records/sync-selection.d.ts.map +1 -0
- package/dist/storage/pdpp-records/sync-selection.js +37 -0
- package/dist/storage/pdpp-records/sync-selection.js.map +1 -0
- package/dist/storage/pdpp-records/types.d.ts +64 -0
- package/dist/storage/pdpp-records/types.d.ts.map +1 -1
- package/dist/storage/pdpp-records/types.js.map +1 -1
- package/dist/sync/delete-data-port.d.ts +15 -0
- package/dist/sync/delete-data-port.d.ts.map +1 -1
- package/dist/sync/delete-data-port.js +19 -7
- package/dist/sync/delete-data-port.js.map +1 -1
- package/dist/sync/index.d.ts +2 -1
- package/dist/sync/index.d.ts.map +1 -1
- package/dist/sync/index.js +2 -1
- package/dist/sync/index.js.map +1 -1
- package/dist/sync/pending-blob-deletions.d.ts +18 -2
- package/dist/sync/pending-blob-deletions.d.ts.map +1 -1
- package/dist/sync/pending-blob-deletions.js +69 -23
- package/dist/sync/pending-blob-deletions.js.map +1 -1
- package/dist/sync/workers/delete.d.ts.map +1 -1
- package/dist/sync/workers/delete.js +332 -142
- package/dist/sync/workers/delete.js.map +1 -1
- package/dist/sync/workers/download.d.ts.map +1 -1
- package/dist/sync/workers/download.js +53 -5
- package/dist/sync/workers/download.js.map +1 -1
- package/dist/sync/workers/upload.d.ts +24 -5
- package/dist/sync/workers/upload.d.ts.map +1 -1
- package/dist/sync/workers/upload.js +160 -275
- package/dist/sync/workers/upload.js.map +1 -1
- package/dist/test-utils/memory-storage.d.ts.map +1 -1
- package/dist/test-utils/memory-storage.js +17 -2
- package/dist/test-utils/memory-storage.js.map +1 -1
- package/package.json +24 -2
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
import { DataFileEnvelopeSchema, decryptWithPassword, deriveScopeKey, encryptWithPassword, } from "@opendatalabs/vana-sdk/browser";
|
|
2
|
+
import { keccak256, stringToHex } from "viem";
|
|
3
|
+
import { isTombstoneRecord } from "../sync/tombstone.js";
|
|
4
|
+
import { readStoredLineage } from "../lineage/lineage.js";
|
|
5
|
+
import { StoredBlobConflictError, } from "../storage/adapters/interface.js";
|
|
6
|
+
export class NeedsFencedPublicationError extends Error {
|
|
7
|
+
scope;
|
|
8
|
+
reason;
|
|
9
|
+
retryable;
|
|
10
|
+
code = "NEEDS_FENCED_PUBLICATION";
|
|
11
|
+
constructor(scope, reason, retryable = true) {
|
|
12
|
+
super(`Needs fenced publication for ${scope}: ${reason}`);
|
|
13
|
+
this.scope = scope;
|
|
14
|
+
this.reason = reason;
|
|
15
|
+
this.retryable = retryable;
|
|
16
|
+
this.name = "NeedsFencedPublicationError";
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
const sameHash = (a, b) => a.toLowerCase() === b.toLowerCase();
|
|
20
|
+
function metadata(scope, collectedAt, sizeBytes) {
|
|
21
|
+
return keccak256(stringToHex(JSON.stringify({ scope, collectedAt, sizeBytes })));
|
|
22
|
+
}
|
|
23
|
+
/** Full-envelope preparation and verification for the shared publication owner. */
|
|
24
|
+
export async function publishVersion(options) {
|
|
25
|
+
const { envelope, storageAdapter } = options;
|
|
26
|
+
const { scope } = envelope;
|
|
27
|
+
const password = Array.from(deriveScopeKey(options.masterKey, scope), (byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
28
|
+
const plaintext = new TextEncoder().encode(JSON.stringify(envelope));
|
|
29
|
+
const lineage = readStoredLineage(envelope.data);
|
|
30
|
+
function candidate(env, bytes, hash) {
|
|
31
|
+
return {
|
|
32
|
+
dataHash: hash,
|
|
33
|
+
metadataHash: metadata(scope, env.collectedAt, bytes.byteLength),
|
|
34
|
+
sizeBytes: bytes.byteLength,
|
|
35
|
+
lineage: readStoredLineage(env.data),
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
return publishPreparedVersion({
|
|
39
|
+
...options,
|
|
40
|
+
scope,
|
|
41
|
+
storageAdapter,
|
|
42
|
+
async prepare() {
|
|
43
|
+
const encrypted = await encryptWithPassword(plaintext, password);
|
|
44
|
+
return {
|
|
45
|
+
encrypted,
|
|
46
|
+
wanted: {
|
|
47
|
+
...candidate(envelope, encrypted, keccak256(plaintext)),
|
|
48
|
+
lineage,
|
|
49
|
+
},
|
|
50
|
+
async verifyStored(bytes) {
|
|
51
|
+
const clear = await decryptWithPassword(bytes, password);
|
|
52
|
+
const env = DataFileEnvelopeSchema.parse(JSON.parse(new TextDecoder().decode(clear)));
|
|
53
|
+
if (env.scope !== scope ||
|
|
54
|
+
!Number.isFinite(Date.parse(env.collectedAt)))
|
|
55
|
+
throw new Error("Publication envelope scope/timestamp mismatch");
|
|
56
|
+
return candidate(env, bytes, keccak256(clear));
|
|
57
|
+
},
|
|
58
|
+
};
|
|
59
|
+
},
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
/** Single reserve/upload/register/recover policy for full and bounded hosts.
|
|
63
|
+
* No deletion/reclaim effect is permitted here. Retries derive evidence from
|
|
64
|
+
* storage and the registry, never from an abandoned attempt's local URL. */
|
|
65
|
+
export async function publishPreparedVersion(options) {
|
|
66
|
+
const { scope, storageAdapter, fencedPublication, signer } = options;
|
|
67
|
+
const isDeleted = (row) => Boolean(row.deletedAt) || isTombstoneRecord(row);
|
|
68
|
+
let fence = await fencedPublication?.read({
|
|
69
|
+
ownerAddress: options.ownerAddress,
|
|
70
|
+
scope,
|
|
71
|
+
});
|
|
72
|
+
let head = fence ? fence.head : options.head;
|
|
73
|
+
await options.checkHead?.(head ?? null);
|
|
74
|
+
function requireFence(reason) {
|
|
75
|
+
if (!fence)
|
|
76
|
+
throw new NeedsFencedPublicationError(scope, reason);
|
|
77
|
+
return fence;
|
|
78
|
+
}
|
|
79
|
+
function checkDeletion() {
|
|
80
|
+
if (!head || !isDeleted(head))
|
|
81
|
+
return;
|
|
82
|
+
requireFence("publication crosses a deletion");
|
|
83
|
+
if (options.acknowledgedDeletionVersion !== BigInt(head.expectedVersion))
|
|
84
|
+
throw new NeedsFencedPublicationError(scope, "tombstone was not acknowledged by this publication");
|
|
85
|
+
}
|
|
86
|
+
checkDeletion();
|
|
87
|
+
const { encrypted, wanted, verifyStored } = await options.prepare();
|
|
88
|
+
const wantedHash = wanted.dataHash;
|
|
89
|
+
const keyUrl = (version) => storageAdapter.urlForKey(`${scope}/${version}`);
|
|
90
|
+
async function readCandidate(version) {
|
|
91
|
+
const bytes = await storageAdapter.download(keyUrl(version));
|
|
92
|
+
return verifyStored(bytes);
|
|
93
|
+
}
|
|
94
|
+
async function commit(version, value, mode) {
|
|
95
|
+
const request = {
|
|
96
|
+
ownerAddress: options.ownerAddress,
|
|
97
|
+
scope,
|
|
98
|
+
dataHash: value.dataHash,
|
|
99
|
+
metadataHash: value.metadataHash,
|
|
100
|
+
expectedVersion: String(version),
|
|
101
|
+
signature: await signer.signAddData({
|
|
102
|
+
ownerAddress: options.ownerAddress,
|
|
103
|
+
scope,
|
|
104
|
+
dataHash: value.dataHash,
|
|
105
|
+
metadataHash: value.metadataHash,
|
|
106
|
+
expectedVersion: version,
|
|
107
|
+
}),
|
|
108
|
+
};
|
|
109
|
+
const lineage = value.lineage;
|
|
110
|
+
let lineageFields;
|
|
111
|
+
if (lineage) {
|
|
112
|
+
if (!signer.signLineageAttestation ||
|
|
113
|
+
(!fencedPublication && !options.lineageGateway))
|
|
114
|
+
throw new Error(`Cannot register derivative ${scope}: lineage registration needs a gateway URL (lineageGateway is not configured)`);
|
|
115
|
+
lineageFields = {
|
|
116
|
+
lineage: lineage.sources,
|
|
117
|
+
lineageSignature: await signer.signLineageAttestation({
|
|
118
|
+
ownerAddress: options.ownerAddress,
|
|
119
|
+
scope,
|
|
120
|
+
expectedVersion: version,
|
|
121
|
+
dataHash: value.dataHash,
|
|
122
|
+
sources: lineage.sources,
|
|
123
|
+
}),
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
let id;
|
|
127
|
+
if (fencedPublication) {
|
|
128
|
+
const previous = requireFence("registration/adoption");
|
|
129
|
+
const result = await fencedPublication.commit({
|
|
130
|
+
...request,
|
|
131
|
+
...lineageFields,
|
|
132
|
+
mode,
|
|
133
|
+
fence: previous,
|
|
134
|
+
});
|
|
135
|
+
const registered = result.fence.head;
|
|
136
|
+
if (result.fence.generation !== previous.generation ||
|
|
137
|
+
!registered ||
|
|
138
|
+
isDeleted(registered) ||
|
|
139
|
+
registered.expectedVersion !== String(version) ||
|
|
140
|
+
!sameHash(registered.dataHash, value.dataHash) ||
|
|
141
|
+
!sameHash(registered.metadataHash, value.metadataHash) ||
|
|
142
|
+
registered.id !== result.dataPointId)
|
|
143
|
+
throw new Error("Gateway returned inconsistent publication evidence");
|
|
144
|
+
fence = result.fence;
|
|
145
|
+
head = registered;
|
|
146
|
+
id = result.dataPointId;
|
|
147
|
+
}
|
|
148
|
+
else if (mode === "adopt") {
|
|
149
|
+
// No remote effect: the caller verified the live row's stored bytes.
|
|
150
|
+
id = head?.id;
|
|
151
|
+
}
|
|
152
|
+
else {
|
|
153
|
+
const result = lineageFields
|
|
154
|
+
? await options.lineageGateway.registerDataPoint({
|
|
155
|
+
...request,
|
|
156
|
+
...lineageFields,
|
|
157
|
+
})
|
|
158
|
+
: await options.gateway.registerDataPoint(request);
|
|
159
|
+
id = result.dataPointId;
|
|
160
|
+
}
|
|
161
|
+
if (!id)
|
|
162
|
+
throw new Error(`Gateway registerDataPoint did not return a dataPointId for ${scope} version ${version}`);
|
|
163
|
+
return {
|
|
164
|
+
version,
|
|
165
|
+
url: keyUrl(version),
|
|
166
|
+
dataPointId: id,
|
|
167
|
+
sizeBytes: value.sizeBytes,
|
|
168
|
+
dataHash: value.dataHash,
|
|
169
|
+
metadataHash: value.metadataHash,
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
// An observed head decides the next version. A local version ahead of the
|
|
173
|
+
// registry would leave our bytes at a future key that later wedges it.
|
|
174
|
+
let version = head === undefined
|
|
175
|
+
? options.preferredVersion
|
|
176
|
+
: BigInt(head?.expectedVersion ?? "0") + 1n;
|
|
177
|
+
let rebased = false;
|
|
178
|
+
for (let step = 0; step < 4; step++) {
|
|
179
|
+
checkDeletion();
|
|
180
|
+
if (head && !isDeleted(head) && sameHash(head.dataHash, wantedHash)) {
|
|
181
|
+
version = BigInt(head.expectedVersion);
|
|
182
|
+
const url = keyUrl(version);
|
|
183
|
+
if (!(await storageAdapter.exists(url))) {
|
|
184
|
+
// Recreating bytes can race a delete's cleanup; that needs the fence.
|
|
185
|
+
requireFence("healing a committed version's missing bytes");
|
|
186
|
+
if (!sameHash(wanted.metadataHash, head.metadataHash))
|
|
187
|
+
throw new Error("Missing committed blob cannot be healed: metadata commitment mismatch");
|
|
188
|
+
try {
|
|
189
|
+
await storageAdapter.upload(`${scope}/${version}`, encrypted);
|
|
190
|
+
}
|
|
191
|
+
catch (error) {
|
|
192
|
+
if (!(error instanceof StoredBlobConflictError))
|
|
193
|
+
throw error;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
const value = await readCandidate(version);
|
|
197
|
+
if (!sameHash(value.dataHash, head.dataHash) ||
|
|
198
|
+
!sameHash(value.metadataHash, head.metadataHash))
|
|
199
|
+
throw new Error("Adopted publication commitment mismatch");
|
|
200
|
+
return commit(version, value, "adopt");
|
|
201
|
+
}
|
|
202
|
+
if (fence)
|
|
203
|
+
version = BigInt(head?.expectedVersion ?? "0") + 1n;
|
|
204
|
+
if (head && BigInt(head.expectedVersion) >= version)
|
|
205
|
+
version = BigInt(head.expectedVersion) + 1n;
|
|
206
|
+
let value = wanted;
|
|
207
|
+
try {
|
|
208
|
+
await storageAdapter.upload(`${scope}/${version}`, encrypted);
|
|
209
|
+
}
|
|
210
|
+
catch (error) {
|
|
211
|
+
if (!(error instanceof StoredBlobConflictError))
|
|
212
|
+
throw error;
|
|
213
|
+
value = await readCandidate(version);
|
|
214
|
+
// Unfenced, resume only our own content: registering it is the same
|
|
215
|
+
// registration a fresh upload would make. A foreign occupant needs
|
|
216
|
+
// adopt-then-supersede, which needs the fence.
|
|
217
|
+
if (!sameHash(value.dataHash, wantedHash))
|
|
218
|
+
requireFence("resuming an occupied unregistered version");
|
|
219
|
+
}
|
|
220
|
+
try {
|
|
221
|
+
const result = await commit(version, value, "register");
|
|
222
|
+
if (sameHash(value.dataHash, wantedHash))
|
|
223
|
+
return result;
|
|
224
|
+
// Verified foreign reservation is now committed. Supersede in this
|
|
225
|
+
// generation, using the returned fence; never re-read across a delete.
|
|
226
|
+
version++;
|
|
227
|
+
}
|
|
228
|
+
catch (error) {
|
|
229
|
+
if (fencedPublication ||
|
|
230
|
+
!(error instanceof Error) ||
|
|
231
|
+
!/Gateway error: 409\b/.test(error.message))
|
|
232
|
+
throw error;
|
|
233
|
+
if (rebased)
|
|
234
|
+
throw error;
|
|
235
|
+
rebased = true;
|
|
236
|
+
head = await options.readHead();
|
|
237
|
+
await options.checkHead?.(head);
|
|
238
|
+
checkDeletion();
|
|
239
|
+
const next = parseGatewayNextVersion(error.message);
|
|
240
|
+
if (!head && next === null)
|
|
241
|
+
throw error;
|
|
242
|
+
const observedNext = BigInt(head?.expectedVersion ?? "0") + 1n;
|
|
243
|
+
if (next !== null && BigInt(next) > observedNext)
|
|
244
|
+
requireFence("gateway version hint advances beyond the observed registry head");
|
|
245
|
+
version = observedNext;
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
throw new Error(`Publication contention for ${scope}; retry next cycle`);
|
|
249
|
+
}
|
|
250
|
+
/** Legacy gateway conflict hints; fenced recovery never guesses from text. */
|
|
251
|
+
export function parseGatewayNextVersion(message) {
|
|
252
|
+
const nextValid = message.match(/next valid version is (\d+)/i);
|
|
253
|
+
if (nextValid)
|
|
254
|
+
return Number(nextValid[1]);
|
|
255
|
+
const effectiveMax = message.match(/effective max \((\d+)/i);
|
|
256
|
+
if (effectiveMax)
|
|
257
|
+
return Number(effectiveMax[1]) + 1;
|
|
258
|
+
const storedValue = message.match(/strictly greater than the stored value (\d+)/i);
|
|
259
|
+
return storedValue ? Number(storedValue[1]) + 1 : null;
|
|
260
|
+
}
|
|
261
|
+
//# sourceMappingURL=publish.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"publish.js","sourceRoot":"","sources":["../../src/publication/publish.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,sBAAsB,EACtB,mBAAmB,EACnB,cAAc,EACd,mBAAmB,GAGpB,MAAM,gCAAgC,CAAC;AACxC,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,MAAM,CAAC;AAC9C,OAAO,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AACzD,OAAO,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAG1D,OAAO,EACL,uBAAuB,GAExB,MAAM,kCAAkC,CAAC;AAgC1C,MAAM,OAAO,2BAA4B,SAAQ,KAAK;IAGzC;IACA;IACA;IAJF,IAAI,GAAG,0BAA0B,CAAC;IAC3C,YACW,KAAa,EACb,MAAc,EACd,YAAqB,IAAI;QAElC,KAAK,CAAC,gCAAgC,KAAK,KAAK,MAAM,EAAE,CAAC,CAAC;QAJjD,UAAK,GAAL,KAAK,CAAQ;QACb,WAAM,GAAN,MAAM,CAAQ;QACd,cAAS,GAAT,SAAS,CAAgB;QAGlC,IAAI,CAAC,IAAI,GAAG,6BAA6B,CAAC;IAC5C,CAAC;CACF;AA6BD,MAAM,QAAQ,GAAG,CAAC,CAAS,EAAE,CAAS,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,EAAE,KAAK,CAAC,CAAC,WAAW,EAAE,CAAC;AAC/E,SAAS,QAAQ,CAAC,KAAa,EAAE,WAAmB,EAAE,SAAiB;IACrE,OAAO,SAAS,CACd,WAAW,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,WAAW,EAAE,SAAS,EAAE,CAAC,CAAC,CAC/D,CAAC;AACJ,CAAC;AAED,mFAAmF;AACnF,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,OAAuB;IAEvB,MAAM,EAAE,QAAQ,EAAE,cAAc,EAAE,GAAG,OAAO,CAAC;IAC7C,MAAM,EAAE,KAAK,EAAE,GAAG,QAAQ,CAAC;IAC3B,MAAM,QAAQ,GAAG,KAAK,CAAC,IAAI,CACzB,cAAc,CAAC,OAAO,CAAC,SAAS,EAAE,KAAK,CAAC,EACxC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAC7C,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACX,MAAM,SAAS,GAAG,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC,CAAC;IACrE,MAAM,OAAO,GAAG,iBAAiB,CAAC,QAAQ,CAAC,IAA+B,CAAC,CAAC;IAC5E,SAAS,SAAS,CAChB,GAAqB,EACrB,KAAiB,EACjB,IAAmB;QAEnB,OAAO;YACL,QAAQ,EAAE,IAAI;YACd,YAAY,EAAE,QAAQ,CAAC,KAAK,EAAE,GAAG,CAAC,WAAW,EAAE,KAAK,CAAC,UAAU,CAAC;YAChE,SAAS,EAAE,KAAK,CAAC,UAAU;YAC3B,OAAO,EAAE,iBAAiB,CAAC,GAAG,CAAC,IAA+B,CAAC;SAChE,CAAC;IACJ,CAAC;IACD,OAAO,sBAAsB,CAAC;QAC5B,GAAG,OAAO;QACV,KAAK;QACL,cAAc;QACd,KAAK,CAAC,OAAO;YACX,MAAM,SAAS,GAAG,MAAM,mBAAmB,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;YACjE,OAAO;gBACL,SAAS;gBACT,MAAM,EAAE;oBACN,GAAG,SAAS,CAAC,QAAQ,EAAE,SAAS,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;oBACvD,OAAO;iBACR;gBACD,KAAK,CAAC,YAAY,CAAC,KAAK;oBACtB,MAAM,KAAK,GAAG,MAAM,mBAAmB,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;oBACzD,MAAM,GAAG,GAAG,sBAAsB,CAAC,KAAK,CACtC,IAAI,CAAC,KAAK,CAAC,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAC5C,CAAC;oBACF,IACE,GAAG,CAAC,KAAK,KAAK,KAAK;wBACnB,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;wBAE7C,MAAM,IAAI,KAAK,CAAC,+CAA+C,CAAC,CAAC;oBACnE,OAAO,SAAS,CAAC,GAAG,EAAE,KAAK,EAAE,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC;gBACjD,CAAC;aACF,CAAC;QACJ,CAAC;KACF,CAAC,CAAC;AACL,CAAC;AAyBD;;4EAE4E;AAC5E,MAAM,CAAC,KAAK,UAAU,sBAAsB,CAC1C,OAA+B;IAE/B,MAAM,EAAE,KAAK,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC;IACrE,MAAM,SAAS,GAAG,CAAC,GAAsB,EAAE,EAAE,CAC3C,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,iBAAiB,CAAC,GAAG,CAAC,CAAC;IACnD,IAAI,KAAK,GAAG,MAAM,iBAAiB,EAAE,IAAI,CAAC;QACxC,YAAY,EAAE,OAAO,CAAC,YAAY;QAClC,KAAK;KACN,CAAC,CAAC;IACH,IAAI,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC;IAC7C,MAAM,OAAO,CAAC,SAAS,EAAE,CAAC,IAAI,IAAI,IAAI,CAAC,CAAC;IACxC,SAAS,YAAY,CAAC,MAAc;QAClC,IAAI,CAAC,KAAK;YAAE,MAAM,IAAI,2BAA2B,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;QACjE,OAAO,KAAK,CAAC;IACf,CAAC;IACD,SAAS,aAAa;QACpB,IAAI,CAAC,IAAI,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC;YAAE,OAAO;QACtC,YAAY,CAAC,gCAAgC,CAAC,CAAC;QAC/C,IAAI,OAAO,CAAC,2BAA2B,KAAK,MAAM,CAAC,IAAI,CAAC,eAAe,CAAC;YACtE,MAAM,IAAI,2BAA2B,CACnC,KAAK,EACL,oDAAoD,CACrD,CAAC;IACN,CAAC;IACD,aAAa,EAAE,CAAC;IAChB,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,YAAY,EAAE,GAAG,MAAM,OAAO,CAAC,OAAO,EAAE,CAAC;IACpE,MAAM,UAAU,GAAG,MAAM,CAAC,QAAQ,CAAC;IACnC,MAAM,MAAM,GAAG,CAAC,OAAe,EAAE,EAAE,CACjC,cAAc,CAAC,SAAS,CAAC,GAAG,KAAK,IAAI,OAAO,EAAE,CAAC,CAAC;IAClD,KAAK,UAAU,aAAa,CAAC,OAAe;QAC1C,MAAM,KAAK,GAAG,MAAM,cAAc,CAAC,QAAQ,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC;QAC7D,OAAO,YAAY,CAAC,KAAK,CAAC,CAAC;IAC7B,CAAC;IACD,KAAK,UAAU,MAAM,CACnB,OAAe,EACf,KAA2B,EAC3B,IAA0B;QAE1B,MAAM,OAAO,GAAG;YACd,YAAY,EAAE,OAAO,CAAC,YAAY;YAClC,KAAK;YACL,QAAQ,EAAE,KAAK,CAAC,QAAQ;YACxB,YAAY,EAAE,KAAK,CAAC,YAAY;YAChC,eAAe,EAAE,MAAM,CAAC,OAAO,CAAC;YAChC,SAAS,EAAE,MAAM,MAAM,CAAC,WAAW,CAAC;gBAClC,YAAY,EAAE,OAAO,CAAC,YAA6B;gBACnD,KAAK;gBACL,QAAQ,EAAE,KAAK,CAAC,QAAQ;gBACxB,YAAY,EAAE,KAAK,CAAC,YAAY;gBAChC,eAAe,EAAE,OAAO;aACzB,CAAC;SACH,CAAC;QACF,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC;QAC9B,IAAI,aACkE,CAAC;QACvE,IAAI,OAAO,EAAE,CAAC;YACZ,IACE,CAAC,MAAM,CAAC,sBAAsB;gBAC9B,CAAC,CAAC,iBAAiB,IAAI,CAAC,OAAO,CAAC,cAAc,CAAC;gBAE/C,MAAM,IAAI,KAAK,CACb,8BAA8B,KAAK,+EAA+E,CACnH,CAAC;YACJ,aAAa,GAAG;gBACd,OAAO,EAAE,OAAO,CAAC,OAAO;gBACxB,gBAAgB,EAAE,MAAM,MAAM,CAAC,sBAAsB,CAAC;oBACpD,YAAY,EAAE,OAAO,CAAC,YAA6B;oBACnD,KAAK;oBACL,eAAe,EAAE,OAAO;oBACxB,QAAQ,EAAE,KAAK,CAAC,QAAQ;oBACxB,OAAO,EAAE,OAAO,CAAC,OAAO;iBACzB,CAAC;aACH,CAAC;QACJ,CAAC;QACD,IAAI,EAAsB,CAAC;QAC3B,IAAI,iBAAiB,EAAE,CAAC;YACtB,MAAM,QAAQ,GAAG,YAAY,CAAC,uBAAuB,CAAC,CAAC;YACvD,MAAM,MAAM,GAAG,MAAM,iBAAiB,CAAC,MAAM,CAAC;gBAC5C,GAAG,OAAO;gBACV,GAAG,aAAa;gBAChB,IAAI;gBACJ,KAAK,EAAE,QAAQ;aAChB,CAAC,CAAC;YACH,MAAM,UAAU,GAAG,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC;YACrC,IACE,MAAM,CAAC,KAAK,CAAC,UAAU,KAAK,QAAQ,CAAC,UAAU;gBAC/C,CAAC,UAAU;gBACX,SAAS,CAAC,UAAU,CAAC;gBACrB,UAAU,CAAC,eAAe,KAAK,MAAM,CAAC,OAAO,CAAC;gBAC9C,CAAC,QAAQ,CAAC,UAAU,CAAC,QAAQ,EAAE,KAAK,CAAC,QAAQ,CAAC;gBAC9C,CAAC,QAAQ,CAAC,UAAU,CAAC,YAAY,EAAE,KAAK,CAAC,YAAY,CAAC;gBACtD,UAAU,CAAC,EAAE,KAAK,MAAM,CAAC,WAAW;gBAEpC,MAAM,IAAI,KAAK,CAAC,oDAAoD,CAAC,CAAC;YACxE,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC;YACrB,IAAI,GAAG,UAAU,CAAC;YAClB,EAAE,GAAG,MAAM,CAAC,WAAW,CAAC;QAC1B,CAAC;aAAM,IAAI,IAAI,KAAK,OAAO,EAAE,CAAC;YAC5B,qEAAqE;YACrE,EAAE,GAAG,IAAI,EAAE,EAAE,CAAC;QAChB,CAAC;aAAM,CAAC;YACN,MAAM,MAAM,GAAG,aAAa;gBAC1B,CAAC,CAAC,MAAM,OAAO,CAAC,cAAe,CAAC,iBAAiB,CAAC;oBAC9C,GAAG,OAAO;oBACV,GAAG,aAAa;iBACjB,CAAC;gBACJ,CAAC,CAAC,MAAM,OAAO,CAAC,OAAO,CAAC,iBAAiB,CAAC,OAAO,CAAC,CAAC;YACrD,EAAE,GAAG,MAAM,CAAC,WAAW,CAAC;QAC1B,CAAC;QACD,IAAI,CAAC,EAAE;YACL,MAAM,IAAI,KAAK,CACb,8DAA8D,KAAK,YAAY,OAAO,EAAE,CACzF,CAAC;QACJ,OAAO;YACL,OAAO;YACP,GAAG,EAAE,MAAM,CAAC,OAAO,CAAC;YACpB,WAAW,EAAE,EAAE;YACf,SAAS,EAAE,KAAK,CAAC,SAAS;YAC1B,QAAQ,EAAE,KAAK,CAAC,QAAQ;YACxB,YAAY,EAAE,KAAK,CAAC,YAAY;SACjC,CAAC;IACJ,CAAC;IACD,0EAA0E;IAC1E,uEAAuE;IACvE,IAAI,OAAO,GACT,IAAI,KAAK,SAAS;QAChB,CAAC,CAAC,OAAO,CAAC,gBAAgB;QAC1B,CAAC,CAAC,MAAM,CAAC,IAAI,EAAE,eAAe,IAAI,GAAG,CAAC,GAAG,EAAE,CAAC;IAChD,IAAI,OAAO,GAAG,KAAK,CAAC;IACpB,KAAK,IAAI,IAAI,GAAG,CAAC,EAAE,IAAI,GAAG,CAAC,EAAE,IAAI,EAAE,EAAE,CAAC;QACpC,aAAa,EAAE,CAAC;QAChB,IAAI,IAAI,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,QAAQ,CAAC,IAAI,CAAC,QAAQ,EAAE,UAAU,CAAC,EAAE,CAAC;YACpE,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;YACvC,MAAM,GAAG,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC;YAC5B,IAAI,CAAC,CAAC,MAAM,cAAc,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC;gBACxC,sEAAsE;gBACtE,YAAY,CAAC,6CAA6C,CAAC,CAAC;gBAC5D,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,YAAY,EAAE,IAAI,CAAC,YAAY,CAAC;oBACnD,MAAM,IAAI,KAAK,CACb,uEAAuE,CACxE,CAAC;gBACJ,IAAI,CAAC;oBACH,MAAM,cAAc,CAAC,MAAM,CAAC,GAAG,KAAK,IAAI,OAAO,EAAE,EAAE,SAAS,CAAC,CAAC;gBAChE,CAAC;gBAAC,OAAO,KAAK,EAAE,CAAC;oBACf,IAAI,CAAC,CAAC,KAAK,YAAY,uBAAuB,CAAC;wBAAE,MAAM,KAAK,CAAC;gBAC/D,CAAC;YACH,CAAC;YACD,MAAM,KAAK,GAAG,MAAM,aAAa,CAAC,OAAO,CAAC,CAAC;YAC3C,IACE,CAAC,QAAQ,CAAC,KAAK,CAAC,QAAQ,EAAE,IAAI,CAAC,QAAQ,CAAC;gBACxC,CAAC,QAAQ,CAAC,KAAK,CAAC,YAAY,EAAE,IAAI,CAAC,YAAY,CAAC;gBAEhD,MAAM,IAAI,KAAK,CAAC,yCAAyC,CAAC,CAAC;YAC7D,OAAO,MAAM,CAAC,OAAO,EAAE,KAAK,EAAE,OAAO,CAAC,CAAC;QACzC,CAAC;QACD,IAAI,KAAK;YAAE,OAAO,GAAG,MAAM,CAAC,IAAI,EAAE,eAAe,IAAI,GAAG,CAAC,GAAG,EAAE,CAAC;QAC/D,IAAI,IAAI,IAAI,MAAM,CAAC,IAAI,CAAC,eAAe,CAAC,IAAI,OAAO;YACjD,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,eAAe,CAAC,GAAG,EAAE,CAAC;QAC9C,IAAI,KAAK,GAAG,MAAM,CAAC;QACnB,IAAI,CAAC;YACH,MAAM,cAAc,CAAC,MAAM,CAAC,GAAG,KAAK,IAAI,OAAO,EAAE,EAAE,SAAS,CAAC,CAAC;QAChE,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,CAAC,CAAC,KAAK,YAAY,uBAAuB,CAAC;gBAAE,MAAM,KAAK,CAAC;YAC7D,KAAK,GAAG,MAAM,aAAa,CAAC,OAAO,CAAC,CAAC;YACrC,oEAAoE;YACpE,mEAAmE;YACnE,+CAA+C;YAC/C,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,QAAQ,EAAE,UAAU,CAAC;gBACvC,YAAY,CAAC,2CAA2C,CAAC,CAAC;QAC9D,CAAC;QACD,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,OAAO,EAAE,KAAK,EAAE,UAAU,CAAC,CAAC;YACxD,IAAI,QAAQ,CAAC,KAAK,CAAC,QAAQ,EAAE,UAAU,CAAC;gBAAE,OAAO,MAAM,CAAC;YACxD,mEAAmE;YACnE,uEAAuE;YACvE,OAAO,EAAE,CAAC;QACZ,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IACE,iBAAiB;gBACjB,CAAC,CAAC,KAAK,YAAY,KAAK,CAAC;gBACzB,CAAC,sBAAsB,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC;gBAE3C,MAAM,KAAK,CAAC;YACd,IAAI,OAAO;gBAAE,MAAM,KAAK,CAAC;YACzB,OAAO,GAAG,IAAI,CAAC;YACf,IAAI,GAAG,MAAM,OAAO,CAAC,QAAQ,EAAE,CAAC;YAChC,MAAM,OAAO,CAAC,SAAS,EAAE,CAAC,IAAI,CAAC,CAAC;YAChC,aAAa,EAAE,CAAC;YAChB,MAAM,IAAI,GAAG,uBAAuB,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;YACpD,IAAI,CAAC,IAAI,IAAI,IAAI,KAAK,IAAI;gBAAE,MAAM,KAAK,CAAC;YACxC,MAAM,YAAY,GAAG,MAAM,CAAC,IAAI,EAAE,eAAe,IAAI,GAAG,CAAC,GAAG,EAAE,CAAC;YAC/D,IAAI,IAAI,KAAK,IAAI,IAAI,MAAM,CAAC,IAAI,CAAC,GAAG,YAAY;gBAC9C,YAAY,CACV,iEAAiE,CAClE,CAAC;YACJ,OAAO,GAAG,YAAY,CAAC;QACzB,CAAC;IACH,CAAC;IACD,MAAM,IAAI,KAAK,CAAC,8BAA8B,KAAK,oBAAoB,CAAC,CAAC;AAC3E,CAAC;AAED,8EAA8E;AAC9E,MAAM,UAAU,uBAAuB,CAAC,OAAe;IACrD,MAAM,SAAS,GAAG,OAAO,CAAC,KAAK,CAAC,8BAA8B,CAAC,CAAC;IAChE,IAAI,SAAS;QAAE,OAAO,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC;IAC3C,MAAM,YAAY,GAAG,OAAO,CAAC,KAAK,CAAC,wBAAwB,CAAC,CAAC;IAC7D,IAAI,YAAY;QAAE,OAAO,MAAM,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;IACrD,MAAM,WAAW,GAAG,OAAO,CAAC,KAAK,CAC/B,+CAA+C,CAChD,CAAC;IACF,OAAO,WAAW,CAAC,CAAC,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;AACzD,CAAC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { type ReplicaOperation } from "./envelope.js";
|
|
2
|
+
export type CompletionOperation = Extract<ReplicaOperation, {
|
|
3
|
+
kind: "stream-complete";
|
|
4
|
+
}>;
|
|
5
|
+
/** `sha256:<hex>` of an operation's canonical JSON, as completion dependencies pin it. */
|
|
6
|
+
export declare function operationDigest(operation: ReplicaOperation): string;
|
|
7
|
+
/**
|
|
8
|
+
* Dependency coverage only: every dependency has exactly one retained variant
|
|
9
|
+
* and it has the pinned digest. `variantDigests` returns the distinct digests
|
|
10
|
+
* of every retained variant of an operation id, across all connections. The
|
|
11
|
+
* rest of availability is `completionAvailability` in `visibility-engine.ts`.
|
|
12
|
+
*/
|
|
13
|
+
export declare function completionDependenciesCovered(completion: CompletionOperation, variantDigests: (writerId: string, operationId: string) => readonly string[]): boolean;
|
|
14
|
+
/** Dependency coverage over a list of imported operations. */
|
|
15
|
+
export declare function hasCompletionDependencies(completion: CompletionOperation, imported: readonly ReplicaOperation[]): Promise<boolean>;
|
|
16
|
+
/** Select current receipt state for ONE validated stream/generation; this is not an availability check. */
|
|
17
|
+
export declare function selectStreamCompletion(operations: readonly Extract<ReplicaOperation, {
|
|
18
|
+
kind: "stream-complete" | "stream-invalidate";
|
|
19
|
+
}>[]): CompletionOperation | null;
|
|
20
|
+
//# sourceMappingURL=completion.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"completion.d.ts","sourceRoot":"","sources":["../../src/replication/completion.ts"],"names":[],"mappings":"AAEA,OAAO,EAIL,KAAK,gBAAgB,EACtB,MAAM,eAAe,CAAC;AAIvB,MAAM,MAAM,mBAAmB,GAAG,OAAO,CACvC,gBAAgB,EAChB;IAAE,IAAI,EAAE,iBAAiB,CAAA;CAAE,CAC5B,CAAC;AAEF,0FAA0F;AAC1F,wBAAgB,eAAe,CAAC,SAAS,EAAE,gBAAgB,GAAG,MAAM,CAEnE;AAED;;;;;GAKG;AACH,wBAAgB,6BAA6B,CAC3C,UAAU,EAAE,mBAAmB,EAC/B,cAAc,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,KAAK,SAAS,MAAM,EAAE,GAC3E,OAAO,CAMT;AAED,8DAA8D;AAC9D,wBAAsB,yBAAyB,CAC7C,UAAU,EAAE,mBAAmB,EAC/B,QAAQ,EAAE,SAAS,gBAAgB,EAAE,GACpC,OAAO,CAAC,OAAO,CAAC,CAelB;AAED,2GAA2G;AAC3G,wBAAgB,sBAAsB,CACpC,UAAU,EAAE,SAAS,OAAO,CAC1B,gBAAgB,EAChB;IAAE,IAAI,EAAE,iBAAiB,GAAG,mBAAmB,CAAA;CAAE,CAClD,EAAE,GACF,mBAAmB,GAAG,IAAI,CA6B5B"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { sha256 } from "@noble/hashes/sha256";
|
|
2
|
+
import { bytesToHex } from "@noble/hashes/utils";
|
|
3
|
+
import { canonicalJson, declarationShape, replicaOperationSchema, } from "./envelope.js";
|
|
4
|
+
import { compareRevisions } from "./revision.js";
|
|
5
|
+
/** `sha256:<hex>` of an operation's canonical JSON, as completion dependencies pin it. */
|
|
6
|
+
export function operationDigest(operation) {
|
|
7
|
+
return `sha256:${bytesToHex(sha256(new TextEncoder().encode(canonicalJson(operation))))}`;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Dependency coverage only: every dependency has exactly one retained variant
|
|
11
|
+
* and it has the pinned digest. `variantDigests` returns the distinct digests
|
|
12
|
+
* of every retained variant of an operation id, across all connections. The
|
|
13
|
+
* rest of availability is `completionAvailability` in `visibility-engine.ts`.
|
|
14
|
+
*/
|
|
15
|
+
export function completionDependenciesCovered(completion, variantDigests) {
|
|
16
|
+
if (completion.dependencies.length === 0)
|
|
17
|
+
return false;
|
|
18
|
+
return completion.dependencies.every((dependency) => {
|
|
19
|
+
const digests = variantDigests(dependency.writerId, dependency.operationId);
|
|
20
|
+
return digests.length === 1 && digests[0] === dependency.digest;
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
/** Dependency coverage over a list of imported operations. */
|
|
24
|
+
export async function hasCompletionDependencies(completion, imported) {
|
|
25
|
+
replicaOperationSchema.parse(completion);
|
|
26
|
+
if (completion.dependencies.length === 0)
|
|
27
|
+
return false;
|
|
28
|
+
const byId = new Map();
|
|
29
|
+
for (const operation of imported) {
|
|
30
|
+
replicaOperationSchema.parse(operation);
|
|
31
|
+
const key = canonicalJson([
|
|
32
|
+
operation.revision.writerId,
|
|
33
|
+
operation.revision.operationId,
|
|
34
|
+
]);
|
|
35
|
+
byId.set(key, (byId.get(key) ?? new Set()).add(operationDigest(operation)));
|
|
36
|
+
}
|
|
37
|
+
return completionDependenciesCovered(completion, (writerId, operationId) => [
|
|
38
|
+
...(byId.get(canonicalJson([writerId, operationId])) ?? []),
|
|
39
|
+
]);
|
|
40
|
+
}
|
|
41
|
+
/** Select current receipt state for ONE validated stream/generation; this is not an availability check. */
|
|
42
|
+
export function selectStreamCompletion(operations) {
|
|
43
|
+
let identity;
|
|
44
|
+
let winner;
|
|
45
|
+
const byId = new Map();
|
|
46
|
+
for (const operation of operations) {
|
|
47
|
+
replicaOperationSchema.parse(operation);
|
|
48
|
+
const key = canonicalJson([
|
|
49
|
+
operation.owner,
|
|
50
|
+
operation.sourceId,
|
|
51
|
+
operation.connectionId,
|
|
52
|
+
operation.methodId,
|
|
53
|
+
operation.generation,
|
|
54
|
+
// Versions of one declared stream are one stream (a rebinding).
|
|
55
|
+
declarationShape(operation.declaration),
|
|
56
|
+
]);
|
|
57
|
+
if (identity !== undefined && identity !== key)
|
|
58
|
+
throw new Error("mixed completion identities");
|
|
59
|
+
identity = key;
|
|
60
|
+
const id = canonicalJson([
|
|
61
|
+
operation.revision.writerId,
|
|
62
|
+
operation.revision.operationId,
|
|
63
|
+
]);
|
|
64
|
+
const encoded = canonicalJson(operation);
|
|
65
|
+
if (byId.has(id) && byId.get(id) !== encoded)
|
|
66
|
+
return null;
|
|
67
|
+
byId.set(id, encoded);
|
|
68
|
+
if (!winner || compareRevisions(operation.revision, winner.revision) > 0)
|
|
69
|
+
winner = operation;
|
|
70
|
+
}
|
|
71
|
+
return winner?.kind === "stream-complete" ? winner : null;
|
|
72
|
+
}
|
|
73
|
+
//# sourceMappingURL=completion.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"completion.js","sourceRoot":"","sources":["../../src/replication/completion.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The durable coordinator rotation over SQL, shared by every host. Node runs
|
|
3
|
+
* these effects synchronously inside the receiver's write transaction;
|
|
4
|
+
* PS-Lite runs them inside its native transaction. Both adapters pass
|
|
5
|
+
* `replicaCoordinatorStateContract`.
|
|
6
|
+
*
|
|
7
|
+
* Ownership is the token in `pdpp_transport_lock`. The adapter does NOT
|
|
8
|
+
* acquire or replace it: Node's PID lock and Lite's boot owner write it.
|
|
9
|
+
*/
|
|
10
|
+
import { type ReplicaEffect, type ReplicaEffectContext } from "./effects.js";
|
|
11
|
+
import { type ReplicaCoordinatorState, type ReplicaRotation } from "./coordinator.js";
|
|
12
|
+
/** Additive, idempotent forward open; no existing row/table is rewritten. */
|
|
13
|
+
export declare const REPLICA_COORDINATOR_SCHEMA = "\nCREATE TABLE IF NOT EXISTS pdpp_transport_coordinator (\n id INTEGER PRIMARY KEY CHECK(id=1),\n version INTEGER NOT NULL DEFAULT 0 CHECK(version>=0),\n rotation_json TEXT NOT NULL\n);\nINSERT OR IGNORE INTO pdpp_transport_coordinator(id,version,rotation_json)\n VALUES(1,0,'{\"writers\":{}}');\n";
|
|
14
|
+
/** The exclusive-owner table both hosts write. Node adds a PID; Lite writes 0. */
|
|
15
|
+
export declare const REPLICA_LOCK_SCHEMA = "CREATE TABLE IF NOT EXISTS pdpp_transport_lock (id INTEGER PRIMARY KEY CHECK(id=1), pid INTEGER NOT NULL, token TEXT NOT NULL);";
|
|
16
|
+
/** Who this adapter acts for; read at every check, never cached. */
|
|
17
|
+
export type ReplicaCoordinatorOwner = () => string | undefined;
|
|
18
|
+
/** The current rotation, only for the owner that holds the lock. */
|
|
19
|
+
export declare function loadReplicaCoordinatorState(owner: ReplicaCoordinatorOwner): ReplicaEffect<ReplicaCoordinatorState>;
|
|
20
|
+
/**
|
|
21
|
+
* Check owner and revision, run `checkpoint`, check the owner again, then
|
|
22
|
+
* write the rotation at the next revision. The caller runs this whole effect
|
|
23
|
+
* in ONE write transaction, so any throw rolls back checkpoint and rotation.
|
|
24
|
+
*/
|
|
25
|
+
export declare function commitReplicaCoordinatorState<T, C extends ReplicaEffectContext = ReplicaEffectContext>(owner: ReplicaCoordinatorOwner, expected: ReplicaCoordinatorState, next: ReplicaRotation, checkpoint: (context: C) => T | Promise<T>): ReplicaEffect<{
|
|
26
|
+
state: ReplicaCoordinatorState;
|
|
27
|
+
result: T;
|
|
28
|
+
}, C>;
|
|
29
|
+
//# sourceMappingURL=coordinator-state.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"coordinator-state.d.ts","sourceRoot":"","sources":["../../src/replication/coordinator-state.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,EAGL,KAAK,aAAa,EAClB,KAAK,oBAAoB,EAC1B,MAAM,cAAc,CAAC;AACtB,OAAO,EAEL,KAAK,uBAAuB,EAC5B,KAAK,eAAe,EACrB,MAAM,kBAAkB,CAAC;AAE1B,6EAA6E;AAC7E,eAAO,MAAM,0BAA0B,kTAQtC,CAAC;AAEF,kFAAkF;AAClF,eAAO,MAAM,mBAAmB,oIACmG,CAAC;AA4BpI,oEAAoE;AACpE,MAAM,MAAM,uBAAuB,GAAG,MAAM,MAAM,GAAG,SAAS,CAAC;AAY/D,oEAAoE;AACpE,wBAAiB,2BAA2B,CAC1C,KAAK,EAAE,uBAAuB,GAC7B,aAAa,CAAC,uBAAuB,CAAC,CAUxC;AAED;;;;GAIG;AACH,wBAAiB,6BAA6B,CAC5C,CAAC,EACD,CAAC,SAAS,oBAAoB,GAAG,oBAAoB,EAErD,KAAK,EAAE,uBAAuB,EAC9B,QAAQ,EAAE,uBAAuB,EACjC,IAAI,EAAE,eAAe,EACrB,UAAU,EAAE,CAAC,OAAO,EAAE,CAAC,KAAK,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,GACzC,aAAa,CAAC;IAAE,KAAK,EAAE,uBAAuB,CAAC;IAAC,MAAM,EAAE,CAAC,CAAA;CAAE,EAAE,CAAC,CAAC,CA0BjE"}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The durable coordinator rotation over SQL, shared by every host. Node runs
|
|
3
|
+
* these effects synchronously inside the receiver's write transaction;
|
|
4
|
+
* PS-Lite runs them inside its native transaction. Both adapters pass
|
|
5
|
+
* `replicaCoordinatorStateContract`.
|
|
6
|
+
*
|
|
7
|
+
* Ownership is the token in `pdpp_transport_lock`. The adapter does NOT
|
|
8
|
+
* acquire or replace it: Node's PID lock and Lite's boot owner write it.
|
|
9
|
+
*/
|
|
10
|
+
import { perform, sql, } from "./effects.js";
|
|
11
|
+
import { ReplicaCoordinatorConflict, } from "./coordinator.js";
|
|
12
|
+
/** Additive, idempotent forward open; no existing row/table is rewritten. */
|
|
13
|
+
export const REPLICA_COORDINATOR_SCHEMA = `
|
|
14
|
+
CREATE TABLE IF NOT EXISTS pdpp_transport_coordinator (
|
|
15
|
+
id INTEGER PRIMARY KEY CHECK(id=1),
|
|
16
|
+
version INTEGER NOT NULL DEFAULT 0 CHECK(version>=0),
|
|
17
|
+
rotation_json TEXT NOT NULL
|
|
18
|
+
);
|
|
19
|
+
INSERT OR IGNORE INTO pdpp_transport_coordinator(id,version,rotation_json)
|
|
20
|
+
VALUES(1,0,'{"writers":{}}');
|
|
21
|
+
`;
|
|
22
|
+
/** The exclusive-owner table both hosts write. Node adds a PID; Lite writes 0. */
|
|
23
|
+
export const REPLICA_LOCK_SCHEMA = "CREATE TABLE IF NOT EXISTS pdpp_transport_lock (id INTEGER PRIMARY KEY CHECK(id=1), pid INTEGER NOT NULL, token TEXT NOT NULL);";
|
|
24
|
+
const PHASES = new Set([
|
|
25
|
+
"retain",
|
|
26
|
+
"prepare",
|
|
27
|
+
"publish",
|
|
28
|
+
"discover",
|
|
29
|
+
"download",
|
|
30
|
+
"fetch",
|
|
31
|
+
"revalidate",
|
|
32
|
+
"import",
|
|
33
|
+
"reconcile",
|
|
34
|
+
]);
|
|
35
|
+
function validate(rotation) {
|
|
36
|
+
if (!rotation ||
|
|
37
|
+
!rotation.writers ||
|
|
38
|
+
typeof rotation.writers !== "object" ||
|
|
39
|
+
Array.isArray(rotation.writers) ||
|
|
40
|
+
(rotation.nextPhase !== undefined && !PHASES.has(rotation.nextPhase)) ||
|
|
41
|
+
Object.entries(rotation.writers).some(([phase, writer]) => !PHASES.has(phase) || typeof writer !== "string"))
|
|
42
|
+
throw new Error("invalid replica coordinator rotation");
|
|
43
|
+
}
|
|
44
|
+
function* currentOwner(owner) {
|
|
45
|
+
const current = owner();
|
|
46
|
+
const held = yield* sql.get("SELECT token FROM pdpp_transport_lock WHERE id=1");
|
|
47
|
+
if (!current || held?.token !== current)
|
|
48
|
+
throw new ReplicaCoordinatorConflict();
|
|
49
|
+
return current;
|
|
50
|
+
}
|
|
51
|
+
/** The current rotation, only for the owner that holds the lock. */
|
|
52
|
+
export function* loadReplicaCoordinatorState(owner) {
|
|
53
|
+
const held = yield* currentOwner(owner);
|
|
54
|
+
const row = yield* sql.get("SELECT version,rotation_json FROM pdpp_transport_coordinator WHERE id=1");
|
|
55
|
+
if (!row || !Number.isSafeInteger(row.version) || row.version < 0)
|
|
56
|
+
throw new Error("invalid replica coordinator revision");
|
|
57
|
+
const rotation = JSON.parse(row.rotation_json);
|
|
58
|
+
validate(rotation);
|
|
59
|
+
return { ...rotation, owner: held, version: row.version };
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Check owner and revision, run `checkpoint`, check the owner again, then
|
|
63
|
+
* write the rotation at the next revision. The caller runs this whole effect
|
|
64
|
+
* in ONE write transaction, so any throw rolls back checkpoint and rotation.
|
|
65
|
+
*/
|
|
66
|
+
export function* commitReplicaCoordinatorState(owner, expected, next, checkpoint) {
|
|
67
|
+
const current = yield* loadReplicaCoordinatorState(owner);
|
|
68
|
+
if (current.owner !== expected.owner || current.version !== expected.version)
|
|
69
|
+
throw new ReplicaCoordinatorConflict();
|
|
70
|
+
if (expected.version === Number.MAX_SAFE_INTEGER)
|
|
71
|
+
throw new RangeError("replica coordinator revision exhausted");
|
|
72
|
+
validate(next);
|
|
73
|
+
const result = yield* perform(checkpoint);
|
|
74
|
+
// Even a nested callback cannot replace the owner and leave a receipt.
|
|
75
|
+
if ((yield* currentOwner(owner)) !== expected.owner)
|
|
76
|
+
throw new ReplicaCoordinatorConflict();
|
|
77
|
+
const { changes } = yield* sql.run(`UPDATE pdpp_transport_coordinator
|
|
78
|
+
SET version=version+1,rotation_json=? WHERE id=1 AND version=?`, JSON.stringify(next), expected.version);
|
|
79
|
+
if (changes !== 1)
|
|
80
|
+
throw new ReplicaCoordinatorConflict();
|
|
81
|
+
const state = yield* loadReplicaCoordinatorState(owner);
|
|
82
|
+
if (state.version !== expected.version + 1 ||
|
|
83
|
+
state.nextPhase !== next.nextPhase ||
|
|
84
|
+
JSON.stringify(state.writers) !== JSON.stringify(next.writers))
|
|
85
|
+
throw new ReplicaCoordinatorConflict();
|
|
86
|
+
return { state, result };
|
|
87
|
+
}
|
|
88
|
+
//# sourceMappingURL=coordinator-state.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"coordinator-state.js","sourceRoot":"","sources":["../../src/replication/coordinator-state.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { type AsyncReplicaCoordinatorStatePort, type ReplicaCoordinatorStatePort } from "./coordinator.js";
|
|
2
|
+
type MaybePromise<T> = T | Promise<T>;
|
|
3
|
+
/**
|
|
4
|
+
* One harness per adapter: Node's synchronous SQLite port and PS-Lite's
|
|
5
|
+
* asynchronous native-transaction port run this same contract. `context` is
|
|
6
|
+
* the port's open transaction, passed to calls made inside a checkpoint; a
|
|
7
|
+
* synchronous port passes `undefined`.
|
|
8
|
+
*/
|
|
9
|
+
export interface ReplicaCoordinatorContractHarness<C = unknown> {
|
|
10
|
+
port(owner: string): ReplicaCoordinatorStatePort | AsyncReplicaCoordinatorStatePort<C>;
|
|
11
|
+
acquire(owner: string): MaybePromise<void>;
|
|
12
|
+
/** Simulate confirmed loss of the old exclusive owner, not lease expiry. */
|
|
13
|
+
recover(owner: string, context?: C): MaybePromise<void>;
|
|
14
|
+
checkpoint(value: string, context?: C): MaybePromise<void>;
|
|
15
|
+
receipts(): MaybePromise<string[]>;
|
|
16
|
+
failCursorWrites(fail: boolean): MaybePromise<void>;
|
|
17
|
+
close(): MaybePromise<void>;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* The caller injects its test runner's `test` and `expect` so this module stays
|
|
21
|
+
* free of Node built-ins and test-runner imports (core must stay browser-safe).
|
|
22
|
+
*/
|
|
23
|
+
export interface ReplicaCoordinatorContractRunner {
|
|
24
|
+
test(name: string, run: () => Promise<void>): void;
|
|
25
|
+
expect(actual: unknown): {
|
|
26
|
+
toBe(expected: unknown): void;
|
|
27
|
+
toEqual(expected: unknown): void;
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
export declare function replicaCoordinatorStateContract<C>({ test, expect }: ReplicaCoordinatorContractRunner, open: () => MaybePromise<ReplicaCoordinatorContractHarness<C>>): void;
|
|
31
|
+
export {};
|
|
32
|
+
//# sourceMappingURL=coordinator-state.test-utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"coordinator-state.test-utils.d.ts","sourceRoot":"","sources":["../../src/replication/coordinator-state.test-utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,gCAAgC,EAErC,KAAK,2BAA2B,EAEjC,MAAM,kBAAkB,CAAC;AAE1B,KAAK,YAAY,CAAC,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;AAMtC;;;;;GAKG;AACH,MAAM,WAAW,iCAAiC,CAAC,CAAC,GAAG,OAAO;IAC5D,IAAI,CACF,KAAK,EAAE,MAAM,GACZ,2BAA2B,GAAG,gCAAgC,CAAC,CAAC,CAAC,CAAC;IACrE,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC;IAC3C,4EAA4E;IAC5E,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC;IACxD,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC;IAC3D,QAAQ,IAAI,YAAY,CAAC,MAAM,EAAE,CAAC,CAAC;IACnC,gBAAgB,CAAC,IAAI,EAAE,OAAO,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC;IACpD,KAAK,IAAI,YAAY,CAAC,IAAI,CAAC,CAAC;CAC7B;AAED;;;GAGG;AACH,MAAM,WAAW,gCAAgC;IAC/C,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IACnD,MAAM,CAAC,MAAM,EAAE,OAAO,GAAG;QACvB,IAAI,CAAC,QAAQ,EAAE,OAAO,GAAG,IAAI,CAAC;QAC9B,OAAO,CAAC,QAAQ,EAAE,OAAO,GAAG,IAAI,CAAC;KAClC,CAAC;CACH;AAED,wBAAgB,+BAA+B,CAAC,CAAC,EAC/C,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,gCAAgC,EAClD,IAAI,EAAE,MAAM,YAAY,CAAC,iCAAiC,CAAC,CAAC,CAAC,CAAC,GAC7D,IAAI,CAoKN"}
|