@enbox/dwn-sdk-js 0.0.7 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/browser.mjs +8 -8
- package/dist/browser.mjs.map +4 -4
- package/dist/esm/generated/precompiled-validators.js +817 -911
- package/dist/esm/generated/precompiled-validators.js.map +1 -1
- package/dist/esm/src/core/constants.js +11 -0
- package/dist/esm/src/core/constants.js.map +1 -0
- package/dist/esm/src/core/core-protocol.js +44 -0
- package/dist/esm/src/core/core-protocol.js.map +1 -0
- package/dist/esm/src/core/dwn-error.js +12 -12
- package/dist/esm/src/core/dwn-error.js.map +1 -1
- package/dist/esm/src/core/grant-authorization.js +16 -3
- package/dist/esm/src/core/grant-authorization.js.map +1 -1
- package/dist/esm/src/core/protocol-authorization-action.js +5 -0
- package/dist/esm/src/core/protocol-authorization-action.js.map +1 -1
- package/dist/esm/src/core/protocol-authorization-validation.js +91 -0
- package/dist/esm/src/core/protocol-authorization-validation.js.map +1 -1
- package/dist/esm/src/core/protocol-authorization.js +53 -30
- package/dist/esm/src/core/protocol-authorization.js.map +1 -1
- package/dist/esm/src/core/records-grant-authorization.js +6 -8
- package/dist/esm/src/core/records-grant-authorization.js.map +1 -1
- package/dist/esm/src/core/resumable-task-manager.js +2 -0
- package/dist/esm/src/core/resumable-task-manager.js.map +1 -1
- package/dist/esm/src/dwn.js +42 -18
- package/dist/esm/src/dwn.js.map +1 -1
- package/dist/esm/src/event-stream/event-emitter-event-log.js +204 -0
- package/dist/esm/src/event-stream/event-emitter-event-log.js.map +1 -0
- package/dist/esm/src/handlers/messages-read.js +7 -11
- package/dist/esm/src/handlers/messages-read.js.map +1 -1
- package/dist/esm/src/handlers/messages-subscribe.js +22 -24
- package/dist/esm/src/handlers/messages-subscribe.js.map +1 -1
- package/dist/esm/src/handlers/messages-sync.js +11 -15
- package/dist/esm/src/handlers/messages-sync.js.map +1 -1
- package/dist/esm/src/handlers/protocols-configure.js +37 -27
- package/dist/esm/src/handlers/protocols-configure.js.map +1 -1
- package/dist/esm/src/handlers/protocols-query.js +7 -11
- package/dist/esm/src/handlers/protocols-query.js.map +1 -1
- package/dist/esm/src/handlers/records-count.js +10 -12
- package/dist/esm/src/handlers/records-count.js.map +1 -1
- package/dist/esm/src/handlers/records-delete.js +10 -18
- package/dist/esm/src/handlers/records-delete.js.map +1 -1
- package/dist/esm/src/handlers/records-query.js +11 -15
- package/dist/esm/src/handlers/records-query.js.map +1 -1
- package/dist/esm/src/handlers/records-read.js +31 -26
- package/dist/esm/src/handlers/records-read.js.map +1 -1
- package/dist/esm/src/handlers/records-subscribe.js +39 -26
- package/dist/esm/src/handlers/records-subscribe.js.map +1 -1
- package/dist/esm/src/handlers/records-write.js +128 -105
- package/dist/esm/src/handlers/records-write.js.map +1 -1
- package/dist/esm/src/index.js +5 -2
- package/dist/esm/src/index.js.map +1 -1
- package/dist/esm/src/interfaces/messages-subscribe.js +1 -0
- package/dist/esm/src/interfaces/messages-subscribe.js.map +1 -1
- package/dist/esm/src/interfaces/protocols-configure.js +33 -3
- package/dist/esm/src/interfaces/protocols-configure.js.map +1 -1
- package/dist/esm/src/interfaces/records-count.js +1 -1
- package/dist/esm/src/interfaces/records-count.js.map +1 -1
- package/dist/esm/src/interfaces/records-delete.js +1 -1
- package/dist/esm/src/interfaces/records-delete.js.map +1 -1
- package/dist/esm/src/interfaces/records-query.js +1 -1
- package/dist/esm/src/interfaces/records-query.js.map +1 -1
- package/dist/esm/src/interfaces/records-read.js +1 -1
- package/dist/esm/src/interfaces/records-read.js.map +1 -1
- package/dist/esm/src/interfaces/records-subscribe.js +2 -1
- package/dist/esm/src/interfaces/records-subscribe.js.map +1 -1
- package/dist/esm/src/interfaces/records-write-signing.js +1 -12
- package/dist/esm/src/interfaces/records-write-signing.js.map +1 -1
- package/dist/esm/src/interfaces/records-write.js +25 -41
- package/dist/esm/src/interfaces/records-write.js.map +1 -1
- package/dist/esm/src/protocols/permission-grant.js +1 -1
- package/dist/esm/src/protocols/permission-grant.js.map +1 -1
- package/dist/esm/src/protocols/permission-request.js +1 -1
- package/dist/esm/src/protocols/permission-request.js.map +1 -1
- package/dist/esm/src/protocols/permissions.js +113 -5
- package/dist/esm/src/protocols/permissions.js.map +1 -1
- package/dist/esm/src/state-index/state-index-level.js +5 -7
- package/dist/esm/src/state-index/state-index-level.js.map +1 -1
- package/dist/esm/src/store/data-store-level.js +110 -33
- package/dist/esm/src/store/data-store-level.js.map +1 -1
- package/dist/esm/src/store/index-level.js +42 -32
- package/dist/esm/src/store/index-level.js.map +1 -1
- package/dist/esm/src/store/storage-controller.js +70 -6
- package/dist/esm/src/store/storage-controller.js.map +1 -1
- package/dist/esm/src/types/permission-types.js.map +1 -1
- package/dist/esm/src/types/protocols-types.js +11 -0
- package/dist/esm/src/types/protocols-types.js.map +1 -1
- package/dist/esm/src/types/records-types.js.map +1 -1
- package/dist/esm/src/utils/hd-key.js +0 -8
- package/dist/esm/src/utils/hd-key.js.map +1 -1
- package/dist/esm/src/utils/messages.js +16 -34
- package/dist/esm/src/utils/messages.js.map +1 -1
- package/dist/esm/src/utils/records.js +5 -43
- package/dist/esm/src/utils/records.js.map +1 -1
- package/dist/esm/tests/core/protocol-authorization.spec.js +2 -1
- package/dist/esm/tests/core/protocol-authorization.spec.js.map +1 -1
- package/dist/esm/tests/dwn.spec.js +32 -43
- package/dist/esm/tests/dwn.spec.js.map +1 -1
- package/dist/esm/tests/event-emitter-event-log.spec.js +305 -0
- package/dist/esm/tests/event-emitter-event-log.spec.js.map +1 -0
- package/dist/esm/tests/features/author-delegated-grant.spec.js +14 -7
- package/dist/esm/tests/features/author-delegated-grant.spec.js.map +1 -1
- package/dist/esm/tests/features/owner-delegated-grant.spec.js +9 -5
- package/dist/esm/tests/features/owner-delegated-grant.spec.js.map +1 -1
- package/dist/esm/tests/features/owner-signature.spec.js +14 -7
- package/dist/esm/tests/features/owner-signature.spec.js.map +1 -1
- package/dist/esm/tests/features/permissions.spec.js +12 -12
- package/dist/esm/tests/features/permissions.spec.js.map +1 -1
- package/dist/esm/tests/features/protocol-composition.spec.js +636 -5
- package/dist/esm/tests/features/protocol-composition.spec.js.map +1 -1
- package/dist/esm/tests/features/protocol-create-action.spec.js +4 -4
- package/dist/esm/tests/features/protocol-create-action.spec.js.map +1 -1
- package/dist/esm/tests/features/protocol-delete-action.spec.js +7 -7
- package/dist/esm/tests/features/protocol-delete-action.spec.js.map +1 -1
- package/dist/esm/tests/features/protocol-update-action.spec.js +4 -4
- package/dist/esm/tests/features/protocol-update-action.spec.js.map +1 -1
- package/dist/esm/tests/features/records-delivery.spec.js +236 -0
- package/dist/esm/tests/features/records-delivery.spec.js.map +1 -0
- package/dist/esm/tests/features/records-immutable.spec.js +315 -0
- package/dist/esm/tests/features/records-immutable.spec.js.map +1 -0
- package/dist/esm/tests/features/records-prune.spec.js +4 -4
- package/dist/esm/tests/features/records-prune.spec.js.map +1 -1
- package/dist/esm/tests/features/records-record-limit.spec.js +542 -0
- package/dist/esm/tests/features/records-record-limit.spec.js.map +1 -0
- package/dist/esm/tests/features/records-squash.spec.js +1055 -0
- package/dist/esm/tests/features/records-squash.spec.js.map +1 -0
- package/dist/esm/tests/features/records-tags.spec.js +16 -4
- package/dist/esm/tests/features/records-tags.spec.js.map +1 -1
- package/dist/esm/tests/features/resumable-tasks.spec.js +7 -8
- package/dist/esm/tests/features/resumable-tasks.spec.js.map +1 -1
- package/dist/esm/tests/handlers/messages-read.spec.js +11 -5
- package/dist/esm/tests/handlers/messages-read.spec.js.map +1 -1
- package/dist/esm/tests/handlers/messages-subscribe.spec.js +169 -22
- package/dist/esm/tests/handlers/messages-subscribe.spec.js.map +1 -1
- package/dist/esm/tests/handlers/messages-sync.spec.js +103 -21
- package/dist/esm/tests/handlers/messages-sync.spec.js.map +1 -1
- package/dist/esm/tests/handlers/protocols-configure.spec.js +5 -5
- package/dist/esm/tests/handlers/protocols-configure.spec.js.map +1 -1
- package/dist/esm/tests/handlers/protocols-query.spec.js +5 -5
- package/dist/esm/tests/handlers/protocols-query.spec.js.map +1 -1
- package/dist/esm/tests/handlers/records-count.spec.js +9 -4
- package/dist/esm/tests/handlers/records-count.spec.js.map +1 -1
- package/dist/esm/tests/handlers/records-delete.spec.js +24 -25
- package/dist/esm/tests/handlers/records-delete.spec.js.map +1 -1
- package/dist/esm/tests/handlers/records-query.spec.js +68 -9
- package/dist/esm/tests/handlers/records-query.spec.js.map +1 -1
- package/dist/esm/tests/handlers/records-read.spec.js +24 -138
- package/dist/esm/tests/handlers/records-read.spec.js.map +1 -1
- package/dist/esm/tests/handlers/records-subscribe.spec.js +175 -35
- package/dist/esm/tests/handlers/records-subscribe.spec.js.map +1 -1
- package/dist/esm/tests/handlers/records-write.spec.js +176 -72
- package/dist/esm/tests/handlers/records-write.spec.js.map +1 -1
- package/dist/esm/tests/interfaces/records-write.spec.js +52 -68
- package/dist/esm/tests/interfaces/records-write.spec.js.map +1 -1
- package/dist/esm/tests/protocols/permission-grant.spec.js +6 -6
- package/dist/esm/tests/protocols/permission-grant.spec.js.map +1 -1
- package/dist/esm/tests/protocols/permission-request.spec.js +4 -4
- package/dist/esm/tests/protocols/permission-request.spec.js.map +1 -1
- package/dist/esm/tests/protocols/permissions.spec.js +4 -4
- package/dist/esm/tests/protocols/permissions.spec.js.map +1 -1
- package/dist/esm/tests/scenarios/aggregator.spec.js +4 -4
- package/dist/esm/tests/scenarios/aggregator.spec.js.map +1 -1
- package/dist/esm/tests/scenarios/deleted-record.spec.js +350 -5
- package/dist/esm/tests/scenarios/deleted-record.spec.js.map +1 -1
- package/dist/esm/tests/scenarios/end-to-end-tests.spec.js +4 -4
- package/dist/esm/tests/scenarios/end-to-end-tests.spec.js.map +1 -1
- package/dist/esm/tests/scenarios/nested-roles.spec.js +4 -4
- package/dist/esm/tests/scenarios/nested-roles.spec.js.map +1 -1
- package/dist/esm/tests/scenarios/subscriptions.spec.js +93 -40
- package/dist/esm/tests/scenarios/subscriptions.spec.js.map +1 -1
- package/dist/esm/tests/store/data-store-level.spec.js +102 -41
- package/dist/esm/tests/store/data-store-level.spec.js.map +1 -1
- package/dist/esm/tests/test-event-stream.js +12 -13
- package/dist/esm/tests/test-event-stream.js.map +1 -1
- package/dist/esm/tests/test-suite.js +10 -4
- package/dist/esm/tests/test-suite.js.map +1 -1
- package/dist/esm/tests/utils/messages.spec.js +12 -5
- package/dist/esm/tests/utils/messages.spec.js.map +1 -1
- package/dist/esm/tests/utils/records.spec.js +8 -12
- package/dist/esm/tests/utils/records.spec.js.map +1 -1
- package/dist/esm/tests/utils/test-data-generator.js +36 -2
- package/dist/esm/tests/utils/test-data-generator.js.map +1 -1
- package/dist/esm/tests/validation/json-schemas/records/records-write.spec.js +37 -8
- package/dist/esm/tests/validation/json-schemas/records/records-write.spec.js.map +1 -1
- package/dist/types/generated/precompiled-validators.d.ts +49 -40
- package/dist/types/generated/precompiled-validators.d.ts.map +1 -1
- package/dist/types/src/core/constants.d.ts +11 -0
- package/dist/types/src/core/constants.d.ts.map +1 -0
- package/dist/types/src/core/core-protocol.d.ts +89 -0
- package/dist/types/src/core/core-protocol.d.ts.map +1 -0
- package/dist/types/src/core/dwn-error.d.ts +12 -12
- package/dist/types/src/core/dwn-error.d.ts.map +1 -1
- package/dist/types/src/core/grant-authorization.d.ts +6 -2
- package/dist/types/src/core/grant-authorization.d.ts.map +1 -1
- package/dist/types/src/core/protocol-authorization-action.d.ts.map +1 -1
- package/dist/types/src/core/protocol-authorization-validation.d.ts +30 -0
- package/dist/types/src/core/protocol-authorization-validation.d.ts.map +1 -1
- package/dist/types/src/core/protocol-authorization.d.ts +19 -11
- package/dist/types/src/core/protocol-authorization.d.ts.map +1 -1
- package/dist/types/src/core/records-grant-authorization.d.ts.map +1 -1
- package/dist/types/src/core/resumable-task-manager.d.ts +2 -1
- package/dist/types/src/core/resumable-task-manager.d.ts.map +1 -1
- package/dist/types/src/dwn.d.ts +19 -7
- package/dist/types/src/dwn.d.ts.map +1 -1
- package/dist/types/src/event-stream/event-emitter-event-log.d.ts +50 -0
- package/dist/types/src/event-stream/event-emitter-event-log.d.ts.map +1 -0
- package/dist/types/src/handlers/messages-read.d.ts +3 -8
- package/dist/types/src/handlers/messages-read.d.ts.map +1 -1
- package/dist/types/src/handlers/messages-subscribe.d.ts +6 -10
- package/dist/types/src/handlers/messages-subscribe.d.ts.map +1 -1
- package/dist/types/src/handlers/messages-sync.d.ts +3 -8
- package/dist/types/src/handlers/messages-sync.d.ts.map +1 -1
- package/dist/types/src/handlers/protocols-configure.d.ts +3 -10
- package/dist/types/src/handlers/protocols-configure.d.ts.map +1 -1
- package/dist/types/src/handlers/protocols-query.d.ts +3 -8
- package/dist/types/src/handlers/protocols-query.d.ts.map +1 -1
- package/dist/types/src/handlers/records-count.d.ts +3 -6
- package/dist/types/src/handlers/records-count.d.ts.map +1 -1
- package/dist/types/src/handlers/records-delete.d.ts +3 -8
- package/dist/types/src/handlers/records-delete.d.ts.map +1 -1
- package/dist/types/src/handlers/records-query.d.ts +3 -8
- package/dist/types/src/handlers/records-query.d.ts.map +1 -1
- package/dist/types/src/handlers/records-read.d.ts +3 -8
- package/dist/types/src/handlers/records-read.d.ts.map +1 -1
- package/dist/types/src/handlers/records-subscribe.d.ts +8 -10
- package/dist/types/src/handlers/records-subscribe.d.ts.map +1 -1
- package/dist/types/src/handlers/records-write.d.ts +12 -25
- package/dist/types/src/handlers/records-write.d.ts.map +1 -1
- package/dist/types/src/index.d.ts +8 -4
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/interfaces/messages-subscribe.d.ts +5 -0
- package/dist/types/src/interfaces/messages-subscribe.d.ts.map +1 -1
- package/dist/types/src/interfaces/protocols-configure.d.ts.map +1 -1
- package/dist/types/src/interfaces/records-subscribe.d.ts +5 -0
- package/dist/types/src/interfaces/records-subscribe.d.ts.map +1 -1
- package/dist/types/src/interfaces/records-write-signing.d.ts +3 -4
- package/dist/types/src/interfaces/records-write-signing.d.ts.map +1 -1
- package/dist/types/src/interfaces/records-write.d.ts +11 -11
- package/dist/types/src/interfaces/records-write.d.ts.map +1 -1
- package/dist/types/src/protocols/permission-grant.d.ts +1 -1
- package/dist/types/src/protocols/permission-grant.d.ts.map +1 -1
- package/dist/types/src/protocols/permission-request.d.ts +1 -1
- package/dist/types/src/protocols/permission-request.d.ts.map +1 -1
- package/dist/types/src/protocols/permissions.d.ts +40 -3
- package/dist/types/src/protocols/permissions.d.ts.map +1 -1
- package/dist/types/src/state-index/state-index-level.d.ts.map +1 -1
- package/dist/types/src/store/data-store-level.d.ts +20 -4
- package/dist/types/src/store/data-store-level.d.ts.map +1 -1
- package/dist/types/src/store/index-level.d.ts +4 -0
- package/dist/types/src/store/index-level.d.ts.map +1 -1
- package/dist/types/src/store/storage-controller.d.ts +20 -6
- package/dist/types/src/store/storage-controller.d.ts.map +1 -1
- package/dist/types/src/types/message-types.d.ts +3 -3
- package/dist/types/src/types/message-types.d.ts.map +1 -1
- package/dist/types/src/types/messages-types.d.ts +12 -3
- package/dist/types/src/types/messages-types.d.ts.map +1 -1
- package/dist/types/src/types/method-handler.d.ts +24 -3
- package/dist/types/src/types/method-handler.d.ts.map +1 -1
- package/dist/types/src/types/permission-types.d.ts +7 -0
- package/dist/types/src/types/permission-types.d.ts.map +1 -1
- package/dist/types/src/types/protocols-types.d.ts +69 -2
- package/dist/types/src/types/protocols-types.d.ts.map +1 -1
- package/dist/types/src/types/records-types.d.ts +23 -6
- package/dist/types/src/types/records-types.d.ts.map +1 -1
- package/dist/types/src/types/subscriptions.d.ts +151 -13
- package/dist/types/src/types/subscriptions.d.ts.map +1 -1
- package/dist/types/src/utils/hd-key.d.ts +1 -9
- package/dist/types/src/utils/hd-key.d.ts.map +1 -1
- package/dist/types/src/utils/messages.d.ts +7 -5
- package/dist/types/src/utils/messages.d.ts.map +1 -1
- package/dist/types/src/utils/records.d.ts +1 -11
- package/dist/types/src/utils/records.d.ts.map +1 -1
- package/dist/types/tests/dwn.spec.d.ts.map +1 -1
- package/dist/types/tests/event-emitter-event-log.spec.d.ts +2 -0
- package/dist/types/tests/event-emitter-event-log.spec.d.ts.map +1 -0
- 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/protocol-composition.spec.d.ts.map +1 -1
- package/dist/types/tests/features/records-delivery.spec.d.ts +2 -0
- package/dist/types/tests/features/records-delivery.spec.d.ts.map +1 -0
- package/dist/types/tests/features/records-immutable.spec.d.ts +2 -0
- package/dist/types/tests/features/records-immutable.spec.d.ts.map +1 -0
- package/dist/types/tests/features/records-record-limit.spec.d.ts +2 -0
- package/dist/types/tests/features/records-record-limit.spec.d.ts.map +1 -0
- package/dist/types/tests/features/records-squash.spec.d.ts +2 -0
- package/dist/types/tests/features/records-squash.spec.d.ts.map +1 -0
- 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-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/messages-sync.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/subscriptions.spec.d.ts.map +1 -1
- package/dist/types/tests/test-event-stream.d.ts +11 -12
- package/dist/types/tests/test-event-stream.d.ts.map +1 -1
- package/dist/types/tests/test-suite.d.ts +2 -2
- package/dist/types/tests/test-suite.d.ts.map +1 -1
- package/dist/types/tests/utils/test-data-generator.d.ts +19 -0
- package/dist/types/tests/utils/test-data-generator.d.ts.map +1 -1
- package/package.json +5 -4
- package/src/core/constants.ts +11 -0
- package/src/core/core-protocol.ts +129 -0
- package/src/core/dwn-error.ts +18 -12
- package/src/core/grant-authorization.ts +20 -3
- package/src/core/protocol-authorization-action.ts +5 -0
- package/src/core/protocol-authorization-validation.ts +133 -0
- package/src/core/protocol-authorization.ts +71 -23
- package/src/core/records-grant-authorization.ts +6 -8
- package/src/core/resumable-task-manager.ts +3 -1
- package/src/dwn.ts +58 -73
- package/src/event-stream/event-emitter-event-log.ts +283 -0
- package/src/handlers/messages-read.ts +8 -9
- package/src/handlers/messages-subscribe.ts +24 -28
- package/src/handlers/messages-sync.ts +10 -16
- package/src/handlers/protocols-configure.ts +47 -32
- package/src/handlers/protocols-query.ts +6 -9
- package/src/handlers/records-count.ts +11 -10
- package/src/handlers/records-delete.ts +12 -21
- package/src/handlers/records-query.ts +12 -12
- package/src/handlers/records-read.ts +34 -22
- package/src/handlers/records-subscribe.ts +47 -26
- package/src/handlers/records-write.ts +152 -119
- package/src/index.ts +9 -5
- package/src/interfaces/messages-subscribe.ts +7 -1
- package/src/interfaces/protocols-configure.ts +51 -3
- package/src/interfaces/records-count.ts +1 -1
- package/src/interfaces/records-delete.ts +1 -1
- package/src/interfaces/records-query.ts +1 -1
- package/src/interfaces/records-read.ts +1 -1
- package/src/interfaces/records-subscribe.ts +8 -1
- package/src/interfaces/records-write-signing.ts +2 -22
- package/src/interfaces/records-write.ts +35 -48
- package/src/protocols/permission-grant.ts +1 -1
- package/src/protocols/permission-request.ts +1 -1
- package/src/protocols/permissions.ts +148 -6
- package/src/state-index/state-index-level.ts +5 -7
- package/src/store/data-store-level.ts +124 -34
- package/src/store/index-level.ts +44 -35
- package/src/store/storage-controller.ts +89 -12
- package/src/types/message-types.ts +3 -3
- package/src/types/messages-types.ts +12 -3
- package/src/types/method-handler.ts +26 -4
- package/src/types/mitt.d.ts +28 -0
- package/src/types/permission-types.ts +7 -0
- package/src/types/protocols-types.ts +78 -1
- package/src/types/records-types.ts +24 -6
- package/src/types/subscriptions.ts +178 -14
- package/src/utils/hd-key.ts +0 -9
- package/src/utils/messages.ts +17 -37
- package/src/utils/records.ts +7 -58
- package/dist/esm/src/event-stream/event-emitter-stream.js +0 -46
- package/dist/esm/src/event-stream/event-emitter-stream.js.map +0 -1
- package/dist/esm/tests/event-stream/event-emitter-stream.spec.js +0 -68
- package/dist/esm/tests/event-stream/event-emitter-stream.spec.js.map +0 -1
- package/dist/esm/tests/event-stream/event-stream.spec.js +0 -114
- package/dist/esm/tests/event-stream/event-stream.spec.js.map +0 -1
- package/dist/types/src/event-stream/event-emitter-stream.d.ts +0 -23
- package/dist/types/src/event-stream/event-emitter-stream.d.ts.map +0 -1
- package/dist/types/tests/event-stream/event-emitter-stream.spec.d.ts +0 -2
- package/dist/types/tests/event-stream/event-emitter-stream.spec.d.ts.map +0 -1
- package/dist/types/tests/event-stream/event-stream.spec.d.ts +0 -2
- package/dist/types/tests/event-stream/event-stream.spec.d.ts.map +0 -1
- package/src/event-stream/event-emitter-stream.ts +0 -69
|
@@ -1,14 +1,21 @@
|
|
|
1
|
+
import type { Filter } from '../types/query-types.js';
|
|
1
2
|
import type { GenericMessage } from '../types/message-types.js';
|
|
3
|
+
import type { MessagesFilter } from '../types/messages-types.js';
|
|
2
4
|
import type { MessageSigner } from '../types/signer.js';
|
|
3
5
|
import type { MessageStore } from '../types/message-store.js';
|
|
4
6
|
import type { ProtocolDefinition } from '../types/protocols-types.js';
|
|
7
|
+
import type { CoreProtocol, CoreProtocolStores } from '../core/core-protocol.js';
|
|
5
8
|
import type { DataEncodedRecordsWriteMessage, RecordsWriteMessage } from '../types/records-types.js';
|
|
6
9
|
import type { PermissionConditions, PermissionGrantData, PermissionRequestData, PermissionRevocationData, PermissionScope, RecordsPermissionScope } from '../types/permission-types.js';
|
|
7
10
|
|
|
11
|
+
import { DwnConstant } from '../core/dwn-constant.js';
|
|
8
12
|
import { Encoder } from '../utils/encoder.js';
|
|
13
|
+
import { FilterUtility } from '../utils/filter.js';
|
|
14
|
+
import { Message } from '../core/message.js';
|
|
9
15
|
import { PermissionGrant } from './permission-grant.js';
|
|
10
16
|
import { PermissionRequest } from './permission-request.js';
|
|
11
|
-
import {
|
|
17
|
+
import { Records } from '../utils/records.js';
|
|
18
|
+
import { RecordsWrite } from '../interfaces/records-write.js';
|
|
12
19
|
import { Time } from '../utils/time.js';
|
|
13
20
|
import { validateJsonSchema } from '../schema-validator.js';
|
|
14
21
|
import { DwnError, DwnErrorCode } from '../core/dwn-error.js';
|
|
@@ -79,12 +86,16 @@ export type PermissionRevocationCreateOptions = {
|
|
|
79
86
|
|
|
80
87
|
/**
|
|
81
88
|
* This is a first-class DWN protocol for managing permission grants of a given DWN.
|
|
89
|
+
*
|
|
90
|
+
* It implements the `CoreProtocol` interface so that its lifecycle hooks
|
|
91
|
+
* (validation, pre-processing, post-processing) are dispatched generically
|
|
92
|
+
* by the `CoreProtocolRegistry` rather than being hardcoded in handlers.
|
|
82
93
|
*/
|
|
83
|
-
export class PermissionsProtocol {
|
|
94
|
+
export class PermissionsProtocol implements CoreProtocol {
|
|
84
95
|
/**
|
|
85
96
|
* The URI of the DWN Permissions protocol.
|
|
86
97
|
*/
|
|
87
|
-
public static readonly uri = 'https://
|
|
98
|
+
public static readonly uri = 'https://identity.foundation/dwn/permissions';
|
|
88
99
|
|
|
89
100
|
/**
|
|
90
101
|
* The protocol path of the `request` record.
|
|
@@ -156,6 +167,137 @@ export class PermissionsProtocol {
|
|
|
156
167
|
}
|
|
157
168
|
};
|
|
158
169
|
|
|
170
|
+
// ---------- CoreProtocol instance accessors ----------
|
|
171
|
+
|
|
172
|
+
/** @inheritdoc */
|
|
173
|
+
public get uri(): string {
|
|
174
|
+
return PermissionsProtocol.uri;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/** @inheritdoc */
|
|
178
|
+
public get definition(): ProtocolDefinition {
|
|
179
|
+
return PermissionsProtocol.definition;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// ---------- CoreProtocol lifecycle hooks ----------
|
|
183
|
+
|
|
184
|
+
/** @inheritdoc */
|
|
185
|
+
public validateRecord(message: RecordsWriteMessage, dataBytes: Uint8Array): void {
|
|
186
|
+
PermissionsProtocol.validateSchema(message, dataBytes);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Pre-processing hook for permission revocation records.
|
|
191
|
+
* Validates that the revocation's `tags.protocol` matches the grant's scoped protocol.
|
|
192
|
+
*/
|
|
193
|
+
public async preProcessWrite(
|
|
194
|
+
tenant: string,
|
|
195
|
+
message: RecordsWriteMessage,
|
|
196
|
+
messageStore: MessageStore,
|
|
197
|
+
): Promise<void> {
|
|
198
|
+
if (message.descriptor.protocolPath !== PermissionsProtocol.revocationPath) {
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// fetch the parent grant to compare the scoped protocol against the revocation tag
|
|
203
|
+
const permissionGrantId = message.descriptor.parentId!;
|
|
204
|
+
const grant = await PermissionsProtocol.fetchGrant(tenant, messageStore, permissionGrantId);
|
|
205
|
+
|
|
206
|
+
const revokeTagProtocol = message.descriptor.tags?.protocol;
|
|
207
|
+
const grantProtocol = 'protocol' in grant.scope ? grant.scope.protocol : undefined;
|
|
208
|
+
if (grantProtocol !== revokeTagProtocol) {
|
|
209
|
+
throw new DwnError(
|
|
210
|
+
DwnErrorCode.PermissionsProtocolValidateRevocationProtocolTagMismatch,
|
|
211
|
+
`Revocation protocol ${revokeTagProtocol} does not match grant protocol ${grantProtocol}`
|
|
212
|
+
);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* Post-processing hook for permission revocation records.
|
|
218
|
+
* When a grant is revoked, all messages authorized by that grant and created
|
|
219
|
+
* after the revocation timestamp are deleted from all stores.
|
|
220
|
+
*
|
|
221
|
+
* Deletion order is deliberate to avoid orphaned data in case of crash:
|
|
222
|
+
* 1. data store (large blobs first)
|
|
223
|
+
* 2. state index (SMT entries)
|
|
224
|
+
* 3. message store
|
|
225
|
+
*/
|
|
226
|
+
public async postProcessWrite(
|
|
227
|
+
tenant: string,
|
|
228
|
+
recordsWrite: RecordsWrite,
|
|
229
|
+
stores: CoreProtocolStores,
|
|
230
|
+
): Promise<void> {
|
|
231
|
+
if (recordsWrite.message.descriptor.protocolPath !== PermissionsProtocol.revocationPath) {
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
const permissionGrantId = recordsWrite.message.descriptor.parentId!;
|
|
236
|
+
const grantAuthorizedMessagesQuery = {
|
|
237
|
+
permissionGrantId,
|
|
238
|
+
dateCreated: { gte: recordsWrite.message.descriptor.messageTimestamp },
|
|
239
|
+
};
|
|
240
|
+
const { messages: grantAuthorizedMessages } = await stores.messageStore.query(tenant, [grantAuthorizedMessagesQuery]);
|
|
241
|
+
|
|
242
|
+
if (grantAuthorizedMessages.length === 0) {
|
|
243
|
+
return;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// 1. Delete data from the data store first to avoid orphaned data blobs in case of crash.
|
|
247
|
+
// Only RecordsWrite messages with data larger than maxDataSizeAllowedToBeEncoded have data in the data store.
|
|
248
|
+
for (const message of grantAuthorizedMessages) {
|
|
249
|
+
if (message.descriptor.method === DwnMethodName.Write) {
|
|
250
|
+
const recordsWriteMessage = message as RecordsWriteMessage;
|
|
251
|
+
if (recordsWriteMessage.descriptor.dataSize > DwnConstant.maxDataSizeAllowedToBeEncoded) {
|
|
252
|
+
await stores.dataStore.delete(tenant, recordsWriteMessage.recordId, recordsWriteMessage.descriptor.dataCid);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
// 2. Compute CIDs and delete from state index before message store to avoid orphaned state entries.
|
|
258
|
+
const messageCids = await Promise.all(grantAuthorizedMessages.map((message): Promise<string> => Message.getCid(message)));
|
|
259
|
+
await stores.stateIndex.delete(tenant, messageCids);
|
|
260
|
+
|
|
261
|
+
// 3. Finally delete all messages from the message store.
|
|
262
|
+
await Promise.all(messageCids.map((cid): Promise<void> => stores.messageStore.delete(tenant, cid)));
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
/** @inheritdoc */
|
|
266
|
+
public mapErrorToStatusCode(errorCode: string): number | undefined {
|
|
267
|
+
if (errorCode.startsWith('PermissionsProtocolValidate')) {
|
|
268
|
+
return 400;
|
|
269
|
+
}
|
|
270
|
+
return undefined;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
* Constructs an additional filter for protocol-scoped message queries so that
|
|
275
|
+
* permission records (grants, requests, revocations) tagged with the target
|
|
276
|
+
* protocol appear alongside that protocol's own records in sync/subscribe/read results.
|
|
277
|
+
*/
|
|
278
|
+
public constructAdditionalMessageFilter(filter: MessagesFilter): Filter | undefined {
|
|
279
|
+
const { protocol, messageTimestamp } = filter;
|
|
280
|
+
if (protocol === undefined) {
|
|
281
|
+
return undefined;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
const taggedFilter = {
|
|
285
|
+
protocol: PermissionsProtocol.uri,
|
|
286
|
+
...Records.convertTagsFilter({ protocol }),
|
|
287
|
+
} as Filter;
|
|
288
|
+
|
|
289
|
+
if (messageTimestamp !== undefined) {
|
|
290
|
+
const messageTimestampFilter = FilterUtility.convertRangeCriterion(messageTimestamp);
|
|
291
|
+
if (messageTimestampFilter) {
|
|
292
|
+
taggedFilter.messageTimestamp = messageTimestampFilter;
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
return taggedFilter;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
// ---------- Static utility methods ----------
|
|
300
|
+
|
|
159
301
|
public static parseRequest(base64UrlEncodedRequest: string): PermissionRequestData {
|
|
160
302
|
return Encoder.base64UrlToObject(base64UrlEncodedRequest);
|
|
161
303
|
}
|
|
@@ -394,7 +536,7 @@ export class PermissionsProtocol {
|
|
|
394
536
|
}
|
|
395
537
|
|
|
396
538
|
const permissionGrantMessage = possibleGrantMessage as DataEncodedRecordsWriteMessage;
|
|
397
|
-
const permissionGrant =
|
|
539
|
+
const permissionGrant = PermissionGrant.parse(permissionGrantMessage);
|
|
398
540
|
|
|
399
541
|
return permissionGrant;
|
|
400
542
|
}
|
|
@@ -421,11 +563,11 @@ export class PermissionsProtocol {
|
|
|
421
563
|
const grant = await PermissionsProtocol.fetchGrant(tenant, messageStore, incomingMessage.descriptor.parentId!);
|
|
422
564
|
return grant.scope;
|
|
423
565
|
} else if (incomingMessage.descriptor.protocolPath === PermissionsProtocol.grantPath) {
|
|
424
|
-
const grant =
|
|
566
|
+
const grant = PermissionGrant.parse(incomingMessage);
|
|
425
567
|
return grant.scope;
|
|
426
568
|
} else {
|
|
427
569
|
// if the record is not a grant or revocation, it must be a request
|
|
428
|
-
const request =
|
|
570
|
+
const request = PermissionRequest.parse(incomingMessage);
|
|
429
571
|
return request.scope;
|
|
430
572
|
}
|
|
431
573
|
}
|
|
@@ -83,7 +83,8 @@ export class StateIndexLevel implements StateIndex {
|
|
|
83
83
|
const globalSmt = await this.getGlobalTree(tenant);
|
|
84
84
|
await globalSmt.insert(messageCid);
|
|
85
85
|
|
|
86
|
-
//
|
|
86
|
+
// Insert into the protocol-scoped tree if the message has a protocol (e.g. RecordsWrite).
|
|
87
|
+
// Non-record messages like ProtocolsConfigure do not have a protocol.
|
|
87
88
|
const protocol = indexes.protocol as string | undefined;
|
|
88
89
|
if (protocol !== undefined) {
|
|
89
90
|
const protoSmt = await this.getProtocolTree(tenant, protocol);
|
|
@@ -104,7 +105,7 @@ export class StateIndexLevel implements StateIndex {
|
|
|
104
105
|
// Delete from global tree
|
|
105
106
|
await globalSmt.delete(messageCid);
|
|
106
107
|
|
|
107
|
-
// Delete from protocol tree if
|
|
108
|
+
// Delete from protocol tree if the message had a protocol
|
|
108
109
|
if (indexes !== undefined) {
|
|
109
110
|
const protocol = indexes.protocol as string | undefined;
|
|
110
111
|
if (protocol !== undefined) {
|
|
@@ -204,11 +205,8 @@ export class StateIndexLevel implements StateIndex {
|
|
|
204
205
|
*/
|
|
205
206
|
private async storeIndexes(tenant: string, messageCid: string, indexes: KeyValues): Promise<void> {
|
|
206
207
|
const metaPartition = await this.getMetaPartition(tenant);
|
|
207
|
-
//
|
|
208
|
-
const minimalIndexes: KeyValues = {};
|
|
209
|
-
if (indexes.protocol !== undefined) {
|
|
210
|
-
minimalIndexes.protocol = indexes.protocol;
|
|
211
|
-
}
|
|
208
|
+
// Store the protocol index needed for deletion
|
|
209
|
+
const minimalIndexes: KeyValues = { protocol: indexes.protocol };
|
|
212
210
|
await metaPartition.put(messageCid, JSON.stringify(minimalIndexes));
|
|
213
211
|
}
|
|
214
212
|
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { ImportResult } from 'ipfs-unixfs-importer';
|
|
2
|
+
import type { LevelWrapper } from './level-wrapper.js';
|
|
2
3
|
import type { DataStore, DataStoreGetResult, DataStorePutResult } from '../types/data-store.js';
|
|
3
4
|
|
|
4
5
|
import { BlockstoreLevel } from './blockstore-level.js';
|
|
@@ -7,12 +8,20 @@ import { DataStream } from '../utils/data-stream.js';
|
|
|
7
8
|
import { exporter } from 'ipfs-unixfs-exporter';
|
|
8
9
|
import { importer } from 'ipfs-unixfs-importer';
|
|
9
10
|
|
|
11
|
+
const textEncoder = new TextEncoder();
|
|
12
|
+
const textDecoder = new TextDecoder();
|
|
13
|
+
|
|
10
14
|
/**
|
|
11
15
|
* A simple implementation of {@link DataStore} that works in both the browser and server-side.
|
|
12
16
|
* Leverages LevelDB under the hood.
|
|
13
17
|
*
|
|
14
|
-
* It has the following structure (`+` represents an additional sublevel
|
|
15
|
-
* '
|
|
18
|
+
* It has the following sublevel structure (`+` represents an additional sublevel):
|
|
19
|
+
* 'refs' + <tenant> + <recordId> : key <dataCid> → JSON { dataSize }
|
|
20
|
+
* 'blocks' + <dataCid> : key <blockCid> → block data (shared)
|
|
21
|
+
* 'refcounts' : key <dataCid> → JSON { count, dataSize }
|
|
22
|
+
*
|
|
23
|
+
* Identical data (same dataCid) is stored only once in the blocks sublevel.
|
|
24
|
+
* Multiple (tenant, recordId) pairs can reference the same blocks.
|
|
16
25
|
*/
|
|
17
26
|
export class DataStoreLevel implements DataStore {
|
|
18
27
|
config: DataStoreLevelConfig;
|
|
@@ -41,29 +50,74 @@ export class DataStoreLevel implements DataStore {
|
|
|
41
50
|
}
|
|
42
51
|
|
|
43
52
|
async put(tenant: string, recordId: string, dataCid: string, dataStream: ReadableStream<Uint8Array>): Promise<DataStorePutResult> {
|
|
44
|
-
|
|
53
|
+
// Check if this exact ref already exists (idempotent re-put).
|
|
54
|
+
const refsPartition = await this.getRefsPartition(tenant, recordId);
|
|
55
|
+
const existingRef = await refsPartition.get(dataCid);
|
|
56
|
+
if (existingRef) {
|
|
57
|
+
await dataStream.cancel();
|
|
58
|
+
const { dataSize } = JSON.parse(textDecoder.decode(existingRef)) as { dataSize: number };
|
|
59
|
+
return { dataSize };
|
|
60
|
+
}
|
|
45
61
|
|
|
46
|
-
|
|
62
|
+
// Check refcount — if > 0, blocks already exist for this dataCid.
|
|
63
|
+
const refcountsPartition = await this.getRefcountsPartition();
|
|
64
|
+
const rawRefcount = await refcountsPartition.get(dataCid);
|
|
65
|
+
const refcountData = rawRefcount
|
|
66
|
+
? JSON.parse(textDecoder.decode(rawRefcount)) as { count: number; dataSize: number }
|
|
67
|
+
: { count: 0, dataSize: 0 };
|
|
68
|
+
|
|
69
|
+
let dataSize: number;
|
|
70
|
+
|
|
71
|
+
if (refcountData.count > 0) {
|
|
72
|
+
// Blocks already exist — skip import.
|
|
73
|
+
await dataStream.cancel();
|
|
74
|
+
dataSize = refcountData.dataSize;
|
|
75
|
+
} else {
|
|
76
|
+
// First write — import blocks into the shared blocks partition.
|
|
77
|
+
const blocksPartition = await this.getBlocksPartition(dataCid);
|
|
78
|
+
const asyncDataBlocks = importer(
|
|
79
|
+
[{ content: DataStream.asAsyncIterable(dataStream) }],
|
|
80
|
+
blocksPartition,
|
|
81
|
+
{ cidVersion: 1 }
|
|
82
|
+
);
|
|
83
|
+
|
|
84
|
+
// NOTE: the last block contains the root CID as well as info to derive the data size.
|
|
85
|
+
let dataDagRoot!: ImportResult;
|
|
86
|
+
for await (dataDagRoot of asyncDataBlocks) { ; }
|
|
87
|
+
dataSize = Number(dataDagRoot.unixfs?.fileSize() ?? dataDagRoot.size);
|
|
88
|
+
}
|
|
47
89
|
|
|
48
|
-
//
|
|
49
|
-
|
|
50
|
-
for await (dataDagRoot of asyncDataBlocks) { ; }
|
|
90
|
+
// Write ref entry.
|
|
91
|
+
await refsPartition.put(dataCid, textEncoder.encode(JSON.stringify({ dataSize })));
|
|
51
92
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
93
|
+
// Increment refcount.
|
|
94
|
+
await refcountsPartition.put(dataCid, textEncoder.encode(JSON.stringify({
|
|
95
|
+
count: refcountData.count + 1,
|
|
96
|
+
dataSize,
|
|
97
|
+
})));
|
|
98
|
+
|
|
99
|
+
return { dataSize };
|
|
55
100
|
}
|
|
56
101
|
|
|
57
102
|
public async get(tenant: string, recordId: string, dataCid: string): Promise<DataStoreGetResult | undefined> {
|
|
58
|
-
|
|
103
|
+
// Check ref exists.
|
|
104
|
+
const refsPartition = await this.getRefsPartition(tenant, recordId);
|
|
105
|
+
const rawRef = await refsPartition.get(dataCid);
|
|
106
|
+
if (!rawRef) {
|
|
107
|
+
return undefined;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const { dataSize } = JSON.parse(textDecoder.decode(rawRef)) as { dataSize: number };
|
|
59
111
|
|
|
60
|
-
|
|
112
|
+
// Export from the shared blocks partition.
|
|
113
|
+
const blocksPartition = await this.getBlocksPartition(dataCid);
|
|
114
|
+
const exists = await blocksPartition.has(dataCid);
|
|
61
115
|
if (!exists) {
|
|
62
116
|
return undefined;
|
|
63
117
|
}
|
|
64
118
|
|
|
65
|
-
//
|
|
66
|
-
const dataDagRoot = await exporter(dataCid,
|
|
119
|
+
// Data is chunked into DAG-PB UnixFS blocks. Re-inflate the chunks.
|
|
120
|
+
const dataDagRoot = await exporter(dataCid, blocksPartition);
|
|
67
121
|
const contentIterator = dataDagRoot.content();
|
|
68
122
|
|
|
69
123
|
const dataStream = new ReadableStream<Uint8Array>({
|
|
@@ -77,21 +131,42 @@ export class DataStoreLevel implements DataStore {
|
|
|
77
131
|
}
|
|
78
132
|
});
|
|
79
133
|
|
|
80
|
-
|
|
134
|
+
return { dataSize, dataStream };
|
|
135
|
+
}
|
|
81
136
|
|
|
82
|
-
|
|
83
|
-
|
|
137
|
+
public async delete(tenant: string, recordId: string, dataCid: string): Promise<void> {
|
|
138
|
+
// Check ref exists.
|
|
139
|
+
const refsPartition = await this.getRefsPartition(tenant, recordId);
|
|
140
|
+
const rawRef = await refsPartition.get(dataCid);
|
|
141
|
+
if (!rawRef) {
|
|
142
|
+
return;
|
|
84
143
|
}
|
|
85
144
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
dataStream,
|
|
89
|
-
};
|
|
90
|
-
}
|
|
145
|
+
// Remove ref.
|
|
146
|
+
await refsPartition.delete(dataCid);
|
|
91
147
|
|
|
92
|
-
|
|
93
|
-
const
|
|
94
|
-
await
|
|
148
|
+
// Decrement refcount and GC blocks if this was the last ref.
|
|
149
|
+
const refcountsPartition = await this.getRefcountsPartition();
|
|
150
|
+
const rawRefcount = await refcountsPartition.get(dataCid);
|
|
151
|
+
if (!rawRefcount) {
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const refcountData = JSON.parse(textDecoder.decode(rawRefcount)) as { count: number; dataSize: number };
|
|
156
|
+
const newCount = refcountData.count - 1;
|
|
157
|
+
|
|
158
|
+
if (newCount <= 0) {
|
|
159
|
+
// Last reference removed — garbage-collect blocks and refcount entry.
|
|
160
|
+
const blocksPartition = await this.getBlocksPartition(dataCid);
|
|
161
|
+
await blocksPartition.clear();
|
|
162
|
+
await refcountsPartition.delete(dataCid);
|
|
163
|
+
} else {
|
|
164
|
+
// Other references remain — update the count.
|
|
165
|
+
await refcountsPartition.put(dataCid, textEncoder.encode(JSON.stringify({
|
|
166
|
+
count : newCount,
|
|
167
|
+
dataSize : refcountData.dataSize,
|
|
168
|
+
})));
|
|
169
|
+
}
|
|
95
170
|
}
|
|
96
171
|
|
|
97
172
|
/**
|
|
@@ -102,15 +177,30 @@ export class DataStoreLevel implements DataStore {
|
|
|
102
177
|
}
|
|
103
178
|
|
|
104
179
|
/**
|
|
105
|
-
* Gets the
|
|
180
|
+
* Gets the refs sublevel for the given tenant and recordId.
|
|
181
|
+
* Caller uses `dataCid` as the key within the returned partition.
|
|
182
|
+
*/
|
|
183
|
+
private async getRefsPartition(tenant: string, recordId: string): Promise<LevelWrapper<Uint8Array>> {
|
|
184
|
+
const refsRoot = await this.blockstore.db.partition('refs');
|
|
185
|
+
const tenantPartition = await refsRoot.partition(tenant);
|
|
186
|
+
return tenantPartition.partition(recordId);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Gets the shared blocks sublevel for the given dataCid.
|
|
191
|
+
* Used as a Blockstore for ipfs-unixfs-importer/exporter.
|
|
192
|
+
*/
|
|
193
|
+
private async getBlocksPartition(dataCid: string): Promise<BlockstoreLevel> {
|
|
194
|
+
const blocksRoot = await this.blockstore.partition('blocks');
|
|
195
|
+
return blocksRoot.partition(dataCid);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Gets the refcounts sublevel.
|
|
200
|
+
* Key: `dataCid` → JSON `{ count, dataSize }`.
|
|
106
201
|
*/
|
|
107
|
-
private async
|
|
108
|
-
|
|
109
|
-
const blockstoreForData = await this.blockstore.partition(dataPartitionName);
|
|
110
|
-
const blockstoreOfGivenTenant = await blockstoreForData.partition(tenant);
|
|
111
|
-
const blockstoreOfGivenRecordId = await blockstoreOfGivenTenant.partition(recordId);
|
|
112
|
-
const blockstoreOfGivenDataCidOfRecordId = await blockstoreOfGivenRecordId.partition(dataCid);
|
|
113
|
-
return blockstoreOfGivenDataCidOfRecordId;
|
|
202
|
+
private async getRefcountsPartition(): Promise<LevelWrapper<Uint8Array>> {
|
|
203
|
+
return this.blockstore.db.partition('refcounts');
|
|
114
204
|
}
|
|
115
205
|
|
|
116
206
|
}
|
|
@@ -118,4 +208,4 @@ export class DataStoreLevel implements DataStore {
|
|
|
118
208
|
export type DataStoreLevelConfig = {
|
|
119
209
|
blockstoreLocation?: string,
|
|
120
210
|
createLevelDatabase?: typeof createLevelDatabase,
|
|
121
|
-
};
|
|
211
|
+
};
|
package/src/store/index-level.ts
CHANGED
|
@@ -477,9 +477,18 @@ export class IndexLevel {
|
|
|
477
477
|
}
|
|
478
478
|
|
|
479
479
|
try {
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
480
|
+
// Execute filters sequentially rather than with Promise.all.
|
|
481
|
+
// Firefox's IndexedDB implementation has two related async issues that cause flaky failures:
|
|
482
|
+
// 1. Concurrent cursors/transactions can silently miss recently-committed data
|
|
483
|
+
// 2. A read transaction opened immediately after a write transaction's oncomplete event
|
|
484
|
+
// can fail to see the written data (write-read race)
|
|
485
|
+
// Serializing eliminates both races. Performance impact is negligible: only 3 call sites
|
|
486
|
+
// (non-owner RecordsQuery/Subscribe/Count) ever pass multiple filters, and each filter
|
|
487
|
+
// reduces to a single bounded LevelDB range scan completing in single-digit ms.
|
|
488
|
+
// See: https://github.com/enboxorg/enbox/issues/264
|
|
489
|
+
for (const filter of filters) {
|
|
490
|
+
await this.executeSingleFilterQuery(tenant, filter, sortProperty, matches, options);
|
|
491
|
+
}
|
|
483
492
|
} catch (error) {
|
|
484
493
|
if ((error as DwnError).code === DwnErrorCode.IndexInvalidSortPropertyInMemory) {
|
|
485
494
|
// return empty results if the sort property is invalid.
|
|
@@ -501,6 +510,10 @@ export class IndexLevel {
|
|
|
501
510
|
|
|
502
511
|
/**
|
|
503
512
|
* Execute a filtered query against a single filter and return all results.
|
|
513
|
+
*
|
|
514
|
+
* Sub-queries (exact match, range, OneOf) are executed sequentially to avoid opening
|
|
515
|
+
* multiple concurrent IndexedDB cursors. Firefox's IDB implementation intermittently
|
|
516
|
+
* drops results when cursors overlap — see https://github.com/enboxorg/enbox/issues/264
|
|
504
517
|
*/
|
|
505
518
|
private async executeSingleFilterQuery(
|
|
506
519
|
tenant: string,
|
|
@@ -510,13 +523,30 @@ export class IndexLevel {
|
|
|
510
523
|
levelOptions?: IndexLevelOptions
|
|
511
524
|
): Promise<void> {
|
|
512
525
|
|
|
513
|
-
//
|
|
514
|
-
const
|
|
526
|
+
// Collects results from each sub-query sequentially to avoid concurrent IndexedDB cursor races in Firefox.
|
|
527
|
+
const processResults = (indexItems: IndexedItem[]): void => {
|
|
528
|
+
for (const indexedItem of indexItems) {
|
|
529
|
+
// short circuit: if a data is already included to the final matched key set (by a different `Filter`),
|
|
530
|
+
// no need to evaluate if the data satisfies this current filter being evaluated
|
|
531
|
+
// otherwise check that the item is a match.
|
|
532
|
+
if (matches.has(indexedItem.messageCid) || !FilterUtility.matchFilter(indexedItem.indexes, filter)) {
|
|
533
|
+
continue;
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
// ensure that each matched item has the sortProperty, otherwise fail the entire query.
|
|
537
|
+
if (indexedItem.indexes[sortProperty] === undefined) {
|
|
538
|
+
throw new DwnError(DwnErrorCode.IndexInvalidSortPropertyInMemory, `invalid sort property ${sortProperty}`);
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
matches.set(indexedItem.messageCid, indexedItem);
|
|
542
|
+
}
|
|
543
|
+
};
|
|
515
544
|
|
|
516
545
|
// If the filter is empty, then we just iterate over one of the indexes that contains all the records and return all items.
|
|
517
546
|
if (isEmptyObject(filter)) {
|
|
518
|
-
const
|
|
519
|
-
|
|
547
|
+
const allItems = await this.getAllItems(tenant, sortProperty);
|
|
548
|
+
processResults(allItems);
|
|
549
|
+
return;
|
|
520
550
|
}
|
|
521
551
|
|
|
522
552
|
// else the filter is not empty
|
|
@@ -526,40 +556,19 @@ export class IndexLevel {
|
|
|
526
556
|
// We will find the union of these many individual queries later.
|
|
527
557
|
if (FilterUtility.isEqualFilter(propertyFilter)) {
|
|
528
558
|
// propertyFilter is an EqualFilter, meaning it is a non-object primitive type
|
|
529
|
-
const
|
|
530
|
-
|
|
559
|
+
const exactMatches = await this.filterExactMatches(tenant, propertyName, propertyFilter, levelOptions);
|
|
560
|
+
processResults(exactMatches);
|
|
531
561
|
} else if (FilterUtility.isOneOfFilter(propertyFilter)) {
|
|
532
562
|
// `propertyFilter` is a OneOfFilter
|
|
533
|
-
// Support OR matches by querying for each
|
|
563
|
+
// Support OR matches by querying for each value separately and sequentially.
|
|
534
564
|
for (const propertyValue of new Set(propertyFilter)) {
|
|
535
|
-
const
|
|
536
|
-
|
|
565
|
+
const exactMatches = await this.filterExactMatches(tenant, propertyName, propertyValue, levelOptions);
|
|
566
|
+
processResults(exactMatches);
|
|
537
567
|
}
|
|
538
568
|
} else if (FilterUtility.isRangeFilter(propertyFilter)) {
|
|
539
569
|
// `propertyFilter` is a `RangeFilter`
|
|
540
|
-
const
|
|
541
|
-
|
|
542
|
-
}
|
|
543
|
-
}
|
|
544
|
-
|
|
545
|
-
// acting as an OR match for the property, any of the promises returning a match will be treated as a property match
|
|
546
|
-
for (const promise of filterPromises) {
|
|
547
|
-
const indexItems = await promise;
|
|
548
|
-
// reminder: the promise returns a list of IndexedItem satisfying a particular property match
|
|
549
|
-
for (const indexedItem of indexItems) {
|
|
550
|
-
// short circuit: if a data is already included to the final matched key set (by a different `Filter`),
|
|
551
|
-
// no need to evaluate if the data satisfies this current filter being evaluated
|
|
552
|
-
// otherwise check that the item is a match.
|
|
553
|
-
if (matches.has(indexedItem.messageCid) || !FilterUtility.matchFilter(indexedItem.indexes, filter)) {
|
|
554
|
-
continue;
|
|
555
|
-
}
|
|
556
|
-
|
|
557
|
-
// ensure that each matched item has the sortProperty, otherwise fail the entire query.
|
|
558
|
-
if (indexedItem.indexes[sortProperty] === undefined) {
|
|
559
|
-
throw new DwnError(DwnErrorCode.IndexInvalidSortPropertyInMemory, `invalid sort property ${sortProperty}`);
|
|
560
|
-
}
|
|
561
|
-
|
|
562
|
-
matches.set(indexedItem.messageCid, indexedItem);
|
|
570
|
+
const rangeMatches = await this.filterRangeMatches(tenant, propertyName, propertyFilter, levelOptions);
|
|
571
|
+
processResults(rangeMatches);
|
|
563
572
|
}
|
|
564
573
|
}
|
|
565
574
|
}
|