@enbox/dwn-sdk-js 0.3.9 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -4
- package/dist/browser.mjs +11 -11
- package/dist/browser.mjs.map +4 -4
- package/dist/esm/generated/precompiled-validators.js +783 -1206
- package/dist/esm/generated/precompiled-validators.js.map +1 -1
- package/dist/esm/src/core/dwn-constant.js +5 -0
- package/dist/esm/src/core/dwn-constant.js.map +1 -1
- package/dist/esm/src/core/dwn-error.js +13 -7
- package/dist/esm/src/core/dwn-error.js.map +1 -1
- package/dist/esm/src/core/grant-authorization.js +9 -18
- package/dist/esm/src/core/grant-authorization.js.map +1 -1
- package/dist/esm/src/core/message-reply.js.map +1 -1
- package/dist/esm/src/core/messages-grant-authorization.js +28 -61
- package/dist/esm/src/core/messages-grant-authorization.js.map +1 -1
- package/dist/esm/src/core/protocol-authorization-action.js +25 -27
- package/dist/esm/src/core/protocol-authorization-action.js.map +1 -1
- package/dist/esm/src/core/protocol-authorization-validation.js +31 -69
- package/dist/esm/src/core/protocol-authorization-validation.js.map +1 -1
- package/dist/esm/src/core/protocol-authorization.js +44 -118
- package/dist/esm/src/core/protocol-authorization.js.map +1 -1
- package/dist/esm/src/core/protocols-grant-authorization.js +5 -5
- package/dist/esm/src/core/protocols-grant-authorization.js.map +1 -1
- package/dist/esm/src/core/recording-validation-state-reader.js +84 -0
- package/dist/esm/src/core/recording-validation-state-reader.js.map +1 -0
- package/dist/esm/src/core/records-grant-authorization.js +11 -11
- package/dist/esm/src/core/records-grant-authorization.js.map +1 -1
- package/dist/esm/src/core/replication-apply.js +295 -0
- package/dist/esm/src/core/replication-apply.js.map +1 -0
- package/dist/esm/src/core/resumable-task-manager.js +5 -4
- package/dist/esm/src/core/resumable-task-manager.js.map +1 -1
- package/dist/esm/src/core/validation-state-reader.js +237 -0
- package/dist/esm/src/core/validation-state-reader.js.map +1 -0
- package/dist/esm/src/dwn.js +261 -16
- package/dist/esm/src/dwn.js.map +1 -1
- package/dist/esm/src/enums/dwn-interface-method.js +0 -1
- package/dist/esm/src/enums/dwn-interface-method.js.map +1 -1
- package/dist/esm/src/event-stream/durable-event-log.js +365 -0
- package/dist/esm/src/event-stream/durable-event-log.js.map +1 -0
- package/dist/esm/src/event-stream/event-emitter-wake-publisher.js +25 -0
- package/dist/esm/src/event-stream/event-emitter-wake-publisher.js.map +1 -0
- package/dist/esm/src/handlers/messages-query.js +159 -0
- package/dist/esm/src/handlers/messages-query.js.map +1 -0
- package/dist/esm/src/handlers/messages-read.js +5 -5
- package/dist/esm/src/handlers/messages-read.js.map +1 -1
- package/dist/esm/src/handlers/messages-subscribe.js +8 -8
- package/dist/esm/src/handlers/messages-subscribe.js.map +1 -1
- package/dist/esm/src/handlers/protocols-configure.js +30 -49
- package/dist/esm/src/handlers/protocols-configure.js.map +1 -1
- package/dist/esm/src/handlers/protocols-query.js +1 -1
- package/dist/esm/src/handlers/protocols-query.js.map +1 -1
- package/dist/esm/src/handlers/records-count.js +20 -11
- package/dist/esm/src/handlers/records-count.js.map +1 -1
- package/dist/esm/src/handlers/records-delete.js +20 -16
- package/dist/esm/src/handlers/records-delete.js.map +1 -1
- package/dist/esm/src/handlers/records-query.js +35 -11
- package/dist/esm/src/handlers/records-query.js.map +1 -1
- package/dist/esm/src/handlers/records-read.js +52 -42
- package/dist/esm/src/handlers/records-read.js.map +1 -1
- package/dist/esm/src/handlers/records-subscribe.js +107 -11
- package/dist/esm/src/handlers/records-subscribe.js.map +1 -1
- package/dist/esm/src/handlers/records-write.js +62 -116
- package/dist/esm/src/handlers/records-write.js.map +1 -1
- package/dist/esm/src/index.js +7 -8
- package/dist/esm/src/index.js.map +1 -1
- package/dist/esm/src/interfaces/messages-query.js +49 -0
- package/dist/esm/src/interfaces/messages-query.js.map +1 -0
- package/dist/esm/src/interfaces/protocols-configure.js +7 -3
- package/dist/esm/src/interfaces/protocols-configure.js.map +1 -1
- package/dist/esm/src/interfaces/protocols-query.js +3 -4
- package/dist/esm/src/interfaces/protocols-query.js.map +1 -1
- package/dist/esm/src/interfaces/records-count.js +4 -3
- package/dist/esm/src/interfaces/records-count.js.map +1 -1
- package/dist/esm/src/interfaces/records-delete.js +21 -4
- package/dist/esm/src/interfaces/records-delete.js.map +1 -1
- package/dist/esm/src/interfaces/records-query.js +4 -3
- package/dist/esm/src/interfaces/records-query.js.map +1 -1
- package/dist/esm/src/interfaces/records-read.js +3 -3
- package/dist/esm/src/interfaces/records-read.js.map +1 -1
- package/dist/esm/src/interfaces/records-subscribe.js +4 -3
- package/dist/esm/src/interfaces/records-subscribe.js.map +1 -1
- package/dist/esm/src/interfaces/records-write.js +27 -13
- package/dist/esm/src/interfaces/records-write.js.map +1 -1
- package/dist/esm/src/protocols/permissions.js +27 -34
- package/dist/esm/src/protocols/permissions.js.map +1 -1
- package/dist/esm/src/store/index-level.js +24 -9
- package/dist/esm/src/store/index-level.js.map +1 -1
- package/dist/esm/src/store/level-wrapper.js +7 -0
- package/dist/esm/src/store/level-wrapper.js.map +1 -1
- package/dist/esm/src/store/message-store-level.js +536 -42
- package/dist/esm/src/store/message-store-level.js.map +1 -1
- package/dist/esm/src/store/storage-controller.js +58 -49
- package/dist/esm/src/store/storage-controller.js.map +1 -1
- package/dist/esm/src/types/message-types.js.map +1 -1
- package/dist/esm/src/types/validation-state-reader.js +2 -0
- package/dist/esm/src/types/validation-state-reader.js.map +1 -0
- package/dist/esm/src/utils/messages.js +17 -0
- package/dist/esm/src/utils/messages.js.map +1 -1
- package/dist/esm/src/utils/record-limit-occupancy.js +244 -0
- package/dist/esm/src/utils/record-limit-occupancy.js.map +1 -0
- package/dist/esm/src/utils/records.js +50 -14
- package/dist/esm/src/utils/records.js.map +1 -1
- package/dist/esm/src/utils/replication.js +85 -0
- package/dist/esm/src/utils/replication.js.map +1 -0
- package/dist/esm/tests/core/grant-authorization.spec.js +4 -4
- package/dist/esm/tests/core/grant-authorization.spec.js.map +1 -1
- package/dist/esm/tests/core/process-message-parity.spec.js +222 -0
- package/dist/esm/tests/core/process-message-parity.spec.js.map +1 -0
- package/dist/esm/tests/core/protocol-authorization.spec.js +5 -2
- package/dist/esm/tests/core/protocol-authorization.spec.js.map +1 -1
- package/dist/esm/tests/core/records-grant-authorization.spec.js +5 -5
- package/dist/esm/tests/core/records-grant-authorization.spec.js.map +1 -1
- package/dist/esm/tests/core/replication-apply.spec.js +274 -0
- package/dist/esm/tests/core/replication-apply.spec.js.map +1 -0
- package/dist/esm/tests/core/replication-replay-property.spec.js +350 -0
- package/dist/esm/tests/core/replication-replay-property.spec.js.map +1 -0
- package/dist/esm/tests/core/validation-read-closure.spec.js +469 -0
- package/dist/esm/tests/core/validation-read-closure.spec.js.map +1 -0
- package/dist/esm/tests/core/validation-state-reader.spec.js +716 -0
- package/dist/esm/tests/core/validation-state-reader.spec.js.map +1 -0
- package/dist/esm/tests/durable-event-log.spec.js +373 -0
- package/dist/esm/tests/durable-event-log.spec.js.map +1 -0
- package/dist/esm/tests/dwn.spec.js +620 -14
- package/dist/esm/tests/dwn.spec.js.map +1 -1
- package/dist/esm/tests/features/author-delegated-grant.spec.js +9 -6
- package/dist/esm/tests/features/author-delegated-grant.spec.js.map +1 -1
- package/dist/esm/tests/features/owner-delegated-grant.spec.js +1 -4
- package/dist/esm/tests/features/owner-delegated-grant.spec.js.map +1 -1
- package/dist/esm/tests/features/owner-signature.spec.js +1 -4
- package/dist/esm/tests/features/owner-signature.spec.js.map +1 -1
- package/dist/esm/tests/features/permissions.spec.js +165 -4
- package/dist/esm/tests/features/permissions.spec.js.map +1 -1
- package/dist/esm/tests/features/protocol-composition.spec.js +8 -11
- package/dist/esm/tests/features/protocol-composition.spec.js.map +1 -1
- package/dist/esm/tests/features/protocol-create-action.spec.js +1 -4
- package/dist/esm/tests/features/protocol-create-action.spec.js.map +1 -1
- package/dist/esm/tests/features/protocol-delete-action.spec.js +3 -5
- package/dist/esm/tests/features/protocol-delete-action.spec.js.map +1 -1
- package/dist/esm/tests/features/protocol-update-action.spec.js +3 -6
- package/dist/esm/tests/features/protocol-update-action.spec.js.map +1 -1
- package/dist/esm/tests/features/records-delivery.spec.js +1 -4
- package/dist/esm/tests/features/records-delivery.spec.js.map +1 -1
- package/dist/esm/tests/features/records-immutable.spec.js +1 -4
- package/dist/esm/tests/features/records-immutable.spec.js.map +1 -1
- package/dist/esm/tests/features/records-nested-query-scope.spec.js +281 -0
- package/dist/esm/tests/features/records-nested-query-scope.spec.js.map +1 -0
- package/dist/esm/tests/features/records-prune-cross-protocol.spec.js +3 -7
- package/dist/esm/tests/features/records-prune-cross-protocol.spec.js.map +1 -1
- package/dist/esm/tests/features/records-prune.spec.js +11 -22
- package/dist/esm/tests/features/records-prune.spec.js.map +1 -1
- package/dist/esm/tests/features/records-record-limit.spec.js +441 -231
- package/dist/esm/tests/features/records-record-limit.spec.js.map +1 -1
- package/dist/esm/tests/features/records-squash.spec.js +6 -4
- package/dist/esm/tests/features/records-squash.spec.js.map +1 -1
- package/dist/esm/tests/features/records-tags.spec.js +1 -4
- package/dist/esm/tests/features/records-tags.spec.js.map +1 -1
- package/dist/esm/tests/features/resumable-tasks.spec.js +3 -5
- package/dist/esm/tests/features/resumable-tasks.spec.js.map +1 -1
- package/dist/esm/tests/fuzz/message-store.fuzz.spec.js +1 -2
- package/dist/esm/tests/fuzz/message-store.fuzz.spec.js.map +1 -1
- package/dist/esm/tests/fuzz/process-message.fuzz.spec.js +2 -4
- package/dist/esm/tests/fuzz/process-message.fuzz.spec.js.map +1 -1
- package/dist/esm/tests/fuzz/schema-validation.fuzz.spec.js +1 -1
- package/dist/esm/tests/fuzz/schema-validation.fuzz.spec.js.map +1 -1
- package/dist/esm/tests/handlers/messages-query.spec.js +246 -0
- package/dist/esm/tests/handlers/messages-query.spec.js.map +1 -0
- package/dist/esm/tests/handlers/messages-read.spec.js +2 -5
- package/dist/esm/tests/handlers/messages-read.spec.js.map +1 -1
- package/dist/esm/tests/handlers/messages-subscribe.spec.js +3 -14
- package/dist/esm/tests/handlers/messages-subscribe.spec.js.map +1 -1
- package/dist/esm/tests/handlers/protocols-configure.spec.js +27 -26
- package/dist/esm/tests/handlers/protocols-configure.spec.js.map +1 -1
- package/dist/esm/tests/handlers/protocols-query.spec.js +1 -4
- package/dist/esm/tests/handlers/protocols-query.spec.js.map +1 -1
- package/dist/esm/tests/handlers/records-count.spec.js +1 -4
- package/dist/esm/tests/handlers/records-count.spec.js.map +1 -1
- package/dist/esm/tests/handlers/records-delete.spec.js +312 -30
- package/dist/esm/tests/handlers/records-delete.spec.js.map +1 -1
- package/dist/esm/tests/handlers/records-query.spec.js +32 -9
- package/dist/esm/tests/handlers/records-query.spec.js.map +1 -1
- package/dist/esm/tests/handlers/records-read.spec.js +4 -4
- package/dist/esm/tests/handlers/records-read.spec.js.map +1 -1
- package/dist/esm/tests/handlers/records-subscribe.spec.js +33 -14
- package/dist/esm/tests/handlers/records-subscribe.spec.js.map +1 -1
- package/dist/esm/tests/handlers/records-write.spec.js +84 -38
- package/dist/esm/tests/handlers/records-write.spec.js.map +1 -1
- package/dist/esm/tests/interfaces/records-delete.spec.js +69 -2
- package/dist/esm/tests/interfaces/records-delete.spec.js.map +1 -1
- package/dist/esm/tests/interfaces/records-write.spec.js +4 -3
- package/dist/esm/tests/interfaces/records-write.spec.js.map +1 -1
- package/dist/esm/tests/protocols/permissions.spec.js +55 -6
- package/dist/esm/tests/protocols/permissions.spec.js.map +1 -1
- package/dist/esm/tests/scenarios/aggregator.spec.js +1 -4
- package/dist/esm/tests/scenarios/aggregator.spec.js.map +1 -1
- package/dist/esm/tests/scenarios/deleted-record.spec.js +1 -4
- package/dist/esm/tests/scenarios/deleted-record.spec.js.map +1 -1
- package/dist/esm/tests/scenarios/end-to-end-tests.spec.js +1 -4
- package/dist/esm/tests/scenarios/end-to-end-tests.spec.js.map +1 -1
- package/dist/esm/tests/scenarios/nested-roles.spec.js +1 -4
- package/dist/esm/tests/scenarios/nested-roles.spec.js.map +1 -1
- package/dist/esm/tests/scenarios/subscriptions.spec.js +1 -4
- package/dist/esm/tests/scenarios/subscriptions.spec.js.map +1 -1
- package/dist/esm/tests/store/message-store-level.spec.js +361 -5
- package/dist/esm/tests/store/message-store-level.spec.js.map +1 -1
- package/dist/esm/tests/store/message-store.spec.js +60 -0
- package/dist/esm/tests/store/message-store.spec.js.map +1 -1
- package/dist/esm/tests/test-event-stream.js +7 -3
- package/dist/esm/tests/test-event-stream.js.map +1 -1
- package/dist/esm/tests/test-stores.js +19 -9
- package/dist/esm/tests/test-stores.js.map +1 -1
- package/dist/esm/tests/test-suite.js +4 -4
- package/dist/esm/tests/test-suite.js.map +1 -1
- package/dist/esm/tests/utils/test-data-generator.js +25 -0
- package/dist/esm/tests/utils/test-data-generator.js.map +1 -1
- package/dist/esm/tests/utils/test-stub-generator.js.map +1 -1
- package/dist/esm/tests/utils/test-validation-state-reader.js +16 -0
- package/dist/esm/tests/utils/test-validation-state-reader.js.map +1 -0
- package/dist/types/generated/precompiled-validators.d.ts +6 -6
- package/dist/types/generated/precompiled-validators.d.ts.map +1 -1
- package/dist/types/src/core/core-protocol.d.ts +3 -3
- package/dist/types/src/core/core-protocol.d.ts.map +1 -1
- package/dist/types/src/core/dwn-constant.d.ts +5 -0
- package/dist/types/src/core/dwn-constant.d.ts.map +1 -1
- package/dist/types/src/core/dwn-error.d.ts +13 -7
- package/dist/types/src/core/dwn-error.d.ts.map +1 -1
- package/dist/types/src/core/grant-authorization.d.ts +5 -5
- package/dist/types/src/core/grant-authorization.d.ts.map +1 -1
- package/dist/types/src/core/message-reply.d.ts +5 -4
- package/dist/types/src/core/message-reply.d.ts.map +1 -1
- package/dist/types/src/core/messages-grant-authorization.d.ts +12 -15
- package/dist/types/src/core/messages-grant-authorization.d.ts.map +1 -1
- package/dist/types/src/core/protocol-authorization-action.d.ts +4 -5
- package/dist/types/src/core/protocol-authorization-action.d.ts.map +1 -1
- package/dist/types/src/core/protocol-authorization-validation.d.ts +13 -16
- package/dist/types/src/core/protocol-authorization-validation.d.ts.map +1 -1
- package/dist/types/src/core/protocol-authorization.d.ts +8 -33
- package/dist/types/src/core/protocol-authorization.d.ts.map +1 -1
- package/dist/types/src/core/protocols-grant-authorization.d.ts +4 -4
- package/dist/types/src/core/protocols-grant-authorization.d.ts.map +1 -1
- package/dist/types/src/core/recording-validation-state-reader.d.ts +75 -0
- package/dist/types/src/core/recording-validation-state-reader.d.ts.map +1 -0
- package/dist/types/src/core/records-grant-authorization.d.ts +8 -8
- package/dist/types/src/core/records-grant-authorization.d.ts.map +1 -1
- package/dist/types/src/core/replication-apply.d.ts +129 -0
- package/dist/types/src/core/replication-apply.d.ts.map +1 -0
- package/dist/types/src/core/resumable-task-manager.d.ts +1 -1
- package/dist/types/src/core/resumable-task-manager.d.ts.map +1 -1
- package/dist/types/src/core/validation-state-reader.d.ts +79 -0
- package/dist/types/src/core/validation-state-reader.d.ts.map +1 -0
- package/dist/types/src/dwn.d.ts +47 -13
- package/dist/types/src/dwn.d.ts.map +1 -1
- package/dist/types/src/enums/dwn-interface-method.d.ts +0 -1
- package/dist/types/src/enums/dwn-interface-method.d.ts.map +1 -1
- package/dist/types/src/event-stream/durable-event-log.d.ts +69 -0
- package/dist/types/src/event-stream/durable-event-log.d.ts.map +1 -0
- package/dist/types/src/event-stream/event-emitter-wake-publisher.d.ts +13 -0
- package/dist/types/src/event-stream/event-emitter-wake-publisher.d.ts.map +1 -0
- package/dist/types/src/handlers/messages-query.d.ts +20 -0
- package/dist/types/src/handlers/messages-query.d.ts.map +1 -0
- package/dist/types/src/handlers/messages-read.d.ts +1 -1
- package/dist/types/src/handlers/messages-read.d.ts.map +1 -1
- package/dist/types/src/handlers/messages-subscribe.d.ts.map +1 -1
- package/dist/types/src/handlers/protocols-configure.d.ts +0 -5
- package/dist/types/src/handlers/protocols-configure.d.ts.map +1 -1
- package/dist/types/src/handlers/records-count.d.ts +2 -1
- package/dist/types/src/handlers/records-count.d.ts.map +1 -1
- package/dist/types/src/handlers/records-delete.d.ts +2 -2
- package/dist/types/src/handlers/records-delete.d.ts.map +1 -1
- package/dist/types/src/handlers/records-query.d.ts +1 -1
- package/dist/types/src/handlers/records-query.d.ts.map +1 -1
- package/dist/types/src/handlers/records-read.d.ts +2 -1
- package/dist/types/src/handlers/records-read.d.ts.map +1 -1
- package/dist/types/src/handlers/records-subscribe.d.ts +4 -5
- package/dist/types/src/handlers/records-subscribe.d.ts.map +1 -1
- package/dist/types/src/handlers/records-write.d.ts +3 -11
- package/dist/types/src/handlers/records-write.d.ts.map +1 -1
- package/dist/types/src/index.d.ts +16 -18
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/interfaces/messages-query.d.ts +23 -0
- package/dist/types/src/interfaces/messages-query.d.ts.map +1 -0
- package/dist/types/src/interfaces/protocols-configure.d.ts +3 -3
- package/dist/types/src/interfaces/protocols-configure.d.ts.map +1 -1
- package/dist/types/src/interfaces/protocols-query.d.ts +2 -2
- package/dist/types/src/interfaces/protocols-query.d.ts.map +1 -1
- package/dist/types/src/interfaces/records-count.d.ts +3 -3
- package/dist/types/src/interfaces/records-count.d.ts.map +1 -1
- package/dist/types/src/interfaces/records-delete.d.ts +11 -3
- package/dist/types/src/interfaces/records-delete.d.ts.map +1 -1
- package/dist/types/src/interfaces/records-query.d.ts +3 -3
- package/dist/types/src/interfaces/records-query.d.ts.map +1 -1
- package/dist/types/src/interfaces/records-read.d.ts +3 -3
- package/dist/types/src/interfaces/records-read.d.ts.map +1 -1
- package/dist/types/src/interfaces/records-subscribe.d.ts +3 -3
- package/dist/types/src/interfaces/records-subscribe.d.ts.map +1 -1
- package/dist/types/src/interfaces/records-write.d.ts +15 -7
- package/dist/types/src/interfaces/records-write.d.ts.map +1 -1
- package/dist/types/src/protocols/permissions.d.ts +9 -12
- package/dist/types/src/protocols/permissions.d.ts.map +1 -1
- package/dist/types/src/store/index-level.d.ts +10 -1
- package/dist/types/src/store/index-level.d.ts.map +1 -1
- package/dist/types/src/store/level-wrapper.d.ts +5 -0
- package/dist/types/src/store/level-wrapper.d.ts.map +1 -1
- package/dist/types/src/store/message-store-level.d.ts +94 -14
- package/dist/types/src/store/message-store-level.d.ts.map +1 -1
- package/dist/types/src/store/storage-controller.d.ts +17 -14
- package/dist/types/src/store/storage-controller.d.ts.map +1 -1
- package/dist/types/src/types/message-store.d.ts +29 -1
- package/dist/types/src/types/message-store.d.ts.map +1 -1
- package/dist/types/src/types/message-types.d.ts +2 -0
- package/dist/types/src/types/message-types.d.ts.map +1 -1
- package/dist/types/src/types/messages-types.d.ts +21 -55
- package/dist/types/src/types/messages-types.d.ts.map +1 -1
- package/dist/types/src/types/method-handler.d.ts +2 -2
- package/dist/types/src/types/method-handler.d.ts.map +1 -1
- package/dist/types/src/types/permission-types.d.ts +1 -1
- package/dist/types/src/types/subscriptions.d.ts +50 -39
- package/dist/types/src/types/subscriptions.d.ts.map +1 -1
- package/dist/types/src/types/validation-state-reader.d.ts +116 -0
- package/dist/types/src/types/validation-state-reader.d.ts.map +1 -0
- package/dist/types/src/utils/messages.d.ts +10 -0
- package/dist/types/src/utils/messages.d.ts.map +1 -1
- package/dist/types/src/utils/record-limit-occupancy.d.ts +40 -0
- package/dist/types/src/utils/record-limit-occupancy.d.ts.map +1 -0
- package/dist/types/src/utils/records.d.ts +25 -3
- package/dist/types/src/utils/records.d.ts.map +1 -1
- package/dist/types/src/utils/replication.d.ts +22 -0
- package/dist/types/src/utils/replication.d.ts.map +1 -0
- package/dist/types/tests/core/process-message-parity.spec.d.ts +2 -0
- package/dist/types/tests/core/process-message-parity.spec.d.ts.map +1 -0
- package/dist/types/tests/core/replication-apply.spec.d.ts +2 -0
- package/dist/types/tests/core/replication-apply.spec.d.ts.map +1 -0
- package/dist/types/tests/core/replication-replay-property.spec.d.ts +2 -0
- package/dist/types/tests/core/replication-replay-property.spec.d.ts.map +1 -0
- package/dist/types/tests/core/validation-read-closure.spec.d.ts +2 -0
- package/dist/types/tests/core/validation-read-closure.spec.d.ts.map +1 -0
- package/dist/types/tests/core/validation-state-reader.spec.d.ts +2 -0
- package/dist/types/tests/core/validation-state-reader.spec.d.ts.map +1 -0
- package/dist/types/tests/durable-event-log.spec.d.ts +2 -0
- package/dist/types/tests/durable-event-log.spec.d.ts.map +1 -0
- package/dist/types/tests/dwn.spec.d.ts.map +1 -1
- package/dist/types/tests/features/author-delegated-grant.spec.d.ts.map +1 -1
- package/dist/types/tests/features/owner-delegated-grant.spec.d.ts.map +1 -1
- package/dist/types/tests/features/owner-signature.spec.d.ts.map +1 -1
- package/dist/types/tests/features/permissions.spec.d.ts.map +1 -1
- package/dist/types/tests/features/protocol-composition.spec.d.ts.map +1 -1
- package/dist/types/tests/features/protocol-create-action.spec.d.ts.map +1 -1
- package/dist/types/tests/features/protocol-delete-action.spec.d.ts.map +1 -1
- package/dist/types/tests/features/protocol-update-action.spec.d.ts.map +1 -1
- package/dist/types/tests/features/records-delivery.spec.d.ts.map +1 -1
- package/dist/types/tests/features/records-immutable.spec.d.ts.map +1 -1
- package/dist/types/tests/features/records-nested-query-scope.spec.d.ts +2 -0
- package/dist/types/tests/features/records-nested-query-scope.spec.d.ts.map +1 -0
- package/dist/types/tests/features/records-prune-cross-protocol.spec.d.ts.map +1 -1
- package/dist/types/tests/features/records-prune.spec.d.ts.map +1 -1
- package/dist/types/tests/features/records-record-limit.spec.d.ts.map +1 -1
- package/dist/types/tests/features/records-squash.spec.d.ts.map +1 -1
- package/dist/types/tests/features/records-tags.spec.d.ts.map +1 -1
- package/dist/types/tests/features/resumable-tasks.spec.d.ts.map +1 -1
- package/dist/types/tests/handlers/messages-query.spec.d.ts +2 -0
- package/dist/types/tests/handlers/messages-query.spec.d.ts.map +1 -0
- package/dist/types/tests/handlers/messages-read.spec.d.ts.map +1 -1
- package/dist/types/tests/handlers/messages-subscribe.spec.d.ts.map +1 -1
- package/dist/types/tests/handlers/protocols-configure.spec.d.ts.map +1 -1
- package/dist/types/tests/handlers/protocols-query.spec.d.ts.map +1 -1
- package/dist/types/tests/handlers/records-count.spec.d.ts.map +1 -1
- package/dist/types/tests/handlers/records-delete.spec.d.ts.map +1 -1
- package/dist/types/tests/handlers/records-query.spec.d.ts.map +1 -1
- package/dist/types/tests/handlers/records-read.spec.d.ts.map +1 -1
- package/dist/types/tests/handlers/records-subscribe.spec.d.ts.map +1 -1
- package/dist/types/tests/handlers/records-write.spec.d.ts.map +1 -1
- package/dist/types/tests/scenarios/deleted-record.spec.d.ts.map +1 -1
- package/dist/types/tests/scenarios/end-to-end-tests.spec.d.ts.map +1 -1
- package/dist/types/tests/scenarios/nested-roles.spec.d.ts.map +1 -1
- package/dist/types/tests/scenarios/subscriptions.spec.d.ts.map +1 -1
- package/dist/types/tests/store/message-store.spec.d.ts.map +1 -1
- package/dist/types/tests/test-event-stream.d.ts +1 -1
- package/dist/types/tests/test-event-stream.d.ts.map +1 -1
- package/dist/types/tests/test-stores.d.ts +5 -4
- package/dist/types/tests/test-stores.d.ts.map +1 -1
- package/dist/types/tests/test-suite.d.ts +1 -2
- package/dist/types/tests/test-suite.d.ts.map +1 -1
- package/dist/types/tests/utils/test-data-generator.d.ts +20 -1
- package/dist/types/tests/utils/test-data-generator.d.ts.map +1 -1
- package/dist/types/tests/utils/test-validation-state-reader.d.ts +15 -0
- package/dist/types/tests/utils/test-validation-state-reader.d.ts.map +1 -0
- package/package.json +2 -2
- package/src/core/core-protocol.ts +3 -3
- package/src/core/dwn-constant.ts +7 -1
- package/src/core/dwn-error.ts +13 -7
- package/src/core/grant-authorization.ts +11 -20
- package/src/core/message-reply.ts +6 -5
- package/src/core/messages-grant-authorization.ts +37 -100
- package/src/core/protocol-authorization-action.ts +29 -38
- package/src/core/protocol-authorization-validation.ts +41 -98
- package/src/core/protocol-authorization.ts +56 -202
- package/src/core/protocols-grant-authorization.ts +9 -9
- package/src/core/recording-validation-state-reader.ts +130 -0
- package/src/core/records-grant-authorization.ts +16 -16
- package/src/core/replication-apply.ts +412 -0
- package/src/core/resumable-task-manager.ts +10 -8
- package/src/core/validation-state-reader.ts +350 -0
- package/src/dwn.ts +417 -30
- package/src/enums/dwn-interface-method.ts +0 -1
- package/src/event-stream/durable-event-log.ts +509 -0
- package/src/event-stream/event-emitter-wake-publisher.ts +34 -0
- package/src/handlers/messages-query.ts +203 -0
- package/src/handlers/messages-read.ts +9 -10
- package/src/handlers/messages-subscribe.ts +12 -13
- package/src/handlers/protocols-configure.ts +37 -58
- package/src/handlers/protocols-query.ts +1 -1
- package/src/handlers/records-count.ts +24 -17
- package/src/handlers/records-delete.ts +29 -27
- package/src/handlers/records-query.ts +38 -17
- package/src/handlers/records-read.ts +63 -50
- package/src/handlers/records-subscribe.ts +132 -19
- package/src/handlers/records-write.ts +77 -168
- package/src/index.ts +16 -20
- package/src/interfaces/messages-query.ts +70 -0
- package/src/interfaces/protocols-configure.ts +12 -4
- package/src/interfaces/protocols-query.ts +4 -5
- package/src/interfaces/records-count.ts +9 -4
- package/src/interfaces/records-delete.ts +25 -5
- package/src/interfaces/records-query.ts +9 -4
- package/src/interfaces/records-read.ts +4 -4
- package/src/interfaces/records-subscribe.ts +9 -4
- package/src/interfaces/records-write.ts +41 -13
- package/src/protocols/permissions.ts +32 -52
- package/src/store/index-level.ts +30 -9
- package/src/store/level-wrapper.ts +9 -1
- package/src/store/message-store-level.ts +757 -47
- package/src/store/storage-controller.ts +74 -63
- package/src/types/message-store.ts +45 -2
- package/src/types/message-types.ts +3 -1
- package/src/types/messages-types.ts +26 -65
- package/src/types/method-handler.ts +3 -3
- package/src/types/permission-types.ts +1 -1
- package/src/types/subscriptions.ts +53 -42
- package/src/types/validation-state-reader.ts +127 -0
- package/src/utils/messages.ts +25 -1
- package/src/utils/record-limit-occupancy.ts +377 -0
- package/src/utils/records.ts +69 -13
- package/src/utils/replication.ts +122 -0
- package/dist/esm/src/core/record-chain.js +0 -64
- package/dist/esm/src/core/record-chain.js.map +0 -1
- package/dist/esm/src/event-stream/event-emitter-event-log.js +0 -334
- package/dist/esm/src/event-stream/event-emitter-event-log.js.map +0 -1
- package/dist/esm/src/handlers/messages-sync.js +0 -581
- package/dist/esm/src/handlers/messages-sync.js.map +0 -1
- package/dist/esm/src/interfaces/messages-sync.js +0 -54
- package/dist/esm/src/interfaces/messages-sync.js.map +0 -1
- package/dist/esm/src/smt/smt-store-level.js +0 -103
- package/dist/esm/src/smt/smt-store-level.js.map +0 -1
- package/dist/esm/src/smt/smt-store-memory.js +0 -41
- package/dist/esm/src/smt/smt-store-memory.js.map +0 -1
- package/dist/esm/src/smt/smt-utils.js +0 -129
- package/dist/esm/src/smt/smt-utils.js.map +0 -1
- package/dist/esm/src/smt/sparse-merkle-tree.js +0 -577
- package/dist/esm/src/smt/sparse-merkle-tree.js.map +0 -1
- package/dist/esm/src/state-index/state-index-level.js +0 -191
- package/dist/esm/src/state-index/state-index-level.js.map +0 -1
- package/dist/esm/src/sync/records-projection.js +0 -228
- package/dist/esm/src/sync/records-projection.js.map +0 -1
- package/dist/esm/src/types/smt-types.js +0 -5
- package/dist/esm/src/types/smt-types.js.map +0 -1
- package/dist/esm/src/types/state-index.js +0 -2
- package/dist/esm/src/types/state-index.js.map +0 -1
- package/dist/esm/tests/event-emitter-event-log.spec.js +0 -499
- package/dist/esm/tests/event-emitter-event-log.spec.js.map +0 -1
- package/dist/esm/tests/handlers/messages-sync.spec.js +0 -1771
- package/dist/esm/tests/handlers/messages-sync.spec.js.map +0 -1
- package/dist/esm/tests/smt/smt-store-level.spec.js +0 -132
- package/dist/esm/tests/smt/smt-store-level.spec.js.map +0 -1
- package/dist/esm/tests/smt/sparse-merkle-tree.spec.js +0 -732
- package/dist/esm/tests/smt/sparse-merkle-tree.spec.js.map +0 -1
- package/dist/esm/tests/state-index/state-index-level.spec.js +0 -245
- package/dist/esm/tests/state-index/state-index-level.spec.js.map +0 -1
- package/dist/esm/tests/sync/records-projection.spec.js +0 -245
- package/dist/esm/tests/sync/records-projection.spec.js.map +0 -1
- package/dist/types/src/core/record-chain.d.ts +0 -24
- package/dist/types/src/core/record-chain.d.ts.map +0 -1
- package/dist/types/src/event-stream/event-emitter-event-log.d.ts +0 -80
- package/dist/types/src/event-stream/event-emitter-event-log.d.ts.map +0 -1
- package/dist/types/src/handlers/messages-sync.d.ts +0 -83
- package/dist/types/src/handlers/messages-sync.d.ts.map +0 -1
- package/dist/types/src/interfaces/messages-sync.d.ts +0 -23
- package/dist/types/src/interfaces/messages-sync.d.ts.map +0 -1
- package/dist/types/src/smt/smt-store-level.d.ts +0 -32
- package/dist/types/src/smt/smt-store-level.d.ts.map +0 -1
- package/dist/types/src/smt/smt-store-memory.d.ts +0 -22
- package/dist/types/src/smt/smt-store-memory.d.ts.map +0 -1
- package/dist/types/src/smt/smt-utils.d.ts +0 -58
- package/dist/types/src/smt/smt-utils.d.ts.map +0 -1
- package/dist/types/src/smt/sparse-merkle-tree.d.ts +0 -124
- package/dist/types/src/smt/sparse-merkle-tree.d.ts.map +0 -1
- package/dist/types/src/state-index/state-index-level.d.ts +0 -83
- package/dist/types/src/state-index/state-index-level.d.ts.map +0 -1
- package/dist/types/src/sync/records-projection.d.ts +0 -98
- package/dist/types/src/sync/records-projection.d.ts.map +0 -1
- package/dist/types/src/types/smt-types.d.ts +0 -81
- package/dist/types/src/types/smt-types.d.ts.map +0 -1
- package/dist/types/src/types/state-index.d.ts +0 -90
- package/dist/types/src/types/state-index.d.ts.map +0 -1
- package/dist/types/tests/event-emitter-event-log.spec.d.ts +0 -2
- package/dist/types/tests/event-emitter-event-log.spec.d.ts.map +0 -1
- package/dist/types/tests/handlers/messages-sync.spec.d.ts +0 -2
- package/dist/types/tests/handlers/messages-sync.spec.d.ts.map +0 -1
- package/dist/types/tests/smt/smt-store-level.spec.d.ts +0 -2
- package/dist/types/tests/smt/smt-store-level.spec.d.ts.map +0 -1
- package/dist/types/tests/smt/sparse-merkle-tree.spec.d.ts +0 -2
- package/dist/types/tests/smt/sparse-merkle-tree.spec.d.ts.map +0 -1
- package/dist/types/tests/state-index/state-index-level.spec.d.ts +0 -2
- package/dist/types/tests/state-index/state-index-level.spec.d.ts.map +0 -1
- package/dist/types/tests/sync/records-projection.spec.d.ts +0 -2
- package/dist/types/tests/sync/records-projection.spec.d.ts.map +0 -1
- package/src/core/record-chain.ts +0 -99
- package/src/event-stream/event-emitter-event-log.ts +0 -430
- package/src/handlers/messages-sync.ts +0 -896
- package/src/interfaces/messages-sync.ts +0 -86
- package/src/smt/smt-store-level.ts +0 -143
- package/src/smt/smt-store-memory.ts +0 -53
- package/src/smt/smt-utils.ts +0 -149
- package/src/smt/sparse-merkle-tree.ts +0 -698
- package/src/state-index/state-index-level.ts +0 -239
- package/src/sync/records-projection.ts +0 -328
- package/src/types/smt-types.ts +0 -95
- package/src/types/state-index.ts +0 -100
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
import * as block from 'multiformats/block';
|
|
2
2
|
import * as cbor from '@ipld/dag-cbor';
|
|
3
|
-
import { BlockstoreLevel } from './blockstore-level.js';
|
|
4
3
|
import { Cid } from '../utils/cid.js';
|
|
5
4
|
import { CID } from 'multiformats/cid';
|
|
6
|
-
import { createLevelDatabase } from './level-wrapper.js';
|
|
7
5
|
import { executeUnlessAborted } from '../utils/abort.js';
|
|
6
|
+
import { FilterUtility } from '../utils/filter.js';
|
|
8
7
|
import { IndexLevel } from './index-level.js';
|
|
9
8
|
import { Message } from '../core/message.js';
|
|
9
|
+
import { Replication } from '../utils/replication.js';
|
|
10
10
|
import { sha256 } from 'multiformats/hashes/sha2';
|
|
11
11
|
import { SortDirection } from '../types/query-types.js';
|
|
12
|
+
import { createLevelDatabase, LevelWrapper } from './level-wrapper.js';
|
|
13
|
+
import { DwnError, DwnErrorCode } from '../core/dwn-error.js';
|
|
12
14
|
/**
|
|
13
15
|
* Default compound indexes that cover the most common DWN query patterns.
|
|
14
16
|
* These are automatically registered unless overridden via config.
|
|
@@ -54,52 +56,150 @@ const DEFAULT_COMPOUND_INDEXES = [
|
|
|
54
56
|
},
|
|
55
57
|
];
|
|
56
58
|
/**
|
|
57
|
-
*
|
|
58
|
-
*
|
|
59
|
+
* Sublevel names under the single Level root. Every mutation touching multiple partitions is
|
|
60
|
+
* committed as one atomic batch on the root, so store↔log divergence is impossible by construction.
|
|
61
|
+
*/
|
|
62
|
+
const BLOCKS_PARTITION = 'blocks';
|
|
63
|
+
const INDEX_PARTITION = 'idx';
|
|
64
|
+
const LOG_PARTITION = 'log';
|
|
65
|
+
const CID_TO_SEQ_PARTITION = 'cid';
|
|
66
|
+
const FINGERPRINT_PARTITION = 'fp';
|
|
67
|
+
const HEADS_PARTITION = 'heads';
|
|
68
|
+
const META_PARTITION = 'meta';
|
|
69
|
+
const EPOCH_KEY = 'epoch';
|
|
70
|
+
const CURRENT_PARTITIONS = new Set([
|
|
71
|
+
BLOCKS_PARTITION,
|
|
72
|
+
INDEX_PARTITION,
|
|
73
|
+
LOG_PARTITION,
|
|
74
|
+
CID_TO_SEQ_PARTITION,
|
|
75
|
+
FINGERPRINT_PARTITION,
|
|
76
|
+
HEADS_PARTITION,
|
|
77
|
+
META_PARTITION,
|
|
78
|
+
]);
|
|
79
|
+
/**
|
|
80
|
+
* A {@link MessageStore} and {@link ReplicationFeedReader} implementation that works in both the
|
|
81
|
+
* browser and server-side, backed by a SINGLE LevelDB root: message blocks, query indexes, the
|
|
82
|
+
* per-tenant replication log, the cid→seq index, fingerprint domains, the tenant counters, and
|
|
83
|
+
* the store epoch are all sublevels of one Level instance, so every mutation commits as one fully
|
|
84
|
+
* atomic batch.
|
|
85
|
+
*
|
|
86
|
+
* A per-tenant async write mutex spans seq assignment through batch write, so commit order equals
|
|
87
|
+
* seq order by construction. Seq assignment is gap-free (a failed batch never persists the head),
|
|
88
|
+
* while the readable log stays sparse after compaction and under filters — readers never assume
|
|
89
|
+
* contiguity.
|
|
59
90
|
*/
|
|
60
91
|
export class MessageStoreLevel {
|
|
61
92
|
config;
|
|
62
|
-
|
|
63
|
-
|
|
93
|
+
wakePublisher;
|
|
94
|
+
partitionsPromise;
|
|
95
|
+
epochPromise;
|
|
96
|
+
writeLocks = new Map();
|
|
64
97
|
/**
|
|
65
98
|
* @param {MessageStoreLevelConfig} config
|
|
66
|
-
* @param {string} config.
|
|
99
|
+
* @param {string} config.location - must be a directory path (relative or absolute) where
|
|
67
100
|
* LevelDB will store its files, or in browsers, the name of the
|
|
68
101
|
* {@link https://developer.mozilla.org/en-US/docs/Web/API/IDBDatabase IDBDatabase} to be opened.
|
|
69
|
-
* @param {string} config.indexLocation - same as config.blockstoreLocation
|
|
70
102
|
* @param {CompoundIndexDefinition[]} config.compoundIndexes - compound indexes to register.
|
|
71
103
|
* Defaults to DEFAULT_COMPOUND_INDEXES which cover the most common DWN query patterns.
|
|
104
|
+
* @param {WakePublisher} config.wakePublisher - optional bus for store-owned wake publication.
|
|
72
105
|
*/
|
|
73
106
|
constructor(config = {}) {
|
|
74
107
|
this.config = {
|
|
75
|
-
|
|
76
|
-
indexLocation: 'INDEX',
|
|
108
|
+
location: 'MESSAGESTORE',
|
|
77
109
|
createLevelDatabase,
|
|
78
110
|
...config
|
|
79
111
|
};
|
|
80
|
-
this.
|
|
81
|
-
location: this.config.blockstoreLocation,
|
|
82
|
-
createLevelDatabase: this.config.createLevelDatabase,
|
|
83
|
-
});
|
|
84
|
-
this.index = new IndexLevel({
|
|
85
|
-
location: this.config.indexLocation,
|
|
86
|
-
createLevelDatabase: this.config.createLevelDatabase,
|
|
87
|
-
compoundIndexes: this.config.compoundIndexes ?? DEFAULT_COMPOUND_INDEXES,
|
|
88
|
-
});
|
|
112
|
+
this.wakePublisher = this.config.wakePublisher;
|
|
89
113
|
}
|
|
90
114
|
async open() {
|
|
91
|
-
await this.
|
|
92
|
-
await
|
|
115
|
+
const partitions = await this.partitions();
|
|
116
|
+
await partitions.root.open();
|
|
117
|
+
await this.assertNoPreSubstrateLayout(partitions);
|
|
118
|
+
await this.getEpoch();
|
|
93
119
|
}
|
|
94
120
|
async close() {
|
|
95
|
-
|
|
96
|
-
|
|
121
|
+
if (this.partitionsPromise === undefined) {
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
const partitions = await this.partitionsPromise;
|
|
125
|
+
await partitions.root.close();
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Lazily creates the single Level root and its partition handles.
|
|
129
|
+
*/
|
|
130
|
+
async partitions() {
|
|
131
|
+
this.partitionsPromise ??= this.createPartitions().catch((error) => {
|
|
132
|
+
this.partitionsPromise = undefined;
|
|
133
|
+
throw error;
|
|
134
|
+
});
|
|
135
|
+
return this.partitionsPromise;
|
|
136
|
+
}
|
|
137
|
+
async createPartitions() {
|
|
138
|
+
const location = this.config.location;
|
|
139
|
+
const db = await this.config.createLevelDatabase(location, { keyEncoding: 'utf8', valueEncoding: 'utf8' });
|
|
140
|
+
const root = new LevelWrapper({ location, createLevelDatabase: this.config.createLevelDatabase, keyEncoding: 'utf8', valueEncoding: 'utf8' }, db);
|
|
141
|
+
// A binary-valued view over the same underlying Level instance — its partitions carry their
|
|
142
|
+
// own value encoding, so block bytes and JSON strings coexist in one atomic batch domain.
|
|
143
|
+
const binaryView = new LevelWrapper({ location, createLevelDatabase: this.config.createLevelDatabase, keyEncoding: 'utf8', valueEncoding: 'binary' }, db);
|
|
144
|
+
return {
|
|
145
|
+
root,
|
|
146
|
+
blocks: await binaryView.partition(BLOCKS_PARTITION),
|
|
147
|
+
log: await root.partition(LOG_PARTITION),
|
|
148
|
+
cidToSeq: await root.partition(CID_TO_SEQ_PARTITION),
|
|
149
|
+
fingerprints: await root.partition(FINGERPRINT_PARTITION),
|
|
150
|
+
heads: await root.partition(HEADS_PARTITION),
|
|
151
|
+
meta: await root.partition(META_PARTITION),
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* The query index over the shared root. Constructed lazily alongside the partitions.
|
|
156
|
+
*/
|
|
157
|
+
indexPromise;
|
|
158
|
+
async index() {
|
|
159
|
+
this.indexPromise ??= (async () => {
|
|
160
|
+
const partitions = await this.partitions();
|
|
161
|
+
const indexRoot = await partitions.root.partition(INDEX_PARTITION);
|
|
162
|
+
return new IndexLevel({
|
|
163
|
+
location: this.config.location,
|
|
164
|
+
createLevelDatabase: this.config.createLevelDatabase,
|
|
165
|
+
compoundIndexes: this.config.compoundIndexes ?? DEFAULT_COMPOUND_INDEXES,
|
|
166
|
+
}, indexRoot);
|
|
167
|
+
})().catch((error) => {
|
|
168
|
+
this.indexPromise = undefined;
|
|
169
|
+
throw error;
|
|
170
|
+
});
|
|
171
|
+
return this.indexPromise;
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Returns the persisted store epoch, generating and persisting a fresh `crypto.randomUUID()`
|
|
175
|
+
* at first open. Replaced only by `clear()` — a full local reset with no surviving cursors.
|
|
176
|
+
*/
|
|
177
|
+
async epoch() {
|
|
178
|
+
return this.getEpoch();
|
|
179
|
+
}
|
|
180
|
+
async getEpoch() {
|
|
181
|
+
this.epochPromise ??= this.initializeEpoch().catch((error) => {
|
|
182
|
+
this.epochPromise = undefined;
|
|
183
|
+
throw error;
|
|
184
|
+
});
|
|
185
|
+
return this.epochPromise;
|
|
186
|
+
}
|
|
187
|
+
async initializeEpoch() {
|
|
188
|
+
const partitions = await this.partitions();
|
|
189
|
+
const existing = await partitions.meta.get(EPOCH_KEY);
|
|
190
|
+
if (existing !== undefined) {
|
|
191
|
+
return existing;
|
|
192
|
+
}
|
|
193
|
+
const freshEpoch = crypto.randomUUID();
|
|
194
|
+
await partitions.meta.put(EPOCH_KEY, freshEpoch);
|
|
195
|
+
return freshEpoch;
|
|
97
196
|
}
|
|
98
197
|
async get(tenant, cidString, options) {
|
|
99
198
|
options?.signal?.throwIfAborted();
|
|
100
|
-
const
|
|
199
|
+
const partitions = await executeUnlessAborted(this.partitions(), options?.signal);
|
|
200
|
+
const tenantBlocks = await executeUnlessAborted(partitions.blocks.partition(tenant), options?.signal);
|
|
101
201
|
const cid = CID.parse(cidString);
|
|
102
|
-
const bytes = await
|
|
202
|
+
const bytes = await tenantBlocks.get(cid.toString(), options);
|
|
103
203
|
if (!bytes) {
|
|
104
204
|
return undefined;
|
|
105
205
|
}
|
|
@@ -112,7 +212,8 @@ export class MessageStoreLevel {
|
|
|
112
212
|
// creates the query options including sorting and pagination.
|
|
113
213
|
// this adds 1 to the limit if provided, that way we can check to see if there are additional results and provide a return cursor.
|
|
114
214
|
const queryOptions = MessageStoreLevel.buildQueryOptions(messageSort, pagination);
|
|
115
|
-
const
|
|
215
|
+
const index = await this.index();
|
|
216
|
+
const results = await index.query(tenant, filters, queryOptions, options);
|
|
116
217
|
let cursor;
|
|
117
218
|
// checks to see if the returned results are greater than the limit, which would indicate additional results.
|
|
118
219
|
if (pagination?.limit !== undefined && pagination.limit < results.length) {
|
|
@@ -134,7 +235,8 @@ export class MessageStoreLevel {
|
|
|
134
235
|
async count(tenant, filters, messageSort, options) {
|
|
135
236
|
options?.signal?.throwIfAborted();
|
|
136
237
|
const queryOptions = MessageStoreLevel.buildQueryOptions(messageSort);
|
|
137
|
-
|
|
238
|
+
const index = await this.index();
|
|
239
|
+
return index.count(tenant, filters, queryOptions, options);
|
|
138
240
|
}
|
|
139
241
|
/**
|
|
140
242
|
* Builds the IndexLevel QueryOptions object given MessageStore sort and pagination parameters.
|
|
@@ -164,29 +266,421 @@ export class MessageStoreLevel {
|
|
|
164
266
|
}
|
|
165
267
|
return { sortDirection, sortProperty, limit, cursor };
|
|
166
268
|
}
|
|
167
|
-
async delete(tenant, cidString, options) {
|
|
168
|
-
options?.signal?.throwIfAborted();
|
|
169
|
-
const partition = await executeUnlessAborted(this.blockstore.partition(tenant), options?.signal);
|
|
170
|
-
const cid = CID.parse(cidString);
|
|
171
|
-
await partition.delete(cid, options);
|
|
172
|
-
await this.index.delete(tenant, cidString, options);
|
|
173
|
-
}
|
|
174
269
|
async put(tenant, message, indexes, options) {
|
|
175
270
|
options?.signal?.throwIfAborted();
|
|
176
|
-
const
|
|
271
|
+
const partitions = await executeUnlessAborted(this.partitions(), options?.signal);
|
|
272
|
+
const index = await executeUnlessAborted(this.index(), options?.signal);
|
|
177
273
|
const encodedMessageBlock = await executeUnlessAborted(block.encode({ value: message, codec: cbor, hasher: sha256 }), options?.signal);
|
|
178
274
|
// MessageStore data may contain `encodedData` which is not taken into account when calculating the blockCID as it is optional data.
|
|
179
|
-
const messageCid = Cid.parseCid(await Message.getCid(message));
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
275
|
+
const messageCid = Cid.parseCid(await Message.getCid(message)).toString();
|
|
276
|
+
return this.withTenantWriteLock(tenant, async () => {
|
|
277
|
+
options?.signal?.throwIfAborted();
|
|
278
|
+
const tenantCidToSeq = await partitions.cidToSeq.partition(tenant);
|
|
279
|
+
const existingSeq = await tenantCidToSeq.get(messageCid, options);
|
|
280
|
+
if (existingSeq !== undefined) {
|
|
281
|
+
// Duplicates mutate nothing and publish no wake.
|
|
282
|
+
return { status: 'duplicate' };
|
|
283
|
+
}
|
|
284
|
+
const head = await this.getHead(partitions, tenant);
|
|
285
|
+
const seq = head + 1n;
|
|
286
|
+
const fingerprintScopes = Replication.computeFingerprintScopes(message, indexes);
|
|
287
|
+
const logEntry = {
|
|
288
|
+
seq: seq.toString(),
|
|
289
|
+
messageCid,
|
|
290
|
+
indexes,
|
|
291
|
+
fingerprintScopes,
|
|
292
|
+
};
|
|
293
|
+
const tenantBlocks = await partitions.blocks.partition(tenant);
|
|
294
|
+
const blockOperation = tenantBlocks.createOperation({ type: 'put', key: messageCid, value: encodedMessageBlock.bytes });
|
|
295
|
+
const tenantLog = await partitions.log.partition(tenant);
|
|
296
|
+
const operations = [
|
|
297
|
+
blockOperation,
|
|
298
|
+
...await index.createPutOperations(tenant, messageCid, indexes),
|
|
299
|
+
tenantLog.createOperation({ type: 'put', key: Replication.encodePositionKey(seq), value: JSON.stringify(logEntry) }),
|
|
300
|
+
tenantCidToSeq.createOperation({ type: 'put', key: messageCid, value: seq.toString() }),
|
|
301
|
+
partitions.heads.createOperation({ type: 'put', key: tenant, value: seq.toString() }),
|
|
302
|
+
...await this.createFingerprintFoldOperations(partitions, tenant, messageCid, fingerprintScopes),
|
|
303
|
+
];
|
|
304
|
+
await partitions.root.batch(operations);
|
|
305
|
+
// Store-owned wake, post-commit, for `inserted` only.
|
|
306
|
+
this.publishWake(tenant, seq);
|
|
307
|
+
return {
|
|
308
|
+
status: 'inserted',
|
|
309
|
+
position: await this.buildToken(tenant, seq, messageCid),
|
|
310
|
+
};
|
|
311
|
+
});
|
|
312
|
+
}
|
|
313
|
+
async updateIndexes(tenant, messageCid, indexes, options) {
|
|
314
|
+
options?.signal?.throwIfAborted();
|
|
315
|
+
const partitions = await executeUnlessAborted(this.partitions(), options?.signal);
|
|
316
|
+
const index = await executeUnlessAborted(this.index(), options?.signal);
|
|
317
|
+
await this.withTenantWriteLock(tenant, async () => {
|
|
318
|
+
const { entry, positionKey, tenantLog } = await this.getLogEntryForMutation(partitions, tenant, messageCid, DwnErrorCode.MessageStoreUpdateIndexesMessageNotFound);
|
|
319
|
+
const storedMessage = await this.readStoredMessage(partitions, tenant, messageCid, DwnErrorCode.MessageStoreUpdateIndexesMessageNotFound, options);
|
|
320
|
+
Replication.assertFingerprintScopesUntouched(entry.fingerprintScopes, storedMessage, messageCid, indexes);
|
|
321
|
+
// Same row, same seq: indexes replaced; fingerprint scopes carried forward verbatim;
|
|
322
|
+
// fingerprints and head stay untouched.
|
|
323
|
+
const updatedEntry = { ...entry, indexes };
|
|
324
|
+
const operations = [
|
|
325
|
+
...await index.createDeleteOperations(tenant, messageCid),
|
|
326
|
+
...await index.createPutOperations(tenant, messageCid, indexes),
|
|
327
|
+
tenantLog.createOperation({ type: 'put', key: positionKey, value: JSON.stringify(updatedEntry) }),
|
|
328
|
+
];
|
|
329
|
+
await partitions.root.batch(operations);
|
|
330
|
+
});
|
|
331
|
+
}
|
|
332
|
+
async updateMessageAndIndexes(tenant, messageCid, message, indexes, options) {
|
|
333
|
+
options?.signal?.throwIfAborted();
|
|
334
|
+
const computedMessageCid = Cid.parseCid(await Message.getCid(message)).toString();
|
|
335
|
+
const normalizedMessageCid = Cid.parseCid(messageCid).toString();
|
|
336
|
+
if (computedMessageCid !== normalizedMessageCid) {
|
|
337
|
+
throw new DwnError(DwnErrorCode.MessageStoreUpdateMessageAndIndexesCidMismatch, `replacement message CID ${computedMessageCid} does not match target CID ${normalizedMessageCid}`);
|
|
338
|
+
}
|
|
339
|
+
const partitions = await executeUnlessAborted(this.partitions(), options?.signal);
|
|
340
|
+
const index = await executeUnlessAborted(this.index(), options?.signal);
|
|
341
|
+
const encodedMessageBlock = await executeUnlessAborted(block.encode({ value: message, codec: cbor, hasher: sha256 }), options?.signal);
|
|
342
|
+
await this.withTenantWriteLock(tenant, async () => {
|
|
343
|
+
const { entry, positionKey, tenantLog } = await this.getLogEntryForMutation(partitions, tenant, normalizedMessageCid, DwnErrorCode.MessageStoreUpdateMessageAndIndexesMessageNotFound);
|
|
344
|
+
Replication.assertFingerprintScopesUntouched(entry.fingerprintScopes, message, normalizedMessageCid, indexes);
|
|
345
|
+
const updatedEntry = { ...entry, indexes };
|
|
346
|
+
const tenantBlocks = await partitions.blocks.partition(tenant);
|
|
347
|
+
const blockOperation = tenantBlocks.createOperation({ type: 'put', key: normalizedMessageCid, value: encodedMessageBlock.bytes });
|
|
348
|
+
const operations = [
|
|
349
|
+
blockOperation,
|
|
350
|
+
...await index.createDeleteOperations(tenant, normalizedMessageCid),
|
|
351
|
+
...await index.createPutOperations(tenant, normalizedMessageCid, indexes),
|
|
352
|
+
tenantLog.createOperation({ type: 'put', key: positionKey, value: JSON.stringify(updatedEntry) }),
|
|
353
|
+
];
|
|
354
|
+
await partitions.root.batch(operations);
|
|
355
|
+
});
|
|
356
|
+
}
|
|
357
|
+
async delete(tenant, cidString, options) {
|
|
358
|
+
options?.signal?.throwIfAborted();
|
|
359
|
+
const partitions = await executeUnlessAborted(this.partitions(), options?.signal);
|
|
360
|
+
const index = await executeUnlessAborted(this.index(), options?.signal);
|
|
361
|
+
const messageCid = CID.parse(cidString).toString();
|
|
362
|
+
await this.withTenantWriteLock(tenant, async () => {
|
|
363
|
+
const tenantCidToSeq = await partitions.cidToSeq.partition(tenant);
|
|
364
|
+
const seqString = await tenantCidToSeq.get(messageCid, options);
|
|
365
|
+
if (seqString === undefined) {
|
|
366
|
+
// Idempotent no-op — the row does not exist.
|
|
367
|
+
return;
|
|
368
|
+
}
|
|
369
|
+
const seq = BigInt(seqString);
|
|
370
|
+
const positionKey = Replication.encodePositionKey(seq);
|
|
371
|
+
const tenantLog = await partitions.log.partition(tenant);
|
|
372
|
+
const serializedEntry = await tenantLog.get(positionKey);
|
|
373
|
+
if (serializedEntry === undefined) {
|
|
374
|
+
throw new DwnError(DwnErrorCode.MessageStoreDeleteLogEntryMissing, `cid index for tenant ${tenant} points to missing log entry at seq ${seqString} (CID ${messageCid})`);
|
|
375
|
+
}
|
|
376
|
+
const entry = JSON.parse(serializedEntry);
|
|
377
|
+
const tenantBlocks = await partitions.blocks.partition(tenant);
|
|
378
|
+
const blockOperation = tenantBlocks.createOperation({ type: 'del', key: messageCid });
|
|
379
|
+
// XOR is self-inverse: folding the persisted scopes again removes the row's contribution.
|
|
380
|
+
const operations = [
|
|
381
|
+
blockOperation,
|
|
382
|
+
...await index.createDeleteOperations(tenant, messageCid),
|
|
383
|
+
tenantLog.createOperation({ type: 'del', key: positionKey }),
|
|
384
|
+
tenantCidToSeq.createOperation({ type: 'del', key: messageCid }),
|
|
385
|
+
...await this.createFingerprintFoldOperations(partitions, tenant, messageCid, entry.fingerprintScopes),
|
|
386
|
+
];
|
|
387
|
+
await partitions.root.batch(operations);
|
|
388
|
+
});
|
|
183
389
|
}
|
|
184
390
|
/**
|
|
185
|
-
* deletes everything in the underlying
|
|
391
|
+
* deletes everything in the underlying store, then persists a fresh epoch — a full local
|
|
392
|
+
* reset after which no previously issued cursor is valid.
|
|
186
393
|
*/
|
|
187
394
|
async clear() {
|
|
188
|
-
await this.
|
|
189
|
-
await
|
|
395
|
+
const partitions = await this.partitions();
|
|
396
|
+
await partitions.root.clear();
|
|
397
|
+
this.epochPromise = undefined;
|
|
398
|
+
await this.getEpoch();
|
|
399
|
+
}
|
|
400
|
+
// ---------------------------------------------------------------------------
|
|
401
|
+
// ReplicationFeedReader
|
|
402
|
+
// ---------------------------------------------------------------------------
|
|
403
|
+
async logRead(tenant, options = {}) {
|
|
404
|
+
const partitions = await this.partitions();
|
|
405
|
+
const { cursor, limit, filters } = options;
|
|
406
|
+
// Head-captured-first: the per-tenant write mutex serializes commits in position order, so an
|
|
407
|
+
// observed head H is a visibility barrier — every position <= H is already committed when the
|
|
408
|
+
// range scans below run, and anything committed afterward has a position > H and waits for
|
|
409
|
+
// the next page.
|
|
410
|
+
const head = await this.getHead(partitions, tenant);
|
|
411
|
+
if (cursor !== undefined) {
|
|
412
|
+
await this.validateCursor(partitions, tenant, cursor, head);
|
|
413
|
+
}
|
|
414
|
+
const startPosition = cursor === undefined ? 0n : BigInt(cursor.position);
|
|
415
|
+
if (head === 0n) {
|
|
416
|
+
// Nothing to scan — caught up at the input position.
|
|
417
|
+
return { events: [], cursor, drained: true };
|
|
418
|
+
}
|
|
419
|
+
const maxResults = limit ?? Number.MAX_SAFE_INTEGER;
|
|
420
|
+
if (maxResults <= 0) {
|
|
421
|
+
return { events: [], cursor, drained: startPosition >= head };
|
|
422
|
+
}
|
|
423
|
+
if (startPosition >= head) {
|
|
424
|
+
// Nothing to scan — caught up at the input position.
|
|
425
|
+
return { events: [], cursor, drained: true };
|
|
426
|
+
}
|
|
427
|
+
const tenantLog = await partitions.log.partition(tenant);
|
|
428
|
+
const tenantBlocks = await partitions.blocks.partition(tenant);
|
|
429
|
+
const iteratorRange = { gt: Replication.encodePositionKey(startPosition), lte: Replication.encodePositionKey(head) };
|
|
430
|
+
const logIterator = tenantLog.iterator(iteratorRange);
|
|
431
|
+
const events = [];
|
|
432
|
+
let drained = true;
|
|
433
|
+
let lastScannedPosition = startPosition;
|
|
434
|
+
let lastDeliveredPosition;
|
|
435
|
+
let lastDeliveredMessageCid;
|
|
436
|
+
for await (const [positionKey, serializedEntry] of logIterator) {
|
|
437
|
+
const position = BigInt(positionKey);
|
|
438
|
+
lastScannedPosition = position;
|
|
439
|
+
const entry = JSON.parse(serializedEntry);
|
|
440
|
+
const event = await this.readEventFromLogEntry(tenantBlocks, position, entry, filters);
|
|
441
|
+
if (event === undefined) {
|
|
442
|
+
continue;
|
|
443
|
+
}
|
|
444
|
+
events.push(event);
|
|
445
|
+
lastDeliveredPosition = position;
|
|
446
|
+
lastDeliveredMessageCid = event.messageCid;
|
|
447
|
+
if (events.length >= maxResults) {
|
|
448
|
+
drained = position >= head;
|
|
449
|
+
break;
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
// High-water cursor: the highest position scanned — head when the scan completed, the stop
|
|
453
|
+
// position otherwise. `messageCid` is set only when the cursor position is a delivered row.
|
|
454
|
+
const cursorPosition = drained ? head : lastScannedPosition;
|
|
455
|
+
const cursorMessageCid = lastDeliveredPosition === cursorPosition ? lastDeliveredMessageCid : undefined;
|
|
456
|
+
const resultCursor = await this.buildToken(tenant, cursorPosition, cursorMessageCid);
|
|
457
|
+
return { events, cursor: resultCursor, drained };
|
|
458
|
+
}
|
|
459
|
+
async readEventFromLogEntry(tenantBlocks, position, entry, filters) {
|
|
460
|
+
if (filters !== undefined && filters.length > 0 && !FilterUtility.matchAnyFilter(entry.indexes, filters)) {
|
|
461
|
+
return undefined;
|
|
462
|
+
}
|
|
463
|
+
const bytes = await tenantBlocks.get(entry.messageCid);
|
|
464
|
+
if (bytes === undefined) {
|
|
465
|
+
// The row was deleted after the head capture; skip — its positions are gone with it.
|
|
466
|
+
return undefined;
|
|
467
|
+
}
|
|
468
|
+
const decodedBlock = await block.decode({ bytes, codec: cbor, hasher: sha256 });
|
|
469
|
+
const message = decodedBlock.value;
|
|
470
|
+
return {
|
|
471
|
+
seq: entry.seq,
|
|
472
|
+
position: position.toString(),
|
|
473
|
+
event: { message },
|
|
474
|
+
indexes: entry.indexes,
|
|
475
|
+
messageCid: entry.messageCid,
|
|
476
|
+
};
|
|
477
|
+
}
|
|
478
|
+
async logBounds(tenant) {
|
|
479
|
+
const partitions = await this.partitions();
|
|
480
|
+
const head = await this.getHead(partitions, tenant);
|
|
481
|
+
if (head === 0n) {
|
|
482
|
+
return undefined;
|
|
483
|
+
}
|
|
484
|
+
// The log is the live set — replay from zero is always available, so the oldest resumable
|
|
485
|
+
// position is always 0 regardless of compaction.
|
|
486
|
+
const oldest = await this.buildToken(tenant, 0n);
|
|
487
|
+
// Resolve the head position for the latest token's messageCid.
|
|
488
|
+
const headKey = Replication.encodePositionKey(head);
|
|
489
|
+
const tenantLog = await partitions.log.partition(tenant);
|
|
490
|
+
let headMessageCid;
|
|
491
|
+
const headLogEntry = await tenantLog.get(headKey);
|
|
492
|
+
if (headLogEntry !== undefined) {
|
|
493
|
+
headMessageCid = JSON.parse(headLogEntry).messageCid;
|
|
494
|
+
}
|
|
495
|
+
const latest = await this.buildToken(tenant, head, headMessageCid);
|
|
496
|
+
return { oldest, latest };
|
|
497
|
+
}
|
|
498
|
+
async fingerprint(tenant, scopes) {
|
|
499
|
+
const partitions = await this.partitions();
|
|
500
|
+
const tenantFingerprints = await partitions.fingerprints.partition(tenant);
|
|
501
|
+
let composed = Replication.emptyFingerprint();
|
|
502
|
+
for (const scope of scopes) {
|
|
503
|
+
const storedHex = await tenantFingerprints.get(MessageStoreLevel.fingerprintKey(scope));
|
|
504
|
+
if (storedHex !== undefined) {
|
|
505
|
+
composed = Replication.xorFingerprint(composed, Replication.hexToFingerprint(storedHex));
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
return Replication.fingerprintToHex(composed);
|
|
509
|
+
}
|
|
510
|
+
// ---------------------------------------------------------------------------
|
|
511
|
+
// Internals
|
|
512
|
+
// ---------------------------------------------------------------------------
|
|
513
|
+
/**
|
|
514
|
+
* Serializes all log-mutating operations for a tenant: the lock spans seq assignment through
|
|
515
|
+
* batch write, so commit order equals seq order (synchronous assignment alone is insufficient —
|
|
516
|
+
* a later batch could land first).
|
|
517
|
+
*/
|
|
518
|
+
async withTenantWriteLock(tenant, task) {
|
|
519
|
+
const previous = this.writeLocks.get(tenant) ?? Promise.resolve();
|
|
520
|
+
let release;
|
|
521
|
+
const current = new Promise((resolve) => { release = resolve; });
|
|
522
|
+
this.writeLocks.set(tenant, current);
|
|
523
|
+
await previous;
|
|
524
|
+
try {
|
|
525
|
+
return await task();
|
|
526
|
+
}
|
|
527
|
+
finally {
|
|
528
|
+
release();
|
|
529
|
+
if (this.writeLocks.get(tenant) === current) {
|
|
530
|
+
this.writeLocks.delete(tenant);
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
/**
|
|
535
|
+
* Reads the tenant's counter high-water (the highest position ever issued), `0n` when unused.
|
|
536
|
+
*/
|
|
537
|
+
async getHead(partitions, tenant) {
|
|
538
|
+
const headString = await partitions.heads.get(tenant);
|
|
539
|
+
return headString === undefined ? 0n : BigInt(headString);
|
|
540
|
+
}
|
|
541
|
+
async assertNoPreSubstrateLayout(partitions) {
|
|
542
|
+
if (await partitions.meta.get(EPOCH_KEY) !== undefined) {
|
|
543
|
+
return;
|
|
544
|
+
}
|
|
545
|
+
for await (const key of partitions.root.keys()) {
|
|
546
|
+
const partition = MessageStoreLevel.parseSublevelPartition(key);
|
|
547
|
+
if (partition === undefined || CURRENT_PARTITIONS.has(partition)) {
|
|
548
|
+
continue;
|
|
549
|
+
}
|
|
550
|
+
throw new DwnError(DwnErrorCode.MessageStorePreSubstrateLayout, `message store location ${this.config.location} contains pre-substrate Level data; reset the store before opening it`);
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
static parseSublevelPartition(key) {
|
|
554
|
+
if (!key.startsWith('!')) {
|
|
555
|
+
return undefined;
|
|
556
|
+
}
|
|
557
|
+
const end = key.indexOf('!', 1);
|
|
558
|
+
return end === -1 ? undefined : key.slice(1, end);
|
|
559
|
+
}
|
|
560
|
+
async readStoredMessage(partitions, tenant, messageCid, notFoundErrorCode, options) {
|
|
561
|
+
const tenantBlocks = await partitions.blocks.partition(tenant);
|
|
562
|
+
const bytes = await tenantBlocks.get(messageCid, options);
|
|
563
|
+
if (bytes === undefined) {
|
|
564
|
+
throw new DwnError(notFoundErrorCode, `no message block found for tenant ${tenant} with CID ${messageCid}`);
|
|
565
|
+
}
|
|
566
|
+
const decodedBlock = await block.decode({ bytes, codec: cbor, hasher: sha256 });
|
|
567
|
+
return decodedBlock.value;
|
|
568
|
+
}
|
|
569
|
+
/**
|
|
570
|
+
* Validates a replication cursor against the tenant stream and persisted store epoch.
|
|
571
|
+
* Throws `EventLogProgressGap` with bounds metadata when the cursor cannot be replayed.
|
|
572
|
+
*/
|
|
573
|
+
async validateCursor(partitions, tenant, cursor, head) {
|
|
574
|
+
const expectedStreamId = await Replication.deriveStreamId(tenant);
|
|
575
|
+
const reason = await this.validateCursorPosition(partitions, tenant, cursor, head, expectedStreamId);
|
|
576
|
+
if (reason === undefined) {
|
|
577
|
+
return;
|
|
578
|
+
}
|
|
579
|
+
const bounds = await this.logBounds(tenant);
|
|
580
|
+
const gapInfo = {
|
|
581
|
+
requested: cursor,
|
|
582
|
+
oldestAvailable: bounds?.oldest ?? cursor,
|
|
583
|
+
latestAvailable: bounds?.latest ?? cursor,
|
|
584
|
+
reason,
|
|
585
|
+
};
|
|
586
|
+
const error = new DwnError(DwnErrorCode.EventLogProgressGap, `progress token gap: ${reason}`);
|
|
587
|
+
error.gapInfo = gapInfo;
|
|
588
|
+
throw error;
|
|
589
|
+
}
|
|
590
|
+
async validateCursorPosition(partitions, tenant, cursor, head, expectedStreamId) {
|
|
591
|
+
if (cursor.streamId !== expectedStreamId) {
|
|
592
|
+
return 'stream_mismatch';
|
|
593
|
+
}
|
|
594
|
+
if (cursor.epoch !== await this.getEpoch()) {
|
|
595
|
+
return 'epoch_mismatch';
|
|
596
|
+
}
|
|
597
|
+
const cursorPosition = BigInt(cursor.position);
|
|
598
|
+
if (cursorPosition > head) {
|
|
599
|
+
return 'token_too_new';
|
|
600
|
+
}
|
|
601
|
+
if (cursor.messageCid === undefined) {
|
|
602
|
+
return undefined;
|
|
603
|
+
}
|
|
604
|
+
const positionMessageCid = await this.getMessageCidAtPosition(partitions, tenant, cursorPosition);
|
|
605
|
+
if (positionMessageCid !== undefined && positionMessageCid !== cursor.messageCid) {
|
|
606
|
+
return 'message_mismatch';
|
|
607
|
+
}
|
|
608
|
+
return undefined;
|
|
609
|
+
}
|
|
610
|
+
async getMessageCidAtPosition(partitions, tenant, position) {
|
|
611
|
+
if (position <= 0n) {
|
|
612
|
+
return undefined;
|
|
613
|
+
}
|
|
614
|
+
const positionKey = Replication.encodePositionKey(position);
|
|
615
|
+
const tenantLog = await partitions.log.partition(tenant);
|
|
616
|
+
const serializedEntry = await tenantLog.get(positionKey);
|
|
617
|
+
if (serializedEntry !== undefined) {
|
|
618
|
+
return JSON.parse(serializedEntry).messageCid;
|
|
619
|
+
}
|
|
620
|
+
return undefined;
|
|
621
|
+
}
|
|
622
|
+
/**
|
|
623
|
+
* Resolves the log row for a same-CID mutation, throwing the given code when no row exists.
|
|
624
|
+
*/
|
|
625
|
+
async getLogEntryForMutation(partitions, tenant, messageCid, notFoundErrorCode) {
|
|
626
|
+
const tenantCidToSeq = await partitions.cidToSeq.partition(tenant);
|
|
627
|
+
const seqString = await tenantCidToSeq.get(messageCid);
|
|
628
|
+
if (seqString === undefined) {
|
|
629
|
+
throw new DwnError(notFoundErrorCode, `no message found for tenant ${tenant} with CID ${messageCid}`);
|
|
630
|
+
}
|
|
631
|
+
const positionKey = Replication.encodePositionKey(BigInt(seqString));
|
|
632
|
+
const tenantLog = await partitions.log.partition(tenant);
|
|
633
|
+
const serializedEntry = await tenantLog.get(positionKey);
|
|
634
|
+
if (serializedEntry === undefined) {
|
|
635
|
+
throw new DwnError(notFoundErrorCode, `no log entry found for tenant ${tenant} at seq ${seqString} (CID ${messageCid})`);
|
|
636
|
+
}
|
|
637
|
+
return { entry: JSON.parse(serializedEntry), positionKey, tenantLog };
|
|
638
|
+
}
|
|
639
|
+
/**
|
|
640
|
+
* Creates the batch operations that fold a message CID's contribution into the given
|
|
641
|
+
* fingerprint domains. XOR is self-inverse, so the same operations serve insert and delete.
|
|
642
|
+
*/
|
|
643
|
+
async createFingerprintFoldOperations(partitions, tenant, messageCid, scopes) {
|
|
644
|
+
const contribution = await Replication.hashMessageCid(messageCid);
|
|
645
|
+
const tenantFingerprints = await partitions.fingerprints.partition(tenant);
|
|
646
|
+
const operations = [];
|
|
647
|
+
for (const scope of scopes) {
|
|
648
|
+
const key = MessageStoreLevel.fingerprintKey(scope);
|
|
649
|
+
const storedHex = await tenantFingerprints.get(key);
|
|
650
|
+
const current = storedHex === undefined ? Replication.emptyFingerprint() : Replication.hexToFingerprint(storedHex);
|
|
651
|
+
const folded = Replication.xorFingerprint(current, contribution);
|
|
652
|
+
operations.push(tenantFingerprints.createOperation({ type: 'put', key, value: Replication.fingerprintToHex(folded) }));
|
|
653
|
+
}
|
|
654
|
+
return operations;
|
|
655
|
+
}
|
|
656
|
+
/**
|
|
657
|
+
* Encodes a fingerprint domain name as a Level key. The uniform prefix keeps the global
|
|
658
|
+
* domain (the empty string) a valid key.
|
|
659
|
+
*/
|
|
660
|
+
static fingerprintKey(scope) {
|
|
661
|
+
return `d${scope}`;
|
|
662
|
+
}
|
|
663
|
+
async buildToken(tenant, position, messageCid) {
|
|
664
|
+
const token = {
|
|
665
|
+
streamId: await Replication.deriveStreamId(tenant),
|
|
666
|
+
epoch: await this.getEpoch(),
|
|
667
|
+
position: position.toString(),
|
|
668
|
+
};
|
|
669
|
+
if (messageCid !== undefined) {
|
|
670
|
+
token.messageCid = messageCid;
|
|
671
|
+
}
|
|
672
|
+
return token;
|
|
673
|
+
}
|
|
674
|
+
/**
|
|
675
|
+
* Publishes a wake post-commit. Best-effort by contract — never throws into the write path.
|
|
676
|
+
*/
|
|
677
|
+
publishWake(tenant, position) {
|
|
678
|
+
try {
|
|
679
|
+
this.wakePublisher?.publish({ tenant, seq: position.toString() });
|
|
680
|
+
}
|
|
681
|
+
catch {
|
|
682
|
+
// A lost wake only delays delivery; consumers' idle re-drain bounds the latency.
|
|
683
|
+
}
|
|
190
684
|
}
|
|
191
685
|
}
|
|
192
686
|
//# sourceMappingURL=message-store-level.js.map
|