@mdbase-dev/sdk 0.0.0-stage → 0.1.0-beta.153
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/LICENSE +21 -0
- package/README.md +844 -2
- package/dist/account-key.d.ts +176 -0
- package/dist/account-key.js +409 -0
- package/dist/account-key.js.map +1 -0
- package/dist/account.d.ts +11 -0
- package/dist/account.js +9 -0
- package/dist/account.js.map +1 -0
- package/dist/app-grant/client.d.ts +209 -0
- package/dist/app-grant/client.js +930 -0
- package/dist/app-grant/client.js.map +1 -0
- package/dist/app-grant/grant-store.d.ts +77 -0
- package/dist/app-grant/grant-store.js +201 -0
- package/dist/app-grant/grant-store.js.map +1 -0
- package/dist/app-grant/index.d.ts +11 -0
- package/dist/app-grant/index.js +10 -0
- package/dist/app-grant/index.js.map +1 -0
- package/dist/app-grant/native-completion.d.ts +22 -0
- package/dist/app-grant/native-completion.js +63 -0
- package/dist/app-grant/native-completion.js.map +1 -0
- package/dist/app-grant/people.d.ts +10 -0
- package/dist/app-grant/people.js +84 -0
- package/dist/app-grant/people.js.map +1 -0
- package/dist/app-grant/setup-attempt.d.ts +12 -0
- package/dist/app-grant/setup-attempt.js +62 -0
- package/dist/app-grant/setup-attempt.js.map +1 -0
- package/dist/app-grant/setup-producer.d.ts +31 -0
- package/dist/app-grant/setup-producer.js +65 -0
- package/dist/app-grant/setup-producer.js.map +1 -0
- package/dist/app-grant/setup.d.ts +11 -0
- package/dist/app-grant/setup.js +222 -0
- package/dist/app-grant/setup.js.map +1 -0
- package/dist/app-host/app-environment.d.ts +27 -0
- package/dist/app-host/app-environment.js +48 -0
- package/dist/app-host/app-environment.js.map +1 -0
- package/dist/app-host/app-origin.d.ts +7 -0
- package/dist/app-host/app-origin.js +48 -0
- package/dist/app-host/app-origin.js.map +1 -0
- package/dist/app-host/bases-pages.d.ts +29 -0
- package/dist/app-host/bases-pages.js +42 -0
- package/dist/app-host/bases-pages.js.map +1 -0
- package/dist/app-host/bases-wire.d.ts +159 -0
- package/dist/app-host/bases-wire.js +376 -0
- package/dist/app-host/bases-wire.js.map +1 -0
- package/dist/app-host/browser-direct-access.d.ts +18 -0
- package/dist/app-host/browser-direct-access.js +17 -0
- package/dist/app-host/browser-direct-access.js.map +1 -0
- package/dist/app-host/cloud-copy-bootstrap.d.ts +127 -0
- package/dist/app-host/cloud-copy-bootstrap.js +606 -0
- package/dist/app-host/cloud-copy-bootstrap.js.map +1 -0
- package/dist/app-host/cloud-copy-host.d.ts +248 -0
- package/dist/app-host/cloud-copy-host.js +1028 -0
- package/dist/app-host/cloud-copy-host.js.map +1 -0
- package/dist/app-host/cloud-copy-persistence-idb.d.ts +33 -0
- package/dist/app-host/cloud-copy-persistence-idb.js +255 -0
- package/dist/app-host/cloud-copy-persistence-idb.js.map +1 -0
- package/dist/app-host/cloud-copy-persistence.d.ts +57 -0
- package/dist/app-host/cloud-copy-persistence.js +341 -0
- package/dist/app-host/cloud-copy-persistence.js.map +1 -0
- package/dist/app-host/collection-control.d.ts +111 -0
- package/dist/app-host/collection-control.js +604 -0
- package/dist/app-host/collection-control.js.map +1 -0
- package/dist/app-host/collection-delete.d.ts +7 -0
- package/dist/app-host/collection-delete.js +54 -0
- package/dist/app-host/collection-delete.js.map +1 -0
- package/dist/app-host/collection-display-name.d.ts +2 -0
- package/dist/app-host/collection-display-name.js +22 -0
- package/dist/app-host/collection-display-name.js.map +1 -0
- package/dist/app-host/collection-join-ledger.d.ts +52 -0
- package/dist/app-host/collection-join-ledger.js +208 -0
- package/dist/app-host/collection-join-ledger.js.map +1 -0
- package/dist/app-host/collection-people.d.ts +32 -0
- package/dist/app-host/collection-people.js +119 -0
- package/dist/app-host/collection-people.js.map +1 -0
- package/dist/app-host/collection-readiness.d.ts +119 -0
- package/dist/app-host/collection-readiness.js +649 -0
- package/dist/app-host/collection-readiness.js.map +1 -0
- package/dist/app-host/collection-setup-journal.d.ts +59 -0
- package/dist/app-host/collection-setup-journal.js +813 -0
- package/dist/app-host/collection-setup-journal.js.map +1 -0
- package/dist/app-host/collection-setup-types.d.ts +155 -0
- package/dist/app-host/collection-setup-types.js +10 -0
- package/dist/app-host/collection-setup-types.js.map +1 -0
- package/dist/app-host/cp-authority.d.ts +65 -0
- package/dist/app-host/cp-authority.js +243 -0
- package/dist/app-host/cp-authority.js.map +1 -0
- package/dist/app-host/cp-people.d.ts +23 -0
- package/dist/app-host/cp-people.js +145 -0
- package/dist/app-host/cp-people.js.map +1 -0
- package/dist/app-host/cp-problem.d.ts +34 -0
- package/dist/app-host/cp-problem.js +114 -0
- package/dist/app-host/cp-problem.js.map +1 -0
- package/dist/app-host/device-key-custody.d.ts +51 -0
- package/dist/app-host/device-key-custody.js +128 -0
- package/dist/app-host/device-key-custody.js.map +1 -0
- package/dist/app-host/device-key-idb.d.ts +31 -0
- package/dist/app-host/device-key-idb.js +176 -0
- package/dist/app-host/device-key-idb.js.map +1 -0
- package/dist/app-host/device-key-provider.d.ts +33 -0
- package/dist/app-host/device-key-provider.js +181 -0
- package/dist/app-host/device-key-provider.js.map +1 -0
- package/dist/app-host/device-registration.d.ts +51 -0
- package/dist/app-host/device-registration.js +180 -0
- package/dist/app-host/device-registration.js.map +1 -0
- package/dist/app-host/foreground-wake.d.ts +43 -0
- package/dist/app-host/foreground-wake.js +113 -0
- package/dist/app-host/foreground-wake.js.map +1 -0
- package/dist/app-host/handover.d.ts +27 -0
- package/dist/app-host/handover.js +82 -0
- package/dist/app-host/handover.js.map +1 -0
- package/dist/app-host/http-log.d.ts +96 -0
- package/dist/app-host/http-log.js +608 -0
- package/dist/app-host/http-log.js.map +1 -0
- package/dist/app-host/index.d.ts +63 -0
- package/dist/app-host/index.js +33 -0
- package/dist/app-host/index.js.map +1 -0
- package/dist/app-host/initial-sync.d.ts +22 -0
- package/dist/app-host/initial-sync.js +41 -0
- package/dist/app-host/initial-sync.js.map +1 -0
- package/dist/app-host/installation-sign-in.d.ts +319 -0
- package/dist/app-host/installation-sign-in.js +1231 -0
- package/dist/app-host/installation-sign-in.js.map +1 -0
- package/dist/app-host/installation-store.d.ts +56 -0
- package/dist/app-host/installation-store.js +285 -0
- package/dist/app-host/installation-store.js.map +1 -0
- package/dist/app-host/ledger-rows.d.ts +3 -0
- package/dist/app-host/ledger-rows.js +23 -0
- package/dist/app-host/ledger-rows.js.map +1 -0
- package/dist/app-host/local-facade.d.ts +24 -0
- package/dist/app-host/local-facade.js +361 -0
- package/dist/app-host/local-facade.js.map +1 -0
- package/dist/app-host/log-pump.d.ts +124 -0
- package/dist/app-host/log-pump.js +326 -0
- package/dist/app-host/log-pump.js.map +1 -0
- package/dist/app-host/log-push.d.ts +90 -0
- package/dist/app-host/log-push.js +376 -0
- package/dist/app-host/log-push.js.map +1 -0
- package/dist/app-host/log-token-cache.d.ts +28 -0
- package/dist/app-host/log-token-cache.js +104 -0
- package/dist/app-host/log-token-cache.js.map +1 -0
- package/dist/app-host/native-sign-in.d.ts +27 -0
- package/dist/app-host/native-sign-in.js +12 -0
- package/dist/app-host/native-sign-in.js.map +1 -0
- package/dist/app-host/noise-custody.d.ts +62 -0
- package/dist/app-host/noise-custody.js +238 -0
- package/dist/app-host/noise-custody.js.map +1 -0
- package/dist/app-host/noise-idb.d.ts +33 -0
- package/dist/app-host/noise-idb.js +199 -0
- package/dist/app-host/noise-idb.js.map +1 -0
- package/dist/app-host/open-wait.d.ts +3 -0
- package/dist/app-host/open-wait.js +17 -0
- package/dist/app-host/open-wait.js.map +1 -0
- package/dist/app-host/opening-progress.d.ts +3 -0
- package/dist/app-host/opening-progress.js +5 -0
- package/dist/app-host/opening-progress.js.map +1 -0
- package/dist/app-host/owner.d.ts +103 -0
- package/dist/app-host/owner.js +262 -0
- package/dist/app-host/owner.js.map +1 -0
- package/dist/app-host/private-bootstrap.d.ts +78 -0
- package/dist/app-host/private-bootstrap.js +230 -0
- package/dist/app-host/private-bootstrap.js.map +1 -0
- package/dist/app-host/private-persistence-idb.d.ts +32 -0
- package/dist/app-host/private-persistence-idb.js +188 -0
- package/dist/app-host/private-persistence-idb.js.map +1 -0
- package/dist/app-host/private-persistence.d.ts +55 -0
- package/dist/app-host/private-persistence.js +257 -0
- package/dist/app-host/private-persistence.js.map +1 -0
- package/dist/app-host/scoped-sign-in-session.d.ts +49 -0
- package/dist/app-host/scoped-sign-in-session.js +243 -0
- package/dist/app-host/scoped-sign-in-session.js.map +1 -0
- package/dist/app-host/setup-engine.d.ts +14 -0
- package/dist/app-host/setup-engine.js +73 -0
- package/dist/app-host/setup-engine.js.map +1 -0
- package/dist/app-host/sign-out-erase.d.ts +37 -0
- package/dist/app-host/sign-out-erase.js +139 -0
- package/dist/app-host/sign-out-erase.js.map +1 -0
- package/dist/app-host/status.d.ts +62 -0
- package/dist/app-host/status.js +43 -0
- package/dist/app-host/status.js.map +1 -0
- package/dist/app-host/storage-probe.d.ts +46 -0
- package/dist/app-host/storage-probe.js +63 -0
- package/dist/app-host/storage-probe.js.map +1 -0
- package/dist/app-host/timer-port.d.ts +29 -0
- package/dist/app-host/timer-port.js +483 -0
- package/dist/app-host/timer-port.js.map +1 -0
- package/dist/app-host/wasm-runtime.d.ts +302 -0
- package/dist/app-host/wasm-runtime.js +1050 -0
- package/dist/app-host/wasm-runtime.js.map +1 -0
- package/dist/app-host/web-collection.d.ts +78 -0
- package/dist/app-host/web-collection.js +671 -0
- package/dist/app-host/web-collection.js.map +1 -0
- package/dist/app-host/web-sign-in-problem.d.ts +17 -0
- package/dist/app-host/web-sign-in-problem.js +59 -0
- package/dist/app-host/web-sign-in-problem.js.map +1 -0
- package/dist/app-host/web-sign-in-session.d.ts +367 -0
- package/dist/app-host/web-sign-in-session.js +1258 -0
- package/dist/app-host/web-sign-in-session.js.map +1 -0
- package/dist/app-storage/app-binary-index.d.ts +33 -0
- package/dist/app-storage/app-binary-index.js +274 -0
- package/dist/app-storage/app-binary-index.js.map +1 -0
- package/dist/app-storage/app-sqlite-index.d.ts +40 -0
- package/dist/app-storage/app-sqlite-index.js +102 -0
- package/dist/app-storage/app-sqlite-index.js.map +1 -0
- package/dist/app-storage/collection-sql.d.ts +39 -0
- package/dist/app-storage/collection-sql.js +93 -0
- package/dist/app-storage/collection-sql.js.map +1 -0
- package/dist/app-storage/index.d.ts +10 -0
- package/dist/app-storage/index.js +7 -0
- package/dist/app-storage/index.js.map +1 -0
- package/dist/app-storage/sqlite-index.d.ts +95 -0
- package/dist/app-storage/sqlite-index.js +280 -0
- package/dist/app-storage/sqlite-index.js.map +1 -0
- package/dist/application/asset-loader.d.ts +22 -0
- package/dist/application/asset-loader.js +130 -0
- package/dist/application/asset-loader.js.map +1 -0
- package/dist/application/asset-pins.json +24 -0
- package/dist/application/browser-capabilities.d.ts +48 -0
- package/dist/application/browser-capabilities.js +87 -0
- package/dist/application/browser-capabilities.js.map +1 -0
- package/dist/application/browser-selection.d.ts +58 -0
- package/dist/application/browser-selection.js +160 -0
- package/dist/application/browser-selection.js.map +1 -0
- package/dist/application/capabilities.d.ts +20 -0
- package/dist/application/capabilities.js +22 -0
- package/dist/application/capabilities.js.map +1 -0
- package/dist/application/collection-selection.d.ts +147 -0
- package/dist/application/collection-selection.js +351 -0
- package/dist/application/collection-selection.js.map +1 -0
- package/dist/application/configuration.d.ts +52 -0
- package/dist/application/configuration.js +568 -0
- package/dist/application/configuration.js.map +1 -0
- package/dist/application/declaration.d.ts +51 -0
- package/dist/application/declaration.js +100 -0
- package/dist/application/declaration.js.map +1 -0
- package/dist/application/entry.d.ts +343 -0
- package/dist/application/entry.js +945 -0
- package/dist/application/entry.js.map +1 -0
- package/dist/application/file-access.d.ts +55 -0
- package/dist/application/file-access.js +93 -0
- package/dist/application/file-access.js.map +1 -0
- package/dist/application/file-readiness.d.ts +16 -0
- package/dist/application/file-readiness.js +53 -0
- package/dist/application/file-readiness.js.map +1 -0
- package/dist/application/forgotten.d.ts +15 -0
- package/dist/application/forgotten.js +70 -0
- package/dist/application/forgotten.js.map +1 -0
- package/dist/application/front-door.d.ts +68 -0
- package/dist/application/front-door.js +66 -0
- package/dist/application/front-door.js.map +1 -0
- package/dist/application/local-build.d.ts +5 -0
- package/dist/application/local-build.js +22 -0
- package/dist/application/local-build.js.map +1 -0
- package/dist/application/manifest.d.ts +16 -0
- package/dist/application/manifest.js +31 -0
- package/dist/application/manifest.js.map +1 -0
- package/dist/application/native-assets.d.ts +25 -0
- package/dist/application/native-assets.js +29 -0
- package/dist/application/native-assets.js.map +1 -0
- package/dist/application/native-authorization-rpc.d.ts +23 -0
- package/dist/application/native-authorization-rpc.js +54 -0
- package/dist/application/native-authorization-rpc.js.map +1 -0
- package/dist/application/notification-readiness.d.ts +33 -0
- package/dist/application/notification-readiness.js +222 -0
- package/dist/application/notification-readiness.js.map +1 -0
- package/dist/application/packaged-assets.d.ts +5 -0
- package/dist/application/packaged-assets.js +55 -0
- package/dist/application/packaged-assets.js.map +1 -0
- package/dist/application/packaged-trust.d.ts +5 -0
- package/dist/application/packaged-trust.js +10 -0
- package/dist/application/packaged-trust.js.map +1 -0
- package/dist/application/people-readiness.d.ts +23 -0
- package/dist/application/people-readiness.js +213 -0
- package/dist/application/people-readiness.js.map +1 -0
- package/dist/application/phase.d.ts +108 -0
- package/dist/application/phase.js +113 -0
- package/dist/application/phase.js.map +1 -0
- package/dist/application/portal-reservation.d.ts +38 -0
- package/dist/application/portal-reservation.js +183 -0
- package/dist/application/portal-reservation.js.map +1 -0
- package/dist/application/private-collections.d.ts +114 -0
- package/dist/application/private-collections.js +415 -0
- package/dist/application/private-collections.js.map +1 -0
- package/dist/application/readiness.d.ts +59 -0
- package/dist/application/readiness.js +223 -0
- package/dist/application/readiness.js.map +1 -0
- package/dist/application/registration-refusal.d.ts +16 -0
- package/dist/application/registration-refusal.js +41 -0
- package/dist/application/registration-refusal.js.map +1 -0
- package/dist/application/registration.d.ts +26 -0
- package/dist/application/registration.js +372 -0
- package/dist/application/registration.js.map +1 -0
- package/dist/application/return-url.d.ts +1 -0
- package/dist/application/return-url.js +40 -0
- package/dist/application/return-url.js.map +1 -0
- package/dist/application/runtime.d.ts +107 -0
- package/dist/application/runtime.js +379 -0
- package/dist/application/runtime.js.map +1 -0
- package/dist/application/scope.d.ts +34 -0
- package/dist/application/scope.js +164 -0
- package/dist/application/scope.js.map +1 -0
- package/dist/application/session-hint.d.ts +21 -0
- package/dist/application/session-hint.js +87 -0
- package/dist/application/session-hint.js.map +1 -0
- package/dist/application/setup.d.ts +105 -0
- package/dist/application/setup.js +137 -0
- package/dist/application/setup.js.map +1 -0
- package/dist/application/sign-out.d.ts +17 -0
- package/dist/application/sign-out.js +28 -0
- package/dist/application/sign-out.js.map +1 -0
- package/dist/application/storage-scope.d.ts +8 -0
- package/dist/application/storage-scope.js +15 -0
- package/dist/application/storage-scope.js.map +1 -0
- package/dist/application/tabs.d.ts +98 -0
- package/dist/application/tabs.js +407 -0
- package/dist/application/tabs.js.map +1 -0
- package/dist/application/timer-criteria.d.ts +5 -0
- package/dist/application/timer-criteria.js +15 -0
- package/dist/application/timer-criteria.js.map +1 -0
- package/dist/application/ui-connections.d.ts +35 -0
- package/dist/application/ui-connections.js +129 -0
- package/dist/application/ui-connections.js.map +1 -0
- package/dist/application/worker-client.d.ts +59 -0
- package/dist/application/worker-client.js +381 -0
- package/dist/application/worker-client.js.map +1 -0
- package/dist/application/worker-host.d.ts +84 -0
- package/dist/application/worker-host.js +547 -0
- package/dist/application/worker-host.js.map +1 -0
- package/dist/application/worker-protocol.d.ts +40 -0
- package/dist/application/worker-protocol.js +83 -0
- package/dist/application/worker-protocol.js.map +1 -0
- package/dist/application/worker-session.d.ts +45 -0
- package/dist/application/worker-session.js +37 -0
- package/dist/application/worker-session.js.map +1 -0
- package/dist/application/worker.d.ts +1 -0
- package/dist/application/worker.js +34835 -0
- package/dist/application/worker.js.map +1 -0
- package/dist/attachment-wire.d.ts +15 -0
- package/dist/attachment-wire.js +47 -0
- package/dist/attachment-wire.js.map +1 -0
- package/dist/browser/mdbase-sdk.js +49398 -0
- package/dist/browser/mdbase-sdk.min.js +130 -0
- package/dist/browser.d.ts +4 -0
- package/dist/browser.js +5 -0
- package/dist/browser.js.map +1 -0
- package/dist/cbor.d.ts +52 -0
- package/dist/cbor.js +480 -0
- package/dist/cbor.js.map +1 -0
- package/dist/client.d.ts +658 -0
- package/dist/client.js +1966 -0
- package/dist/client.js.map +1 -0
- package/dist/codec.d.ts +82 -0
- package/dist/codec.js +388 -0
- package/dist/codec.js.map +1 -0
- package/dist/collection-availability.d.ts +34 -0
- package/dist/collection-availability.js +63 -0
- package/dist/collection-availability.js.map +1 -0
- package/dist/contract-query.d.ts +31 -0
- package/dist/contract-query.js +13 -0
- package/dist/contract-query.js.map +1 -0
- package/dist/contract-records.d.ts +51 -0
- package/dist/contract-records.js +14 -0
- package/dist/contract-records.js.map +1 -0
- package/dist/describe-error.d.ts +61 -0
- package/dist/describe-error.js +452 -0
- package/dist/describe-error.js.map +1 -0
- package/dist/errors.d.ts +65 -0
- package/dist/errors.js +94 -0
- package/dist/errors.js.map +1 -0
- package/dist/files.d.ts +147 -0
- package/dist/files.js +537 -0
- package/dist/files.js.map +1 -0
- package/dist/holds.d.ts +74 -0
- package/dist/holds.js +118 -0
- package/dist/holds.js.map +1 -0
- package/dist/index.d.ts +36 -0
- package/dist/index.js +22 -0
- package/dist/index.js.map +1 -0
- package/dist/keys.d.ts +40 -0
- package/dist/keys.js +181 -0
- package/dist/keys.js.map +1 -0
- package/dist/live.d.ts +38 -0
- package/dist/live.js +82 -0
- package/dist/live.js.map +1 -0
- package/dist/local-build.d.ts +13 -0
- package/dist/local-build.js +47 -0
- package/dist/local-build.js.map +1 -0
- package/dist/native/provenance.json +23 -0
- package/dist/native/runtime.wasm +0 -0
- package/dist/native/sqlite3.d.ts +5 -0
- package/dist/native/sqlite3.js +17392 -0
- package/dist/native/sqlite3.js.map +1 -0
- package/dist/native/sqlite3.wasm +0 -0
- package/dist/node.d.ts +63 -0
- package/dist/node.js +255 -0
- package/dist/node.js.map +1 -0
- package/dist/notifications.d.ts +95 -0
- package/dist/notifications.js +226 -0
- package/dist/notifications.js.map +1 -0
- package/dist/offline-shell-build.d.ts +25 -0
- package/dist/offline-shell-build.js +78 -0
- package/dist/offline-shell-build.js.map +1 -0
- package/dist/offline-shell-worker.d.ts +46 -0
- package/dist/offline-shell-worker.js +115 -0
- package/dist/offline-shell-worker.js.map +1 -0
- package/dist/people.d.ts +82 -0
- package/dist/people.js +199 -0
- package/dist/people.js.map +1 -0
- package/dist/presence.d.ts +21 -0
- package/dist/presence.js +109 -0
- package/dist/presence.js.map +1 -0
- package/dist/private-account.d.ts +197 -0
- package/dist/private-account.js +510 -0
- package/dist/private-account.js.map +1 -0
- package/dist/private.d.ts +55 -0
- package/dist/private.js +69 -0
- package/dist/private.js.map +1 -0
- package/dist/read-helpers.d.ts +32 -0
- package/dist/read-helpers.js +181 -0
- package/dist/read-helpers.js.map +1 -0
- package/dist/records/session.d.ts +60 -0
- package/dist/records/session.js +534 -0
- package/dist/records/session.js.map +1 -0
- package/dist/records/types.d.ts +76 -0
- package/dist/records/types.js +2 -0
- package/dist/records/types.js.map +1 -0
- package/dist/records/witness.d.ts +19 -0
- package/dist/records/witness.js +64 -0
- package/dist/records/witness.js.map +1 -0
- package/dist/records.d.ts +23 -0
- package/dist/records.js +234 -0
- package/dist/records.js.map +1 -0
- package/dist/runtime/wasm.d.ts +69 -0
- package/dist/runtime/wasm.js +274 -0
- package/dist/runtime/wasm.js.map +1 -0
- package/dist/runtime-wire.d.ts +71 -0
- package/dist/runtime-wire.js +71 -0
- package/dist/runtime-wire.js.map +1 -0
- package/dist/saved-views.d.ts +29 -0
- package/dist/saved-views.js +171 -0
- package/dist/saved-views.js.map +1 -0
- package/dist/semver.d.ts +22 -0
- package/dist/semver.js +63 -0
- package/dist/semver.js.map +1 -0
- package/dist/session.d.ts +65 -0
- package/dist/session.js +332 -0
- package/dist/session.js.map +1 -0
- package/dist/status.d.ts +46 -0
- package/dist/status.js +59 -0
- package/dist/status.js.map +1 -0
- package/dist/strength.d.ts +12 -0
- package/dist/strength.js +194 -0
- package/dist/strength.js.map +1 -0
- package/dist/testing/application.d.ts +59 -0
- package/dist/testing/application.js +230 -0
- package/dist/testing/application.js.map +1 -0
- package/dist/testing/index.d.ts +10 -0
- package/dist/testing/index.js +6 -0
- package/dist/testing/index.js.map +1 -0
- package/dist/testing/memory-contracts.d.ts +40 -0
- package/dist/testing/memory-contracts.js +127 -0
- package/dist/testing/memory-contracts.js.map +1 -0
- package/dist/testing/memory-replica.d.ts +191 -0
- package/dist/testing/memory-replica.js +1465 -0
- package/dist/testing/memory-replica.js.map +1 -0
- package/dist/testing/noise-server.d.ts +11 -0
- package/dist/testing/noise-server.js +65 -0
- package/dist/testing/noise-server.js.map +1 -0
- package/dist/testing/test-app.d.ts +93 -0
- package/dist/testing/test-app.js +140 -0
- package/dist/testing/test-app.js.map +1 -0
- package/dist/thin-client.d.ts +67 -0
- package/dist/thin-client.js +42 -0
- package/dist/thin-client.js.map +1 -0
- package/dist/timers.d.ts +158 -0
- package/dist/timers.js +258 -0
- package/dist/timers.js.map +1 -0
- package/dist/transport/bases-rpc.d.ts +15 -0
- package/dist/transport/bases-rpc.js +61 -0
- package/dist/transport/bases-rpc.js.map +1 -0
- package/dist/transport/control-route.d.ts +34 -0
- package/dist/transport/control-route.js +84 -0
- package/dist/transport/control-route.js.map +1 -0
- package/dist/transport/inprocess.d.ts +13 -0
- package/dist/transport/inprocess.js +37 -0
- package/dist/transport/inprocess.js.map +1 -0
- package/dist/transport/local-link.d.ts +53 -0
- package/dist/transport/local-link.js +66 -0
- package/dist/transport/local-link.js.map +1 -0
- package/dist/transport/next-relay.d.ts +64 -0
- package/dist/transport/next-relay.js +222 -0
- package/dist/transport/next-relay.js.map +1 -0
- package/dist/transport/noise-session.d.ts +88 -0
- package/dist/transport/noise-session.js +264 -0
- package/dist/transport/noise-session.js.map +1 -0
- package/dist/transport/noise.d.ts +102 -0
- package/dist/transport/noise.js +307 -0
- package/dist/transport/noise.js.map +1 -0
- package/dist/transport/original-session.d.ts +6 -0
- package/dist/transport/original-session.js +6 -0
- package/dist/transport/original-session.js.map +1 -0
- package/dist/transport/port.d.ts +67 -0
- package/dist/transport/port.js +184 -0
- package/dist/transport/port.js.map +1 -0
- package/dist/transport/read-fence-policy.d.ts +3 -0
- package/dist/transport/read-fence-policy.js +11 -0
- package/dist/transport/read-fence-policy.js.map +1 -0
- package/dist/transport/relay-close.d.ts +31 -0
- package/dist/transport/relay-close.js +23 -0
- package/dist/transport/relay-close.js.map +1 -0
- package/dist/transport/relay-pipe.d.ts +15 -0
- package/dist/transport/relay-pipe.js +190 -0
- package/dist/transport/relay-pipe.js.map +1 -0
- package/dist/transport/relay.d.ts +36 -0
- package/dist/transport/relay.js +45 -0
- package/dist/transport/relay.js.map +1 -0
- package/dist/type-packs.d.ts +124 -0
- package/dist/type-packs.js +188 -0
- package/dist/type-packs.js.map +1 -0
- package/dist/validation.d.ts +30 -0
- package/dist/validation.js +125 -0
- package/dist/validation.js.map +1 -0
- package/dist/values.d.ts +37 -0
- package/dist/values.js +133 -0
- package/dist/values.js.map +1 -0
- package/dist/view-sources.d.ts +31 -0
- package/dist/view-sources.js +78 -0
- package/dist/view-sources.js.map +1 -0
- package/dist/wire.d.ts +857 -0
- package/dist/wire.js +915 -0
- package/dist/wire.js.map +1 -0
- package/dist/witness.d.ts +7 -0
- package/dist/witness.js +82 -0
- package/dist/witness.js.map +1 -0
- package/dist/write-ledger.d.ts +41 -0
- package/dist/write-ledger.js +184 -0
- package/dist/write-ledger.js.map +1 -0
- package/node_modules/@mdbase-dev/connect-protocol/dist/application-authorization.d.ts +32 -0
- package/node_modules/@mdbase-dev/connect-protocol/dist/application-authorization.js +207 -0
- package/node_modules/@mdbase-dev/connect-protocol/dist/application-contract.d.ts +23 -0
- package/node_modules/@mdbase-dev/connect-protocol/dist/application-contract.js +76 -0
- package/node_modules/@mdbase-dev/connect-protocol/dist/application-declaration.d.ts +120 -0
- package/node_modules/@mdbase-dev/connect-protocol/dist/application-declaration.js +1 -0
- package/node_modules/@mdbase-dev/connect-protocol/dist/authorization-errors.d.ts +34 -0
- package/node_modules/@mdbase-dev/connect-protocol/dist/authorization-errors.js +52 -0
- package/node_modules/@mdbase-dev/connect-protocol/dist/capabilities.d.ts +73 -0
- package/node_modules/@mdbase-dev/connect-protocol/dist/capabilities.js +92 -0
- package/node_modules/@mdbase-dev/connect-protocol/dist/change-events.generated.d.ts +48 -0
- package/node_modules/@mdbase-dev/connect-protocol/dist/change-events.generated.js +18 -0
- package/node_modules/@mdbase-dev/connect-protocol/dist/collection-setup.d.ts +93 -0
- package/node_modules/@mdbase-dev/connect-protocol/dist/collection-setup.js +7 -0
- package/node_modules/@mdbase-dev/connect-protocol/dist/compatibility.d.ts +37 -0
- package/node_modules/@mdbase-dev/connect-protocol/dist/compatibility.js +65 -0
- package/node_modules/@mdbase-dev/connect-protocol/dist/connect-problems.generated.d.ts +1533 -0
- package/node_modules/@mdbase-dev/connect-protocol/dist/connect-problems.generated.js +598 -0
- package/node_modules/@mdbase-dev/connect-protocol/dist/files.d.ts +288 -0
- package/node_modules/@mdbase-dev/connect-protocol/dist/files.js +426 -0
- package/node_modules/@mdbase-dev/connect-protocol/dist/index.d.ts +723 -0
- package/node_modules/@mdbase-dev/connect-protocol/dist/index.js +104 -0
- package/node_modules/@mdbase-dev/connect-protocol/dist/manifest-validators.generated.d.ts +13 -0
- package/node_modules/@mdbase-dev/connect-protocol/dist/manifest-validators.generated.js +7940 -0
- package/node_modules/@mdbase-dev/connect-protocol/dist/manifest.d.ts +72 -0
- package/node_modules/@mdbase-dev/connect-protocol/dist/manifest.js +643 -0
- package/node_modules/@mdbase-dev/connect-protocol/dist/mutation-fingerprint.d.ts +6 -0
- package/node_modules/@mdbase-dev/connect-protocol/dist/mutation-fingerprint.js +116 -0
- package/node_modules/@mdbase-dev/connect-protocol/dist/next-authorization.d.ts +30 -0
- package/node_modules/@mdbase-dev/connect-protocol/dist/next-authorization.js +90 -0
- package/node_modules/@mdbase-dev/connect-protocol/dist/operations.d.ts +15 -0
- package/node_modules/@mdbase-dev/connect-protocol/dist/operations.js +196 -0
- package/node_modules/@mdbase-dev/connect-protocol/dist/people.d.ts +33 -0
- package/node_modules/@mdbase-dev/connect-protocol/dist/people.js +26 -0
- package/node_modules/@mdbase-dev/connect-protocol/dist/records.d.ts +108 -0
- package/node_modules/@mdbase-dev/connect-protocol/dist/records.js +3 -0
- package/node_modules/@mdbase-dev/connect-protocol/dist/type-packs.d.ts +154 -0
- package/node_modules/@mdbase-dev/connect-protocol/dist/type-packs.js +30 -0
- package/node_modules/@mdbase-dev/connect-protocol/package.json +49 -0
- package/node_modules/@mdbase-dev/connect-protocol/schemas/application-capability-catalog.v1.json +40 -0
- package/node_modules/@mdbase-dev/connect-protocol/schemas/application-capability-catalog.v2.json +68 -0
- package/node_modules/@mdbase-dev/connect-protocol/schemas/catalog.json +17 -0
- package/node_modules/@mdbase-dev/connect-protocol/schemas/change-events.v1.json +48 -0
- package/node_modules/@mdbase-dev/connect-protocol/schemas/connect-problem.v1.schema.json +5665 -0
- package/node_modules/@mdbase-dev/connect-protocol/schemas/connect-problems.v1.catalog.json +373 -0
- package/node_modules/@mdbase-dev/connect-protocol/schemas/connect-protocol.v1.schema.json +901 -0
- package/node_modules/@mdbase-dev/connect-protocol/schemas/data-contract.schema.json +163 -0
- package/node_modules/@mdbase-dev/connect-protocol/schemas/encrypted-relay.v1.schema.json +63 -0
- package/node_modules/@mdbase-dev/connect-protocol/schemas/encrypted-relay.v2.schema.json +63 -0
- package/node_modules/@mdbase-dev/connect-protocol/schemas/encrypted-relay.v3.schema.json +64 -0
- package/node_modules/@mdbase-dev/connect-protocol/schemas/files.v1.schema.json +551 -0
- package/node_modules/@mdbase-dev/connect-protocol/schemas/interop/v0.1/action-cancellation.schema.json +6 -0
- package/node_modules/@mdbase-dev/connect-protocol/schemas/interop/v0.1/action-invocation.schema.json +6 -0
- package/node_modules/@mdbase-dev/connect-protocol/schemas/interop/v0.1/action-outcome.schema.json +6 -0
- package/node_modules/@mdbase-dev/connect-protocol/schemas/interop/v0.1/action-provider-declaration.schema.json +6 -0
- package/node_modules/@mdbase-dev/connect-protocol/schemas/interop/v0.1/action-request.schema.json +6 -0
- package/node_modules/@mdbase-dev/connect-protocol/schemas/interop/v0.1/conformance-claim.schema.json +6 -0
- package/node_modules/@mdbase-dev/connect-protocol/schemas/interop/v0.1/event-source-declaration.schema.json +6 -0
- package/node_modules/@mdbase-dev/connect-protocol/schemas/interop/v0.1/event.schema.json +6 -0
- package/node_modules/@mdbase-dev/connect-protocol/schemas/interop/v0.1/profile.schema.json +456 -0
- package/node_modules/@mdbase-dev/connect-protocol/schemas/mdbase-app.legacy-v1.schema.json +430 -0
- package/node_modules/@mdbase-dev/connect-protocol/schemas/mdbase-app.schema.json +437 -0
- package/node_modules/@mdbase-dev/connect-protocol/schemas/mdbase-query.v0.3.schema.json +176 -0
- package/node_modules/@mdbase-dev/connect-protocol/schemas/next-trust.v1.schema.json +54 -0
- package/node_modules/@mdbase-dev/connect-protocol/schemas/notification-webhook.v1.schema.json +56 -0
- package/node_modules/@mdbase-dev/connect-protocol/schemas/operation-catalog.v1.json +43 -0
- package/node_modules/@mdbase-dev/connect-protocol/schemas/operation-mutation-classification.v1.json +554 -0
- package/node_modules/@mdbase-dev/connect-protocol/schemas/sync.v1.schema.json +483 -0
- package/node_modules/@noble/hashes/LICENSE +21 -0
- package/node_modules/@noble/hashes/README.md +478 -0
- package/node_modules/@noble/hashes/_blake.d.ts +20 -0
- package/node_modules/@noble/hashes/_blake.js +59 -0
- package/node_modules/@noble/hashes/_md.d.ts +95 -0
- package/node_modules/@noble/hashes/_md.js +209 -0
- package/node_modules/@noble/hashes/_u64.d.ts +36 -0
- package/node_modules/@noble/hashes/_u64.js +77 -0
- package/node_modules/@noble/hashes/argon2.d.ts +148 -0
- package/node_modules/@noble/hashes/argon2.js +577 -0
- package/node_modules/@noble/hashes/blake1.d.ts +162 -0
- package/node_modules/@noble/hashes/blake1.js +630 -0
- package/node_modules/@noble/hashes/blake2.d.ts +203 -0
- package/node_modules/@noble/hashes/blake2.js +573 -0
- package/node_modules/@noble/hashes/blake3.d.ts +68 -0
- package/node_modules/@noble/hashes/blake3.js +310 -0
- package/node_modules/@noble/hashes/eskdf.d.ts +91 -0
- package/node_modules/@noble/hashes/eskdf.js +225 -0
- package/node_modules/@noble/hashes/hkdf.d.ts +70 -0
- package/node_modules/@noble/hashes/hkdf.js +152 -0
- package/node_modules/@noble/hashes/hmac.d.ts +50 -0
- package/node_modules/@noble/hashes/hmac.js +93 -0
- package/node_modules/@noble/hashes/index.d.ts +1 -0
- package/node_modules/@noble/hashes/index.js +34 -0
- package/node_modules/@noble/hashes/legacy.d.ts +107 -0
- package/node_modules/@noble/hashes/legacy.js +348 -0
- package/node_modules/@noble/hashes/package.json +91 -0
- package/node_modules/@noble/hashes/pbkdf2.d.ts +66 -0
- package/node_modules/@noble/hashes/pbkdf2.js +176 -0
- package/node_modules/@noble/hashes/scrypt.d.ts +98 -0
- package/node_modules/@noble/hashes/scrypt.js +304 -0
- package/node_modules/@noble/hashes/sha2.d.ts +176 -0
- package/node_modules/@noble/hashes/sha2.js +446 -0
- package/node_modules/@noble/hashes/sha3-addons.d.ts +533 -0
- package/node_modules/@noble/hashes/sha3-addons.js +973 -0
- package/node_modules/@noble/hashes/sha3.d.ts +208 -0
- package/node_modules/@noble/hashes/sha3.js +475 -0
- package/node_modules/@noble/hashes/src/_blake.ts +67 -0
- package/node_modules/@noble/hashes/src/_md.ts +227 -0
- package/node_modules/@noble/hashes/src/_u64.ts +111 -0
- package/node_modules/@noble/hashes/src/argon2.ts +693 -0
- package/node_modules/@noble/hashes/src/blake1.ts +677 -0
- package/node_modules/@noble/hashes/src/blake2.ts +648 -0
- package/node_modules/@noble/hashes/src/blake3.ts +344 -0
- package/node_modules/@noble/hashes/src/eskdf.ts +265 -0
- package/node_modules/@noble/hashes/src/hkdf.ts +161 -0
- package/node_modules/@noble/hashes/src/hmac.ts +130 -0
- package/node_modules/@noble/hashes/src/index.ts +33 -0
- package/node_modules/@noble/hashes/src/legacy.ts +361 -0
- package/node_modules/@noble/hashes/src/pbkdf2.ts +219 -0
- package/node_modules/@noble/hashes/src/scrypt.ts +375 -0
- package/node_modules/@noble/hashes/src/sha2.ts +486 -0
- package/node_modules/@noble/hashes/src/sha3-addons.ts +1157 -0
- package/node_modules/@noble/hashes/src/sha3.ts +525 -0
- package/node_modules/@noble/hashes/src/utils.ts +978 -0
- package/node_modules/@noble/hashes/src/webcrypto.ts +283 -0
- package/node_modules/@noble/hashes/utils.d.ts +560 -0
- package/node_modules/@noble/hashes/utils.js +687 -0
- package/node_modules/@noble/hashes/webcrypto.d.ts +121 -0
- package/node_modules/@noble/hashes/webcrypto.js +228 -0
- package/node_modules/yaml/LICENSE +13 -0
- package/node_modules/yaml/README.md +172 -0
- package/node_modules/yaml/bin.mjs +11 -0
- package/node_modules/yaml/browser/dist/compose/compose-collection.js +88 -0
- package/node_modules/yaml/browser/dist/compose/compose-doc.js +43 -0
- package/node_modules/yaml/browser/dist/compose/compose-node.js +109 -0
- package/node_modules/yaml/browser/dist/compose/compose-scalar.js +86 -0
- package/node_modules/yaml/browser/dist/compose/composer.js +219 -0
- package/node_modules/yaml/browser/dist/compose/resolve-block-map.js +115 -0
- package/node_modules/yaml/browser/dist/compose/resolve-block-scalar.js +198 -0
- package/node_modules/yaml/browser/dist/compose/resolve-block-seq.js +49 -0
- package/node_modules/yaml/browser/dist/compose/resolve-end.js +37 -0
- package/node_modules/yaml/browser/dist/compose/resolve-flow-collection.js +207 -0
- package/node_modules/yaml/browser/dist/compose/resolve-flow-scalar.js +226 -0
- package/node_modules/yaml/browser/dist/compose/resolve-props.js +146 -0
- package/node_modules/yaml/browser/dist/compose/util-contains-newline.js +34 -0
- package/node_modules/yaml/browser/dist/compose/util-empty-scalar-position.js +26 -0
- package/node_modules/yaml/browser/dist/compose/util-flow-indent-check.js +15 -0
- package/node_modules/yaml/browser/dist/compose/util-map-includes.js +13 -0
- package/node_modules/yaml/browser/dist/doc/Document.js +335 -0
- package/node_modules/yaml/browser/dist/doc/anchors.js +71 -0
- package/node_modules/yaml/browser/dist/doc/applyReviver.js +55 -0
- package/node_modules/yaml/browser/dist/doc/createNode.js +88 -0
- package/node_modules/yaml/browser/dist/doc/directives.js +176 -0
- package/node_modules/yaml/browser/dist/errors.js +57 -0
- package/node_modules/yaml/browser/dist/index.js +17 -0
- package/node_modules/yaml/browser/dist/log.js +11 -0
- package/node_modules/yaml/browser/dist/nodes/Alias.js +118 -0
- package/node_modules/yaml/browser/dist/nodes/Collection.js +147 -0
- package/node_modules/yaml/browser/dist/nodes/Node.js +38 -0
- package/node_modules/yaml/browser/dist/nodes/Pair.js +36 -0
- package/node_modules/yaml/browser/dist/nodes/Scalar.js +24 -0
- package/node_modules/yaml/browser/dist/nodes/YAMLMap.js +144 -0
- package/node_modules/yaml/browser/dist/nodes/YAMLSeq.js +113 -0
- package/node_modules/yaml/browser/dist/nodes/addPairToJSMap.js +63 -0
- package/node_modules/yaml/browser/dist/nodes/identity.js +36 -0
- package/node_modules/yaml/browser/dist/nodes/toJS.js +37 -0
- package/node_modules/yaml/browser/dist/parse/cst-scalar.js +214 -0
- package/node_modules/yaml/browser/dist/parse/cst-stringify.js +61 -0
- package/node_modules/yaml/browser/dist/parse/cst-visit.js +97 -0
- package/node_modules/yaml/browser/dist/parse/cst.js +98 -0
- package/node_modules/yaml/browser/dist/parse/lexer.js +721 -0
- package/node_modules/yaml/browser/dist/parse/line-counter.js +39 -0
- package/node_modules/yaml/browser/dist/parse/parser.js +975 -0
- package/node_modules/yaml/browser/dist/public-api.js +102 -0
- package/node_modules/yaml/browser/dist/schema/Schema.js +37 -0
- package/node_modules/yaml/browser/dist/schema/common/map.js +17 -0
- package/node_modules/yaml/browser/dist/schema/common/null.js +15 -0
- package/node_modules/yaml/browser/dist/schema/common/seq.js +17 -0
- package/node_modules/yaml/browser/dist/schema/common/string.js +14 -0
- package/node_modules/yaml/browser/dist/schema/core/bool.js +19 -0
- package/node_modules/yaml/browser/dist/schema/core/float.js +43 -0
- package/node_modules/yaml/browser/dist/schema/core/int.js +38 -0
- package/node_modules/yaml/browser/dist/schema/core/schema.js +23 -0
- package/node_modules/yaml/browser/dist/schema/json/schema.js +62 -0
- package/node_modules/yaml/browser/dist/schema/tags.js +96 -0
- package/node_modules/yaml/browser/dist/schema/yaml-1.1/binary.js +58 -0
- package/node_modules/yaml/browser/dist/schema/yaml-1.1/bool.js +26 -0
- package/node_modules/yaml/browser/dist/schema/yaml-1.1/float.js +46 -0
- package/node_modules/yaml/browser/dist/schema/yaml-1.1/int.js +71 -0
- package/node_modules/yaml/browser/dist/schema/yaml-1.1/merge.js +67 -0
- package/node_modules/yaml/browser/dist/schema/yaml-1.1/omap.js +74 -0
- package/node_modules/yaml/browser/dist/schema/yaml-1.1/pairs.js +78 -0
- package/node_modules/yaml/browser/dist/schema/yaml-1.1/schema.js +39 -0
- package/node_modules/yaml/browser/dist/schema/yaml-1.1/set.js +93 -0
- package/node_modules/yaml/browser/dist/schema/yaml-1.1/timestamp.js +101 -0
- package/node_modules/yaml/browser/dist/stringify/foldFlowLines.js +146 -0
- package/node_modules/yaml/browser/dist/stringify/stringify.js +129 -0
- package/node_modules/yaml/browser/dist/stringify/stringifyCollection.js +153 -0
- package/node_modules/yaml/browser/dist/stringify/stringifyComment.js +20 -0
- package/node_modules/yaml/browser/dist/stringify/stringifyDocument.js +85 -0
- package/node_modules/yaml/browser/dist/stringify/stringifyNumber.js +25 -0
- package/node_modules/yaml/browser/dist/stringify/stringifyPair.js +150 -0
- package/node_modules/yaml/browser/dist/stringify/stringifyString.js +336 -0
- package/node_modules/yaml/browser/dist/util.js +11 -0
- package/node_modules/yaml/browser/dist/visit.js +233 -0
- package/node_modules/yaml/browser/index.js +5 -0
- package/node_modules/yaml/browser/package.json +3 -0
- package/node_modules/yaml/dist/cli.d.ts +8 -0
- package/node_modules/yaml/dist/cli.mjs +201 -0
- package/node_modules/yaml/dist/compose/compose-collection.d.ts +11 -0
- package/node_modules/yaml/dist/compose/compose-collection.js +90 -0
- package/node_modules/yaml/dist/compose/compose-doc.d.ts +7 -0
- package/node_modules/yaml/dist/compose/compose-doc.js +45 -0
- package/node_modules/yaml/dist/compose/compose-node.d.ts +29 -0
- package/node_modules/yaml/dist/compose/compose-node.js +112 -0
- package/node_modules/yaml/dist/compose/compose-scalar.d.ts +5 -0
- package/node_modules/yaml/dist/compose/compose-scalar.js +88 -0
- package/node_modules/yaml/dist/compose/composer.d.ts +63 -0
- package/node_modules/yaml/dist/compose/composer.js +224 -0
- package/node_modules/yaml/dist/compose/resolve-block-map.d.ts +6 -0
- package/node_modules/yaml/dist/compose/resolve-block-map.js +117 -0
- package/node_modules/yaml/dist/compose/resolve-block-scalar.d.ts +11 -0
- package/node_modules/yaml/dist/compose/resolve-block-scalar.js +200 -0
- package/node_modules/yaml/dist/compose/resolve-block-seq.d.ts +6 -0
- package/node_modules/yaml/dist/compose/resolve-block-seq.js +51 -0
- package/node_modules/yaml/dist/compose/resolve-end.d.ts +6 -0
- package/node_modules/yaml/dist/compose/resolve-end.js +39 -0
- package/node_modules/yaml/dist/compose/resolve-flow-collection.d.ts +7 -0
- package/node_modules/yaml/dist/compose/resolve-flow-collection.js +209 -0
- package/node_modules/yaml/dist/compose/resolve-flow-scalar.d.ts +10 -0
- package/node_modules/yaml/dist/compose/resolve-flow-scalar.js +228 -0
- package/node_modules/yaml/dist/compose/resolve-props.d.ts +23 -0
- package/node_modules/yaml/dist/compose/resolve-props.js +148 -0
- package/node_modules/yaml/dist/compose/util-contains-newline.d.ts +2 -0
- package/node_modules/yaml/dist/compose/util-contains-newline.js +36 -0
- package/node_modules/yaml/dist/compose/util-empty-scalar-position.d.ts +2 -0
- package/node_modules/yaml/dist/compose/util-empty-scalar-position.js +28 -0
- package/node_modules/yaml/dist/compose/util-flow-indent-check.d.ts +3 -0
- package/node_modules/yaml/dist/compose/util-flow-indent-check.js +17 -0
- package/node_modules/yaml/dist/compose/util-map-includes.d.ts +4 -0
- package/node_modules/yaml/dist/compose/util-map-includes.js +15 -0
- package/node_modules/yaml/dist/doc/Document.d.ts +141 -0
- package/node_modules/yaml/dist/doc/Document.js +337 -0
- package/node_modules/yaml/dist/doc/anchors.d.ts +24 -0
- package/node_modules/yaml/dist/doc/anchors.js +76 -0
- package/node_modules/yaml/dist/doc/applyReviver.d.ts +9 -0
- package/node_modules/yaml/dist/doc/applyReviver.js +57 -0
- package/node_modules/yaml/dist/doc/createNode.d.ts +17 -0
- package/node_modules/yaml/dist/doc/createNode.js +90 -0
- package/node_modules/yaml/dist/doc/directives.d.ts +49 -0
- package/node_modules/yaml/dist/doc/directives.js +178 -0
- package/node_modules/yaml/dist/errors.d.ts +21 -0
- package/node_modules/yaml/dist/errors.js +62 -0
- package/node_modules/yaml/dist/index.d.ts +25 -0
- package/node_modules/yaml/dist/index.js +50 -0
- package/node_modules/yaml/dist/log.d.ts +3 -0
- package/node_modules/yaml/dist/log.js +19 -0
- package/node_modules/yaml/dist/nodes/Alias.d.ts +29 -0
- package/node_modules/yaml/dist/nodes/Alias.js +120 -0
- package/node_modules/yaml/dist/nodes/Collection.d.ts +73 -0
- package/node_modules/yaml/dist/nodes/Collection.js +151 -0
- package/node_modules/yaml/dist/nodes/Node.d.ts +53 -0
- package/node_modules/yaml/dist/nodes/Node.js +40 -0
- package/node_modules/yaml/dist/nodes/Pair.d.ts +22 -0
- package/node_modules/yaml/dist/nodes/Pair.js +39 -0
- package/node_modules/yaml/dist/nodes/Scalar.d.ts +47 -0
- package/node_modules/yaml/dist/nodes/Scalar.js +27 -0
- package/node_modules/yaml/dist/nodes/YAMLMap.d.ts +53 -0
- package/node_modules/yaml/dist/nodes/YAMLMap.js +147 -0
- package/node_modules/yaml/dist/nodes/YAMLSeq.d.ts +60 -0
- package/node_modules/yaml/dist/nodes/YAMLSeq.js +115 -0
- package/node_modules/yaml/dist/nodes/addPairToJSMap.d.ts +4 -0
- package/node_modules/yaml/dist/nodes/addPairToJSMap.js +65 -0
- package/node_modules/yaml/dist/nodes/identity.d.ts +23 -0
- package/node_modules/yaml/dist/nodes/identity.js +53 -0
- package/node_modules/yaml/dist/nodes/toJS.d.ts +29 -0
- package/node_modules/yaml/dist/nodes/toJS.js +39 -0
- package/node_modules/yaml/dist/options.d.ts +350 -0
- package/node_modules/yaml/dist/parse/cst-scalar.d.ts +64 -0
- package/node_modules/yaml/dist/parse/cst-scalar.js +218 -0
- package/node_modules/yaml/dist/parse/cst-stringify.d.ts +8 -0
- package/node_modules/yaml/dist/parse/cst-stringify.js +63 -0
- package/node_modules/yaml/dist/parse/cst-visit.d.ts +39 -0
- package/node_modules/yaml/dist/parse/cst-visit.js +99 -0
- package/node_modules/yaml/dist/parse/cst.d.ts +109 -0
- package/node_modules/yaml/dist/parse/cst.js +112 -0
- package/node_modules/yaml/dist/parse/lexer.d.ts +87 -0
- package/node_modules/yaml/dist/parse/lexer.js +723 -0
- package/node_modules/yaml/dist/parse/line-counter.d.ts +22 -0
- package/node_modules/yaml/dist/parse/line-counter.js +41 -0
- package/node_modules/yaml/dist/parse/parser.d.ts +84 -0
- package/node_modules/yaml/dist/parse/parser.js +980 -0
- package/node_modules/yaml/dist/public-api.d.ts +44 -0
- package/node_modules/yaml/dist/public-api.js +107 -0
- package/node_modules/yaml/dist/schema/Schema.d.ts +17 -0
- package/node_modules/yaml/dist/schema/Schema.js +39 -0
- package/node_modules/yaml/dist/schema/common/map.d.ts +2 -0
- package/node_modules/yaml/dist/schema/common/map.js +19 -0
- package/node_modules/yaml/dist/schema/common/null.d.ts +4 -0
- package/node_modules/yaml/dist/schema/common/null.js +17 -0
- package/node_modules/yaml/dist/schema/common/seq.d.ts +2 -0
- package/node_modules/yaml/dist/schema/common/seq.js +19 -0
- package/node_modules/yaml/dist/schema/common/string.d.ts +2 -0
- package/node_modules/yaml/dist/schema/common/string.js +16 -0
- package/node_modules/yaml/dist/schema/core/bool.d.ts +4 -0
- package/node_modules/yaml/dist/schema/core/bool.js +21 -0
- package/node_modules/yaml/dist/schema/core/float.d.ts +4 -0
- package/node_modules/yaml/dist/schema/core/float.js +47 -0
- package/node_modules/yaml/dist/schema/core/int.d.ts +4 -0
- package/node_modules/yaml/dist/schema/core/int.js +42 -0
- package/node_modules/yaml/dist/schema/core/schema.d.ts +1 -0
- package/node_modules/yaml/dist/schema/core/schema.js +25 -0
- package/node_modules/yaml/dist/schema/json/schema.d.ts +2 -0
- package/node_modules/yaml/dist/schema/json/schema.js +64 -0
- package/node_modules/yaml/dist/schema/json-schema.d.ts +69 -0
- package/node_modules/yaml/dist/schema/tags.d.ts +48 -0
- package/node_modules/yaml/dist/schema/tags.js +99 -0
- package/node_modules/yaml/dist/schema/types.d.ts +92 -0
- package/node_modules/yaml/dist/schema/yaml-1.1/binary.d.ts +2 -0
- package/node_modules/yaml/dist/schema/yaml-1.1/binary.js +70 -0
- package/node_modules/yaml/dist/schema/yaml-1.1/bool.d.ts +7 -0
- package/node_modules/yaml/dist/schema/yaml-1.1/bool.js +29 -0
- package/node_modules/yaml/dist/schema/yaml-1.1/float.d.ts +4 -0
- package/node_modules/yaml/dist/schema/yaml-1.1/float.js +50 -0
- package/node_modules/yaml/dist/schema/yaml-1.1/int.d.ts +5 -0
- package/node_modules/yaml/dist/schema/yaml-1.1/int.js +76 -0
- package/node_modules/yaml/dist/schema/yaml-1.1/merge.d.ts +9 -0
- package/node_modules/yaml/dist/schema/yaml-1.1/merge.js +71 -0
- package/node_modules/yaml/dist/schema/yaml-1.1/omap.d.ts +22 -0
- package/node_modules/yaml/dist/schema/yaml-1.1/omap.js +77 -0
- package/node_modules/yaml/dist/schema/yaml-1.1/pairs.d.ts +10 -0
- package/node_modules/yaml/dist/schema/yaml-1.1/pairs.js +82 -0
- package/node_modules/yaml/dist/schema/yaml-1.1/schema.d.ts +1 -0
- package/node_modules/yaml/dist/schema/yaml-1.1/schema.js +41 -0
- package/node_modules/yaml/dist/schema/yaml-1.1/set.d.ts +28 -0
- package/node_modules/yaml/dist/schema/yaml-1.1/set.js +96 -0
- package/node_modules/yaml/dist/schema/yaml-1.1/timestamp.d.ts +6 -0
- package/node_modules/yaml/dist/schema/yaml-1.1/timestamp.js +105 -0
- package/node_modules/yaml/dist/stringify/foldFlowLines.d.ts +34 -0
- package/node_modules/yaml/dist/stringify/foldFlowLines.js +151 -0
- package/node_modules/yaml/dist/stringify/stringify.d.ts +21 -0
- package/node_modules/yaml/dist/stringify/stringify.js +132 -0
- package/node_modules/yaml/dist/stringify/stringifyCollection.d.ts +17 -0
- package/node_modules/yaml/dist/stringify/stringifyCollection.js +155 -0
- package/node_modules/yaml/dist/stringify/stringifyComment.d.ts +10 -0
- package/node_modules/yaml/dist/stringify/stringifyComment.js +24 -0
- package/node_modules/yaml/dist/stringify/stringifyDocument.d.ts +4 -0
- package/node_modules/yaml/dist/stringify/stringifyDocument.js +87 -0
- package/node_modules/yaml/dist/stringify/stringifyNumber.d.ts +2 -0
- package/node_modules/yaml/dist/stringify/stringifyNumber.js +27 -0
- package/node_modules/yaml/dist/stringify/stringifyPair.d.ts +3 -0
- package/node_modules/yaml/dist/stringify/stringifyPair.js +152 -0
- package/node_modules/yaml/dist/stringify/stringifyString.d.ts +9 -0
- package/node_modules/yaml/dist/stringify/stringifyString.js +338 -0
- package/node_modules/yaml/dist/test-events.d.ts +4 -0
- package/node_modules/yaml/dist/test-events.js +134 -0
- package/node_modules/yaml/dist/util.d.ts +16 -0
- package/node_modules/yaml/dist/util.js +28 -0
- package/node_modules/yaml/dist/visit.d.ts +102 -0
- package/node_modules/yaml/dist/visit.js +236 -0
- package/node_modules/yaml/package.json +97 -0
- package/node_modules/yaml/util.js +2 -0
- package/package.json +108 -4
- package/vendor/mdbase-connect-sdk-e6e66bcd172f.json +13 -0
- package/vendor/mdbase-dev-connect-protocol-0.1.0-beta.145-e6e66bcd172f.tgz +0 -0
package/dist/client.js
ADDED
|
@@ -0,0 +1,1966 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `MdbaseClient`: the SDK's main entry point over any transport.
|
|
3
|
+
*
|
|
4
|
+
* - Reads return the replica's local view: confirmed state plus pending local
|
|
5
|
+
* mutations (§3).
|
|
6
|
+
* - Live queries are pushed (§4). Use a `limit` window with `include` off for lists,
|
|
7
|
+
* and fetch bodies per record on demand.
|
|
8
|
+
* - Writes are intents with optimistic results (§5). The SDK mints mutation IDs, so
|
|
9
|
+
* resubmitting after a broken link is idempotent.
|
|
10
|
+
* - Receipts move pending → confirmed / rejected (§6). Status is "confirmed through
|
|
11
|
+
* N, plus pending" (§7).
|
|
12
|
+
* - The client reconnects by itself and restores every subscription. Offline is never
|
|
13
|
+
* an error.
|
|
14
|
+
*/
|
|
15
|
+
import { Float64 } from "./cbor.js";
|
|
16
|
+
import { encodeAppBasesRequest, decodeAppBasesResult, encodeAppBasesDiscoveryRequest, decodeAppBasesDiscoveryResult, encodeAppBasesSourceRequest, decodeAppBasesSourceResult } from "./app-host/bases-wire.js";
|
|
17
|
+
import { appBasesPages } from "./app-host/bases-pages.js";
|
|
18
|
+
import { list, uint, uuid as uuidCodec, uuidFromBytes } from "./codec.js";
|
|
19
|
+
import { recordValidations, captureValidation, bindValidation } from "./validation.js";
|
|
20
|
+
import { isMdbaseError, mdbaseError, MdbaseError } from "./errors.js";
|
|
21
|
+
import { FilesApi } from "./files.js";
|
|
22
|
+
import { readMany as readManyRecords, queryPages as readQueryPages } from "./read-helpers.js";
|
|
23
|
+
import { RecordsApi } from "./records.js";
|
|
24
|
+
import { executeSavedView, listSavedViews, readSavedViewSource } from "./saved-views.js";
|
|
25
|
+
import { createViewSource, updateViewSource, readViewSourceRecord } from "./view-sources.js";
|
|
26
|
+
import { contractRecordWire, contractWritePlan } from "./contract-records.js";
|
|
27
|
+
import { contractQueryResult } from "./contract-query.js";
|
|
28
|
+
import { LiveResult } from "./live.js";
|
|
29
|
+
import { PresenceApi } from "./presence.js";
|
|
30
|
+
import { rememberWitness, witnessesFor } from "./witness.js";
|
|
31
|
+
import { API_VERSIONS, problemCodec, schemaToError, Session, withMutation } from "./session.js";
|
|
32
|
+
import { usesNextRelayFence } from "./transport/read-fence-policy.js";
|
|
33
|
+
import { applyTypePack, planTypePack } from "./type-packs.js";
|
|
34
|
+
import { diffEdits, revisionOf, toFmMap, toValue, uuidv7, valueEquals } from "./values.js";
|
|
35
|
+
import { pendingDevice, recoveryKeyCreated, recoveryKeyStatus, accountKeyStatus, } from "./private.js";
|
|
36
|
+
import { backlinksResult, describeResult, newTypeFile, describeTypingResult, DESCRIBE_TYPING_MAX_PATHS, DESCRIBE_TYPING_MAX_TYPES, listPendingResult, listResourcesResult, resourceView, } from "./wire.js";
|
|
37
|
+
import { memoryWriteLedgerStore, WriteLedger } from "./write-ledger.js";
|
|
38
|
+
import { changesResult, conflictEntry, fenceApply, fenceReport, fenceResult, hold, holdResolution, include as includeCodec, queryResult, queryUpdate, receipt as receiptCodec, recordView, submitParams, submitResult, syncStatus, appliedPrefix as appliedPrefixCodec, appliedPrefixParams, } from "./wire.js";
|
|
39
|
+
/** Snapshot plain query data without losing Float64 kind/signed zero, exact
|
|
40
|
+
* integers, ordered Maps or omitted fields (structuredClone loses prototypes). */
|
|
41
|
+
function snapshotQueryValue(value) {
|
|
42
|
+
if (value instanceof Float64)
|
|
43
|
+
return new Float64(value.value);
|
|
44
|
+
if (value instanceof Date)
|
|
45
|
+
return new Date(value);
|
|
46
|
+
if (Array.isArray(value))
|
|
47
|
+
return value.map(item => snapshotQueryValue(item));
|
|
48
|
+
if (value instanceof Map)
|
|
49
|
+
return new Map([...value].map(([key, item]) => [key, snapshotQueryValue(item)]));
|
|
50
|
+
if (value !== null && typeof value === "object")
|
|
51
|
+
return Object.fromEntries(Object.entries(value).map(([key, item]) => [key, snapshotQueryValue(item)]));
|
|
52
|
+
return value;
|
|
53
|
+
}
|
|
54
|
+
function refValue(ref) {
|
|
55
|
+
if (typeof ref === "string")
|
|
56
|
+
return uuidCodec.enc(ref);
|
|
57
|
+
if ("id" in ref && typeof ref.id === "string")
|
|
58
|
+
return uuidCodec.enc(ref.id);
|
|
59
|
+
return ref.path;
|
|
60
|
+
}
|
|
61
|
+
function refId(ref) {
|
|
62
|
+
if (typeof ref === "string")
|
|
63
|
+
return ref;
|
|
64
|
+
if ("id" in ref)
|
|
65
|
+
return ref.id;
|
|
66
|
+
throw mdbaseError("invalid_request", "this operation needs a record ID, not a path; get() the record first");
|
|
67
|
+
}
|
|
68
|
+
function includeValue(inc) {
|
|
69
|
+
return inc ? includeCodec.enc(inc) : undefined;
|
|
70
|
+
}
|
|
71
|
+
/** The account secret `R` is exactly 32 bytes; refused locally before any request. */
|
|
72
|
+
function accountSecret(secret) {
|
|
73
|
+
if (!(secret instanceof Uint8Array) || secret.length !== 32)
|
|
74
|
+
throw mdbaseError("invalid_request", "the account secret is 32 bytes", "account_secret_length");
|
|
75
|
+
}
|
|
76
|
+
function params(entries) {
|
|
77
|
+
const m = new Map();
|
|
78
|
+
for (const [k, v] of entries)
|
|
79
|
+
if (v !== undefined)
|
|
80
|
+
m.set(k, v);
|
|
81
|
+
return m;
|
|
82
|
+
}
|
|
83
|
+
function withCurrentGrant(hello, grant) {
|
|
84
|
+
const prior = hello.grant;
|
|
85
|
+
if (!grant || prior.grant === grant.grant && prior.role === grant.role
|
|
86
|
+
&& valueEquals(prior.capabilities, grant.capabilities)
|
|
87
|
+
&& valueEquals(prior.fileFolders ?? null, grant.fileFolders ?? null))
|
|
88
|
+
return hello;
|
|
89
|
+
return { ...hello, grant };
|
|
90
|
+
}
|
|
91
|
+
/** The mutation and record IDs an intent name stands for in one collection. */
|
|
92
|
+
async function intentIds(collection, intent) {
|
|
93
|
+
if (typeof intent !== "string" || !intent)
|
|
94
|
+
throw mdbaseError("invalid_request", "intent must be a non-empty string");
|
|
95
|
+
const b = new Uint8Array(await crypto.subtle.digest("SHA-256", new TextEncoder().encode(`mdbase-intent/1\0${collection}\0${intent}`)));
|
|
96
|
+
const id = (o) => { const u = b.slice(o, o + 16); u[6] = 0x80 | (u[6] & 0x0f); u[8] = 0x80 | (u[8] & 0x3f); return uuidFromBytes(u); };
|
|
97
|
+
return [id(0), id(16)];
|
|
98
|
+
}
|
|
99
|
+
// Log confirmation and local publication are independent outcomes. Rejected or
|
|
100
|
+
// unknown receipts settle both promises; confirmation alone cannot drop a file
|
|
101
|
+
// write still publishing. Always inspect Write's merged publication metadata.
|
|
102
|
+
function needsReceipt(r) {
|
|
103
|
+
return r.state === "pending" || (r.state === "confirmed" && r.published === "publishing");
|
|
104
|
+
}
|
|
105
|
+
function mergePublication(r, previous) {
|
|
106
|
+
const prior = previous.published;
|
|
107
|
+
return prior === "published" || prior === "not_published" || (r.published === undefined && prior !== undefined)
|
|
108
|
+
? { ...r, published: prior }
|
|
109
|
+
: r;
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* A submitted write. `receipt` is what the replica answered (usually `pending` with
|
|
113
|
+
* optimistic `records`). `confirmed` settles when it leaves `pending`.
|
|
114
|
+
*/
|
|
115
|
+
export class Write {
|
|
116
|
+
receipt;
|
|
117
|
+
listeners = new Set();
|
|
118
|
+
settle;
|
|
119
|
+
settlePublished;
|
|
120
|
+
/** Resolves with the confirmed receipt; rejects with the problem if rejected or unknown. */
|
|
121
|
+
confirmed;
|
|
122
|
+
/** Final local file result, possibly BEFORE log confirmation. `not_published`
|
|
123
|
+
* is a final non-publication, not success. Without files, settles at confirmation.
|
|
124
|
+
* Rejects rejected/unknown outcomes; not a native custody/durable ACK gate. */
|
|
125
|
+
published;
|
|
126
|
+
constructor(receipt) {
|
|
127
|
+
this.receipt = receipt;
|
|
128
|
+
this.confirmed = new Promise((resolve, reject) => (this.settle = { resolve, reject }));
|
|
129
|
+
this.published = new Promise((resolve, reject) => (this.settlePublished = { resolve, reject }));
|
|
130
|
+
// Callers that only look at `receipt` must not see unhandled rejections.
|
|
131
|
+
this.confirmed.catch(() => { });
|
|
132
|
+
this.published.catch(() => { });
|
|
133
|
+
this.update(receipt);
|
|
134
|
+
}
|
|
135
|
+
get mutationId() {
|
|
136
|
+
return this.receipt.mutation;
|
|
137
|
+
}
|
|
138
|
+
/** The records as they now read locally (optimistic until confirmed). */
|
|
139
|
+
get records() {
|
|
140
|
+
return this.receipt.records ?? [];
|
|
141
|
+
}
|
|
142
|
+
get state() {
|
|
143
|
+
return this.receipt.state;
|
|
144
|
+
}
|
|
145
|
+
onReceipt(fn) {
|
|
146
|
+
this.listeners.add(fn);
|
|
147
|
+
return () => this.listeners.delete(fn);
|
|
148
|
+
}
|
|
149
|
+
/** SDK-internal: apply a newer receipt. Kept in the published types (apps' tests use it). */
|
|
150
|
+
update(r) {
|
|
151
|
+
let merged = r.records || !this.receipt.records ? r : { ...r, records: this.receipt.records };
|
|
152
|
+
// Preserve final publication through delayed/missing pushes without mutating input.
|
|
153
|
+
merged = mergePublication(merged, this.receipt);
|
|
154
|
+
this.receipt = merged;
|
|
155
|
+
for (const fn of [...this.listeners])
|
|
156
|
+
fn(this.receipt);
|
|
157
|
+
// A contradictory rejected/unknown packet must not resolve publication first.
|
|
158
|
+
if (r.state === "rejected" || r.state === "unknown") {
|
|
159
|
+
const error = new MdbaseError(r.problem ?? {
|
|
160
|
+
code: r.state === "unknown" ? "outcome_unknown" : "internal",
|
|
161
|
+
recovery: r.state === "unknown" ? "resolve_outcome" : "contact_support",
|
|
162
|
+
message: `mutation ${r.state}`,
|
|
163
|
+
}, { mutationId: r.mutation });
|
|
164
|
+
this.settle.reject(error);
|
|
165
|
+
this.settlePublished.reject(error);
|
|
166
|
+
}
|
|
167
|
+
else {
|
|
168
|
+
const pub = this.receipt.published;
|
|
169
|
+
if (pub === "published" || pub === "not_published" || (r.state === "confirmed" && pub === undefined)) {
|
|
170
|
+
this.settlePublished.resolve(this.receipt);
|
|
171
|
+
}
|
|
172
|
+
if (r.state === "confirmed")
|
|
173
|
+
this.settle.resolve(this.receipt);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
/** A live query handle. */
|
|
178
|
+
export class LiveQuery {
|
|
179
|
+
client;
|
|
180
|
+
query;
|
|
181
|
+
include;
|
|
182
|
+
result = new LiveResult();
|
|
183
|
+
listeners = new Set();
|
|
184
|
+
subId = null;
|
|
185
|
+
closedFlag = false;
|
|
186
|
+
/** Rejects if the first subscribe fails; resolves on the first snapshot. */
|
|
187
|
+
ready;
|
|
188
|
+
readyResolve;
|
|
189
|
+
readyReject;
|
|
190
|
+
/** The last error from (re)subscribing, if the subscription is currently broken. */
|
|
191
|
+
error = null;
|
|
192
|
+
/** @internal */
|
|
193
|
+
constructor(client, query, include) {
|
|
194
|
+
this.client = client;
|
|
195
|
+
this.query = query;
|
|
196
|
+
this.include = include;
|
|
197
|
+
this.ready = new Promise((res, rej) => ((this.readyResolve = res), (this.readyReject = rej)));
|
|
198
|
+
this.ready.catch(() => { });
|
|
199
|
+
}
|
|
200
|
+
get records() {
|
|
201
|
+
return this.result.records;
|
|
202
|
+
}
|
|
203
|
+
get complete() {
|
|
204
|
+
return this.result.complete;
|
|
205
|
+
}
|
|
206
|
+
get asOf() {
|
|
207
|
+
return this.result.asOf;
|
|
208
|
+
}
|
|
209
|
+
get stale() {
|
|
210
|
+
return this.result.stale;
|
|
211
|
+
}
|
|
212
|
+
/** Full native metadata bound to asOf; never reconstructed from the window. */
|
|
213
|
+
get metadata() {
|
|
214
|
+
return this.result.metadata;
|
|
215
|
+
}
|
|
216
|
+
get closed() {
|
|
217
|
+
return this.closedFlag;
|
|
218
|
+
}
|
|
219
|
+
get(id) {
|
|
220
|
+
return this.result.get(id);
|
|
221
|
+
}
|
|
222
|
+
/** Call `fn` now and on every change. Returns an unsubscribe function. */
|
|
223
|
+
subscribe(fn) {
|
|
224
|
+
this.listeners.add(fn);
|
|
225
|
+
fn(this, null);
|
|
226
|
+
return () => this.listeners.delete(fn);
|
|
227
|
+
}
|
|
228
|
+
/** Change the query (for example a larger `limit` window). Re-subscribes. */
|
|
229
|
+
async setQuery(query, include = this.include) {
|
|
230
|
+
this.query = query;
|
|
231
|
+
this.include = include;
|
|
232
|
+
this.result.markStale();
|
|
233
|
+
this.emit(null);
|
|
234
|
+
await this.client._resubscribeLive(this);
|
|
235
|
+
}
|
|
236
|
+
close() {
|
|
237
|
+
if (this.closedFlag)
|
|
238
|
+
return;
|
|
239
|
+
this.closedFlag = true;
|
|
240
|
+
this.result.markStale();
|
|
241
|
+
this.client._closeLive(this);
|
|
242
|
+
this.listeners.clear();
|
|
243
|
+
}
|
|
244
|
+
/** @internal */
|
|
245
|
+
_params() {
|
|
246
|
+
return params([
|
|
247
|
+
[0, toValue(this.query)],
|
|
248
|
+
[1, includeValue(this.include)],
|
|
249
|
+
]);
|
|
250
|
+
}
|
|
251
|
+
/** @internal */
|
|
252
|
+
_setSub(id) {
|
|
253
|
+
this.subId = id;
|
|
254
|
+
}
|
|
255
|
+
/** @internal */
|
|
256
|
+
get _sub() {
|
|
257
|
+
return this.subId;
|
|
258
|
+
}
|
|
259
|
+
/** @internal */
|
|
260
|
+
_apply(u) {
|
|
261
|
+
if (this.closedFlag)
|
|
262
|
+
return;
|
|
263
|
+
this.result.apply(u);
|
|
264
|
+
if (u.kind === "snapshot") {
|
|
265
|
+
this.error = null;
|
|
266
|
+
this.readyResolve();
|
|
267
|
+
}
|
|
268
|
+
this.emit(u);
|
|
269
|
+
}
|
|
270
|
+
/** @internal */
|
|
271
|
+
_markStale() {
|
|
272
|
+
this.result.markStale();
|
|
273
|
+
this.emit(null);
|
|
274
|
+
}
|
|
275
|
+
/** @internal */
|
|
276
|
+
_fail(e, first) {
|
|
277
|
+
this.error = e;
|
|
278
|
+
if (first)
|
|
279
|
+
this.readyReject(e);
|
|
280
|
+
this.emit(null);
|
|
281
|
+
}
|
|
282
|
+
emit(u) {
|
|
283
|
+
for (const fn of [...this.listeners])
|
|
284
|
+
fn(this, u);
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
/** Collection data reads, not status/receipts/writes or historical proof probes. */
|
|
288
|
+
const READ_METHODS = new Set([
|
|
289
|
+
"get", "query", "query_contract_v1", "subscribe", "changes", "backlinks", "list_resources", "get_resource",
|
|
290
|
+
"describe", "describe_typing", "list_files", "get_file", "read_file",
|
|
291
|
+
"list_holds", "list_conflicts", "validate", "new_type_file", "get_contract_v1", "map_contract_write_v1",
|
|
292
|
+
"plan_type_pack_v1",
|
|
293
|
+
]);
|
|
294
|
+
export class MdbaseClient {
|
|
295
|
+
opts;
|
|
296
|
+
session;
|
|
297
|
+
linkState = "open";
|
|
298
|
+
linkError = null;
|
|
299
|
+
linkListeners = new Set();
|
|
300
|
+
lastStatus;
|
|
301
|
+
currentHello;
|
|
302
|
+
statusListeners = new Set();
|
|
303
|
+
lives = new Set();
|
|
304
|
+
livesBySub = new Map();
|
|
305
|
+
subscribing = 0;
|
|
306
|
+
early = new Map();
|
|
307
|
+
writes = new Map();
|
|
308
|
+
unclaimed = new Map();
|
|
309
|
+
reconnectWaiters = [];
|
|
310
|
+
restorers = [];
|
|
311
|
+
fenceHandler = null;
|
|
312
|
+
fenceReads;
|
|
313
|
+
readFloor;
|
|
314
|
+
readFencedAt = null;
|
|
315
|
+
readFenceWaiters = [];
|
|
316
|
+
closing = false;
|
|
317
|
+
collectionId;
|
|
318
|
+
holdsWatch = {
|
|
319
|
+
listeners: new Set(),
|
|
320
|
+
last: null,
|
|
321
|
+
};
|
|
322
|
+
conflictsWatch = {
|
|
323
|
+
listeners: new Set(),
|
|
324
|
+
last: null,
|
|
325
|
+
};
|
|
326
|
+
/** Files: handles, uploads, downloads (§10). */
|
|
327
|
+
files;
|
|
328
|
+
/** Presence on records (§11). */
|
|
329
|
+
presence;
|
|
330
|
+
writeLedger;
|
|
331
|
+
ledgerStarted = false;
|
|
332
|
+
transportDeferred = false;
|
|
333
|
+
constructor(opts, session) {
|
|
334
|
+
this.opts = opts;
|
|
335
|
+
this.session = session;
|
|
336
|
+
this.collectionId = session.hello.collection;
|
|
337
|
+
this.lastStatus = session.hello.status;
|
|
338
|
+
this.currentHello = withCurrentGrant(session.hello, session.hello.status.grant);
|
|
339
|
+
this.fenceReads = usesNextRelayFence(opts.connector);
|
|
340
|
+
this.readFloor = session.hello.status.confirmedThrough;
|
|
341
|
+
this.files = new FilesApi(this);
|
|
342
|
+
this.presence = new PresenceApi(this);
|
|
343
|
+
this.writeLedger = new WriteLedger(opts.ledger ?? memoryWriteLedgerStore(), {
|
|
344
|
+
receipt: (id) => this.receipt(id),
|
|
345
|
+
pendingWrites: () => this.pendingWrites(),
|
|
346
|
+
});
|
|
347
|
+
this.attach(session);
|
|
348
|
+
void session.request("subscribe_status", null).catch(() => { });
|
|
349
|
+
if (opts.ledger)
|
|
350
|
+
this.startLedger();
|
|
351
|
+
}
|
|
352
|
+
/**
|
|
353
|
+
* Unconfirmed and refused writes, and the overall save state. Survives a
|
|
354
|
+
* reload when the connector persists it (web apps do). Event-driven: it
|
|
355
|
+
* settles from receipts and the replica's status push, never by polling.
|
|
356
|
+
*/
|
|
357
|
+
get ledger() {
|
|
358
|
+
this.startLedger();
|
|
359
|
+
return this.writeLedger;
|
|
360
|
+
}
|
|
361
|
+
startLedger() {
|
|
362
|
+
if (this.ledgerStarted)
|
|
363
|
+
return;
|
|
364
|
+
this.ledgerStarted = true;
|
|
365
|
+
let pending = -1;
|
|
366
|
+
this.onStatus((s) => {
|
|
367
|
+
const fell = pending > s.pending;
|
|
368
|
+
pending = s.pending;
|
|
369
|
+
this.updateDeferred();
|
|
370
|
+
if (fell)
|
|
371
|
+
void this.writeLedger.recheck();
|
|
372
|
+
else if (s.pending > this.writeLedger.pendingCount)
|
|
373
|
+
void this.writeLedger.adopt();
|
|
374
|
+
});
|
|
375
|
+
this.onLink(() => this.updateDeferred());
|
|
376
|
+
void this.writeLedger.restore();
|
|
377
|
+
}
|
|
378
|
+
updateDeferred() {
|
|
379
|
+
this.writeLedger.setDeferred(this.transportDeferred || this.linkState !== "open" || this.lastStatus.connection === "offline");
|
|
380
|
+
}
|
|
381
|
+
/** @internal The host's transport cannot deliver writes right now (offline, unreachable, busy). */
|
|
382
|
+
setTransportDeferred(deferred) {
|
|
383
|
+
this.transportDeferred = deferred;
|
|
384
|
+
if (this.ledgerStarted)
|
|
385
|
+
this.updateDeferred();
|
|
386
|
+
}
|
|
387
|
+
/**
|
|
388
|
+
* Connect to a replica and say hello. With `waitForDevice`, a private collection with
|
|
389
|
+
* no device online keeps retrying (calling `onWaiting`) instead of failing.
|
|
390
|
+
* A next-relay pre-live grant waits up to 30 seconds on this same connector.
|
|
391
|
+
* This is initial hello only: no application request or unknown write is replayed.
|
|
392
|
+
*/
|
|
393
|
+
static async connect(opts) {
|
|
394
|
+
// A next-relay connection keeps its original connector/policy across awaits.
|
|
395
|
+
const nextRelay = usesNextRelayFence(opts.connector);
|
|
396
|
+
if (nextRelay)
|
|
397
|
+
opts = { ...opts };
|
|
398
|
+
let delay = (opts.reconnect && opts.reconnect.minDelayMs) || 1000;
|
|
399
|
+
let signal = opts.signal;
|
|
400
|
+
let deadline;
|
|
401
|
+
let timer;
|
|
402
|
+
let timeout;
|
|
403
|
+
const stopped = () => opts.signal?.aborted || !timeout ? mdbaseError("cancelled", "connect aborted") : timeout;
|
|
404
|
+
try {
|
|
405
|
+
for (;;) {
|
|
406
|
+
try {
|
|
407
|
+
const attemptSignal = signal;
|
|
408
|
+
const session = await new Promise((resolve, reject) => {
|
|
409
|
+
const abort = () => reject(stopped());
|
|
410
|
+
if (attemptSignal?.aborted) {
|
|
411
|
+
abort();
|
|
412
|
+
return;
|
|
413
|
+
}
|
|
414
|
+
attemptSignal?.addEventListener("abort", abort, { once: true });
|
|
415
|
+
// A host bridge may ignore cancellation. Settle on the deadline and
|
|
416
|
+
// retire any late hello; it must never publish a client or send work.
|
|
417
|
+
void Session.open(opts.connector, MdbaseClient.helloParams(opts), attemptSignal).then(opened => {
|
|
418
|
+
attemptSignal?.removeEventListener("abort", abort);
|
|
419
|
+
if (attemptSignal?.aborted) {
|
|
420
|
+
try {
|
|
421
|
+
opened.close();
|
|
422
|
+
}
|
|
423
|
+
catch { /* Late initial hello: close best effort. */ }
|
|
424
|
+
reject(stopped());
|
|
425
|
+
}
|
|
426
|
+
else
|
|
427
|
+
resolve(opened);
|
|
428
|
+
}, error => { attemptSignal?.removeEventListener("abort", abort); reject(error); });
|
|
429
|
+
});
|
|
430
|
+
if (signal?.aborted) {
|
|
431
|
+
session.close();
|
|
432
|
+
throw stopped();
|
|
433
|
+
}
|
|
434
|
+
return new MdbaseClient(opts, session);
|
|
435
|
+
}
|
|
436
|
+
catch (e) {
|
|
437
|
+
if (signal?.aborted)
|
|
438
|
+
throw stopped();
|
|
439
|
+
// 4403 is an authority refusal, NOT pending. Only the fixed pre-live
|
|
440
|
+
// Native pipe close may defer this original next-relay initial hello.
|
|
441
|
+
const pending = nextRelay && isMdbaseError(e, "unavailable") && e.reason === "grant_inactive"
|
|
442
|
+
&& e.details instanceof Map && e.details.get("relayCloseCode") === 4000;
|
|
443
|
+
if (!(pending || opts.waitForDevice && isMdbaseError(e, "unavailable") && e.reason === "no_device_online"))
|
|
444
|
+
throw e;
|
|
445
|
+
if (pending && !deadline) {
|
|
446
|
+
deadline = new AbortController();
|
|
447
|
+
timeout = mdbaseError("unavailable", "The approved grant did not become live within 30 seconds", { reason: "grant_activation_timeout", details: e.details });
|
|
448
|
+
signal = opts.signal ? AbortSignal.any([opts.signal, deadline.signal]) : deadline.signal;
|
|
449
|
+
timer = setTimeout(() => deadline.abort(), 30_000);
|
|
450
|
+
}
|
|
451
|
+
opts.onWaiting?.(e);
|
|
452
|
+
await new Promise((res, rej) => {
|
|
453
|
+
const done = () => { clearTimeout(t); signal?.removeEventListener("abort", abort); };
|
|
454
|
+
const abort = () => { done(); rej(stopped()); };
|
|
455
|
+
const t = setTimeout(() => { done(); res(); }, pending ? Math.min(1000, delay) : delay);
|
|
456
|
+
if (signal?.aborted)
|
|
457
|
+
abort();
|
|
458
|
+
else
|
|
459
|
+
signal?.addEventListener("abort", abort, { once: true });
|
|
460
|
+
});
|
|
461
|
+
delay = Math.min(15_000, delay * 2);
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
finally {
|
|
466
|
+
clearTimeout(timer);
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
static helloParams(opts) {
|
|
470
|
+
const tz = opts.timezone ?? Intl.DateTimeFormat().resolvedOptions().timeZone;
|
|
471
|
+
return {
|
|
472
|
+
versions: API_VERSIONS,
|
|
473
|
+
clientName: opts.app.name,
|
|
474
|
+
clientVersion: opts.app.version,
|
|
475
|
+
features: [...new Set([...(opts.features ?? []), "declared-collection-setup-v1"])],
|
|
476
|
+
...(tz ? { timezone: tz } : {}),
|
|
477
|
+
};
|
|
478
|
+
}
|
|
479
|
+
// ---------------------------------------------------------------- session and link
|
|
480
|
+
/** The negotiated session, with its latest role-effective grant. */
|
|
481
|
+
get hello() {
|
|
482
|
+
this.currentSession();
|
|
483
|
+
return this.currentHello;
|
|
484
|
+
}
|
|
485
|
+
get collection() {
|
|
486
|
+
return this.collectionId;
|
|
487
|
+
}
|
|
488
|
+
get link() {
|
|
489
|
+
return this.linkState;
|
|
490
|
+
}
|
|
491
|
+
/**
|
|
492
|
+
* Why the link is not open: the last connect error. `reason: "no_device_online"`
|
|
493
|
+
* means a private collection has no device replica reachable right now; show
|
|
494
|
+
* "waiting for one of your devices" rather than an error.
|
|
495
|
+
*/
|
|
496
|
+
get linkProblem() {
|
|
497
|
+
return this.linkError;
|
|
498
|
+
}
|
|
499
|
+
onLink(fn) {
|
|
500
|
+
this.linkListeners.add(fn);
|
|
501
|
+
return () => this.linkListeners.delete(fn);
|
|
502
|
+
}
|
|
503
|
+
close() {
|
|
504
|
+
this.closing = true;
|
|
505
|
+
this.recordSessions.dispose();
|
|
506
|
+
this.session?.close();
|
|
507
|
+
this.setLink("closed");
|
|
508
|
+
this.wakeReadFence();
|
|
509
|
+
}
|
|
510
|
+
/** Next-relay consistency floor ONLY; not keyed/readable/Saved authority. */
|
|
511
|
+
get readFenced() { return this.readFencedAt !== null; }
|
|
512
|
+
/** @internal Wait for the next-relay consistency floor (other connectors never wait). */
|
|
513
|
+
async readFence(signal) {
|
|
514
|
+
if (!this.fenceReads)
|
|
515
|
+
return;
|
|
516
|
+
while (this.readFencedAt !== null) {
|
|
517
|
+
if (this.linkState === "closed")
|
|
518
|
+
throw this.linkError ?? mdbaseError("unavailable", "client closed");
|
|
519
|
+
if (signal?.aborted)
|
|
520
|
+
throw mdbaseError("cancelled", "aborted while the replica catches up");
|
|
521
|
+
await new Promise(res => {
|
|
522
|
+
const done = () => {
|
|
523
|
+
signal?.removeEventListener("abort", done);
|
|
524
|
+
const i = this.readFenceWaiters.indexOf(done);
|
|
525
|
+
if (i >= 0)
|
|
526
|
+
this.readFenceWaiters.splice(i, 1);
|
|
527
|
+
res();
|
|
528
|
+
};
|
|
529
|
+
this.readFenceWaiters.push(done);
|
|
530
|
+
signal?.addEventListener("abort", done, { once: true });
|
|
531
|
+
});
|
|
532
|
+
}
|
|
533
|
+
if (this.linkState === "closed")
|
|
534
|
+
throw this.linkError ?? mdbaseError("unavailable", "client closed");
|
|
535
|
+
if (signal?.aborted)
|
|
536
|
+
throw mdbaseError("cancelled", "aborted while the replica catches up");
|
|
537
|
+
}
|
|
538
|
+
/** Recheck session identity after waiting for catch-up (SDK-internal use). */
|
|
539
|
+
async readyForRead(signal, immediate = false) {
|
|
540
|
+
if (!this.fenceReads)
|
|
541
|
+
return immediate ? this.currentSession() : this.ready(signal);
|
|
542
|
+
for (;;) {
|
|
543
|
+
const s = immediate ? this.currentSession() : await this.ready(signal);
|
|
544
|
+
await this.readFence(signal);
|
|
545
|
+
if (s === this.session && !s.closed && this.readFencedAt === null)
|
|
546
|
+
return s;
|
|
547
|
+
if (immediate)
|
|
548
|
+
this.currentSession();
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
wakeReadFence() { for (const w of this.readFenceWaiters.splice(0))
|
|
552
|
+
w(); }
|
|
553
|
+
noteReplica(s) {
|
|
554
|
+
if (!this.fenceReads)
|
|
555
|
+
return;
|
|
556
|
+
const behind = s.hello.status.confirmedThrough < this.readFloor;
|
|
557
|
+
this.readFencedAt = behind ? this.readFloor : null;
|
|
558
|
+
if (!behind)
|
|
559
|
+
this.wakeReadFence();
|
|
560
|
+
}
|
|
561
|
+
noteReceipt(r) {
|
|
562
|
+
if (this.ledgerStarted)
|
|
563
|
+
this.writeLedger.observe(r);
|
|
564
|
+
if (this.fenceReads && this.readFencedAt === null && r.state === "confirmed" && r.seq !== undefined) {
|
|
565
|
+
this.readFloor = Math.max(this.readFloor, r.seq);
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
/** The open session, or throw `unavailable` (SDK-internal use). */
|
|
569
|
+
currentSession() {
|
|
570
|
+
if (!this.session || this.session.closed) {
|
|
571
|
+
if (this.linkState === "closed" && this.linkError)
|
|
572
|
+
throw this.linkError;
|
|
573
|
+
throw mdbaseError("unavailable", "not connected to the replica", { reason: "reconnecting" });
|
|
574
|
+
}
|
|
575
|
+
return this.session;
|
|
576
|
+
}
|
|
577
|
+
/** @internal Wait until a session is open (or the client closes). */
|
|
578
|
+
async ready(signal) {
|
|
579
|
+
for (;;) {
|
|
580
|
+
if (this.linkState === "closed")
|
|
581
|
+
throw this.linkError ?? mdbaseError("unavailable", "client closed");
|
|
582
|
+
if (this.session && !this.session.closed)
|
|
583
|
+
return this.session;
|
|
584
|
+
if (signal?.aborted)
|
|
585
|
+
throw mdbaseError("cancelled", "aborted while reconnecting");
|
|
586
|
+
await new Promise((res) => {
|
|
587
|
+
this.reconnectWaiters.push(res);
|
|
588
|
+
signal?.addEventListener("abort", () => res(), { once: true });
|
|
589
|
+
});
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
/**
|
|
593
|
+
* Advanced: run a request on the current session; on a dropped link, wait for the
|
|
594
|
+
* reconnect and retry when `retry` (idempotent requests only).
|
|
595
|
+
*/
|
|
596
|
+
async call(method, p, o = {}) {
|
|
597
|
+
// Pin the decoder before any route/request await. Observation must use the
|
|
598
|
+
// SAME codec passed to dispatch, not caller options mutated while pending.
|
|
599
|
+
const codec = o.codec;
|
|
600
|
+
const read = this.fenceReads && READ_METHODS.has(method);
|
|
601
|
+
for (;;) {
|
|
602
|
+
const s = read ? await this.readyForRead(o.signal, !o.retry) : o.retry ? await this.ready(o.signal) : this.currentSession();
|
|
603
|
+
try {
|
|
604
|
+
const r = await s.request(method, p, { ...(o.signal ? { signal: o.signal } : {}), ...(codec ? { codec } : {}) });
|
|
605
|
+
if (read && (s !== this.session || s.closed || this.closing)) {
|
|
606
|
+
if (o.retry && !this.closing)
|
|
607
|
+
continue;
|
|
608
|
+
throw mdbaseError("unavailable", "the session changed during the read", { reason: "reconnecting" });
|
|
609
|
+
}
|
|
610
|
+
// Explicit receipt probes are not reads, but their decoded confirmed
|
|
611
|
+
// position contributes to next-relay consistency just like a push.
|
|
612
|
+
// Never observe a late/closed session or an arbitrary caller codec.
|
|
613
|
+
if (s === this.session && !s.closed && !this.closing &&
|
|
614
|
+
codec === receiptCodec && (method === "receipt" || method === "await"))
|
|
615
|
+
this.noteReceipt(r);
|
|
616
|
+
return r;
|
|
617
|
+
}
|
|
618
|
+
catch (e) {
|
|
619
|
+
const lost = isMdbaseError(e, "unavailable") && s.closed;
|
|
620
|
+
if (!(o.retry && lost && this.opts.reconnect !== false && !this.closing))
|
|
621
|
+
throw e;
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
/** @internal Register work to redo on every new session (re-subscribe). */
|
|
626
|
+
onSession(fn) {
|
|
627
|
+
this.restorers.push(fn);
|
|
628
|
+
}
|
|
629
|
+
/** Pass head witnesses between replicas (log-entry.md §11); best effort. */
|
|
630
|
+
relayWitnesses(s) {
|
|
631
|
+
const own = s.hello.headWitness;
|
|
632
|
+
if (own)
|
|
633
|
+
rememberWitness(this.collectionId, own, s.device);
|
|
634
|
+
const others = witnessesFor(this.collectionId, own);
|
|
635
|
+
if (others.length) {
|
|
636
|
+
// Proposed method; a replica without it answers unknown_method, which is fine.
|
|
637
|
+
void s.request("report_witnesses", params([[0, others]])).catch(() => { });
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
/** Transport-authenticated replica identity (Noise target), NOT hello/witness
|
|
641
|
+
* self-claims. Absent when the transport has no authenticated device identity. */
|
|
642
|
+
get authenticatedDevice() {
|
|
643
|
+
return this.currentSession().device;
|
|
644
|
+
}
|
|
645
|
+
/** The replica's latest signed head witness from `hello`, if it sent one. */
|
|
646
|
+
get headWitness() {
|
|
647
|
+
return this.session?.hello.headWitness;
|
|
648
|
+
}
|
|
649
|
+
attach(s) {
|
|
650
|
+
this.relayWitnesses(s);
|
|
651
|
+
s.onPush("query_update", (p) => {
|
|
652
|
+
if (this.fenceReads && this.session !== s)
|
|
653
|
+
return;
|
|
654
|
+
let u;
|
|
655
|
+
try {
|
|
656
|
+
u = queryUpdate.dec(p);
|
|
657
|
+
}
|
|
658
|
+
catch {
|
|
659
|
+
return;
|
|
660
|
+
}
|
|
661
|
+
const l = this.livesBySub.get(u.sub);
|
|
662
|
+
if (l)
|
|
663
|
+
l._apply(u);
|
|
664
|
+
else if (this.subscribing > 0) {
|
|
665
|
+
// The first push can overtake the `subscribe` response: hold it briefly.
|
|
666
|
+
// Bounded: a few pushes per pending subscribe at most.
|
|
667
|
+
const q = this.early.get(u.sub) ?? [];
|
|
668
|
+
if (q.length < 64 && this.early.size < 64) {
|
|
669
|
+
q.push(u);
|
|
670
|
+
this.early.set(u.sub, q);
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
});
|
|
674
|
+
s.onPush("receipt", (p) => {
|
|
675
|
+
try {
|
|
676
|
+
const r = receiptCodec.dec(p);
|
|
677
|
+
if (this.session === s)
|
|
678
|
+
this.noteReceipt(r);
|
|
679
|
+
const w = this.writes.get(r.mutation);
|
|
680
|
+
if (w) {
|
|
681
|
+
w.update(r);
|
|
682
|
+
if (!needsReceipt(w.receipt))
|
|
683
|
+
this.writes.delete(r.mutation);
|
|
684
|
+
}
|
|
685
|
+
else {
|
|
686
|
+
// The push can overtake the `submit` response; keep it for `_track`.
|
|
687
|
+
const prior = this.unclaimed.get(r.mutation);
|
|
688
|
+
this.unclaimed.set(r.mutation, prior ? mergePublication(r, prior) : r);
|
|
689
|
+
if (this.unclaimed.size > 256)
|
|
690
|
+
this.unclaimed.delete(this.unclaimed.keys().next().value);
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
catch {
|
|
694
|
+
// ignore malformed receipts from a newer replica
|
|
695
|
+
}
|
|
696
|
+
});
|
|
697
|
+
s.onPush("status", (p) => {
|
|
698
|
+
if (this.session !== s)
|
|
699
|
+
return;
|
|
700
|
+
try {
|
|
701
|
+
this.setStatus(syncStatus.dec(p));
|
|
702
|
+
}
|
|
703
|
+
catch {
|
|
704
|
+
// ignore
|
|
705
|
+
}
|
|
706
|
+
});
|
|
707
|
+
s.onPush("holds", (p) => {
|
|
708
|
+
try {
|
|
709
|
+
this.emitHolds(list(hold).dec(p));
|
|
710
|
+
}
|
|
711
|
+
catch {
|
|
712
|
+
// ignore
|
|
713
|
+
}
|
|
714
|
+
});
|
|
715
|
+
s.onPush("conflicts", (p) => {
|
|
716
|
+
try {
|
|
717
|
+
this.emitConflicts(list(conflictEntry).dec(p));
|
|
718
|
+
}
|
|
719
|
+
catch {
|
|
720
|
+
// ignore
|
|
721
|
+
}
|
|
722
|
+
});
|
|
723
|
+
s.onPush("closed", (p) => {
|
|
724
|
+
// The replica ended the session (grant revoked, collection gone).
|
|
725
|
+
let err;
|
|
726
|
+
try {
|
|
727
|
+
err = new MdbaseError(problemCodec.dec(p));
|
|
728
|
+
}
|
|
729
|
+
catch {
|
|
730
|
+
err = mdbaseError("unauthenticated", "the replica closed the session");
|
|
731
|
+
}
|
|
732
|
+
s.closeWith(err);
|
|
733
|
+
});
|
|
734
|
+
s.handle("fence_apply", async (p) => {
|
|
735
|
+
if (!this.fenceHandler) {
|
|
736
|
+
throw mdbaseError("invalid_request", "this client offers no fence", { reason: "unknown_method" });
|
|
737
|
+
}
|
|
738
|
+
return fenceResult.enc(await this.fenceHandler(fenceApply.dec(p)));
|
|
739
|
+
});
|
|
740
|
+
s.onClose((e) => this.onSessionClosed(s, e));
|
|
741
|
+
}
|
|
742
|
+
onSessionClosed(s, e) {
|
|
743
|
+
if (this.session !== s)
|
|
744
|
+
return;
|
|
745
|
+
for (const l of this.lives) {
|
|
746
|
+
l._setSub(null);
|
|
747
|
+
l._markStale();
|
|
748
|
+
}
|
|
749
|
+
this.livesBySub.clear();
|
|
750
|
+
this.early.clear();
|
|
751
|
+
if (this.closing || this.opts.reconnect === false) {
|
|
752
|
+
this.setLink("closed", e ?? null);
|
|
753
|
+
return;
|
|
754
|
+
}
|
|
755
|
+
if (e && (e.code === "unauthenticated" || e.code === "upgrade_required" || e.code === "forbidden")) {
|
|
756
|
+
// Retrying cannot help: the app must reauthorize or upgrade.
|
|
757
|
+
this.setLink("closed", e);
|
|
758
|
+
return;
|
|
759
|
+
}
|
|
760
|
+
this.setLink("reconnecting", e ?? null);
|
|
761
|
+
void this.reconnectLoop();
|
|
762
|
+
}
|
|
763
|
+
async reconnectLoop() {
|
|
764
|
+
const cfg = this.opts.reconnect || {};
|
|
765
|
+
const min = cfg.minDelayMs ?? 250;
|
|
766
|
+
const max = cfg.maxDelayMs ?? 15_000;
|
|
767
|
+
let delay = min;
|
|
768
|
+
while (!this.closing) {
|
|
769
|
+
try {
|
|
770
|
+
const s = await Session.open(this.opts.connector, MdbaseClient.helloParams(this.opts));
|
|
771
|
+
if (this.closing) {
|
|
772
|
+
s.close();
|
|
773
|
+
return;
|
|
774
|
+
}
|
|
775
|
+
if (s.hello.collection !== this.collectionId) {
|
|
776
|
+
s.close();
|
|
777
|
+
throw mdbaseError("invalid_request", "reconnected to a different collection");
|
|
778
|
+
}
|
|
779
|
+
this.session = s;
|
|
780
|
+
this.currentHello = withCurrentGrant(s.hello, s.hello.status.grant);
|
|
781
|
+
this.attach(s);
|
|
782
|
+
this.noteReplica(s);
|
|
783
|
+
this.setStatus(s.hello.status);
|
|
784
|
+
this.setLink("open");
|
|
785
|
+
for (const w of this.reconnectWaiters.splice(0))
|
|
786
|
+
w();
|
|
787
|
+
await this.restore(s);
|
|
788
|
+
return;
|
|
789
|
+
}
|
|
790
|
+
catch (e) {
|
|
791
|
+
if (isMdbaseError(e) && ["unauthenticated", "forbidden", "upgrade_required"].includes(e.code)) {
|
|
792
|
+
this.setLink("closed", e);
|
|
793
|
+
return;
|
|
794
|
+
}
|
|
795
|
+
if (isMdbaseError(e))
|
|
796
|
+
this.setLink("reconnecting", e);
|
|
797
|
+
// Jittered exponential backoff (at least the replica's retry hint).
|
|
798
|
+
const hint = isMdbaseError(e) ? (e.retryAfterMs ?? 0) : 0;
|
|
799
|
+
await new Promise((r) => setTimeout(r, Math.max(hint, delay * (0.5 + Math.random() / 2))));
|
|
800
|
+
delay = Math.min(max, delay * 2);
|
|
801
|
+
}
|
|
802
|
+
}
|
|
803
|
+
}
|
|
804
|
+
async restore(s) {
|
|
805
|
+
const jobs = [];
|
|
806
|
+
jobs.push(s.request("subscribe_status", null).catch(() => { }));
|
|
807
|
+
if (this.holdsWatch.listeners.size)
|
|
808
|
+
jobs.push(this.refreshHolds(true).catch(() => { }));
|
|
809
|
+
if (this.conflictsWatch.listeners.size)
|
|
810
|
+
jobs.push(this.refreshConflicts(true).catch(() => { }));
|
|
811
|
+
for (const l of this.lives)
|
|
812
|
+
jobs.push(this._resubscribeLive(l, false, s).catch(() => { }));
|
|
813
|
+
// Recover unsettled confirmation OR publication after a dropped session.
|
|
814
|
+
for (const [id, w] of this.writes) {
|
|
815
|
+
jobs.push(this.awaitReceipt(id)
|
|
816
|
+
.then((r) => {
|
|
817
|
+
w.update(r);
|
|
818
|
+
if (!needsReceipt(w.receipt))
|
|
819
|
+
this.writes.delete(id);
|
|
820
|
+
})
|
|
821
|
+
.catch(() => { }));
|
|
822
|
+
}
|
|
823
|
+
for (const fn of this.restorers)
|
|
824
|
+
jobs.push(Promise.resolve(fn(s)).catch(() => { }));
|
|
825
|
+
await Promise.all(jobs);
|
|
826
|
+
}
|
|
827
|
+
setLink(s, why = null) {
|
|
828
|
+
if (this.linkState === "closed")
|
|
829
|
+
return;
|
|
830
|
+
if (s === "closed" && this.fenceReads)
|
|
831
|
+
queueMicrotask(() => this.wakeReadFence());
|
|
832
|
+
if (this.linkState === s && this.linkError?.reason === why?.reason)
|
|
833
|
+
return;
|
|
834
|
+
this.linkState = s;
|
|
835
|
+
this.linkError = s === "open" ? null : why;
|
|
836
|
+
if (s === "closed")
|
|
837
|
+
for (const wake of this.reconnectWaiters.splice(0))
|
|
838
|
+
wake();
|
|
839
|
+
for (const fn of [...this.linkListeners])
|
|
840
|
+
fn(s, this.linkError);
|
|
841
|
+
}
|
|
842
|
+
// ---------------------------------------------------------------- status (§7)
|
|
843
|
+
/** The latest known status. */
|
|
844
|
+
get status() {
|
|
845
|
+
return this.lastStatus;
|
|
846
|
+
}
|
|
847
|
+
/** Fresh status from the replica. */
|
|
848
|
+
async getStatus() {
|
|
849
|
+
if (!this.fenceReads) {
|
|
850
|
+
const s = await this.call("get_status", null, { codec: syncStatus, retry: true });
|
|
851
|
+
this.setStatus(s);
|
|
852
|
+
return s;
|
|
853
|
+
}
|
|
854
|
+
for (;;) {
|
|
855
|
+
const session = await this.ready();
|
|
856
|
+
let s;
|
|
857
|
+
try {
|
|
858
|
+
s = await session.request("get_status", null, { codec: syncStatus });
|
|
859
|
+
}
|
|
860
|
+
catch (e) {
|
|
861
|
+
if (isMdbaseError(e, "unavailable") && session.closed && !this.closing && this.opts.reconnect !== false)
|
|
862
|
+
continue;
|
|
863
|
+
throw e;
|
|
864
|
+
}
|
|
865
|
+
if (session !== this.session || session.closed) {
|
|
866
|
+
if (this.closing)
|
|
867
|
+
throw mdbaseError("unavailable", "client closed");
|
|
868
|
+
continue;
|
|
869
|
+
}
|
|
870
|
+
this.setStatus(s);
|
|
871
|
+
return s;
|
|
872
|
+
}
|
|
873
|
+
}
|
|
874
|
+
/**
|
|
875
|
+
* Read the retained historical chain at a captured hosted fence. READ/session
|
|
876
|
+
* authorization is unchanged. No automatic retry/reconnect: a handover must
|
|
877
|
+
* revalidate the authenticated session and all readiness gates after any await.
|
|
878
|
+
* Missing chain or any error means remain hosted; never substitute asOf or the
|
|
879
|
+
* current head/generations. Decoding does not authenticate a witness or switch.
|
|
880
|
+
*/
|
|
881
|
+
async appliedPrefix(seq, signal) {
|
|
882
|
+
const p = appliedPrefixParams.enc({ seq });
|
|
883
|
+
const r = await this.call("applied_prefix", p, {
|
|
884
|
+
codec: appliedPrefixCodec, retry: false, ...(signal ? { signal } : {}),
|
|
885
|
+
});
|
|
886
|
+
if (r.seq !== seq || (r.chain !== undefined && (seq === 0 || r.appliedThrough < seq))) {
|
|
887
|
+
throw mdbaseError("invalid_request", "applied_prefix: inconsistent historical proof", "prefix_response");
|
|
888
|
+
}
|
|
889
|
+
return r;
|
|
890
|
+
}
|
|
891
|
+
/** Call `fn` with the status now and on every change (pushed, ≤ 4/s). */
|
|
892
|
+
onStatus(fn) {
|
|
893
|
+
this.statusListeners.add(fn);
|
|
894
|
+
fn(this.lastStatus);
|
|
895
|
+
return () => this.statusListeners.delete(fn);
|
|
896
|
+
}
|
|
897
|
+
setStatus(s) {
|
|
898
|
+
if (this.fenceReads) {
|
|
899
|
+
if (this.readFencedAt !== null && s.confirmedThrough >= this.readFencedAt) {
|
|
900
|
+
this.readFencedAt = null;
|
|
901
|
+
this.wakeReadFence();
|
|
902
|
+
}
|
|
903
|
+
if (this.readFencedAt === null)
|
|
904
|
+
this.readFloor = Math.max(this.readFloor, s.confirmedThrough);
|
|
905
|
+
}
|
|
906
|
+
this.lastStatus = s;
|
|
907
|
+
this.currentHello = withCurrentGrant(this.currentHello, s.grant);
|
|
908
|
+
for (const fn of [...this.statusListeners])
|
|
909
|
+
fn(s);
|
|
910
|
+
}
|
|
911
|
+
// ---------------------------------------------------------------- reads (§4)
|
|
912
|
+
/**
|
|
913
|
+
* Catalog summary: types with their version, source revision and contract
|
|
914
|
+
* implementations, contracts, settings (§4.4). `definitions` adds each type's
|
|
915
|
+
* definition and schema and each contract's schemas, bounded by the replica
|
|
916
|
+
* (`too_large` when over budget; read them with `resources.get` instead).
|
|
917
|
+
* A producer that cannot describe definitions fails `upgrade_required`.
|
|
918
|
+
*/
|
|
919
|
+
async describe(options = {}) {
|
|
920
|
+
const o = options instanceof AbortSignal ? { signal: options } : options;
|
|
921
|
+
const result = await this.call("describe", o.definitions ? params([[0, true]]) : null, {
|
|
922
|
+
codec: describeResult,
|
|
923
|
+
retry: true,
|
|
924
|
+
...(o.signal ? { signal: o.signal } : {}),
|
|
925
|
+
});
|
|
926
|
+
if (o.definitions && (result.types.some((t) => t.definition === undefined) || result.contracts.some((c) => c.schemas === undefined))) {
|
|
927
|
+
throw mdbaseError("upgrade_required", "this replica does not describe definitions", "describe_definitions_unsupported");
|
|
928
|
+
}
|
|
929
|
+
return result;
|
|
930
|
+
}
|
|
931
|
+
/**
|
|
932
|
+
* Create a type from its complete type-file `document`. The replica's Core
|
|
933
|
+
* derives the name and path (`<types_folder>/<name>.md`) from the local
|
|
934
|
+
* catalog, then the document is written create-only there, so a concurrent
|
|
935
|
+
* creator loses with `conflict`. Refusals: `invalid_record` /
|
|
936
|
+
* `invalid_type_definition` (unusable name, or the definition is invalid
|
|
937
|
+
* when written), `conflict` / `type_conflict` (the name exists under any
|
|
938
|
+
* casing) or `path_taken`, and `forbidden` without `definitions.manage`.
|
|
939
|
+
* Update a type with `resources.put(path, document, { baseRevision })`,
|
|
940
|
+
* using the path and revision from {@link describe}.
|
|
941
|
+
*/
|
|
942
|
+
async createType(document, o = {}) {
|
|
943
|
+
const { name, path } = await this.call("new_type_file", params([[0, document]]), {
|
|
944
|
+
codec: newTypeFile,
|
|
945
|
+
retry: true,
|
|
946
|
+
...(o.signal ? { signal: o.signal } : {}),
|
|
947
|
+
});
|
|
948
|
+
const write = await this.resources.put(path, document, { ...o, mustNotExist: true });
|
|
949
|
+
return { name, path, write };
|
|
950
|
+
}
|
|
951
|
+
/**
|
|
952
|
+
* Core's temporal typing for top-level fields under a query's exact `types` OR list
|
|
953
|
+
* (`date` / `date_time` only when every type agrees; otherwise `none`). The answer is
|
|
954
|
+
* bound to a catalog generation; nested paths are `none`. The SDK never infers typing.
|
|
955
|
+
*/
|
|
956
|
+
describeTyping(types, paths, signal) {
|
|
957
|
+
const strings = (a, max) => Array.isArray(a) && a.length <= max && a.every((s) => typeof s === "string" && s.length > 0 && s.length <= 1024);
|
|
958
|
+
if (!strings(types, DESCRIBE_TYPING_MAX_TYPES) || !strings(paths, DESCRIBE_TYPING_MAX_PATHS)) {
|
|
959
|
+
return Promise.reject(mdbaseError("invalid_request", "describe_typing: at most 64 types and 256 non-empty paths", "typing_bounds"));
|
|
960
|
+
}
|
|
961
|
+
return this.call("describe_typing", params([[0, [...types]], [1, [...paths]]]), {
|
|
962
|
+
codec: describeTypingResult, retry: true, ...(signal ? { signal } : {}),
|
|
963
|
+
}).then((r) => {
|
|
964
|
+
if (r.fields.length !== paths.length || r.fields.some((f, i) => f.path !== paths[i])) {
|
|
965
|
+
throw mdbaseError("internal", "describe_typing answered for different paths", "invalid_typing_response");
|
|
966
|
+
}
|
|
967
|
+
return r;
|
|
968
|
+
});
|
|
969
|
+
}
|
|
970
|
+
/** One record by ID or path. Throws `not_found`. */
|
|
971
|
+
get(ref, include, signal) {
|
|
972
|
+
return this.call("get", params([
|
|
973
|
+
[0, refValue(ref)],
|
|
974
|
+
[1, includeValue(include)],
|
|
975
|
+
]), { codec: recordView, retry: true, ...(signal ? { signal } : {}) });
|
|
976
|
+
}
|
|
977
|
+
/**
|
|
978
|
+
* One record's valid view through a record contract, mapped by the replica's
|
|
979
|
+
* Core. Refusals: `not_found` (no record, or `data_contract_not_implemented`
|
|
980
|
+
* when it does not implement the contract), `not_found`/`data_contract_not_found`,
|
|
981
|
+
* `invalid_request`/`data_contract_ambiguous` (select `type`; details: the types),
|
|
982
|
+
* `invalid_record`/`data_contract_record_invalid` (with issues), and
|
|
983
|
+
* `upgrade_required`/`contract_records_unsupported` from an older replica.
|
|
984
|
+
*/
|
|
985
|
+
async getContract(ref, contract, signal) {
|
|
986
|
+
const r = await this.contractCall("get_contract_v1", params([
|
|
987
|
+
[0, 1], [1, refValue(ref)], [2, contract.id], [3, contract.version], [4, contract.type],
|
|
988
|
+
]), contractRecordWire, signal);
|
|
989
|
+
if (r.version !== 1)
|
|
990
|
+
throw mdbaseError("upgrade_required", "unsupported contract record", "contract_records_unsupported");
|
|
991
|
+
return {
|
|
992
|
+
id: r.id, path: r.path, revision: r.revision, fields: r.fields,
|
|
993
|
+
contract: { id: contract.id, version: contract.version, digest: r.contractDigest, type: r.type, implementationDigest: r.implementationDigest },
|
|
994
|
+
};
|
|
995
|
+
}
|
|
996
|
+
/** @internal Contract fields as ordinary record changes, mapped by the replica. */
|
|
997
|
+
async contractPlan(contract, target, fields, unset, signal, promote = false) {
|
|
998
|
+
const plan = await this.contractCall("map_contract_write_v1", params([
|
|
999
|
+
[0, 1], [1, contract.id], [2, contract.version], [3, contract.type],
|
|
1000
|
+
[4, target === undefined ? undefined : refValue(target)], [5, fields], [6, unset?.length ? unset : undefined],
|
|
1001
|
+
[7, promote ? true : undefined],
|
|
1002
|
+
]), contractWritePlan, signal);
|
|
1003
|
+
if (plan.version !== 1)
|
|
1004
|
+
throw mdbaseError("upgrade_required", "unsupported contract write plan", "contract_records_unsupported");
|
|
1005
|
+
return plan;
|
|
1006
|
+
}
|
|
1007
|
+
contractCall(method, p, codec, signal) {
|
|
1008
|
+
return this.call(method, p, { codec, retry: true, ...(signal ? { signal } : {}) }).catch((e) => {
|
|
1009
|
+
if (isMdbaseError(e, "invalid_request") && e.reason === "unknown_method") {
|
|
1010
|
+
throw mdbaseError("upgrade_required", "this replica does not serve contract-scoped records", "contract_records_unsupported");
|
|
1011
|
+
}
|
|
1012
|
+
throw e;
|
|
1013
|
+
});
|
|
1014
|
+
}
|
|
1015
|
+
/** Bounded ordered ordinary gets. Missing/refused reads fail the batch;
|
|
1016
|
+
* duplicate slots and the original signal are retained, not a snapshot. */
|
|
1017
|
+
readMany(refs, include, signal, options = {}) {
|
|
1018
|
+
return readManyRecords(this, refs, include, signal, options);
|
|
1019
|
+
}
|
|
1020
|
+
/** One record, or `null` when there is none. */
|
|
1021
|
+
async find(ref, include, signal) {
|
|
1022
|
+
try {
|
|
1023
|
+
return await this.get(ref, include, signal);
|
|
1024
|
+
}
|
|
1025
|
+
catch (e) {
|
|
1026
|
+
if (isMdbaseError(e, "not_found"))
|
|
1027
|
+
return null;
|
|
1028
|
+
throw e;
|
|
1029
|
+
}
|
|
1030
|
+
}
|
|
1031
|
+
/**
|
|
1032
|
+
* One page of a query, evaluated by the replica (body predicates and links
|
|
1033
|
+
* included, §4). Pass `result.cursor` back as `query.cursor` for the next.
|
|
1034
|
+
* `opts.contract` is not supported by the live replica dispatch. Supplying it
|
|
1035
|
+
* returns `invalid_request` with reason `unknown_param`; no contract filter
|
|
1036
|
+
* is applied. The option remains encoded unchanged for API compatibility.
|
|
1037
|
+
*/
|
|
1038
|
+
query(query, include, signal, opts = {}) {
|
|
1039
|
+
return this.call("query", params([
|
|
1040
|
+
[0, toValue(query)],
|
|
1041
|
+
[1, includeValue(include)],
|
|
1042
|
+
[2, opts.contract],
|
|
1043
|
+
]), { codec: queryResult, retry: true, ...(signal ? { signal } : {}) });
|
|
1044
|
+
}
|
|
1045
|
+
/** Canonical contract projections with validation issues and ordinary cursor
|
|
1046
|
+
* metadata. Positive page sizes above the producer's 128-record maximum clamp
|
|
1047
|
+
* to 128; use the returned cursor for the remaining records. Old producers
|
|
1048
|
+
* refuse; there is no SDK projection fallback. */
|
|
1049
|
+
async queryContract(contract, options = {}) {
|
|
1050
|
+
const requested = options.pageSize ?? 128;
|
|
1051
|
+
if (!Number.isInteger(requested) || requested < 1 || !contract.id || contract.id.length > 1024 || !contract.version || contract.version.length > 128
|
|
1052
|
+
|| (options.where !== undefined && (!options.where || options.where.length > 4096)))
|
|
1053
|
+
throw mdbaseError("invalid_request", "invalid contract page selection");
|
|
1054
|
+
const limit = Math.min(requested, 128);
|
|
1055
|
+
const result = await this.call("query_contract_v1", params([
|
|
1056
|
+
[0, 1], [1, contract.id], [2, contract.version], [3, limit], [4, options.cursor], [5, options.where],
|
|
1057
|
+
]), { codec: contractQueryResult, retry: true, ...(options.signal ? { signal: options.signal } : {}) });
|
|
1058
|
+
if (result.version !== 1)
|
|
1059
|
+
throw mdbaseError("upgrade_required", "unsupported contract query result", "contract_query_version");
|
|
1060
|
+
if (result.records.length !== result.page.records.length || result.records.some((record, i) => record.id !== result.page.records[i]?.id))
|
|
1061
|
+
throw mdbaseError("unavailable", "invalid contract query record binding", "contract_query_response");
|
|
1062
|
+
return result;
|
|
1063
|
+
}
|
|
1064
|
+
/** Records whose links resolve to `target` (index-backed, §4.2). */
|
|
1065
|
+
backlinks(target, o = {}) {
|
|
1066
|
+
return this.call("backlinks", params([
|
|
1067
|
+
[0, refValue(target)],
|
|
1068
|
+
[1, includeValue(o.include)],
|
|
1069
|
+
[2, o.limit],
|
|
1070
|
+
[3, o.cursor],
|
|
1071
|
+
]), { codec: backlinksResult, retry: true, ...(o.signal ? { signal: o.signal } : {}) });
|
|
1072
|
+
}
|
|
1073
|
+
/** Definitions: `mdbase.yaml`, type files, contract files (§4.1). */
|
|
1074
|
+
resources = {
|
|
1075
|
+
get: (path, signal) => this.call("get_resource", params([[0, path]]), { codec: resourceView, retry: true, ...(signal ? { signal } : {}) }),
|
|
1076
|
+
/** One bounded resource inventory page. Text defaults to false; limit defaults
|
|
1077
|
+
* to 64 (maximum 128). Preserve folder/text/limit on continuation. Collect all
|
|
1078
|
+
* pages before assessing a complete snapshot; stale/expired cursors require
|
|
1079
|
+
* explicit reassessment, never an automatic restart. */
|
|
1080
|
+
list: (o = {}) => this.call("list_resources", params([
|
|
1081
|
+
[0, o.folder],
|
|
1082
|
+
[1, o.text],
|
|
1083
|
+
[2, o.cursor],
|
|
1084
|
+
[3, o.limit],
|
|
1085
|
+
]), { codec: listResourcesResult, retry: true, ...(o.signal ? { signal: o.signal } : {}) }),
|
|
1086
|
+
/**
|
|
1087
|
+
* Write a resource. CAS: pass the revision you read as `baseRevision`, or
|
|
1088
|
+
* `mustNotExist` to create only (a taken path is `conflict` / `path_taken`). Not both.
|
|
1089
|
+
*/
|
|
1090
|
+
put: (path, doc, o = {}) => {
|
|
1091
|
+
if (o.baseRevision && o.mustNotExist) {
|
|
1092
|
+
return Promise.reject(mdbaseError("invalid_request", "baseRevision and mustNotExist are exclusive"));
|
|
1093
|
+
}
|
|
1094
|
+
return this.one({
|
|
1095
|
+
kind: "resource_put",
|
|
1096
|
+
path,
|
|
1097
|
+
doc,
|
|
1098
|
+
...(o.baseRevision ? { baseRevision: o.baseRevision } : {}),
|
|
1099
|
+
...(o.mustNotExist ? { mustNotExist: true } : {}),
|
|
1100
|
+
}, o);
|
|
1101
|
+
},
|
|
1102
|
+
delete: (path, o = {}) => this.one({ kind: "resource_delete", path, ...(o.baseRevision ? { baseRevision: o.baseRevision } : {}) }, o),
|
|
1103
|
+
};
|
|
1104
|
+
/** Type packs: `plan` (writes nothing), review, then `apply(plan)` as one
|
|
1105
|
+
* guarded all-or-nothing mutation (needs `definitions.manage`). */
|
|
1106
|
+
typePacks = Object.freeze({
|
|
1107
|
+
plan: (pack, options) => planTypePack(this, pack, options),
|
|
1108
|
+
apply: (plan, o) => applyTypePack(this, plan, o),
|
|
1109
|
+
});
|
|
1110
|
+
/** Shared native record editor leases; no app-specific record index. */
|
|
1111
|
+
recordSessions = new RecordsApi(this);
|
|
1112
|
+
records = Object.freeze({ open: (ref, options) => this.recordSessions.open(ref, options) });
|
|
1113
|
+
/** Saved views (§4.3). Source helpers use ordinary record ops/full CAS reads;
|
|
1114
|
+
* web app-host Bases reads use the separate native AppBases methods. */
|
|
1115
|
+
views = {
|
|
1116
|
+
createSource: (input, options) => createViewSource(this, input, options),
|
|
1117
|
+
updateSource: (seen, document, options) => updateViewSource(this, seen, document, options),
|
|
1118
|
+
getSourceRecord: (source, options = {}) => readViewSourceRecord(this, source, options),
|
|
1119
|
+
/** `mdbase.view` sources (spec 12 `list_views` shape), resolved over served
|
|
1120
|
+
* contract queries: the native runtime has no `list_views` RPC. */
|
|
1121
|
+
list: (folder, signal) => listSavedViews(this, folder, signal),
|
|
1122
|
+
/** Spec 11 named-view resolution, executed as one ordinary native query. */
|
|
1123
|
+
execute: (source, view, o = {}) => executeSavedView(this, source, view, o),
|
|
1124
|
+
readSource: (source, signal) => readSavedViewSource(this, source, signal),
|
|
1125
|
+
};
|
|
1126
|
+
/**
|
|
1127
|
+
* Pending mutations of this session's owner (its grant, or the device), oldest
|
|
1128
|
+
* first (§6): a new session after a reload sees writes made before it. Follow
|
|
1129
|
+
* each with `awaitReceipt`, or keep the `mutationId` you chose and call
|
|
1130
|
+
* `receipt(id)` / `awaitReceipt(id)`. Pages through everything.
|
|
1131
|
+
*/
|
|
1132
|
+
async pendingWrites(signal) {
|
|
1133
|
+
const out = [];
|
|
1134
|
+
let after;
|
|
1135
|
+
for (;;) {
|
|
1136
|
+
const page = await this.call("list_pending", params([[0, after ? uuidCodec.enc(after) : undefined]]), { codec: listPendingResult, retry: true, ...(signal ? { signal } : {}) });
|
|
1137
|
+
out.push(...page.pending);
|
|
1138
|
+
if (!page.next)
|
|
1139
|
+
return out;
|
|
1140
|
+
after = page.next;
|
|
1141
|
+
}
|
|
1142
|
+
}
|
|
1143
|
+
/**
|
|
1144
|
+
* What a rename would do to links (dry run, §5): the rewrites, and links left broken.
|
|
1145
|
+
* Throws the problem the real rename would get.
|
|
1146
|
+
*/
|
|
1147
|
+
async preflightRename(target, to, o = {}) {
|
|
1148
|
+
return this.preflight({ kind: "rename", id: target.id, from: target.path, to, updateRefs: o.updateRefs ?? true });
|
|
1149
|
+
}
|
|
1150
|
+
/** Links a delete would break (dry run, §5). */
|
|
1151
|
+
async preflightDelete(target) {
|
|
1152
|
+
return this.preflight({ kind: "delete", id: refId(target) });
|
|
1153
|
+
}
|
|
1154
|
+
async preflight(op) {
|
|
1155
|
+
const [w] = await this.submit([op], { dryRun: true });
|
|
1156
|
+
const r = w.receipt;
|
|
1157
|
+
if (r.state === "rejected")
|
|
1158
|
+
throw new MdbaseError(r.problem);
|
|
1159
|
+
return r.preflight ?? { rewrites: [], broken: [] };
|
|
1160
|
+
}
|
|
1161
|
+
/** Every page of a query. Cursor resets require explicit caller clearing;
|
|
1162
|
+
* otherwise cursor_expired/cursor_stale is thrown unchanged. */
|
|
1163
|
+
async *pages(query, include, signal, options = {}) {
|
|
1164
|
+
const original = snapshotQueryValue(query);
|
|
1165
|
+
const originalInclude = include ? { ...include } : undefined;
|
|
1166
|
+
let cursor = original.cursor, restarted = false, pagesYielded = 0;
|
|
1167
|
+
delete original.cursor;
|
|
1168
|
+
for (;;) {
|
|
1169
|
+
signal?.throwIfAborted();
|
|
1170
|
+
let page;
|
|
1171
|
+
try {
|
|
1172
|
+
page = await this.query(cursor !== undefined ? { ...original, cursor } : { ...original }, originalInclude, signal);
|
|
1173
|
+
}
|
|
1174
|
+
catch (error) {
|
|
1175
|
+
if (!options.onReset || restarted || !isMdbaseError(error, "invalid_request") || error.recovery !== "fix_request" ||
|
|
1176
|
+
(error.reason !== "cursor_expired" && error.reason !== "cursor_stale"))
|
|
1177
|
+
throw error;
|
|
1178
|
+
signal?.throwIfAborted();
|
|
1179
|
+
await options.onReset({ reason: error.reason, error, pagesYielded, ...(signal ? { signal } : {}) });
|
|
1180
|
+
signal?.throwIfAborted();
|
|
1181
|
+
restarted = true;
|
|
1182
|
+
cursor = undefined;
|
|
1183
|
+
pagesYielded = 0;
|
|
1184
|
+
continue;
|
|
1185
|
+
}
|
|
1186
|
+
signal?.throwIfAborted();
|
|
1187
|
+
yield page;
|
|
1188
|
+
pagesYielded++;
|
|
1189
|
+
if (!page.cursor)
|
|
1190
|
+
return;
|
|
1191
|
+
cursor = page.cursor;
|
|
1192
|
+
}
|
|
1193
|
+
}
|
|
1194
|
+
/** Shared alias of pages(): whole native pages, no implicit cursor reset. */
|
|
1195
|
+
queryPages(query, include, signal, options = {}) {
|
|
1196
|
+
return readQueryPages(this, query, include, signal, options);
|
|
1197
|
+
}
|
|
1198
|
+
/**
|
|
1199
|
+
* A live query. Subscribe for pushes; keep lists cheap with a `limit` window and no
|
|
1200
|
+
* bodies, and widen the window with `setQuery`.
|
|
1201
|
+
*/
|
|
1202
|
+
live(query, include) {
|
|
1203
|
+
const l = new LiveQuery(this, query, include);
|
|
1204
|
+
this.lives.add(l);
|
|
1205
|
+
void this._resubscribeLive(l, true);
|
|
1206
|
+
return l;
|
|
1207
|
+
}
|
|
1208
|
+
/** @internal */
|
|
1209
|
+
async _resubscribeLive(l, first = false, forSession) {
|
|
1210
|
+
const old = l._sub;
|
|
1211
|
+
if (old !== null) {
|
|
1212
|
+
this.livesBySub.delete(old);
|
|
1213
|
+
l._setSub(null);
|
|
1214
|
+
void this.call("unsubscribe", params([[0, old]])).catch(() => { });
|
|
1215
|
+
}
|
|
1216
|
+
try {
|
|
1217
|
+
const s = await this.readyForRead();
|
|
1218
|
+
if (this.fenceReads && (l.closed || (forSession && forSession !== s)))
|
|
1219
|
+
return;
|
|
1220
|
+
this.subscribing++;
|
|
1221
|
+
let id;
|
|
1222
|
+
try {
|
|
1223
|
+
const r = await s.request("subscribe", l._params());
|
|
1224
|
+
id = uint.dec(r.get(0) ?? null);
|
|
1225
|
+
}
|
|
1226
|
+
finally {
|
|
1227
|
+
this.subscribing--;
|
|
1228
|
+
}
|
|
1229
|
+
const early = this.early.get(id) ?? [];
|
|
1230
|
+
this.early.delete(id);
|
|
1231
|
+
if (this.subscribing === 0)
|
|
1232
|
+
this.early.clear();
|
|
1233
|
+
if (l.closed || (this.fenceReads && (s !== this.session || s.closed))) {
|
|
1234
|
+
void s.request("unsubscribe", params([[0, id]])).catch(() => { });
|
|
1235
|
+
return;
|
|
1236
|
+
}
|
|
1237
|
+
l._setSub(id);
|
|
1238
|
+
this.livesBySub.set(id, l);
|
|
1239
|
+
for (const u of early)
|
|
1240
|
+
l._apply(u);
|
|
1241
|
+
}
|
|
1242
|
+
catch (e) {
|
|
1243
|
+
const err = schemaToError(e, "subscribe");
|
|
1244
|
+
// A dropped link is retried by the reconnect loop; anything else is the query's error.
|
|
1245
|
+
if (!(isMdbaseError(err, "unavailable") && this.linkState === "reconnecting"))
|
|
1246
|
+
l._fail(err, first);
|
|
1247
|
+
}
|
|
1248
|
+
}
|
|
1249
|
+
/** @internal */
|
|
1250
|
+
_closeLive(l) {
|
|
1251
|
+
this.lives.delete(l);
|
|
1252
|
+
const id = l._sub;
|
|
1253
|
+
if (id !== null) {
|
|
1254
|
+
this.livesBySub.delete(id);
|
|
1255
|
+
void this.call("unsubscribe", params([[0, id]])).catch(() => { });
|
|
1256
|
+
}
|
|
1257
|
+
}
|
|
1258
|
+
/** One page of the change feed. A `reset: true` result means "re-read what you cache". */
|
|
1259
|
+
changes(cursor, limit, signal) {
|
|
1260
|
+
return this.call("changes", params([
|
|
1261
|
+
[0, cursor ?? null],
|
|
1262
|
+
[1, limit],
|
|
1263
|
+
]), { codec: changesResult, retry: true, ...(signal ? { signal } : {}) });
|
|
1264
|
+
}
|
|
1265
|
+
/**
|
|
1266
|
+
* Follow the change feed from `cursor`. `onChanges` gets every batch; `reset: true`
|
|
1267
|
+
* means the app must re-read what it caches. Survives reconnects.
|
|
1268
|
+
* `ready` confirms the first watch ACK, not a complete/current collection cache.
|
|
1269
|
+
* After reconnect, observe `state`; an already-resolved promise is not freshness.
|
|
1270
|
+
*/
|
|
1271
|
+
watchChanges(cursor, onChanges) {
|
|
1272
|
+
let last = cursor, stopped = false, epoch = 0;
|
|
1273
|
+
let state = "starting", error = null;
|
|
1274
|
+
let active = null, controller = null;
|
|
1275
|
+
let offPush = null, offClose = null;
|
|
1276
|
+
let clearEarly = () => { };
|
|
1277
|
+
let resolveReady, rejectReady;
|
|
1278
|
+
const ready = new Promise((resolve, reject) => { resolveReady = resolve; rejectReady = reject; });
|
|
1279
|
+
// Legacy callers may use only the callable stop function.
|
|
1280
|
+
ready.catch(() => { });
|
|
1281
|
+
const listeners = new Set();
|
|
1282
|
+
const notify = (listener) => {
|
|
1283
|
+
try {
|
|
1284
|
+
listener(state, error);
|
|
1285
|
+
}
|
|
1286
|
+
catch { /* Observer exceptions cannot break cleanup. */ }
|
|
1287
|
+
};
|
|
1288
|
+
const publish = (next, why = null) => {
|
|
1289
|
+
state = next;
|
|
1290
|
+
error = why;
|
|
1291
|
+
for (const listener of [...listeners])
|
|
1292
|
+
notify(listener);
|
|
1293
|
+
};
|
|
1294
|
+
const release = () => {
|
|
1295
|
+
offPush?.();
|
|
1296
|
+
offPush = null;
|
|
1297
|
+
offClose?.();
|
|
1298
|
+
offClose = null;
|
|
1299
|
+
const pending = controller;
|
|
1300
|
+
controller = null;
|
|
1301
|
+
clearEarly();
|
|
1302
|
+
clearEarly = () => { };
|
|
1303
|
+
pending?.abort();
|
|
1304
|
+
active = null;
|
|
1305
|
+
};
|
|
1306
|
+
const detach = () => {
|
|
1307
|
+
this.restorers = this.restorers.filter(f => f !== start);
|
|
1308
|
+
offLink();
|
|
1309
|
+
};
|
|
1310
|
+
const fail = (why) => {
|
|
1311
|
+
if (stopped)
|
|
1312
|
+
return;
|
|
1313
|
+
stopped = true;
|
|
1314
|
+
epoch++;
|
|
1315
|
+
release();
|
|
1316
|
+
detach();
|
|
1317
|
+
rejectReady(why);
|
|
1318
|
+
publish("failed", why);
|
|
1319
|
+
listeners.clear();
|
|
1320
|
+
};
|
|
1321
|
+
const stop = () => {
|
|
1322
|
+
if (state === "closed")
|
|
1323
|
+
return;
|
|
1324
|
+
stopped = true;
|
|
1325
|
+
epoch++;
|
|
1326
|
+
release();
|
|
1327
|
+
detach();
|
|
1328
|
+
rejectReady(mdbaseError("cancelled", "change watch closed"));
|
|
1329
|
+
publish("closed");
|
|
1330
|
+
listeners.clear();
|
|
1331
|
+
};
|
|
1332
|
+
const start = async (s) => {
|
|
1333
|
+
if (stopped || s.closed || s !== this.session || active === s)
|
|
1334
|
+
return;
|
|
1335
|
+
epoch++;
|
|
1336
|
+
release();
|
|
1337
|
+
active = s;
|
|
1338
|
+
const generation = epoch;
|
|
1339
|
+
const current = () => !stopped && generation === epoch && !s.closed && s === this.session;
|
|
1340
|
+
controller = new AbortController();
|
|
1341
|
+
const signal = controller.signal;
|
|
1342
|
+
try {
|
|
1343
|
+
await this.readFence(signal);
|
|
1344
|
+
}
|
|
1345
|
+
catch (e) {
|
|
1346
|
+
if (current())
|
|
1347
|
+
fail(schemaToError(e, "changes watch"));
|
|
1348
|
+
return;
|
|
1349
|
+
}
|
|
1350
|
+
if (!current())
|
|
1351
|
+
return;
|
|
1352
|
+
let initial = true, early = [], weight = 0;
|
|
1353
|
+
clearEarly = () => { early.length = 0; weight = 0; };
|
|
1354
|
+
const deliver = (batch) => {
|
|
1355
|
+
if (!current())
|
|
1356
|
+
return;
|
|
1357
|
+
last = batch.cursor;
|
|
1358
|
+
try {
|
|
1359
|
+
onChanges(batch);
|
|
1360
|
+
}
|
|
1361
|
+
catch { /* Consumer owns its callback failure. */ }
|
|
1362
|
+
};
|
|
1363
|
+
offClose = s.onClose(why => {
|
|
1364
|
+
if (stopped || generation !== epoch)
|
|
1365
|
+
return;
|
|
1366
|
+
epoch++;
|
|
1367
|
+
release();
|
|
1368
|
+
const problem = why ?? mdbaseError("unavailable", "change watch session closed");
|
|
1369
|
+
rejectReady(problem);
|
|
1370
|
+
publish("stale", problem);
|
|
1371
|
+
});
|
|
1372
|
+
offPush = s.onPush("changes", payload => {
|
|
1373
|
+
if (!current())
|
|
1374
|
+
return;
|
|
1375
|
+
try {
|
|
1376
|
+
const batch = changesResult.dec(payload);
|
|
1377
|
+
if (!initial) {
|
|
1378
|
+
deliver(batch);
|
|
1379
|
+
return;
|
|
1380
|
+
}
|
|
1381
|
+
// A batched transport can deliver pushes before the request continuation.
|
|
1382
|
+
// Bound retained startup metadata without making a measured-heap claim.
|
|
1383
|
+
const charge = 64 + batch.cursor.length * 2 + batch.changes.reduce((n, c) => n + 384 + c.path.length * 2, 0);
|
|
1384
|
+
if (early.length >= 16 || weight + charge > 1024 * 1024) {
|
|
1385
|
+
fail(mdbaseError("unavailable", "change watch startup buffer exceeded"));
|
|
1386
|
+
return;
|
|
1387
|
+
}
|
|
1388
|
+
weight += charge;
|
|
1389
|
+
early.push(batch);
|
|
1390
|
+
}
|
|
1391
|
+
catch (e) {
|
|
1392
|
+
fail(schemaToError(e, "changes push"));
|
|
1393
|
+
}
|
|
1394
|
+
});
|
|
1395
|
+
publish("starting");
|
|
1396
|
+
if (!current())
|
|
1397
|
+
return;
|
|
1398
|
+
try {
|
|
1399
|
+
const first = await s.request("changes", params([[0, last ?? null], [2, true]]), { codec: changesResult, signal });
|
|
1400
|
+
if (!current())
|
|
1401
|
+
return;
|
|
1402
|
+
last = first.cursor;
|
|
1403
|
+
if (first.changes.length || first.reset)
|
|
1404
|
+
deliver(first);
|
|
1405
|
+
while (early.length && current())
|
|
1406
|
+
deliver(early.shift());
|
|
1407
|
+
clearEarly();
|
|
1408
|
+
initial = false;
|
|
1409
|
+
if (!current())
|
|
1410
|
+
return;
|
|
1411
|
+
publish("active");
|
|
1412
|
+
if (current())
|
|
1413
|
+
resolveReady();
|
|
1414
|
+
}
|
|
1415
|
+
catch (e) {
|
|
1416
|
+
if (current())
|
|
1417
|
+
fail(schemaToError(e, "changes watch"));
|
|
1418
|
+
}
|
|
1419
|
+
};
|
|
1420
|
+
const offLink = this.onLink((link, why) => {
|
|
1421
|
+
if (link === "closed") {
|
|
1422
|
+
if (why)
|
|
1423
|
+
fail(why);
|
|
1424
|
+
else
|
|
1425
|
+
stop();
|
|
1426
|
+
}
|
|
1427
|
+
});
|
|
1428
|
+
this.onSession(start);
|
|
1429
|
+
void this.ready().then(start).catch(e => fail(schemaToError(e, "changes watch")));
|
|
1430
|
+
const watch = Object.assign(stop, { ready, subscribe: (listener) => {
|
|
1431
|
+
if (state !== "closed" && state !== "failed")
|
|
1432
|
+
listeners.add(listener);
|
|
1433
|
+
notify(listener);
|
|
1434
|
+
return () => { listeners.delete(listener); };
|
|
1435
|
+
} });
|
|
1436
|
+
Object.defineProperties(watch, { state: { get: () => state }, error: { get: () => error } });
|
|
1437
|
+
return Object.freeze(watch);
|
|
1438
|
+
}
|
|
1439
|
+
async validate(refs, signal) {
|
|
1440
|
+
signal?.throwIfAborted();
|
|
1441
|
+
const { targets, expected } = captureValidation(refs);
|
|
1442
|
+
const result = await this.call("validate", params([[0, targets]]), {
|
|
1443
|
+
codec: recordValidations, retry: true, ...(signal ? { signal } : {}),
|
|
1444
|
+
});
|
|
1445
|
+
signal?.throwIfAborted();
|
|
1446
|
+
return bindValidation(result, expected);
|
|
1447
|
+
}
|
|
1448
|
+
/** Independent native app-host Bases READ, exact original held session.
|
|
1449
|
+
* Unsupported transports refuse: no generic execute_view/data fallback. */
|
|
1450
|
+
async executeAppBases(request, signal) {
|
|
1451
|
+
const window = request.window === undefined ? null : { ...request.window };
|
|
1452
|
+
const bytes = encodeAppBasesRequest({ ...request, ...(window === null ? {} : { window }) });
|
|
1453
|
+
const session = await this.readyForRead(signal);
|
|
1454
|
+
const reply = await session.readAppBases(bytes, signal);
|
|
1455
|
+
if (session !== this.session || session.closed || this.readFencedAt !== null)
|
|
1456
|
+
throw mdbaseError("unavailable", "original Bases session is no longer current");
|
|
1457
|
+
try {
|
|
1458
|
+
return decodeAppBasesResult(reply, window);
|
|
1459
|
+
}
|
|
1460
|
+
catch (error) {
|
|
1461
|
+
throw schemaToError(error, "app Bases result");
|
|
1462
|
+
}
|
|
1463
|
+
}
|
|
1464
|
+
/** All rows of a saved view, page by page over native windows (prod
|
|
1465
|
+
* `executeViewPages`). Each page is an independent READ; `changedSinceFirst`
|
|
1466
|
+
* marks pages taken after the collection changed. */
|
|
1467
|
+
executeAppBasesPages(request, options = {}) {
|
|
1468
|
+
return appBasesPages((r, signal) => this.executeAppBases(r, signal), request, options);
|
|
1469
|
+
}
|
|
1470
|
+
/** One bounded native discovery page. Empty views do not mean EOF when a
|
|
1471
|
+
* continuation is present. Supplied handles never silently restart/retry. */
|
|
1472
|
+
async listAppBasesViews(request, signal) {
|
|
1473
|
+
const limit = request.limit, bytes = encodeAppBasesDiscoveryRequest(request);
|
|
1474
|
+
const session = await this.readyForRead(signal);
|
|
1475
|
+
const reply = await session.readAppBasesDiscovery("list-views", bytes, signal);
|
|
1476
|
+
try {
|
|
1477
|
+
if (session !== this.session || session.closed || this.readFencedAt !== null)
|
|
1478
|
+
throw mdbaseError("unavailable", "original Bases session is no longer current");
|
|
1479
|
+
return decodeAppBasesDiscoveryResult(reply, limit);
|
|
1480
|
+
}
|
|
1481
|
+
catch (error) {
|
|
1482
|
+
throw schemaToError(error, "app Bases discovery");
|
|
1483
|
+
}
|
|
1484
|
+
finally {
|
|
1485
|
+
reply.fill(0);
|
|
1486
|
+
}
|
|
1487
|
+
}
|
|
1488
|
+
/** Exact current source for one native UUID/SHA/original ordinal, never path
|
|
1489
|
+
* aliases, inferred catalog entries, generic RPC or cached document fallback. */
|
|
1490
|
+
async readAppBasesViewSource(request, signal) {
|
|
1491
|
+
const original = { ...request }, bytes = encodeAppBasesSourceRequest(original);
|
|
1492
|
+
const session = await this.readyForRead(signal);
|
|
1493
|
+
const reply = await session.readAppBasesDiscovery("read-view-source", bytes, signal);
|
|
1494
|
+
try {
|
|
1495
|
+
if (session !== this.session || session.closed || this.readFencedAt !== null)
|
|
1496
|
+
throw mdbaseError("unavailable", "original Bases session is no longer current");
|
|
1497
|
+
return decodeAppBasesSourceResult(reply, original);
|
|
1498
|
+
}
|
|
1499
|
+
catch (error) {
|
|
1500
|
+
throw schemaToError(error, "app Bases exact source");
|
|
1501
|
+
}
|
|
1502
|
+
finally {
|
|
1503
|
+
reply.fill(0);
|
|
1504
|
+
}
|
|
1505
|
+
}
|
|
1506
|
+
// ---------------------------------------------------------------- writes (§5, §6)
|
|
1507
|
+
/** Submit operations as one mutation (or one per op with `allowPartial`). */
|
|
1508
|
+
async submit(ops, o = {}) {
|
|
1509
|
+
if (this.hello.version.minor >= 3 && this.hello.grant.role === "viewer")
|
|
1510
|
+
throw mdbaseError("forbidden", "You have view access, so changes can't be saved.", "read_only");
|
|
1511
|
+
if (o.intent !== undefined && o.mutationId === undefined)
|
|
1512
|
+
o = { ...o, mutationId: (await intentIds(this.collectionId, o.intent))[0] };
|
|
1513
|
+
const p = { ops, mutationId: o.mutationId ?? uuidv7() };
|
|
1514
|
+
if (o.allowPartial) {
|
|
1515
|
+
p.allowPartial = true;
|
|
1516
|
+
p.mutationIds = ops.map((_, i) => (i === 0 ? p.mutationId : uuidv7()));
|
|
1517
|
+
}
|
|
1518
|
+
if (o.conflictMode)
|
|
1519
|
+
p.conflictMode = o.conflictMode;
|
|
1520
|
+
if (o.timezone)
|
|
1521
|
+
p.timezone = o.timezone;
|
|
1522
|
+
if (o.dryRun)
|
|
1523
|
+
p.dryRun = true;
|
|
1524
|
+
if (o.include)
|
|
1525
|
+
p.include = o.include;
|
|
1526
|
+
if (o.wait)
|
|
1527
|
+
p.wait = o.wait;
|
|
1528
|
+
// The ledger holds the intent before the replica can capture it.
|
|
1529
|
+
if (!o.dryRun) {
|
|
1530
|
+
this.startLedger();
|
|
1531
|
+
await this.writeLedger.begin(p.mutationIds ?? [p.mutationId], ops);
|
|
1532
|
+
}
|
|
1533
|
+
// Idempotent by mutation ID: safe to resubmit after a dropped link.
|
|
1534
|
+
let receipts;
|
|
1535
|
+
try {
|
|
1536
|
+
receipts = await this.call("submit", submitParams.enc(p), {
|
|
1537
|
+
codec: submitResult,
|
|
1538
|
+
retry: true,
|
|
1539
|
+
...(o.signal ? { signal: o.signal } : {}),
|
|
1540
|
+
});
|
|
1541
|
+
}
|
|
1542
|
+
catch (e) {
|
|
1543
|
+
// A dry run captures nothing. Otherwise the request may have been captured
|
|
1544
|
+
// whatever the code says: report the original identity, never a new one.
|
|
1545
|
+
if (o.dryRun)
|
|
1546
|
+
throw e;
|
|
1547
|
+
void this.writeLedger.settleAll(p.mutationIds ?? [p.mutationId]);
|
|
1548
|
+
throw withMutation(e, "submit", p.mutationIds ? { mutationIds: p.mutationIds } : { mutationId: p.mutationId });
|
|
1549
|
+
}
|
|
1550
|
+
return receipts.map((r) => (o.dryRun ? new Write(r) : this._track(r)));
|
|
1551
|
+
}
|
|
1552
|
+
async one(op, o) {
|
|
1553
|
+
const [w] = await this.submit([op], o);
|
|
1554
|
+
return w;
|
|
1555
|
+
}
|
|
1556
|
+
/** Create a record. The ID is minted here, so the app can use it before confirmation. */
|
|
1557
|
+
create(input, o = {}) {
|
|
1558
|
+
if (o.intent !== undefined && o.mutationId === undefined)
|
|
1559
|
+
return intentIds(this.collectionId, o.intent).then(([mutationId, id]) => this.create({ ...input, id: input.id ?? id }, { ...o, mutationId }));
|
|
1560
|
+
if (input.contract)
|
|
1561
|
+
return this.createThroughContract(input, input.contract, o);
|
|
1562
|
+
const op = { kind: "create", id: input.id ?? uuidv7() };
|
|
1563
|
+
if (input.path !== undefined)
|
|
1564
|
+
op.path = input.path;
|
|
1565
|
+
if (input.type !== undefined)
|
|
1566
|
+
op.type = input.type;
|
|
1567
|
+
if (input.frontmatter !== undefined)
|
|
1568
|
+
op.frontmatter = toFmMap(input.frontmatter);
|
|
1569
|
+
if (input.body !== undefined)
|
|
1570
|
+
op.body = input.body;
|
|
1571
|
+
if (input.document !== undefined)
|
|
1572
|
+
op.document = input.document;
|
|
1573
|
+
return this.one(op, o).then(write => Object.assign(write, { recordId: op.id }));
|
|
1574
|
+
}
|
|
1575
|
+
async createThroughContract(input, contract, o) {
|
|
1576
|
+
if (input.document !== undefined)
|
|
1577
|
+
throw mdbaseError("invalid_request", "a contract create takes frontmatter fields, not a document");
|
|
1578
|
+
if (input.type !== undefined && contract.type !== undefined && input.type.toLowerCase() !== contract.type.toLowerCase()) {
|
|
1579
|
+
throw mdbaseError("invalid_request", "the create type and contract type must name the same type");
|
|
1580
|
+
}
|
|
1581
|
+
const selector = contract.type === undefined && input.type !== undefined ? { ...contract, type: input.type } : contract;
|
|
1582
|
+
const plan = await this.contractPlan(selector, undefined, toFmMap(input.frontmatter ?? {}), undefined, o.signal);
|
|
1583
|
+
const op = { kind: "create", id: input.id ?? uuidv7(), type: plan.type, frontmatter: plan.patch };
|
|
1584
|
+
if (input.path !== undefined)
|
|
1585
|
+
op.path = input.path;
|
|
1586
|
+
if (input.body !== undefined)
|
|
1587
|
+
op.body = input.body;
|
|
1588
|
+
return Object.assign(await this.one(op, o), { recordId: op.id });
|
|
1589
|
+
}
|
|
1590
|
+
/**
|
|
1591
|
+
* Update a record by field-level intent. Pass the `RecordView` you edited from, and
|
|
1592
|
+
* the SDK sends `base` for every key you set or unset, and the body as a 3-way merge,
|
|
1593
|
+
* so concurrent edits merge instead of last-writer-wins.
|
|
1594
|
+
*/
|
|
1595
|
+
update(target, changes, o = {}) {
|
|
1596
|
+
if (changes.contract)
|
|
1597
|
+
return this.updateThroughContract(target, changes, changes.contract, o);
|
|
1598
|
+
return this.one(this.updateOp(target, changes), o);
|
|
1599
|
+
}
|
|
1600
|
+
async updateThroughContract(target, c, contract, o) {
|
|
1601
|
+
if (c.add || c.remove) {
|
|
1602
|
+
throw mdbaseError("invalid_request", "a contract update takes only patch, unset, body and ifRevision");
|
|
1603
|
+
}
|
|
1604
|
+
const plan = await this.contractPlan(contract, target, c.patch ? toFmMap(c.patch) : undefined, c.unset, o.signal, c.promote);
|
|
1605
|
+
// The body is the record's own, not a contract field: it rides the same op.
|
|
1606
|
+
const seen = typeof target === "object" && "frontmatter" in target ? target : plan.id;
|
|
1607
|
+
const body = this.updateOp(seen, { body: c.body, bodyEdits: c.bodyEdits, bodyBase: c.bodyBase });
|
|
1608
|
+
const op = { kind: "update", id: plan.id };
|
|
1609
|
+
if (body.body !== undefined)
|
|
1610
|
+
op.body = body.body;
|
|
1611
|
+
if (body.bodyEdits)
|
|
1612
|
+
op.bodyEdits = body.bodyEdits;
|
|
1613
|
+
if (body.bodyBase !== undefined)
|
|
1614
|
+
op.bodyBase = body.bodyBase;
|
|
1615
|
+
if (plan.patch.size)
|
|
1616
|
+
op.patch = plan.patch;
|
|
1617
|
+
if (plan.unset.length)
|
|
1618
|
+
op.unset = plan.unset;
|
|
1619
|
+
if (plan.base.length)
|
|
1620
|
+
op.base = plan.base;
|
|
1621
|
+
if (c.updateRefs !== undefined)
|
|
1622
|
+
op.updateRefs = c.updateRefs;
|
|
1623
|
+
if (c.ifRevision)
|
|
1624
|
+
op.ifRevision = c.ifRevision;
|
|
1625
|
+
return this.one(op, o);
|
|
1626
|
+
}
|
|
1627
|
+
/** Build an `update` op (exposed for batching with `submit`). */
|
|
1628
|
+
updateOp(target, c) {
|
|
1629
|
+
if (c.contract || c.promote)
|
|
1630
|
+
throw mdbaseError("invalid_request", "contract updates are mapped by the replica; use update()");
|
|
1631
|
+
const op = { kind: "update", id: refId(target) };
|
|
1632
|
+
const seen = typeof target === "object" && "frontmatter" in target ? target : null;
|
|
1633
|
+
if (c.updateRefs !== undefined)
|
|
1634
|
+
op.updateRefs = c.updateRefs;
|
|
1635
|
+
if (c.patch)
|
|
1636
|
+
op.patch = toFmMap(c.patch);
|
|
1637
|
+
if (c.unset?.length)
|
|
1638
|
+
op.unset = c.unset;
|
|
1639
|
+
if (c.add)
|
|
1640
|
+
op.add = new Map(Object.entries(c.add).map(([k, v]) => [k, v.map(toValue)]));
|
|
1641
|
+
if (c.remove)
|
|
1642
|
+
op.remove = new Map(Object.entries(c.remove).map(([k, v]) => [k, v.map(toValue)]));
|
|
1643
|
+
if (c.ifRevision)
|
|
1644
|
+
op.ifRevision = c.ifRevision;
|
|
1645
|
+
if (c.bodyEdits?.length) {
|
|
1646
|
+
op.bodyEdits = c.bodyEdits;
|
|
1647
|
+
const base = c.bodyBase ?? (seen?.body !== undefined ? revisionOf(seen.body) : undefined);
|
|
1648
|
+
if (!base)
|
|
1649
|
+
throw mdbaseError("invalid_request", "bodyEdits need bodyBase (or a RecordView read with its body)");
|
|
1650
|
+
op.bodyBase = base;
|
|
1651
|
+
}
|
|
1652
|
+
else if (c.body !== undefined) {
|
|
1653
|
+
if (seen?.body !== undefined && c.bodyBase === undefined) {
|
|
1654
|
+
// Small edits travel as edits; the replica merges them against the base.
|
|
1655
|
+
const edits = diffEdits(seen.body, c.body);
|
|
1656
|
+
if (edits.length) {
|
|
1657
|
+
op.bodyEdits = edits;
|
|
1658
|
+
op.bodyBase = revisionOf(seen.body);
|
|
1659
|
+
}
|
|
1660
|
+
}
|
|
1661
|
+
else {
|
|
1662
|
+
op.body = c.body;
|
|
1663
|
+
if (c.bodyBase)
|
|
1664
|
+
op.bodyBase = c.bodyBase;
|
|
1665
|
+
}
|
|
1666
|
+
}
|
|
1667
|
+
if (seen) {
|
|
1668
|
+
const base = [];
|
|
1669
|
+
const touched = [...(op.patch?.keys() ?? []), ...(op.unset ?? [])];
|
|
1670
|
+
for (const k of new Set(touched)) {
|
|
1671
|
+
const v = seen.frontmatter.get(k);
|
|
1672
|
+
base.push(v === undefined ? { key: k } : { key: k, observed: v });
|
|
1673
|
+
}
|
|
1674
|
+
if (base.length)
|
|
1675
|
+
op.base = base;
|
|
1676
|
+
}
|
|
1677
|
+
// Drop patch keys whose value didn't change from what was read: no-op writes
|
|
1678
|
+
// should not create conflicts.
|
|
1679
|
+
if (seen && op.patch) {
|
|
1680
|
+
for (const [k, v] of [...op.patch]) {
|
|
1681
|
+
const cur = seen.frontmatter.get(k);
|
|
1682
|
+
if (cur !== undefined && valueEquals(cur, v)) {
|
|
1683
|
+
op.patch.delete(k);
|
|
1684
|
+
op.base = op.base?.filter((b) => b.key !== k);
|
|
1685
|
+
}
|
|
1686
|
+
}
|
|
1687
|
+
if (op.patch.size === 0)
|
|
1688
|
+
delete op.patch;
|
|
1689
|
+
if (op.base?.length === 0)
|
|
1690
|
+
delete op.base;
|
|
1691
|
+
}
|
|
1692
|
+
return op;
|
|
1693
|
+
}
|
|
1694
|
+
/** Replace a record's whole document. */
|
|
1695
|
+
replaceDocument(target, document, o = {}) {
|
|
1696
|
+
const seen = typeof target === "object" && "path" in target && "revision" in target ? target : null;
|
|
1697
|
+
const op = { kind: "document", id: refId(target), new: { path: seen?.path ?? "", doc: document } };
|
|
1698
|
+
if (!seen)
|
|
1699
|
+
throw mdbaseError("invalid_request", "replaceDocument needs the RecordView it was edited from");
|
|
1700
|
+
if (seen.document !== undefined)
|
|
1701
|
+
op.base = { path: seen.path, doc: seen.document };
|
|
1702
|
+
if (o.ifRevision)
|
|
1703
|
+
op.ifRevision = o.ifRevision;
|
|
1704
|
+
return this.one(op, o);
|
|
1705
|
+
}
|
|
1706
|
+
/**
|
|
1707
|
+
* Delete a record. With a RecordView, a concurrent change supersedes the delete.
|
|
1708
|
+
* With `contract`, the record must implement it; the delete is then bound to
|
|
1709
|
+
* the revision checked, so a concurrent change supersedes it.
|
|
1710
|
+
*/
|
|
1711
|
+
delete(target, o = {}) {
|
|
1712
|
+
if (o.contract) {
|
|
1713
|
+
const { contract, ...rest } = o;
|
|
1714
|
+
return this.contractPlan(contract, target, undefined, undefined, o.signal).then((plan) => {
|
|
1715
|
+
const op = { kind: "delete", id: plan.id, baseRevision: plan.revision };
|
|
1716
|
+
if (rest.ifRevision)
|
|
1717
|
+
op.ifRevision = rest.ifRevision;
|
|
1718
|
+
return this.one(op, rest);
|
|
1719
|
+
});
|
|
1720
|
+
}
|
|
1721
|
+
const op = { kind: "delete", id: refId(target) };
|
|
1722
|
+
if (typeof target === "object" && "revision" in target)
|
|
1723
|
+
op.baseRevision = target.revision;
|
|
1724
|
+
if (o.ifRevision)
|
|
1725
|
+
op.ifRevision = o.ifRevision;
|
|
1726
|
+
return this.one(op, o);
|
|
1727
|
+
}
|
|
1728
|
+
/** Rename (move) a record; `updateRefs` rewrites links to it (default true).
|
|
1729
|
+
* With `contract`, the record must implement it, and moves from its current path. */
|
|
1730
|
+
rename(target, to, o = {}) {
|
|
1731
|
+
if (o.contract) {
|
|
1732
|
+
const { contract, ...rest } = o;
|
|
1733
|
+
return this.contractPlan(contract, target.id, undefined, undefined, o.signal).then((plan) => this.rename({ ...target, id: plan.id, path: plan.path }, to, rest));
|
|
1734
|
+
}
|
|
1735
|
+
const op = { kind: "rename", id: target.id, from: target.path, to, updateRefs: o.updateRefs ?? true };
|
|
1736
|
+
if (o.ifRevision)
|
|
1737
|
+
op.ifRevision = o.ifRevision;
|
|
1738
|
+
return this.one(op, o);
|
|
1739
|
+
}
|
|
1740
|
+
/** @internal Track a receipt the replica returned outside `submit`. */
|
|
1741
|
+
_track(r) {
|
|
1742
|
+
this.noteReceipt(r);
|
|
1743
|
+
let initial = r;
|
|
1744
|
+
const later = this.unclaimed.get(r.mutation);
|
|
1745
|
+
if (later) {
|
|
1746
|
+
this.unclaimed.delete(r.mutation);
|
|
1747
|
+
// Reconcile BEFORE constructing Write: a confirmed response that omits
|
|
1748
|
+
// publication is not the no-files case when a buffered push knows it.
|
|
1749
|
+
// Preserve response confirmation/seq/relocation/records over early pending
|
|
1750
|
+
// publication. Rejected/unknown response outcomes always remain refusals.
|
|
1751
|
+
initial = mergePublication(r, later);
|
|
1752
|
+
if (r.state === "pending" || (r.state === "confirmed" && (later.state === "rejected" || later.state === "unknown"))) {
|
|
1753
|
+
initial = mergePublication({ ...r, ...later }, initial);
|
|
1754
|
+
}
|
|
1755
|
+
}
|
|
1756
|
+
let w = this.writes.get(r.mutation);
|
|
1757
|
+
if (w)
|
|
1758
|
+
w.update(initial);
|
|
1759
|
+
else
|
|
1760
|
+
w = new Write(initial);
|
|
1761
|
+
if (needsReceipt(w.receipt))
|
|
1762
|
+
this.writes.set(r.mutation, w);
|
|
1763
|
+
else
|
|
1764
|
+
this.writes.delete(r.mutation);
|
|
1765
|
+
return w;
|
|
1766
|
+
}
|
|
1767
|
+
/**
|
|
1768
|
+
* Settle a write for the UI: waits up to `timeoutMs` (default 3 s) for the log
|
|
1769
|
+
* to confirm it. Still pending then is `saved_on_device`, not a failure: the
|
|
1770
|
+
* write is kept on this device and the ledger settles it later. With
|
|
1771
|
+
* `readBack`, `record` is the record as it now reads here.
|
|
1772
|
+
*/
|
|
1773
|
+
async save(write, o = {}) {
|
|
1774
|
+
const w = await write;
|
|
1775
|
+
let r = w.receipt;
|
|
1776
|
+
if (r.state === "pending") {
|
|
1777
|
+
try {
|
|
1778
|
+
r = await this.awaitReceipt(w.mutationId, o.timeoutMs ?? 3000, o.signal);
|
|
1779
|
+
w.update(r);
|
|
1780
|
+
}
|
|
1781
|
+
catch (e) {
|
|
1782
|
+
if (isMdbaseError(e, "cancelled"))
|
|
1783
|
+
throw e;
|
|
1784
|
+
// The wait failing says nothing about the write: it is on this device.
|
|
1785
|
+
r = w.receipt;
|
|
1786
|
+
}
|
|
1787
|
+
}
|
|
1788
|
+
const outcome = r.state === "confirmed" ? "confirmed"
|
|
1789
|
+
: r.state === "pending" ? "saved_on_device" : "rejected";
|
|
1790
|
+
const records = w.records;
|
|
1791
|
+
let record = records[0];
|
|
1792
|
+
if (o.readBack && outcome !== "rejected" && record) {
|
|
1793
|
+
record = (await this.find(record.id, undefined, o.signal).catch(() => null)) ?? record;
|
|
1794
|
+
}
|
|
1795
|
+
return Object.freeze({
|
|
1796
|
+
outcome,
|
|
1797
|
+
mutationId: w.mutationId,
|
|
1798
|
+
records,
|
|
1799
|
+
...(record ? { record } : {}),
|
|
1800
|
+
...(outcome === "rejected" && r.problem ? { problem: r.problem } : {}),
|
|
1801
|
+
});
|
|
1802
|
+
}
|
|
1803
|
+
/** The current receipt of a mutation. Failures carry `mutationId`. */
|
|
1804
|
+
receipt(mutation, signal) {
|
|
1805
|
+
return this.call("receipt", params([[0, uuidCodec.enc(mutation)]]), {
|
|
1806
|
+
codec: receiptCodec,
|
|
1807
|
+
retry: true,
|
|
1808
|
+
...(signal ? { signal } : {}),
|
|
1809
|
+
}).catch((e) => { throw withMutation(e, "receipt", { mutationId: mutation }); });
|
|
1810
|
+
}
|
|
1811
|
+
/** Resolves when the receipt leaves `pending`, or at `timeoutMs` with the pending receipt. */
|
|
1812
|
+
awaitReceipt(mutation, timeoutMs, signal) {
|
|
1813
|
+
return this.call("await", params([
|
|
1814
|
+
[0, uuidCodec.enc(mutation)],
|
|
1815
|
+
[1, timeoutMs],
|
|
1816
|
+
]), { codec: receiptCodec, retry: true, ...(signal ? { signal } : {}) }).catch((e) => { throw withMutation(e, "await", { mutationId: mutation }); });
|
|
1817
|
+
}
|
|
1818
|
+
// ---------------------------------------------------------------- holds and conflicts (§8)
|
|
1819
|
+
listHolds(signal) {
|
|
1820
|
+
return this.call("list_holds", null, { codec: list(hold), retry: true, ...(signal ? { signal } : {}) });
|
|
1821
|
+
}
|
|
1822
|
+
/** Call `fn` with the holds now and on every change. */
|
|
1823
|
+
onHolds(fn) {
|
|
1824
|
+
const w = this.holdsWatch;
|
|
1825
|
+
w.listeners.add(fn);
|
|
1826
|
+
if (w.last)
|
|
1827
|
+
fn(w.last);
|
|
1828
|
+
if (w.listeners.size === 1)
|
|
1829
|
+
void this.refreshHolds(true).catch(() => { });
|
|
1830
|
+
return () => w.listeners.delete(fn);
|
|
1831
|
+
}
|
|
1832
|
+
async refreshHolds(subscribe) {
|
|
1833
|
+
if (subscribe)
|
|
1834
|
+
await this.call("subscribe_holds", null, { retry: true });
|
|
1835
|
+
this.emitHolds(await this.listHolds());
|
|
1836
|
+
}
|
|
1837
|
+
emitHolds(h) {
|
|
1838
|
+
this.holdsWatch.last = h;
|
|
1839
|
+
for (const fn of [...this.holdsWatch.listeners])
|
|
1840
|
+
fn(h);
|
|
1841
|
+
}
|
|
1842
|
+
/** Resolve a hold. Submits an ordinary mutation and returns its write. */
|
|
1843
|
+
async resolveHold(id, how, use) {
|
|
1844
|
+
const r = await this.call("resolve_hold", params([
|
|
1845
|
+
[0, uuidCodec.enc(id)],
|
|
1846
|
+
[1, holdResolution.enc(how)],
|
|
1847
|
+
[2, use],
|
|
1848
|
+
]), { codec: receiptCodec });
|
|
1849
|
+
return this._track(r);
|
|
1850
|
+
}
|
|
1851
|
+
listConflicts(record, signal) {
|
|
1852
|
+
return this.call("list_conflicts", record ? params([[0, uuidCodec.enc(record)]]) : null, {
|
|
1853
|
+
codec: list(conflictEntry),
|
|
1854
|
+
retry: true,
|
|
1855
|
+
...(signal ? { signal } : {}),
|
|
1856
|
+
});
|
|
1857
|
+
}
|
|
1858
|
+
onConflicts(fn) {
|
|
1859
|
+
const w = this.conflictsWatch;
|
|
1860
|
+
w.listeners.add(fn);
|
|
1861
|
+
if (w.last)
|
|
1862
|
+
fn(w.last);
|
|
1863
|
+
if (w.listeners.size === 1)
|
|
1864
|
+
void this.refreshConflicts(true).catch(() => { });
|
|
1865
|
+
return () => w.listeners.delete(fn);
|
|
1866
|
+
}
|
|
1867
|
+
async refreshConflicts(subscribe) {
|
|
1868
|
+
if (subscribe)
|
|
1869
|
+
await this.call("subscribe_conflicts", null, { retry: true });
|
|
1870
|
+
this.emitConflicts(await this.listConflicts());
|
|
1871
|
+
}
|
|
1872
|
+
emitConflicts(c) {
|
|
1873
|
+
this.conflictsWatch.last = c;
|
|
1874
|
+
for (const fn of [...this.conflictsWatch.listeners])
|
|
1875
|
+
fn(c);
|
|
1876
|
+
}
|
|
1877
|
+
/** Accept what the record kept. */
|
|
1878
|
+
dismissConflict(entry, o = {}) {
|
|
1879
|
+
return this.one({ kind: "conflict_dismiss", mutation: entry.mutation, record: entry.conflict.id }, o);
|
|
1880
|
+
}
|
|
1881
|
+
/** Resolve a field conflict with a chosen value, dismissing it in the same mutation. */
|
|
1882
|
+
async resolveConflict(entry, field, chosen, o = {}) {
|
|
1883
|
+
const [w] = await this.submit([
|
|
1884
|
+
{ kind: "update", id: entry.conflict.id, patch: new Map([[field, toValue(chosen)]]) },
|
|
1885
|
+
{ kind: "conflict_dismiss", mutation: entry.mutation, record: entry.conflict.id },
|
|
1886
|
+
], o);
|
|
1887
|
+
return w;
|
|
1888
|
+
}
|
|
1889
|
+
// ---------------------------------------------------------------- private collections (§8.3)
|
|
1890
|
+
/**
|
|
1891
|
+
* Devices waiting for approval in a private collection. Offered only to the app
|
|
1892
|
+
* hosting the replica (no grant). `exchangeReady` is challenge/reveal readiness,
|
|
1893
|
+
* NOT approval/key delivery. Once ready, `sas` is the host-only comparison code
|
|
1894
|
+
* to display beside Allow; the user must check it matches on the other computer.
|
|
1895
|
+
*/
|
|
1896
|
+
pendingDevices(signal) {
|
|
1897
|
+
return this.call("pending_devices", null, { codec: list(pendingDevice), retry: true, ...(signal ? { signal } : {}) });
|
|
1898
|
+
}
|
|
1899
|
+
/**
|
|
1900
|
+
* Confirm with the six digits displayed for the current host exchange, only
|
|
1901
|
+
* after the user visually checks they match on the requesting computer. Nothing
|
|
1902
|
+
* is typed. Never substitute a newer code after the user confirms. The replica
|
|
1903
|
+
* checks the current context and awaits the actual approval disposition.
|
|
1904
|
+
*/
|
|
1905
|
+
approveDevice(device, sas) {
|
|
1906
|
+
if (!/^\d{6}$/.test(sas)) {
|
|
1907
|
+
return Promise.reject(mdbaseError("invalid_request", "the code is six digits", "invalid_sas"));
|
|
1908
|
+
}
|
|
1909
|
+
return this.call("approve_device", params([
|
|
1910
|
+
[0, uuidCodec.enc(device)],
|
|
1911
|
+
[1, sas],
|
|
1912
|
+
])).then(() => { });
|
|
1913
|
+
}
|
|
1914
|
+
/** Dismiss this request until a fresh commitment is logged, including automatic renewal. Not revocation. */
|
|
1915
|
+
rejectDevice(device) {
|
|
1916
|
+
return this.call("reject_device", params([[0, uuidCodec.enc(device)]])).then(() => { });
|
|
1917
|
+
}
|
|
1918
|
+
/**
|
|
1919
|
+
* The account key (AK1 §6; hosting app only, never over a cloud-copy thin session).
|
|
1920
|
+
* `secret` is the 32-byte account secret `R`; the replica derives the collection's
|
|
1921
|
+
* recovery device from it and drops it. The SDK keeps no copy.
|
|
1922
|
+
*/
|
|
1923
|
+
accountKey = {
|
|
1924
|
+
/** Setup step 2: check the enrolment and key the recovery device (an ordinary mutation). */
|
|
1925
|
+
setup: async (secret) => {
|
|
1926
|
+
accountSecret(secret);
|
|
1927
|
+
const r = await this.call("account_key_setup", params([[0, secret]]), { codec: receiptCodec });
|
|
1928
|
+
return this._track(r);
|
|
1929
|
+
},
|
|
1930
|
+
/** Unlock step 3: start the recovery-signed self-grant; poll `status` until `keyed` or `refused`. */
|
|
1931
|
+
unlock: async (secret) => {
|
|
1932
|
+
accountSecret(secret);
|
|
1933
|
+
await this.call("account_key_unlock", params([[0, secret]]));
|
|
1934
|
+
},
|
|
1935
|
+
status: (signal) => this.call("account_key_status", null, { codec: accountKeyStatus, retry: true, ...(signal ? { signal } : {}) }),
|
|
1936
|
+
};
|
|
1937
|
+
/** Recovery key for a private collection (hosting app only). */
|
|
1938
|
+
recoveryKey = {
|
|
1939
|
+
/** Whether a recovery key is set up for this collection. */
|
|
1940
|
+
status: () => this.call("recovery_key_status", null, { codec: recoveryKeyStatus, retry: true }),
|
|
1941
|
+
/**
|
|
1942
|
+
* Create a recovery key. The returned phrase is shown to the user once and never
|
|
1943
|
+
* stored by the SDK; the replica keys a recovery device with it.
|
|
1944
|
+
*/
|
|
1945
|
+
create: () => this.call("recovery_key_create", null, { codec: recoveryKeyCreated }),
|
|
1946
|
+
/** Join this device using a recovery phrase (when no other device is available). */
|
|
1947
|
+
import: (phrase) => this.call("recovery_key_import", params([[0, phrase.trim()]])).then(() => { }),
|
|
1948
|
+
};
|
|
1949
|
+
// ---------------------------------------------------------------- editor fence (§14)
|
|
1950
|
+
/**
|
|
1951
|
+
* Offer the editor fence: the replica will route changes to open files through
|
|
1952
|
+
* `handler`. Requires the "fence" feature in `hello`.
|
|
1953
|
+
*/
|
|
1954
|
+
offerFence(handler) {
|
|
1955
|
+
this.fenceHandler = handler;
|
|
1956
|
+
}
|
|
1957
|
+
/** Report the files open in editors (on open, close, dirty change, and after saves). */
|
|
1958
|
+
reportFence(open) {
|
|
1959
|
+
return this.call("fence_report", fenceReport.enc({ open }), { retry: true }).then(() => { });
|
|
1960
|
+
}
|
|
1961
|
+
}
|
|
1962
|
+
/** Connect to a replica. */
|
|
1963
|
+
export function connect(opts) {
|
|
1964
|
+
return MdbaseClient.connect(opts);
|
|
1965
|
+
}
|
|
1966
|
+
//# sourceMappingURL=client.js.map
|