@enbox/dwn-sdk-js 0.4.0 → 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 +8 -8
- package/dist/browser.mjs.map +4 -4
- package/dist/esm/generated/precompiled-validators.js +799 -885
- 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 +12 -4
- 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 -45
- 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 +30 -68
- 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 +123 -28
- package/dist/esm/src/core/replication-apply.js.map +1 -1
- 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 +165 -132
- 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 +6 -7
- package/dist/esm/src/index.js.map +1 -1
- package/dist/esm/src/interfaces/{messages-sync.js → messages-query.js} +21 -15
- 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 +55 -1
- package/dist/esm/tests/core/replication-apply.spec.js.map +1 -1
- 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 +504 -35
- 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 +82 -36
- 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 -2
- 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 +12 -4
- 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 -14
- 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 +36 -0
- package/dist/types/src/core/replication-apply.d.ts.map +1 -1
- 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 +33 -20
- 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 +14 -16
- 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 -37
- 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-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 +12 -4
- package/src/core/grant-authorization.ts +11 -20
- package/src/core/message-reply.ts +6 -5
- package/src/core/messages-grant-authorization.ts +37 -70
- package/src/core/protocol-authorization-action.ts +29 -38
- package/src/core/protocol-authorization-validation.ts +39 -96
- 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 +172 -32
- package/src/core/resumable-task-manager.ts +10 -8
- package/src/core/validation-state-reader.ts +350 -0
- package/src/dwn.ts +285 -192
- 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 +14 -17
- 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 -45
- 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 -278
- package/dist/esm/src/handlers/messages-sync.js.map +0 -1
- 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/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 -1088
- 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/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 -39
- package/dist/types/src/handlers/messages-sync.d.ts.map +0 -1
- package/dist/types/src/interfaces/messages-sync.d.ts +0 -20
- 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/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/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 -403
- package/src/interfaces/messages-sync.ts +0 -69
- 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/types/smt-types.ts +0 -95
- package/src/types/state-index.ts +0 -100
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import sinon from 'sinon';
|
|
2
|
+
import nestedProtocol from './vectors/protocol-definitions/nested.json' with { type: 'json' };
|
|
2
3
|
import { Dwn } from '../src/dwn.js';
|
|
3
|
-
import { TestDataGenerator } from './utils/test-data-generator.js';
|
|
4
4
|
import { TestEventLog } from './test-event-stream.js';
|
|
5
5
|
import { TestStores } from './test-stores.js';
|
|
6
6
|
import { afterAll, beforeAll, beforeEach, describe, expect, it } from 'bun:test';
|
|
7
|
-
import { DataStoreLevel,
|
|
7
|
+
import { DataStoreLevel, DataStream, DwnErrorCode, DwnMethodName, Jws, Message, MessageStoreLevel, RecordsDelete, RecordsRead, RecordsWrite, ResumableTaskStoreLevel, Time } from '../src/index.js';
|
|
8
|
+
import { defaultTestProtocolDefinition, TestDataGenerator } from './utils/test-data-generator.js';
|
|
8
9
|
import { DidKey, UniversalResolver } from '@enbox/dids';
|
|
9
10
|
export function testDwnClass() {
|
|
10
11
|
describe('DWN', () => {
|
|
@@ -12,7 +13,6 @@ export function testDwnClass() {
|
|
|
12
13
|
let messageStore;
|
|
13
14
|
let dataStore;
|
|
14
15
|
let resumableTaskStore;
|
|
15
|
-
let stateIndex;
|
|
16
16
|
let eventLog;
|
|
17
17
|
let dwn;
|
|
18
18
|
// important to follow the `beforeAll` and `afterAll` pattern to initialize and clean the stores in tests
|
|
@@ -23,9 +23,8 @@ export function testDwnClass() {
|
|
|
23
23
|
messageStore = stores.messageStore;
|
|
24
24
|
dataStore = stores.dataStore;
|
|
25
25
|
resumableTaskStore = stores.resumableTaskStore;
|
|
26
|
-
stateIndex = stores.stateIndex;
|
|
27
26
|
eventLog = TestEventLog.get();
|
|
28
|
-
dwn = await Dwn.create({ didResolver, messageStore, dataStore,
|
|
27
|
+
dwn = await Dwn.create({ didResolver, messageStore, dataStore, eventLog, resumableTaskStore });
|
|
29
28
|
});
|
|
30
29
|
beforeEach(async () => {
|
|
31
30
|
sinon.restore(); // wipe all stubs/spies/mocks/fakes from previous test
|
|
@@ -81,15 +80,11 @@ export function testDwnClass() {
|
|
|
81
80
|
const messageStoreStub = sinon.createStubInstance(MessageStoreLevel);
|
|
82
81
|
const dataStoreStub = sinon.createStubInstance(DataStoreLevel);
|
|
83
82
|
const resumableTaskStoreStub = sinon.createStubInstance(ResumableTaskStoreLevel);
|
|
84
|
-
const stateIndexStub = sinon.createStubInstance(StateIndexLevel);
|
|
85
|
-
const eventLogStub = sinon.createStubInstance(EventEmitterEventLog);
|
|
86
83
|
const dwnWithConfig = await Dwn.create({
|
|
87
84
|
tenantGate: blockAllTenantGate,
|
|
88
85
|
messageStore: messageStoreStub,
|
|
89
86
|
dataStore: dataStoreStub,
|
|
90
87
|
resumableTaskStore: resumableTaskStoreStub,
|
|
91
|
-
stateIndex: stateIndexStub,
|
|
92
|
-
eventLog: eventLogStub
|
|
93
88
|
});
|
|
94
89
|
const alice = await TestDataGenerator.generateDidKeyPersona();
|
|
95
90
|
const { author, message } = await TestDataGenerator.generateRecordsQuery({ author: alice });
|
|
@@ -109,15 +104,11 @@ export function testDwnClass() {
|
|
|
109
104
|
const messageStoreStub = sinon.createStubInstance(MessageStoreLevel);
|
|
110
105
|
const dataStoreStub = sinon.createStubInstance(DataStoreLevel);
|
|
111
106
|
const resumableTaskStoreStub = sinon.createStubInstance(ResumableTaskStoreLevel);
|
|
112
|
-
const stateIndexStub = sinon.createStubInstance(StateIndexLevel);
|
|
113
|
-
const eventLogStub = sinon.createStubInstance(EventEmitterEventLog);
|
|
114
107
|
const dwnWithConfig = await Dwn.create({
|
|
115
108
|
tenantGate: blockAllTenantGate,
|
|
116
109
|
messageStore: messageStoreStub,
|
|
117
110
|
dataStore: dataStoreStub,
|
|
118
111
|
resumableTaskStore: resumableTaskStoreStub,
|
|
119
|
-
stateIndex: stateIndexStub,
|
|
120
|
-
eventLog: eventLogStub
|
|
121
112
|
});
|
|
122
113
|
const alice = await TestDataGenerator.generateDidKeyPersona();
|
|
123
114
|
const { author, message } = await TestDataGenerator.generateRecordsQuery({ author: alice });
|
|
@@ -128,34 +119,14 @@ export function testDwnClass() {
|
|
|
128
119
|
});
|
|
129
120
|
});
|
|
130
121
|
describe('applyReplicatedMessage()', () => {
|
|
131
|
-
it('returns Duplicate
|
|
122
|
+
it('returns Duplicate for an exact replay already in the message store', async () => {
|
|
132
123
|
const alice = await TestDataGenerator.generateDidKeyPersona();
|
|
133
124
|
await TestDataGenerator.installDefaultTestProtocol(dwn, alice);
|
|
134
125
|
const { message, dataStream } = await TestDataGenerator.generateRecordsWrite({ author: alice });
|
|
135
126
|
const initialReply = await dwn.processMessage(alice.did, message, { dataStream });
|
|
136
127
|
expect(initialReply.status.code).toBe(202);
|
|
137
|
-
const messageCid = await Message.getCid(message);
|
|
138
|
-
await stateIndex.delete(alice.did, [messageCid]);
|
|
139
|
-
expect(await stateIndex.getLeaves(alice.did, [])).not.toContain(messageCid);
|
|
140
128
|
const result = await dwn.applyReplicatedMessage(alice.did, message);
|
|
141
129
|
expect(result).toEqual({ kind: 'Duplicate' });
|
|
142
|
-
expect(await stateIndex.getLeaves(alice.did, [])).toContain(messageCid);
|
|
143
|
-
});
|
|
144
|
-
it('returns Duplicate and repairs the event log for an exact replay already in the message store and state index', async () => {
|
|
145
|
-
const alice = await TestDataGenerator.generateDidKeyPersona();
|
|
146
|
-
await TestDataGenerator.installDefaultTestProtocol(dwn, alice);
|
|
147
|
-
const { message, dataStream } = await TestDataGenerator.generateRecordsWrite({ author: alice });
|
|
148
|
-
const initialReply = await dwn.processMessage(alice.did, message, { dataStream });
|
|
149
|
-
expect(initialReply.status.code).toBe(202);
|
|
150
|
-
const messageCid = await Message.getCid(message);
|
|
151
|
-
expect(await stateIndex.getLeaves(alice.did, [])).toContain(messageCid);
|
|
152
|
-
await eventLog.close();
|
|
153
|
-
await eventLog.open();
|
|
154
|
-
expect((await eventLog.read(alice.did)).events).toEqual([]);
|
|
155
|
-
const result = await dwn.applyReplicatedMessage(alice.did, message);
|
|
156
|
-
expect(result).toEqual({ kind: 'Duplicate' });
|
|
157
|
-
const { events } = await eventLog.read(alice.did);
|
|
158
|
-
expect(events.map(event => event.messageCid)).toContain(messageCid);
|
|
159
130
|
});
|
|
160
131
|
it('returns resolved cross-protocol role dependencies for replicated role-authorized queries', async () => {
|
|
161
132
|
const alice = await TestDataGenerator.generateDidKeyPersona();
|
|
@@ -205,7 +176,7 @@ export function testDwnClass() {
|
|
|
205
176
|
author: bob,
|
|
206
177
|
protocolRole: 'threads:thread/participant',
|
|
207
178
|
filter: {
|
|
208
|
-
contextId: 'thread-context
|
|
179
|
+
contextId: 'thread-context',
|
|
209
180
|
protocol: commentsProtocol.protocol,
|
|
210
181
|
protocolPath: 'thread/comment',
|
|
211
182
|
},
|
|
@@ -263,6 +234,504 @@ export function testDwnClass() {
|
|
|
263
234
|
const result = await dwn.applyReplicatedMessage(alice.did, child.message);
|
|
264
235
|
expect(result).toEqual({ kind: 'Duplicate' });
|
|
265
236
|
});
|
|
237
|
+
it('classifies a replicated write older than the squash floor as Superseded', async () => {
|
|
238
|
+
const alice = await TestDataGenerator.generateDidKeyPersona();
|
|
239
|
+
const squashProtocol = {
|
|
240
|
+
protocol: 'https://example.com/replicated-squash',
|
|
241
|
+
published: true,
|
|
242
|
+
types: {
|
|
243
|
+
document: {},
|
|
244
|
+
patch: {},
|
|
245
|
+
},
|
|
246
|
+
structure: {
|
|
247
|
+
document: {
|
|
248
|
+
patch: {
|
|
249
|
+
$immutable: true,
|
|
250
|
+
$squash: true,
|
|
251
|
+
},
|
|
252
|
+
},
|
|
253
|
+
},
|
|
254
|
+
};
|
|
255
|
+
const protocolsConfigure = await TestDataGenerator.generateProtocolsConfigure({
|
|
256
|
+
author: alice,
|
|
257
|
+
protocolDefinition: squashProtocol,
|
|
258
|
+
});
|
|
259
|
+
expect((await dwn.processMessage(alice.did, protocolsConfigure.message)).status.code).toBe(202);
|
|
260
|
+
const document = await TestDataGenerator.generateRecordsWrite({
|
|
261
|
+
author: alice,
|
|
262
|
+
protocol: squashProtocol.protocol,
|
|
263
|
+
protocolPath: 'document',
|
|
264
|
+
});
|
|
265
|
+
expect((await dwn.processMessage(alice.did, document.message, { dataStream: document.dataStream })).status.code).toBe(202);
|
|
266
|
+
const squashTimestamp = Time.createOffsetTimestamp({ seconds: 10 });
|
|
267
|
+
const squashRecord = await TestDataGenerator.generateRecordsWrite({
|
|
268
|
+
author: alice,
|
|
269
|
+
protocol: squashProtocol.protocol,
|
|
270
|
+
protocolPath: 'document/patch',
|
|
271
|
+
parentContextId: document.message.contextId,
|
|
272
|
+
dateCreated: squashTimestamp,
|
|
273
|
+
messageTimestamp: squashTimestamp,
|
|
274
|
+
squash: true,
|
|
275
|
+
});
|
|
276
|
+
expect((await dwn.processMessage(alice.did, squashRecord.message, { dataStream: squashRecord.dataStream })).status.code).toBe(202);
|
|
277
|
+
// A replica replaying a pre-squash write is a normal multi-replica race: it must
|
|
278
|
+
// converge as a no-op, never surface as a terminal failure.
|
|
279
|
+
const olderTimestamp = Time.createOffsetTimestamp({ seconds: 5 });
|
|
280
|
+
const olderPatch = await TestDataGenerator.generateRecordsWrite({
|
|
281
|
+
author: alice,
|
|
282
|
+
protocol: squashProtocol.protocol,
|
|
283
|
+
protocolPath: 'document/patch',
|
|
284
|
+
parentContextId: document.message.contextId,
|
|
285
|
+
dateCreated: olderTimestamp,
|
|
286
|
+
messageTimestamp: olderTimestamp,
|
|
287
|
+
});
|
|
288
|
+
const result = await dwn.applyReplicatedMessage(alice.did, olderPatch.message, { dataStream: olderPatch.dataStream });
|
|
289
|
+
expect(result).toEqual({ kind: 'Superseded' });
|
|
290
|
+
});
|
|
291
|
+
it('converges to the same deleted state when a write and an older tombstone apply in either order', async () => {
|
|
292
|
+
const alice = await TestDataGenerator.generateDidKeyPersona();
|
|
293
|
+
// one shared ProtocolsConfigure message, processed on BOTH replicas so their
|
|
294
|
+
// state roots remain directly comparable
|
|
295
|
+
const protocolsConfigure = await TestDataGenerator.generateProtocolsConfigure({
|
|
296
|
+
author: alice,
|
|
297
|
+
protocolDefinition: defaultTestProtocolDefinition,
|
|
298
|
+
});
|
|
299
|
+
expect((await dwn.processMessage(alice.did, protocolsConfigure.message)).status.code).toBe(202);
|
|
300
|
+
// initial write -> tombstone -> newer update, with strictly increasing timestamps
|
|
301
|
+
const datePublished = '2025-01-05T00:00:00.000000Z';
|
|
302
|
+
const initialWrite = await TestDataGenerator.generateRecordsWrite({ author: alice });
|
|
303
|
+
await Time.minimalSleep();
|
|
304
|
+
const recordsDelete = await RecordsDelete.create({
|
|
305
|
+
recordId: initialWrite.message.recordId,
|
|
306
|
+
signer: Jws.createSigner(alice),
|
|
307
|
+
});
|
|
308
|
+
await Time.minimalSleep();
|
|
309
|
+
const updateDataBytes = TestDataGenerator.randomBytes(32);
|
|
310
|
+
const update = await RecordsWrite.createFrom({
|
|
311
|
+
recordsWriteMessage: initialWrite.message,
|
|
312
|
+
data: updateDataBytes,
|
|
313
|
+
published: true,
|
|
314
|
+
datePublished,
|
|
315
|
+
tags: { team: 'blue' },
|
|
316
|
+
signer: Jws.createSigner(alice),
|
|
317
|
+
});
|
|
318
|
+
// replica A: initial write and the newer update land first, the older tombstone last
|
|
319
|
+
expect(await dwn.applyReplicatedMessage(alice.did, initialWrite.message, { dataStream: DataStream.fromBytes(initialWrite.dataBytes) })).toEqual(expect.objectContaining({ kind: 'Applied' }));
|
|
320
|
+
expect(await dwn.applyReplicatedMessage(alice.did, update.message, { dataStream: DataStream.fromBytes(updateDataBytes) })).toEqual(expect.objectContaining({ kind: 'Applied' }));
|
|
321
|
+
expect(await dwn.applyReplicatedMessage(alice.did, recordsDelete.message)).toEqual(expect.objectContaining({ kind: 'Applied' }));
|
|
322
|
+
// replica B: the tombstone lands before the newer update
|
|
323
|
+
const messageStoreB = new MessageStoreLevel({
|
|
324
|
+
location: 'TEST-MESSAGESTORE-DELETEWINS',
|
|
325
|
+
});
|
|
326
|
+
const dataStoreB = new DataStoreLevel({ blockstoreLocation: 'TEST-DATASTORE-DELETEWINS' });
|
|
327
|
+
const resumableTaskStoreB = new ResumableTaskStoreLevel({ location: 'TEST-RESUMABLE-TASK-STORE-DELETEWINS' });
|
|
328
|
+
const dwnB = await Dwn.create({
|
|
329
|
+
didResolver,
|
|
330
|
+
messageStore: messageStoreB,
|
|
331
|
+
dataStore: dataStoreB,
|
|
332
|
+
resumableTaskStore: resumableTaskStoreB,
|
|
333
|
+
});
|
|
334
|
+
try {
|
|
335
|
+
await messageStoreB.clear();
|
|
336
|
+
await dataStoreB.clear();
|
|
337
|
+
await resumableTaskStoreB.clear();
|
|
338
|
+
expect((await dwnB.processMessage(alice.did, protocolsConfigure.message)).status.code).toBe(202);
|
|
339
|
+
expect(await dwnB.applyReplicatedMessage(alice.did, initialWrite.message, { dataStream: DataStream.fromBytes(initialWrite.dataBytes) })).toEqual(expect.objectContaining({ kind: 'Applied' }));
|
|
340
|
+
expect(await dwnB.applyReplicatedMessage(alice.did, recordsDelete.message)).toEqual(expect.objectContaining({ kind: 'Applied' }));
|
|
341
|
+
expect(await dwnB.applyReplicatedMessage(alice.did, update.message, { dataStream: DataStream.fromBytes(updateDataBytes) })).toEqual({ kind: 'Superseded' });
|
|
342
|
+
// both replicas read the record as deleted and retain the update-derived tombstone visibility
|
|
343
|
+
const readA = await RecordsRead.create({
|
|
344
|
+
signer: Jws.createSigner(alice),
|
|
345
|
+
filter: { recordId: initialWrite.message.recordId },
|
|
346
|
+
});
|
|
347
|
+
expect((await dwn.processMessage(alice.did, readA.message)).status.code).toBe(404);
|
|
348
|
+
const readB = await RecordsRead.create({
|
|
349
|
+
signer: Jws.createSigner(alice),
|
|
350
|
+
filter: { recordId: initialWrite.message.recordId },
|
|
351
|
+
});
|
|
352
|
+
expect((await dwnB.processMessage(alice.did, readB.message)).status.code).toBe(404);
|
|
353
|
+
const tombstoneFilter = {
|
|
354
|
+
'method': 'Delete',
|
|
355
|
+
'tag.team': 'blue',
|
|
356
|
+
'published': true,
|
|
357
|
+
'datePublished': datePublished,
|
|
358
|
+
};
|
|
359
|
+
const { messages: tombstonesA } = await messageStore.query(alice.did, [tombstoneFilter]);
|
|
360
|
+
const { messages: tombstonesB } = await messageStoreB.query(alice.did, [tombstoneFilter]);
|
|
361
|
+
expect(tombstonesA.length).toBe(1);
|
|
362
|
+
expect(tombstonesB.length).toBe(1);
|
|
363
|
+
expect(await Message.getCid(tombstonesA[0])).toBe(await Message.getCid(recordsDelete.message));
|
|
364
|
+
expect(await Message.getCid(tombstonesB[0])).toBe(await Message.getCid(recordsDelete.message));
|
|
365
|
+
}
|
|
366
|
+
finally {
|
|
367
|
+
await dwnB.close();
|
|
368
|
+
}
|
|
369
|
+
});
|
|
370
|
+
it('rejects a tombstone-beaten replicated write when its data does not match its descriptor', async () => {
|
|
371
|
+
const alice = await TestDataGenerator.generateDidKeyPersona();
|
|
372
|
+
const protocolsConfigure = await TestDataGenerator.generateProtocolsConfigure({
|
|
373
|
+
author: alice,
|
|
374
|
+
protocolDefinition: defaultTestProtocolDefinition,
|
|
375
|
+
});
|
|
376
|
+
expect((await dwn.processMessage(alice.did, protocolsConfigure.message)).status.code).toBe(202);
|
|
377
|
+
const initialWrite = await TestDataGenerator.generateRecordsWrite({ author: alice });
|
|
378
|
+
expect(await dwn.applyReplicatedMessage(alice.did, initialWrite.message, { dataStream: DataStream.fromBytes(initialWrite.dataBytes) })).toEqual(expect.objectContaining({ kind: 'Applied' }));
|
|
379
|
+
const recordsDelete = await RecordsDelete.create({
|
|
380
|
+
recordId: initialWrite.message.recordId,
|
|
381
|
+
signer: Jws.createSigner(alice),
|
|
382
|
+
});
|
|
383
|
+
expect(await dwn.applyReplicatedMessage(alice.did, recordsDelete.message)).toEqual(expect.objectContaining({ kind: 'Applied' }));
|
|
384
|
+
await Time.minimalSleep();
|
|
385
|
+
const updateDataBytes = TestDataGenerator.randomBytes(32);
|
|
386
|
+
const update = await RecordsWrite.createFrom({
|
|
387
|
+
recordsWriteMessage: initialWrite.message,
|
|
388
|
+
data: updateDataBytes,
|
|
389
|
+
tags: { team: 'blue' },
|
|
390
|
+
signer: Jws.createSigner(alice),
|
|
391
|
+
});
|
|
392
|
+
const malformedDataBytes = TestDataGenerator.randomBytes(32);
|
|
393
|
+
const result = await dwn.applyReplicatedMessage(alice.did, update.message, { dataStream: DataStream.fromBytes(malformedDataBytes) });
|
|
394
|
+
expect(result.kind).toBe('Invalid');
|
|
395
|
+
expect(result.reason).toContain(DwnErrorCode.RecordsWriteDataCidMismatch);
|
|
396
|
+
const updateCid = await Message.getCid(update.message);
|
|
397
|
+
const { messages } = await messageStore.query(alice.did, [{ recordId: initialWrite.message.recordId }]);
|
|
398
|
+
const messageCids = await Promise.all(messages.map(message => Message.getCid(message)));
|
|
399
|
+
expect(messageCids).not.toContain(updateCid);
|
|
400
|
+
const { messages: reindexedTombstones } = await messageStore.query(alice.did, [{
|
|
401
|
+
method: DwnMethodName.Delete,
|
|
402
|
+
'tag.team': 'blue',
|
|
403
|
+
}]);
|
|
404
|
+
expect(reindexedTombstones.length).toBe(0);
|
|
405
|
+
});
|
|
406
|
+
it('reports missing data for tombstone-beaten replicated writes without data', async () => {
|
|
407
|
+
const alice = await TestDataGenerator.generateDidKeyPersona();
|
|
408
|
+
await TestDataGenerator.installDefaultTestProtocol(dwn, alice);
|
|
409
|
+
const scenarios = [
|
|
410
|
+
{ dataBytes: TestDataGenerator.randomBytes(32), storage: 'inline' },
|
|
411
|
+
{ dataBytes: TestDataGenerator.randomBytes(31_000), storage: 'data-store' },
|
|
412
|
+
];
|
|
413
|
+
for (const { dataBytes, storage } of scenarios) {
|
|
414
|
+
const initialWrite = await TestDataGenerator.generateRecordsWrite({ author: alice, data: dataBytes });
|
|
415
|
+
expect(await dwn.applyReplicatedMessage(alice.did, initialWrite.message, { dataStream: DataStream.fromBytes(dataBytes) })).toEqual(expect.objectContaining({ kind: 'Applied' }));
|
|
416
|
+
const recordsDelete = await RecordsDelete.create({
|
|
417
|
+
recordId: initialWrite.message.recordId,
|
|
418
|
+
signer: Jws.createSigner(alice),
|
|
419
|
+
});
|
|
420
|
+
expect(await dwn.applyReplicatedMessage(alice.did, recordsDelete.message)).toEqual(expect.objectContaining({ kind: 'Applied' }));
|
|
421
|
+
await Time.minimalSleep();
|
|
422
|
+
const update = await RecordsWrite.createFrom({
|
|
423
|
+
recordsWriteMessage: initialWrite.message,
|
|
424
|
+
tags: { storage },
|
|
425
|
+
signer: Jws.createSigner(alice),
|
|
426
|
+
});
|
|
427
|
+
const result = await dwn.applyReplicatedMessage(alice.did, update.message);
|
|
428
|
+
expect(result).toEqual({
|
|
429
|
+
kind: 'Incomplete',
|
|
430
|
+
missing: [{
|
|
431
|
+
type: 'RecordData',
|
|
432
|
+
recordId: update.message.recordId,
|
|
433
|
+
dataCid: update.message.descriptor.dataCid,
|
|
434
|
+
protocol: update.message.descriptor.protocol,
|
|
435
|
+
}],
|
|
436
|
+
});
|
|
437
|
+
const updateCid = await Message.getCid(update.message);
|
|
438
|
+
expect(await messageStore.get(alice.did, updateCid)).toBeUndefined();
|
|
439
|
+
const { messages: reindexedTombstones } = await messageStore.query(alice.did, [{
|
|
440
|
+
method: DwnMethodName.Delete,
|
|
441
|
+
'tag.storage': storage,
|
|
442
|
+
}]);
|
|
443
|
+
expect(reindexedTombstones.length).toBe(0);
|
|
444
|
+
}
|
|
445
|
+
});
|
|
446
|
+
it('accepts a tombstone-beaten replicated write with a valid large data stream', async () => {
|
|
447
|
+
const alice = await TestDataGenerator.generateDidKeyPersona();
|
|
448
|
+
await TestDataGenerator.installDefaultTestProtocol(dwn, alice);
|
|
449
|
+
const initialWrite = await TestDataGenerator.generateRecordsWrite({ author: alice });
|
|
450
|
+
expect(await dwn.applyReplicatedMessage(alice.did, initialWrite.message, { dataStream: DataStream.fromBytes(initialWrite.dataBytes) })).toEqual(expect.objectContaining({ kind: 'Applied' }));
|
|
451
|
+
const recordsDelete = await RecordsDelete.create({
|
|
452
|
+
recordId: initialWrite.message.recordId,
|
|
453
|
+
signer: Jws.createSigner(alice),
|
|
454
|
+
});
|
|
455
|
+
expect(await dwn.applyReplicatedMessage(alice.did, recordsDelete.message)).toEqual(expect.objectContaining({ kind: 'Applied' }));
|
|
456
|
+
await Time.minimalSleep();
|
|
457
|
+
const updateDataBytes = TestDataGenerator.randomBytes(31_000);
|
|
458
|
+
const update = await RecordsWrite.createFrom({
|
|
459
|
+
recordsWriteMessage: initialWrite.message,
|
|
460
|
+
data: updateDataBytes,
|
|
461
|
+
tags: { storage: 'data-store' },
|
|
462
|
+
signer: Jws.createSigner(alice),
|
|
463
|
+
});
|
|
464
|
+
expect(await dwn.applyReplicatedMessage(alice.did, update.message, { dataStream: DataStream.fromBytes(updateDataBytes) })).toEqual({ kind: 'Superseded' });
|
|
465
|
+
const updateCid = await Message.getCid(update.message);
|
|
466
|
+
expect(await messageStore.get(alice.did, updateCid)).toBeDefined();
|
|
467
|
+
const { messages: reindexedTombstones } = await messageStore.query(alice.did, [{
|
|
468
|
+
method: DwnMethodName.Delete,
|
|
469
|
+
'tag.storage': 'data-store',
|
|
470
|
+
}]);
|
|
471
|
+
expect(reindexedTombstones.length).toBe(1);
|
|
472
|
+
expect(await Message.getCid(reindexedTombstones[0])).toBe(await Message.getCid(recordsDelete.message));
|
|
473
|
+
});
|
|
474
|
+
it('applies the handler stale-tombstone gate on resumable-task replay', async () => {
|
|
475
|
+
const alice = await TestDataGenerator.generateDidKeyPersona();
|
|
476
|
+
await TestDataGenerator.installDefaultTestProtocol(dwn, alice);
|
|
477
|
+
const initialWrite = await TestDataGenerator.generateRecordsWrite({ author: alice });
|
|
478
|
+
expect((await dwn.processMessage(alice.did, initialWrite.message, { dataStream: initialWrite.dataStream })).status.code).toBe(202);
|
|
479
|
+
// generate the losing delete first so it is older than the tombstone admitted below
|
|
480
|
+
const staleDelete = await RecordsDelete.create({
|
|
481
|
+
recordId: initialWrite.message.recordId,
|
|
482
|
+
signer: Jws.createSigner(alice),
|
|
483
|
+
});
|
|
484
|
+
await Time.minimalSleep();
|
|
485
|
+
const recordsDelete = await RecordsDelete.create({
|
|
486
|
+
recordId: initialWrite.message.recordId,
|
|
487
|
+
signer: Jws.createSigner(alice),
|
|
488
|
+
});
|
|
489
|
+
expect((await dwn.processMessage(alice.did, recordsDelete.message)).status.code).toBe(202);
|
|
490
|
+
// replaying the beaten delete through the task path must be a no-op — the same lattice
|
|
491
|
+
// gate as admission — leaving the canonical tombstone as the record's newest message
|
|
492
|
+
await dwn['storageController'].performRecordsDelete({ tenant: alice.did, message: staleDelete.message });
|
|
493
|
+
const tombstoneCid = await Message.getCid(recordsDelete.message);
|
|
494
|
+
const staleDeleteCid = await Message.getCid(staleDelete.message);
|
|
495
|
+
expect(await messageStore.get(alice.did, tombstoneCid)).toBeDefined();
|
|
496
|
+
expect(await messageStore.get(alice.did, staleDeleteCid)).toBeUndefined();
|
|
497
|
+
});
|
|
498
|
+
it('converges competing plain tombstones to one canonical winner in either order', async () => {
|
|
499
|
+
const alice = await TestDataGenerator.generateDidKeyPersona();
|
|
500
|
+
const protocolsConfigure = await TestDataGenerator.generateProtocolsConfigure({
|
|
501
|
+
author: alice,
|
|
502
|
+
protocolDefinition: defaultTestProtocolDefinition,
|
|
503
|
+
});
|
|
504
|
+
expect((await dwn.processMessage(alice.did, protocolsConfigure.message)).status.code).toBe(202);
|
|
505
|
+
// two independently authored plain tombstones for the same record, d1 older than d2
|
|
506
|
+
const initialWrite = await TestDataGenerator.generateRecordsWrite({ author: alice });
|
|
507
|
+
const d1 = await RecordsDelete.create({
|
|
508
|
+
recordId: initialWrite.message.recordId,
|
|
509
|
+
signer: Jws.createSigner(alice),
|
|
510
|
+
});
|
|
511
|
+
await Time.minimalSleep();
|
|
512
|
+
const d2 = await RecordsDelete.create({
|
|
513
|
+
recordId: initialWrite.message.recordId,
|
|
514
|
+
signer: Jws.createSigner(alice),
|
|
515
|
+
});
|
|
516
|
+
// replica A: d1 lands first, then d2 displaces it
|
|
517
|
+
expect(await dwn.applyReplicatedMessage(alice.did, initialWrite.message, { dataStream: DataStream.fromBytes(initialWrite.dataBytes) })).toEqual(expect.objectContaining({ kind: 'Applied' }));
|
|
518
|
+
expect(await dwn.applyReplicatedMessage(alice.did, d1.message)).toEqual(expect.objectContaining({ kind: 'Applied' }));
|
|
519
|
+
expect(await dwn.applyReplicatedMessage(alice.did, d2.message)).toEqual(expect.objectContaining({ kind: 'Applied' }));
|
|
520
|
+
// replica B: d2 lands first; the beaten d1 converges as a Superseded no-op
|
|
521
|
+
const messageStoreB = new MessageStoreLevel({
|
|
522
|
+
location: 'TEST-MESSAGESTORE-DELETEWINS',
|
|
523
|
+
});
|
|
524
|
+
const dataStoreB = new DataStoreLevel({ blockstoreLocation: 'TEST-DATASTORE-DELETEWINS' });
|
|
525
|
+
const resumableTaskStoreB = new ResumableTaskStoreLevel({ location: 'TEST-RESUMABLE-TASK-STORE-DELETEWINS' });
|
|
526
|
+
const dwnB = await Dwn.create({
|
|
527
|
+
didResolver,
|
|
528
|
+
messageStore: messageStoreB,
|
|
529
|
+
dataStore: dataStoreB,
|
|
530
|
+
resumableTaskStore: resumableTaskStoreB,
|
|
531
|
+
});
|
|
532
|
+
try {
|
|
533
|
+
await messageStoreB.clear();
|
|
534
|
+
await dataStoreB.clear();
|
|
535
|
+
await resumableTaskStoreB.clear();
|
|
536
|
+
expect((await dwnB.processMessage(alice.did, protocolsConfigure.message)).status.code).toBe(202);
|
|
537
|
+
expect(await dwnB.applyReplicatedMessage(alice.did, initialWrite.message, { dataStream: DataStream.fromBytes(initialWrite.dataBytes) })).toEqual(expect.objectContaining({ kind: 'Applied' }));
|
|
538
|
+
expect(await dwnB.applyReplicatedMessage(alice.did, d2.message)).toEqual(expect.objectContaining({ kind: 'Applied' }));
|
|
539
|
+
expect(await dwnB.applyReplicatedMessage(alice.did, d1.message)).toEqual({ kind: 'Superseded' });
|
|
540
|
+
// both replicas hold d2 as the canonical tombstone
|
|
541
|
+
const d1Cid = await Message.getCid(d1.message);
|
|
542
|
+
const d2Cid = await Message.getCid(d2.message);
|
|
543
|
+
expect(await messageStore.get(alice.did, d2Cid)).toBeDefined();
|
|
544
|
+
expect(await messageStore.get(alice.did, d1Cid)).toBeUndefined();
|
|
545
|
+
expect(await messageStoreB.get(alice.did, d2Cid)).toBeDefined();
|
|
546
|
+
expect(await messageStoreB.get(alice.did, d1Cid)).toBeUndefined();
|
|
547
|
+
}
|
|
548
|
+
finally {
|
|
549
|
+
await dwnB.close();
|
|
550
|
+
}
|
|
551
|
+
});
|
|
552
|
+
it('a prune beats a newer plain delete in either order and purges descendants on both replicas', async () => {
|
|
553
|
+
const alice = await TestDataGenerator.generateDidKeyPersona();
|
|
554
|
+
const nestedProtocolDefinition = nestedProtocol;
|
|
555
|
+
const protocolsConfigure = await TestDataGenerator.generateProtocolsConfigure({
|
|
556
|
+
author: alice,
|
|
557
|
+
protocolDefinition: nestedProtocolDefinition,
|
|
558
|
+
});
|
|
559
|
+
expect((await dwn.processMessage(alice.did, protocolsConfigure.message)).status.code).toBe(202);
|
|
560
|
+
// parent foo with child bar, then a prune of foo and a NEWER plain delete of foo
|
|
561
|
+
const foo = await TestDataGenerator.generateRecordsWrite({
|
|
562
|
+
author: alice,
|
|
563
|
+
protocol: nestedProtocolDefinition.protocol,
|
|
564
|
+
protocolPath: 'foo',
|
|
565
|
+
schema: nestedProtocolDefinition.types.foo.schema,
|
|
566
|
+
dataFormat: nestedProtocolDefinition.types.foo.dataFormats[0],
|
|
567
|
+
});
|
|
568
|
+
const bar = await TestDataGenerator.generateRecordsWrite({
|
|
569
|
+
author: alice,
|
|
570
|
+
protocol: nestedProtocolDefinition.protocol,
|
|
571
|
+
protocolPath: 'foo/bar',
|
|
572
|
+
parentContextId: foo.message.contextId,
|
|
573
|
+
schema: nestedProtocolDefinition.types.bar.schema,
|
|
574
|
+
dataFormat: nestedProtocolDefinition.types.bar.dataFormats[0],
|
|
575
|
+
});
|
|
576
|
+
const prune = await RecordsDelete.create({
|
|
577
|
+
recordId: foo.message.recordId,
|
|
578
|
+
prune: true,
|
|
579
|
+
signer: Jws.createSigner(alice),
|
|
580
|
+
});
|
|
581
|
+
await Time.minimalSleep();
|
|
582
|
+
const plainDelete = await RecordsDelete.create({
|
|
583
|
+
recordId: foo.message.recordId,
|
|
584
|
+
signer: Jws.createSigner(alice),
|
|
585
|
+
});
|
|
586
|
+
// replica A: prune first, then the newer plain delete loses on class
|
|
587
|
+
expect(await dwn.applyReplicatedMessage(alice.did, foo.message, { dataStream: DataStream.fromBytes(foo.dataBytes) })).toEqual(expect.objectContaining({ kind: 'Applied' }));
|
|
588
|
+
expect(await dwn.applyReplicatedMessage(alice.did, bar.message, { dataStream: DataStream.fromBytes(bar.dataBytes) })).toEqual(expect.objectContaining({ kind: 'Applied' }));
|
|
589
|
+
expect(await dwn.applyReplicatedMessage(alice.did, prune.message)).toEqual(expect.objectContaining({ kind: 'Applied' }));
|
|
590
|
+
expect(await dwn.applyReplicatedMessage(alice.did, plainDelete.message)).toEqual({ kind: 'Superseded' });
|
|
591
|
+
// replica B: the newer plain delete lands first; the prune still wins and cascades
|
|
592
|
+
const messageStoreB = new MessageStoreLevel({
|
|
593
|
+
location: 'TEST-MESSAGESTORE-DELETEWINS',
|
|
594
|
+
});
|
|
595
|
+
const dataStoreB = new DataStoreLevel({ blockstoreLocation: 'TEST-DATASTORE-DELETEWINS' });
|
|
596
|
+
const resumableTaskStoreB = new ResumableTaskStoreLevel({ location: 'TEST-RESUMABLE-TASK-STORE-DELETEWINS' });
|
|
597
|
+
const dwnB = await Dwn.create({
|
|
598
|
+
didResolver,
|
|
599
|
+
messageStore: messageStoreB,
|
|
600
|
+
dataStore: dataStoreB,
|
|
601
|
+
resumableTaskStore: resumableTaskStoreB,
|
|
602
|
+
});
|
|
603
|
+
try {
|
|
604
|
+
await messageStoreB.clear();
|
|
605
|
+
await dataStoreB.clear();
|
|
606
|
+
await resumableTaskStoreB.clear();
|
|
607
|
+
expect((await dwnB.processMessage(alice.did, protocolsConfigure.message)).status.code).toBe(202);
|
|
608
|
+
expect(await dwnB.applyReplicatedMessage(alice.did, foo.message, { dataStream: DataStream.fromBytes(foo.dataBytes) })).toEqual(expect.objectContaining({ kind: 'Applied' }));
|
|
609
|
+
expect(await dwnB.applyReplicatedMessage(alice.did, bar.message, { dataStream: DataStream.fromBytes(bar.dataBytes) })).toEqual(expect.objectContaining({ kind: 'Applied' }));
|
|
610
|
+
expect(await dwnB.applyReplicatedMessage(alice.did, plainDelete.message)).toEqual(expect.objectContaining({ kind: 'Applied' }));
|
|
611
|
+
expect(await dwnB.applyReplicatedMessage(alice.did, prune.message)).toEqual(expect.objectContaining({ kind: 'Applied' }));
|
|
612
|
+
// the child is purged on both replicas
|
|
613
|
+
const barCid = await Message.getCid(bar.message);
|
|
614
|
+
expect(await messageStore.get(alice.did, barCid)).toBeUndefined();
|
|
615
|
+
expect(await messageStoreB.get(alice.did, barCid)).toBeUndefined();
|
|
616
|
+
}
|
|
617
|
+
finally {
|
|
618
|
+
await dwnB.close();
|
|
619
|
+
}
|
|
620
|
+
});
|
|
621
|
+
const deepNestedProtocol = {
|
|
622
|
+
protocol: 'https://example.com/deep-nested',
|
|
623
|
+
published: false,
|
|
624
|
+
types: {
|
|
625
|
+
foo: {},
|
|
626
|
+
bar: {},
|
|
627
|
+
baz: {},
|
|
628
|
+
qux: {},
|
|
629
|
+
},
|
|
630
|
+
structure: {
|
|
631
|
+
foo: {
|
|
632
|
+
bar: {
|
|
633
|
+
baz: {
|
|
634
|
+
qux: {},
|
|
635
|
+
},
|
|
636
|
+
},
|
|
637
|
+
},
|
|
638
|
+
},
|
|
639
|
+
};
|
|
640
|
+
// Installs `deepNestedProtocol` on the local DWN and builds a foo -> bar -> baz -> qux
|
|
641
|
+
// record chain under it. None of the generated records are stored; each test applies the
|
|
642
|
+
// subset it needs to shape local ancestor presence.
|
|
643
|
+
async function generateDeepNestedChain(alice) {
|
|
644
|
+
const protocolsConfigure = await TestDataGenerator.generateProtocolsConfigure({
|
|
645
|
+
author: alice,
|
|
646
|
+
protocolDefinition: deepNestedProtocol,
|
|
647
|
+
});
|
|
648
|
+
expect((await dwn.processMessage(alice.did, protocolsConfigure.message)).status.code).toBe(202);
|
|
649
|
+
const foo = await TestDataGenerator.generateRecordsWrite({
|
|
650
|
+
author: alice,
|
|
651
|
+
protocol: deepNestedProtocol.protocol,
|
|
652
|
+
protocolPath: 'foo',
|
|
653
|
+
});
|
|
654
|
+
const bar = await TestDataGenerator.generateRecordsWrite({
|
|
655
|
+
author: alice,
|
|
656
|
+
protocol: deepNestedProtocol.protocol,
|
|
657
|
+
protocolPath: 'foo/bar',
|
|
658
|
+
parentContextId: foo.message.contextId,
|
|
659
|
+
});
|
|
660
|
+
const baz = await TestDataGenerator.generateRecordsWrite({
|
|
661
|
+
author: alice,
|
|
662
|
+
protocol: deepNestedProtocol.protocol,
|
|
663
|
+
protocolPath: 'foo/bar/baz',
|
|
664
|
+
parentContextId: bar.message.contextId,
|
|
665
|
+
});
|
|
666
|
+
const qux = await TestDataGenerator.generateRecordsWrite({
|
|
667
|
+
author: alice,
|
|
668
|
+
protocol: deepNestedProtocol.protocol,
|
|
669
|
+
protocolPath: 'foo/bar/baz/qux',
|
|
670
|
+
parentContextId: baz.message.contextId,
|
|
671
|
+
});
|
|
672
|
+
return { foo, bar, baz, qux };
|
|
673
|
+
}
|
|
674
|
+
it('emits one ref per missing contextId segment in a single Incomplete', async () => {
|
|
675
|
+
const alice = await TestDataGenerator.generateDidKeyPersona();
|
|
676
|
+
const { foo, bar, baz, qux } = await generateDeepNestedChain(alice);
|
|
677
|
+
// no ancestors are present locally: a single apply names every missing segment,
|
|
678
|
+
// root-first, with the immediate parent keeping its existing Parent ref shape
|
|
679
|
+
const result = await dwn.applyReplicatedMessage(alice.did, qux.message, { dataStream: DataStream.fromBytes(qux.dataBytes) });
|
|
680
|
+
expect(result).toEqual({
|
|
681
|
+
kind: 'Incomplete',
|
|
682
|
+
missing: [
|
|
683
|
+
{ type: 'Ancestor', recordId: foo.message.recordId, protocol: deepNestedProtocol.protocol },
|
|
684
|
+
{ type: 'Ancestor', recordId: bar.message.recordId, protocol: deepNestedProtocol.protocol },
|
|
685
|
+
{ type: 'Parent', recordId: baz.message.recordId, protocol: deepNestedProtocol.protocol },
|
|
686
|
+
],
|
|
687
|
+
});
|
|
688
|
+
});
|
|
689
|
+
it('lists only the locally-missing ancestor segments when part of the chain is present', async () => {
|
|
690
|
+
const alice = await TestDataGenerator.generateDidKeyPersona();
|
|
691
|
+
const { foo, bar, baz, qux } = await generateDeepNestedChain(alice);
|
|
692
|
+
// the root of the chain is already present locally
|
|
693
|
+
expect(await dwn.applyReplicatedMessage(alice.did, foo.message, { dataStream: DataStream.fromBytes(foo.dataBytes) })).toEqual(expect.objectContaining({ kind: 'Applied' }));
|
|
694
|
+
const result = await dwn.applyReplicatedMessage(alice.did, qux.message, { dataStream: DataStream.fromBytes(qux.dataBytes) });
|
|
695
|
+
expect(result).toEqual({
|
|
696
|
+
kind: 'Incomplete',
|
|
697
|
+
missing: [
|
|
698
|
+
{ type: 'Ancestor', recordId: bar.message.recordId, protocol: deepNestedProtocol.protocol },
|
|
699
|
+
{ type: 'Parent', recordId: baz.message.recordId, protocol: deepNestedProtocol.protocol },
|
|
700
|
+
],
|
|
701
|
+
});
|
|
702
|
+
});
|
|
703
|
+
it('resolves a deep ancestor chain in a bounded number of fetch-and-retry passes', async () => {
|
|
704
|
+
const alice = await TestDataGenerator.generateDidKeyPersona();
|
|
705
|
+
const { foo, bar, baz, qux } = await generateDeepNestedChain(alice);
|
|
706
|
+
const chainWritesByRecordId = new Map([
|
|
707
|
+
[foo.message.recordId, foo],
|
|
708
|
+
[bar.message.recordId, bar],
|
|
709
|
+
[baz.message.recordId, baz],
|
|
710
|
+
]);
|
|
711
|
+
// engine loop: apply the deepest record, fetch whatever the Incomplete names, retry
|
|
712
|
+
let applyAttempts = 0;
|
|
713
|
+
let result;
|
|
714
|
+
do {
|
|
715
|
+
applyAttempts += 1;
|
|
716
|
+
result = await dwn.applyReplicatedMessage(alice.did, qux.message, { dataStream: DataStream.fromBytes(qux.dataBytes) });
|
|
717
|
+
if (result.kind === 'Incomplete') {
|
|
718
|
+
for (const ref of result.missing) {
|
|
719
|
+
if (ref.type !== 'Ancestor' && ref.type !== 'Parent') {
|
|
720
|
+
throw new Error(`Incomplete named an unexpected dependency ref type: ${ref.type}`);
|
|
721
|
+
}
|
|
722
|
+
const ancestor = chainWritesByRecordId.get(ref.recordId);
|
|
723
|
+
if (ancestor === undefined) {
|
|
724
|
+
throw new Error(`Incomplete named an unexpected ancestor: ${ref.recordId}`);
|
|
725
|
+
}
|
|
726
|
+
expect(await dwn.applyReplicatedMessage(alice.did, ancestor.message, { dataStream: DataStream.fromBytes(ancestor.dataBytes) })).toEqual(expect.objectContaining({ kind: 'Applied' }));
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
} while (result.kind === 'Incomplete' && applyAttempts < 4);
|
|
730
|
+
// the full 4-level chain resolves in two passes — one Incomplete naming all three
|
|
731
|
+
// ancestors, then a clean apply — never one round trip per ancestry level
|
|
732
|
+
expect(result).toEqual(expect.objectContaining({ kind: 'Applied' }));
|
|
733
|
+
expect(applyAttempts).toBe(2);
|
|
734
|
+
});
|
|
266
735
|
});
|
|
267
736
|
});
|
|
268
737
|
}
|