@enbox/dwn-sdk-js 0.3.9 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -4
- package/dist/browser.mjs +11 -11
- package/dist/browser.mjs.map +4 -4
- package/dist/esm/generated/precompiled-validators.js +783 -1206
- package/dist/esm/generated/precompiled-validators.js.map +1 -1
- package/dist/esm/src/core/dwn-constant.js +5 -0
- package/dist/esm/src/core/dwn-constant.js.map +1 -1
- package/dist/esm/src/core/dwn-error.js +13 -7
- package/dist/esm/src/core/dwn-error.js.map +1 -1
- package/dist/esm/src/core/grant-authorization.js +9 -18
- package/dist/esm/src/core/grant-authorization.js.map +1 -1
- package/dist/esm/src/core/message-reply.js.map +1 -1
- package/dist/esm/src/core/messages-grant-authorization.js +28 -61
- package/dist/esm/src/core/messages-grant-authorization.js.map +1 -1
- package/dist/esm/src/core/protocol-authorization-action.js +25 -27
- package/dist/esm/src/core/protocol-authorization-action.js.map +1 -1
- package/dist/esm/src/core/protocol-authorization-validation.js +31 -69
- package/dist/esm/src/core/protocol-authorization-validation.js.map +1 -1
- package/dist/esm/src/core/protocol-authorization.js +44 -118
- package/dist/esm/src/core/protocol-authorization.js.map +1 -1
- package/dist/esm/src/core/protocols-grant-authorization.js +5 -5
- package/dist/esm/src/core/protocols-grant-authorization.js.map +1 -1
- package/dist/esm/src/core/recording-validation-state-reader.js +84 -0
- package/dist/esm/src/core/recording-validation-state-reader.js.map +1 -0
- package/dist/esm/src/core/records-grant-authorization.js +11 -11
- package/dist/esm/src/core/records-grant-authorization.js.map +1 -1
- package/dist/esm/src/core/replication-apply.js +295 -0
- package/dist/esm/src/core/replication-apply.js.map +1 -0
- package/dist/esm/src/core/resumable-task-manager.js +5 -4
- package/dist/esm/src/core/resumable-task-manager.js.map +1 -1
- package/dist/esm/src/core/validation-state-reader.js +237 -0
- package/dist/esm/src/core/validation-state-reader.js.map +1 -0
- package/dist/esm/src/dwn.js +261 -16
- package/dist/esm/src/dwn.js.map +1 -1
- package/dist/esm/src/enums/dwn-interface-method.js +0 -1
- package/dist/esm/src/enums/dwn-interface-method.js.map +1 -1
- package/dist/esm/src/event-stream/durable-event-log.js +365 -0
- package/dist/esm/src/event-stream/durable-event-log.js.map +1 -0
- package/dist/esm/src/event-stream/event-emitter-wake-publisher.js +25 -0
- package/dist/esm/src/event-stream/event-emitter-wake-publisher.js.map +1 -0
- package/dist/esm/src/handlers/messages-query.js +159 -0
- package/dist/esm/src/handlers/messages-query.js.map +1 -0
- package/dist/esm/src/handlers/messages-read.js +5 -5
- package/dist/esm/src/handlers/messages-read.js.map +1 -1
- package/dist/esm/src/handlers/messages-subscribe.js +8 -8
- package/dist/esm/src/handlers/messages-subscribe.js.map +1 -1
- package/dist/esm/src/handlers/protocols-configure.js +30 -49
- package/dist/esm/src/handlers/protocols-configure.js.map +1 -1
- package/dist/esm/src/handlers/protocols-query.js +1 -1
- package/dist/esm/src/handlers/protocols-query.js.map +1 -1
- package/dist/esm/src/handlers/records-count.js +20 -11
- package/dist/esm/src/handlers/records-count.js.map +1 -1
- package/dist/esm/src/handlers/records-delete.js +20 -16
- package/dist/esm/src/handlers/records-delete.js.map +1 -1
- package/dist/esm/src/handlers/records-query.js +35 -11
- package/dist/esm/src/handlers/records-query.js.map +1 -1
- package/dist/esm/src/handlers/records-read.js +52 -42
- package/dist/esm/src/handlers/records-read.js.map +1 -1
- package/dist/esm/src/handlers/records-subscribe.js +107 -11
- package/dist/esm/src/handlers/records-subscribe.js.map +1 -1
- package/dist/esm/src/handlers/records-write.js +62 -116
- package/dist/esm/src/handlers/records-write.js.map +1 -1
- package/dist/esm/src/index.js +7 -8
- package/dist/esm/src/index.js.map +1 -1
- package/dist/esm/src/interfaces/messages-query.js +49 -0
- package/dist/esm/src/interfaces/messages-query.js.map +1 -0
- package/dist/esm/src/interfaces/protocols-configure.js +7 -3
- package/dist/esm/src/interfaces/protocols-configure.js.map +1 -1
- package/dist/esm/src/interfaces/protocols-query.js +3 -4
- package/dist/esm/src/interfaces/protocols-query.js.map +1 -1
- package/dist/esm/src/interfaces/records-count.js +4 -3
- package/dist/esm/src/interfaces/records-count.js.map +1 -1
- package/dist/esm/src/interfaces/records-delete.js +21 -4
- package/dist/esm/src/interfaces/records-delete.js.map +1 -1
- package/dist/esm/src/interfaces/records-query.js +4 -3
- package/dist/esm/src/interfaces/records-query.js.map +1 -1
- package/dist/esm/src/interfaces/records-read.js +3 -3
- package/dist/esm/src/interfaces/records-read.js.map +1 -1
- package/dist/esm/src/interfaces/records-subscribe.js +4 -3
- package/dist/esm/src/interfaces/records-subscribe.js.map +1 -1
- package/dist/esm/src/interfaces/records-write.js +27 -13
- package/dist/esm/src/interfaces/records-write.js.map +1 -1
- package/dist/esm/src/protocols/permissions.js +27 -34
- package/dist/esm/src/protocols/permissions.js.map +1 -1
- package/dist/esm/src/store/index-level.js +24 -9
- package/dist/esm/src/store/index-level.js.map +1 -1
- package/dist/esm/src/store/level-wrapper.js +7 -0
- package/dist/esm/src/store/level-wrapper.js.map +1 -1
- package/dist/esm/src/store/message-store-level.js +536 -42
- package/dist/esm/src/store/message-store-level.js.map +1 -1
- package/dist/esm/src/store/storage-controller.js +58 -49
- package/dist/esm/src/store/storage-controller.js.map +1 -1
- package/dist/esm/src/types/message-types.js.map +1 -1
- package/dist/esm/src/types/validation-state-reader.js +2 -0
- package/dist/esm/src/types/validation-state-reader.js.map +1 -0
- package/dist/esm/src/utils/messages.js +17 -0
- package/dist/esm/src/utils/messages.js.map +1 -1
- package/dist/esm/src/utils/record-limit-occupancy.js +244 -0
- package/dist/esm/src/utils/record-limit-occupancy.js.map +1 -0
- package/dist/esm/src/utils/records.js +50 -14
- package/dist/esm/src/utils/records.js.map +1 -1
- package/dist/esm/src/utils/replication.js +85 -0
- package/dist/esm/src/utils/replication.js.map +1 -0
- package/dist/esm/tests/core/grant-authorization.spec.js +4 -4
- package/dist/esm/tests/core/grant-authorization.spec.js.map +1 -1
- package/dist/esm/tests/core/process-message-parity.spec.js +222 -0
- package/dist/esm/tests/core/process-message-parity.spec.js.map +1 -0
- package/dist/esm/tests/core/protocol-authorization.spec.js +5 -2
- package/dist/esm/tests/core/protocol-authorization.spec.js.map +1 -1
- package/dist/esm/tests/core/records-grant-authorization.spec.js +5 -5
- package/dist/esm/tests/core/records-grant-authorization.spec.js.map +1 -1
- package/dist/esm/tests/core/replication-apply.spec.js +274 -0
- package/dist/esm/tests/core/replication-apply.spec.js.map +1 -0
- package/dist/esm/tests/core/replication-replay-property.spec.js +350 -0
- package/dist/esm/tests/core/replication-replay-property.spec.js.map +1 -0
- package/dist/esm/tests/core/validation-read-closure.spec.js +469 -0
- package/dist/esm/tests/core/validation-read-closure.spec.js.map +1 -0
- package/dist/esm/tests/core/validation-state-reader.spec.js +716 -0
- package/dist/esm/tests/core/validation-state-reader.spec.js.map +1 -0
- package/dist/esm/tests/durable-event-log.spec.js +373 -0
- package/dist/esm/tests/durable-event-log.spec.js.map +1 -0
- package/dist/esm/tests/dwn.spec.js +620 -14
- package/dist/esm/tests/dwn.spec.js.map +1 -1
- package/dist/esm/tests/features/author-delegated-grant.spec.js +9 -6
- package/dist/esm/tests/features/author-delegated-grant.spec.js.map +1 -1
- package/dist/esm/tests/features/owner-delegated-grant.spec.js +1 -4
- package/dist/esm/tests/features/owner-delegated-grant.spec.js.map +1 -1
- package/dist/esm/tests/features/owner-signature.spec.js +1 -4
- package/dist/esm/tests/features/owner-signature.spec.js.map +1 -1
- package/dist/esm/tests/features/permissions.spec.js +165 -4
- package/dist/esm/tests/features/permissions.spec.js.map +1 -1
- package/dist/esm/tests/features/protocol-composition.spec.js +8 -11
- package/dist/esm/tests/features/protocol-composition.spec.js.map +1 -1
- package/dist/esm/tests/features/protocol-create-action.spec.js +1 -4
- package/dist/esm/tests/features/protocol-create-action.spec.js.map +1 -1
- package/dist/esm/tests/features/protocol-delete-action.spec.js +3 -5
- package/dist/esm/tests/features/protocol-delete-action.spec.js.map +1 -1
- package/dist/esm/tests/features/protocol-update-action.spec.js +3 -6
- package/dist/esm/tests/features/protocol-update-action.spec.js.map +1 -1
- package/dist/esm/tests/features/records-delivery.spec.js +1 -4
- package/dist/esm/tests/features/records-delivery.spec.js.map +1 -1
- package/dist/esm/tests/features/records-immutable.spec.js +1 -4
- package/dist/esm/tests/features/records-immutable.spec.js.map +1 -1
- package/dist/esm/tests/features/records-nested-query-scope.spec.js +281 -0
- package/dist/esm/tests/features/records-nested-query-scope.spec.js.map +1 -0
- package/dist/esm/tests/features/records-prune-cross-protocol.spec.js +3 -7
- package/dist/esm/tests/features/records-prune-cross-protocol.spec.js.map +1 -1
- package/dist/esm/tests/features/records-prune.spec.js +11 -22
- package/dist/esm/tests/features/records-prune.spec.js.map +1 -1
- package/dist/esm/tests/features/records-record-limit.spec.js +441 -231
- package/dist/esm/tests/features/records-record-limit.spec.js.map +1 -1
- package/dist/esm/tests/features/records-squash.spec.js +6 -4
- package/dist/esm/tests/features/records-squash.spec.js.map +1 -1
- package/dist/esm/tests/features/records-tags.spec.js +1 -4
- package/dist/esm/tests/features/records-tags.spec.js.map +1 -1
- package/dist/esm/tests/features/resumable-tasks.spec.js +3 -5
- package/dist/esm/tests/features/resumable-tasks.spec.js.map +1 -1
- package/dist/esm/tests/fuzz/message-store.fuzz.spec.js +1 -2
- package/dist/esm/tests/fuzz/message-store.fuzz.spec.js.map +1 -1
- package/dist/esm/tests/fuzz/process-message.fuzz.spec.js +2 -4
- package/dist/esm/tests/fuzz/process-message.fuzz.spec.js.map +1 -1
- package/dist/esm/tests/fuzz/schema-validation.fuzz.spec.js +1 -1
- package/dist/esm/tests/fuzz/schema-validation.fuzz.spec.js.map +1 -1
- package/dist/esm/tests/handlers/messages-query.spec.js +246 -0
- package/dist/esm/tests/handlers/messages-query.spec.js.map +1 -0
- package/dist/esm/tests/handlers/messages-read.spec.js +2 -5
- package/dist/esm/tests/handlers/messages-read.spec.js.map +1 -1
- package/dist/esm/tests/handlers/messages-subscribe.spec.js +3 -14
- package/dist/esm/tests/handlers/messages-subscribe.spec.js.map +1 -1
- package/dist/esm/tests/handlers/protocols-configure.spec.js +27 -26
- package/dist/esm/tests/handlers/protocols-configure.spec.js.map +1 -1
- package/dist/esm/tests/handlers/protocols-query.spec.js +1 -4
- package/dist/esm/tests/handlers/protocols-query.spec.js.map +1 -1
- package/dist/esm/tests/handlers/records-count.spec.js +1 -4
- package/dist/esm/tests/handlers/records-count.spec.js.map +1 -1
- package/dist/esm/tests/handlers/records-delete.spec.js +312 -30
- package/dist/esm/tests/handlers/records-delete.spec.js.map +1 -1
- package/dist/esm/tests/handlers/records-query.spec.js +32 -9
- package/dist/esm/tests/handlers/records-query.spec.js.map +1 -1
- package/dist/esm/tests/handlers/records-read.spec.js +4 -4
- package/dist/esm/tests/handlers/records-read.spec.js.map +1 -1
- package/dist/esm/tests/handlers/records-subscribe.spec.js +33 -14
- package/dist/esm/tests/handlers/records-subscribe.spec.js.map +1 -1
- package/dist/esm/tests/handlers/records-write.spec.js +84 -38
- package/dist/esm/tests/handlers/records-write.spec.js.map +1 -1
- package/dist/esm/tests/interfaces/records-delete.spec.js +69 -2
- package/dist/esm/tests/interfaces/records-delete.spec.js.map +1 -1
- package/dist/esm/tests/interfaces/records-write.spec.js +4 -3
- package/dist/esm/tests/interfaces/records-write.spec.js.map +1 -1
- package/dist/esm/tests/protocols/permissions.spec.js +55 -6
- package/dist/esm/tests/protocols/permissions.spec.js.map +1 -1
- package/dist/esm/tests/scenarios/aggregator.spec.js +1 -4
- package/dist/esm/tests/scenarios/aggregator.spec.js.map +1 -1
- package/dist/esm/tests/scenarios/deleted-record.spec.js +1 -4
- package/dist/esm/tests/scenarios/deleted-record.spec.js.map +1 -1
- package/dist/esm/tests/scenarios/end-to-end-tests.spec.js +1 -4
- package/dist/esm/tests/scenarios/end-to-end-tests.spec.js.map +1 -1
- package/dist/esm/tests/scenarios/nested-roles.spec.js +1 -4
- package/dist/esm/tests/scenarios/nested-roles.spec.js.map +1 -1
- package/dist/esm/tests/scenarios/subscriptions.spec.js +1 -4
- package/dist/esm/tests/scenarios/subscriptions.spec.js.map +1 -1
- package/dist/esm/tests/store/message-store-level.spec.js +361 -5
- package/dist/esm/tests/store/message-store-level.spec.js.map +1 -1
- package/dist/esm/tests/store/message-store.spec.js +60 -0
- package/dist/esm/tests/store/message-store.spec.js.map +1 -1
- package/dist/esm/tests/test-event-stream.js +7 -3
- package/dist/esm/tests/test-event-stream.js.map +1 -1
- package/dist/esm/tests/test-stores.js +19 -9
- package/dist/esm/tests/test-stores.js.map +1 -1
- package/dist/esm/tests/test-suite.js +4 -4
- package/dist/esm/tests/test-suite.js.map +1 -1
- package/dist/esm/tests/utils/test-data-generator.js +25 -0
- package/dist/esm/tests/utils/test-data-generator.js.map +1 -1
- package/dist/esm/tests/utils/test-stub-generator.js.map +1 -1
- package/dist/esm/tests/utils/test-validation-state-reader.js +16 -0
- package/dist/esm/tests/utils/test-validation-state-reader.js.map +1 -0
- package/dist/types/generated/precompiled-validators.d.ts +6 -6
- package/dist/types/generated/precompiled-validators.d.ts.map +1 -1
- package/dist/types/src/core/core-protocol.d.ts +3 -3
- package/dist/types/src/core/core-protocol.d.ts.map +1 -1
- package/dist/types/src/core/dwn-constant.d.ts +5 -0
- package/dist/types/src/core/dwn-constant.d.ts.map +1 -1
- package/dist/types/src/core/dwn-error.d.ts +13 -7
- package/dist/types/src/core/dwn-error.d.ts.map +1 -1
- package/dist/types/src/core/grant-authorization.d.ts +5 -5
- package/dist/types/src/core/grant-authorization.d.ts.map +1 -1
- package/dist/types/src/core/message-reply.d.ts +5 -4
- package/dist/types/src/core/message-reply.d.ts.map +1 -1
- package/dist/types/src/core/messages-grant-authorization.d.ts +12 -15
- package/dist/types/src/core/messages-grant-authorization.d.ts.map +1 -1
- package/dist/types/src/core/protocol-authorization-action.d.ts +4 -5
- package/dist/types/src/core/protocol-authorization-action.d.ts.map +1 -1
- package/dist/types/src/core/protocol-authorization-validation.d.ts +13 -16
- package/dist/types/src/core/protocol-authorization-validation.d.ts.map +1 -1
- package/dist/types/src/core/protocol-authorization.d.ts +8 -33
- package/dist/types/src/core/protocol-authorization.d.ts.map +1 -1
- package/dist/types/src/core/protocols-grant-authorization.d.ts +4 -4
- package/dist/types/src/core/protocols-grant-authorization.d.ts.map +1 -1
- package/dist/types/src/core/recording-validation-state-reader.d.ts +75 -0
- package/dist/types/src/core/recording-validation-state-reader.d.ts.map +1 -0
- package/dist/types/src/core/records-grant-authorization.d.ts +8 -8
- package/dist/types/src/core/records-grant-authorization.d.ts.map +1 -1
- package/dist/types/src/core/replication-apply.d.ts +129 -0
- package/dist/types/src/core/replication-apply.d.ts.map +1 -0
- package/dist/types/src/core/resumable-task-manager.d.ts +1 -1
- package/dist/types/src/core/resumable-task-manager.d.ts.map +1 -1
- package/dist/types/src/core/validation-state-reader.d.ts +79 -0
- package/dist/types/src/core/validation-state-reader.d.ts.map +1 -0
- package/dist/types/src/dwn.d.ts +47 -13
- package/dist/types/src/dwn.d.ts.map +1 -1
- package/dist/types/src/enums/dwn-interface-method.d.ts +0 -1
- package/dist/types/src/enums/dwn-interface-method.d.ts.map +1 -1
- package/dist/types/src/event-stream/durable-event-log.d.ts +69 -0
- package/dist/types/src/event-stream/durable-event-log.d.ts.map +1 -0
- package/dist/types/src/event-stream/event-emitter-wake-publisher.d.ts +13 -0
- package/dist/types/src/event-stream/event-emitter-wake-publisher.d.ts.map +1 -0
- package/dist/types/src/handlers/messages-query.d.ts +20 -0
- package/dist/types/src/handlers/messages-query.d.ts.map +1 -0
- package/dist/types/src/handlers/messages-read.d.ts +1 -1
- package/dist/types/src/handlers/messages-read.d.ts.map +1 -1
- package/dist/types/src/handlers/messages-subscribe.d.ts.map +1 -1
- package/dist/types/src/handlers/protocols-configure.d.ts +0 -5
- package/dist/types/src/handlers/protocols-configure.d.ts.map +1 -1
- package/dist/types/src/handlers/records-count.d.ts +2 -1
- package/dist/types/src/handlers/records-count.d.ts.map +1 -1
- package/dist/types/src/handlers/records-delete.d.ts +2 -2
- package/dist/types/src/handlers/records-delete.d.ts.map +1 -1
- package/dist/types/src/handlers/records-query.d.ts +1 -1
- package/dist/types/src/handlers/records-query.d.ts.map +1 -1
- package/dist/types/src/handlers/records-read.d.ts +2 -1
- package/dist/types/src/handlers/records-read.d.ts.map +1 -1
- package/dist/types/src/handlers/records-subscribe.d.ts +4 -5
- package/dist/types/src/handlers/records-subscribe.d.ts.map +1 -1
- package/dist/types/src/handlers/records-write.d.ts +3 -11
- package/dist/types/src/handlers/records-write.d.ts.map +1 -1
- package/dist/types/src/index.d.ts +16 -18
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/interfaces/messages-query.d.ts +23 -0
- package/dist/types/src/interfaces/messages-query.d.ts.map +1 -0
- package/dist/types/src/interfaces/protocols-configure.d.ts +3 -3
- package/dist/types/src/interfaces/protocols-configure.d.ts.map +1 -1
- package/dist/types/src/interfaces/protocols-query.d.ts +2 -2
- package/dist/types/src/interfaces/protocols-query.d.ts.map +1 -1
- package/dist/types/src/interfaces/records-count.d.ts +3 -3
- package/dist/types/src/interfaces/records-count.d.ts.map +1 -1
- package/dist/types/src/interfaces/records-delete.d.ts +11 -3
- package/dist/types/src/interfaces/records-delete.d.ts.map +1 -1
- package/dist/types/src/interfaces/records-query.d.ts +3 -3
- package/dist/types/src/interfaces/records-query.d.ts.map +1 -1
- package/dist/types/src/interfaces/records-read.d.ts +3 -3
- package/dist/types/src/interfaces/records-read.d.ts.map +1 -1
- package/dist/types/src/interfaces/records-subscribe.d.ts +3 -3
- package/dist/types/src/interfaces/records-subscribe.d.ts.map +1 -1
- package/dist/types/src/interfaces/records-write.d.ts +15 -7
- package/dist/types/src/interfaces/records-write.d.ts.map +1 -1
- package/dist/types/src/protocols/permissions.d.ts +9 -12
- package/dist/types/src/protocols/permissions.d.ts.map +1 -1
- package/dist/types/src/store/index-level.d.ts +10 -1
- package/dist/types/src/store/index-level.d.ts.map +1 -1
- package/dist/types/src/store/level-wrapper.d.ts +5 -0
- package/dist/types/src/store/level-wrapper.d.ts.map +1 -1
- package/dist/types/src/store/message-store-level.d.ts +94 -14
- package/dist/types/src/store/message-store-level.d.ts.map +1 -1
- package/dist/types/src/store/storage-controller.d.ts +17 -14
- package/dist/types/src/store/storage-controller.d.ts.map +1 -1
- package/dist/types/src/types/message-store.d.ts +29 -1
- package/dist/types/src/types/message-store.d.ts.map +1 -1
- package/dist/types/src/types/message-types.d.ts +2 -0
- package/dist/types/src/types/message-types.d.ts.map +1 -1
- package/dist/types/src/types/messages-types.d.ts +21 -55
- package/dist/types/src/types/messages-types.d.ts.map +1 -1
- package/dist/types/src/types/method-handler.d.ts +2 -2
- package/dist/types/src/types/method-handler.d.ts.map +1 -1
- package/dist/types/src/types/permission-types.d.ts +1 -1
- package/dist/types/src/types/subscriptions.d.ts +50 -39
- package/dist/types/src/types/subscriptions.d.ts.map +1 -1
- package/dist/types/src/types/validation-state-reader.d.ts +116 -0
- package/dist/types/src/types/validation-state-reader.d.ts.map +1 -0
- package/dist/types/src/utils/messages.d.ts +10 -0
- package/dist/types/src/utils/messages.d.ts.map +1 -1
- package/dist/types/src/utils/record-limit-occupancy.d.ts +40 -0
- package/dist/types/src/utils/record-limit-occupancy.d.ts.map +1 -0
- package/dist/types/src/utils/records.d.ts +25 -3
- package/dist/types/src/utils/records.d.ts.map +1 -1
- package/dist/types/src/utils/replication.d.ts +22 -0
- package/dist/types/src/utils/replication.d.ts.map +1 -0
- package/dist/types/tests/core/process-message-parity.spec.d.ts +2 -0
- package/dist/types/tests/core/process-message-parity.spec.d.ts.map +1 -0
- package/dist/types/tests/core/replication-apply.spec.d.ts +2 -0
- package/dist/types/tests/core/replication-apply.spec.d.ts.map +1 -0
- package/dist/types/tests/core/replication-replay-property.spec.d.ts +2 -0
- package/dist/types/tests/core/replication-replay-property.spec.d.ts.map +1 -0
- package/dist/types/tests/core/validation-read-closure.spec.d.ts +2 -0
- package/dist/types/tests/core/validation-read-closure.spec.d.ts.map +1 -0
- package/dist/types/tests/core/validation-state-reader.spec.d.ts +2 -0
- package/dist/types/tests/core/validation-state-reader.spec.d.ts.map +1 -0
- package/dist/types/tests/durable-event-log.spec.d.ts +2 -0
- package/dist/types/tests/durable-event-log.spec.d.ts.map +1 -0
- package/dist/types/tests/dwn.spec.d.ts.map +1 -1
- package/dist/types/tests/features/author-delegated-grant.spec.d.ts.map +1 -1
- package/dist/types/tests/features/owner-delegated-grant.spec.d.ts.map +1 -1
- package/dist/types/tests/features/owner-signature.spec.d.ts.map +1 -1
- package/dist/types/tests/features/permissions.spec.d.ts.map +1 -1
- package/dist/types/tests/features/protocol-composition.spec.d.ts.map +1 -1
- package/dist/types/tests/features/protocol-create-action.spec.d.ts.map +1 -1
- package/dist/types/tests/features/protocol-delete-action.spec.d.ts.map +1 -1
- package/dist/types/tests/features/protocol-update-action.spec.d.ts.map +1 -1
- package/dist/types/tests/features/records-delivery.spec.d.ts.map +1 -1
- package/dist/types/tests/features/records-immutable.spec.d.ts.map +1 -1
- package/dist/types/tests/features/records-nested-query-scope.spec.d.ts +2 -0
- package/dist/types/tests/features/records-nested-query-scope.spec.d.ts.map +1 -0
- package/dist/types/tests/features/records-prune-cross-protocol.spec.d.ts.map +1 -1
- package/dist/types/tests/features/records-prune.spec.d.ts.map +1 -1
- package/dist/types/tests/features/records-record-limit.spec.d.ts.map +1 -1
- package/dist/types/tests/features/records-squash.spec.d.ts.map +1 -1
- package/dist/types/tests/features/records-tags.spec.d.ts.map +1 -1
- package/dist/types/tests/features/resumable-tasks.spec.d.ts.map +1 -1
- package/dist/types/tests/handlers/messages-query.spec.d.ts +2 -0
- package/dist/types/tests/handlers/messages-query.spec.d.ts.map +1 -0
- package/dist/types/tests/handlers/messages-read.spec.d.ts.map +1 -1
- package/dist/types/tests/handlers/messages-subscribe.spec.d.ts.map +1 -1
- package/dist/types/tests/handlers/protocols-configure.spec.d.ts.map +1 -1
- package/dist/types/tests/handlers/protocols-query.spec.d.ts.map +1 -1
- package/dist/types/tests/handlers/records-count.spec.d.ts.map +1 -1
- package/dist/types/tests/handlers/records-delete.spec.d.ts.map +1 -1
- package/dist/types/tests/handlers/records-query.spec.d.ts.map +1 -1
- package/dist/types/tests/handlers/records-read.spec.d.ts.map +1 -1
- package/dist/types/tests/handlers/records-subscribe.spec.d.ts.map +1 -1
- package/dist/types/tests/handlers/records-write.spec.d.ts.map +1 -1
- package/dist/types/tests/scenarios/deleted-record.spec.d.ts.map +1 -1
- package/dist/types/tests/scenarios/end-to-end-tests.spec.d.ts.map +1 -1
- package/dist/types/tests/scenarios/nested-roles.spec.d.ts.map +1 -1
- package/dist/types/tests/scenarios/subscriptions.spec.d.ts.map +1 -1
- package/dist/types/tests/store/message-store.spec.d.ts.map +1 -1
- package/dist/types/tests/test-event-stream.d.ts +1 -1
- package/dist/types/tests/test-event-stream.d.ts.map +1 -1
- package/dist/types/tests/test-stores.d.ts +5 -4
- package/dist/types/tests/test-stores.d.ts.map +1 -1
- package/dist/types/tests/test-suite.d.ts +1 -2
- package/dist/types/tests/test-suite.d.ts.map +1 -1
- package/dist/types/tests/utils/test-data-generator.d.ts +20 -1
- package/dist/types/tests/utils/test-data-generator.d.ts.map +1 -1
- package/dist/types/tests/utils/test-validation-state-reader.d.ts +15 -0
- package/dist/types/tests/utils/test-validation-state-reader.d.ts.map +1 -0
- package/package.json +2 -2
- package/src/core/core-protocol.ts +3 -3
- package/src/core/dwn-constant.ts +7 -1
- package/src/core/dwn-error.ts +13 -7
- package/src/core/grant-authorization.ts +11 -20
- package/src/core/message-reply.ts +6 -5
- package/src/core/messages-grant-authorization.ts +37 -100
- package/src/core/protocol-authorization-action.ts +29 -38
- package/src/core/protocol-authorization-validation.ts +41 -98
- package/src/core/protocol-authorization.ts +56 -202
- package/src/core/protocols-grant-authorization.ts +9 -9
- package/src/core/recording-validation-state-reader.ts +130 -0
- package/src/core/records-grant-authorization.ts +16 -16
- package/src/core/replication-apply.ts +412 -0
- package/src/core/resumable-task-manager.ts +10 -8
- package/src/core/validation-state-reader.ts +350 -0
- package/src/dwn.ts +417 -30
- package/src/enums/dwn-interface-method.ts +0 -1
- package/src/event-stream/durable-event-log.ts +509 -0
- package/src/event-stream/event-emitter-wake-publisher.ts +34 -0
- package/src/handlers/messages-query.ts +203 -0
- package/src/handlers/messages-read.ts +9 -10
- package/src/handlers/messages-subscribe.ts +12 -13
- package/src/handlers/protocols-configure.ts +37 -58
- package/src/handlers/protocols-query.ts +1 -1
- package/src/handlers/records-count.ts +24 -17
- package/src/handlers/records-delete.ts +29 -27
- package/src/handlers/records-query.ts +38 -17
- package/src/handlers/records-read.ts +63 -50
- package/src/handlers/records-subscribe.ts +132 -19
- package/src/handlers/records-write.ts +77 -168
- package/src/index.ts +16 -20
- package/src/interfaces/messages-query.ts +70 -0
- package/src/interfaces/protocols-configure.ts +12 -4
- package/src/interfaces/protocols-query.ts +4 -5
- package/src/interfaces/records-count.ts +9 -4
- package/src/interfaces/records-delete.ts +25 -5
- package/src/interfaces/records-query.ts +9 -4
- package/src/interfaces/records-read.ts +4 -4
- package/src/interfaces/records-subscribe.ts +9 -4
- package/src/interfaces/records-write.ts +41 -13
- package/src/protocols/permissions.ts +32 -52
- package/src/store/index-level.ts +30 -9
- package/src/store/level-wrapper.ts +9 -1
- package/src/store/message-store-level.ts +757 -47
- package/src/store/storage-controller.ts +74 -63
- package/src/types/message-store.ts +45 -2
- package/src/types/message-types.ts +3 -1
- package/src/types/messages-types.ts +26 -65
- package/src/types/method-handler.ts +3 -3
- package/src/types/permission-types.ts +1 -1
- package/src/types/subscriptions.ts +53 -42
- package/src/types/validation-state-reader.ts +127 -0
- package/src/utils/messages.ts +25 -1
- package/src/utils/record-limit-occupancy.ts +377 -0
- package/src/utils/records.ts +69 -13
- package/src/utils/replication.ts +122 -0
- package/dist/esm/src/core/record-chain.js +0 -64
- package/dist/esm/src/core/record-chain.js.map +0 -1
- package/dist/esm/src/event-stream/event-emitter-event-log.js +0 -334
- package/dist/esm/src/event-stream/event-emitter-event-log.js.map +0 -1
- package/dist/esm/src/handlers/messages-sync.js +0 -581
- package/dist/esm/src/handlers/messages-sync.js.map +0 -1
- package/dist/esm/src/interfaces/messages-sync.js +0 -54
- package/dist/esm/src/interfaces/messages-sync.js.map +0 -1
- package/dist/esm/src/smt/smt-store-level.js +0 -103
- package/dist/esm/src/smt/smt-store-level.js.map +0 -1
- package/dist/esm/src/smt/smt-store-memory.js +0 -41
- package/dist/esm/src/smt/smt-store-memory.js.map +0 -1
- package/dist/esm/src/smt/smt-utils.js +0 -129
- package/dist/esm/src/smt/smt-utils.js.map +0 -1
- package/dist/esm/src/smt/sparse-merkle-tree.js +0 -577
- package/dist/esm/src/smt/sparse-merkle-tree.js.map +0 -1
- package/dist/esm/src/state-index/state-index-level.js +0 -191
- package/dist/esm/src/state-index/state-index-level.js.map +0 -1
- package/dist/esm/src/sync/records-projection.js +0 -228
- package/dist/esm/src/sync/records-projection.js.map +0 -1
- package/dist/esm/src/types/smt-types.js +0 -5
- package/dist/esm/src/types/smt-types.js.map +0 -1
- package/dist/esm/src/types/state-index.js +0 -2
- package/dist/esm/src/types/state-index.js.map +0 -1
- package/dist/esm/tests/event-emitter-event-log.spec.js +0 -499
- package/dist/esm/tests/event-emitter-event-log.spec.js.map +0 -1
- package/dist/esm/tests/handlers/messages-sync.spec.js +0 -1771
- package/dist/esm/tests/handlers/messages-sync.spec.js.map +0 -1
- package/dist/esm/tests/smt/smt-store-level.spec.js +0 -132
- package/dist/esm/tests/smt/smt-store-level.spec.js.map +0 -1
- package/dist/esm/tests/smt/sparse-merkle-tree.spec.js +0 -732
- package/dist/esm/tests/smt/sparse-merkle-tree.spec.js.map +0 -1
- package/dist/esm/tests/state-index/state-index-level.spec.js +0 -245
- package/dist/esm/tests/state-index/state-index-level.spec.js.map +0 -1
- package/dist/esm/tests/sync/records-projection.spec.js +0 -245
- package/dist/esm/tests/sync/records-projection.spec.js.map +0 -1
- package/dist/types/src/core/record-chain.d.ts +0 -24
- package/dist/types/src/core/record-chain.d.ts.map +0 -1
- package/dist/types/src/event-stream/event-emitter-event-log.d.ts +0 -80
- package/dist/types/src/event-stream/event-emitter-event-log.d.ts.map +0 -1
- package/dist/types/src/handlers/messages-sync.d.ts +0 -83
- package/dist/types/src/handlers/messages-sync.d.ts.map +0 -1
- package/dist/types/src/interfaces/messages-sync.d.ts +0 -23
- package/dist/types/src/interfaces/messages-sync.d.ts.map +0 -1
- package/dist/types/src/smt/smt-store-level.d.ts +0 -32
- package/dist/types/src/smt/smt-store-level.d.ts.map +0 -1
- package/dist/types/src/smt/smt-store-memory.d.ts +0 -22
- package/dist/types/src/smt/smt-store-memory.d.ts.map +0 -1
- package/dist/types/src/smt/smt-utils.d.ts +0 -58
- package/dist/types/src/smt/smt-utils.d.ts.map +0 -1
- package/dist/types/src/smt/sparse-merkle-tree.d.ts +0 -124
- package/dist/types/src/smt/sparse-merkle-tree.d.ts.map +0 -1
- package/dist/types/src/state-index/state-index-level.d.ts +0 -83
- package/dist/types/src/state-index/state-index-level.d.ts.map +0 -1
- package/dist/types/src/sync/records-projection.d.ts +0 -98
- package/dist/types/src/sync/records-projection.d.ts.map +0 -1
- package/dist/types/src/types/smt-types.d.ts +0 -81
- package/dist/types/src/types/smt-types.d.ts.map +0 -1
- package/dist/types/src/types/state-index.d.ts +0 -90
- package/dist/types/src/types/state-index.d.ts.map +0 -1
- package/dist/types/tests/event-emitter-event-log.spec.d.ts +0 -2
- package/dist/types/tests/event-emitter-event-log.spec.d.ts.map +0 -1
- package/dist/types/tests/handlers/messages-sync.spec.d.ts +0 -2
- package/dist/types/tests/handlers/messages-sync.spec.d.ts.map +0 -1
- package/dist/types/tests/smt/smt-store-level.spec.d.ts +0 -2
- package/dist/types/tests/smt/smt-store-level.spec.d.ts.map +0 -1
- package/dist/types/tests/smt/sparse-merkle-tree.spec.d.ts +0 -2
- package/dist/types/tests/smt/sparse-merkle-tree.spec.d.ts.map +0 -1
- package/dist/types/tests/state-index/state-index-level.spec.d.ts +0 -2
- package/dist/types/tests/state-index/state-index-level.spec.d.ts.map +0 -1
- package/dist/types/tests/sync/records-projection.spec.d.ts +0 -2
- package/dist/types/tests/sync/records-projection.spec.d.ts.map +0 -1
- package/src/core/record-chain.ts +0 -99
- package/src/event-stream/event-emitter-event-log.ts +0 -430
- package/src/handlers/messages-sync.ts +0 -896
- package/src/interfaces/messages-sync.ts +0 -86
- package/src/smt/smt-store-level.ts +0 -143
- package/src/smt/smt-store-memory.ts +0 -53
- package/src/smt/smt-utils.ts +0 -149
- package/src/smt/sparse-merkle-tree.ts +0 -698
- package/src/state-index/state-index-level.ts +0 -239
- package/src/sync/records-projection.ts +0 -328
- package/src/types/smt-types.ts +0 -95
- package/src/types/state-index.ts +0 -100
|
@@ -1,1771 +0,0 @@
|
|
|
1
|
-
import freeForAll from '../vectors/protocol-definitions/free-for-all.json' with { type: 'json' };
|
|
2
|
-
import { Jws } from '../../src/utils/jws.js';
|
|
3
|
-
import { KEY_DELIVERY_PROTOCOL_URI } from '../../src/core/constants.js';
|
|
4
|
-
import { Message } from '../../src/core/message.js';
|
|
5
|
-
import { MessagesSync } from '../../src/interfaces/messages-sync.js';
|
|
6
|
-
import { MessagesSyncHandler } from '../../src/handlers/messages-sync.js';
|
|
7
|
-
import sinon from 'sinon';
|
|
8
|
-
import { TestDataGenerator } from '../utils/test-data-generator.js';
|
|
9
|
-
import { TestEventLog } from '../test-event-stream.js';
|
|
10
|
-
import { TestStores } from '../test-stores.js';
|
|
11
|
-
import { afterAll, beforeAll, beforeEach, describe, expect, it } from 'bun:test';
|
|
12
|
-
import { DataStream, Dwn, DwnErrorCode, DwnInterfaceName, DwnMethodName, Encoder, PermissionGrant, PermissionsProtocol, RECORDS_PROJECTION_ROOT_VERSION, RecordsProjection, Time } from '../../src/index.js';
|
|
13
|
-
import { DidKey, UniversalResolver } from '@enbox/dids';
|
|
14
|
-
export function testMessagesSyncHandler() {
|
|
15
|
-
describe('MessagesSyncHandler.handle()', () => {
|
|
16
|
-
let didResolver;
|
|
17
|
-
let messageStore;
|
|
18
|
-
let dataStore;
|
|
19
|
-
let resumableTaskStore;
|
|
20
|
-
let stateIndex;
|
|
21
|
-
let eventLog;
|
|
22
|
-
let dwn;
|
|
23
|
-
beforeAll(async () => {
|
|
24
|
-
didResolver = new UniversalResolver({ didResolvers: [DidKey] });
|
|
25
|
-
const stores = TestStores.get();
|
|
26
|
-
messageStore = stores.messageStore;
|
|
27
|
-
dataStore = stores.dataStore;
|
|
28
|
-
resumableTaskStore = stores.resumableTaskStore;
|
|
29
|
-
stateIndex = stores.stateIndex;
|
|
30
|
-
eventLog = TestEventLog.get();
|
|
31
|
-
dwn = await Dwn.create({ didResolver, messageStore, dataStore, stateIndex, eventLog, resumableTaskStore });
|
|
32
|
-
});
|
|
33
|
-
beforeEach(async () => {
|
|
34
|
-
await messageStore.clear();
|
|
35
|
-
await dataStore.clear();
|
|
36
|
-
await resumableTaskStore.clear();
|
|
37
|
-
await stateIndex.clear();
|
|
38
|
-
});
|
|
39
|
-
afterAll(async () => {
|
|
40
|
-
await dwn.close();
|
|
41
|
-
});
|
|
42
|
-
describe('root action', () => {
|
|
43
|
-
it('returns the empty root hash for a tenant with no messages', async () => {
|
|
44
|
-
const alice = await TestDataGenerator.generateDidKeyPersona();
|
|
45
|
-
const { message } = await MessagesSync.create({
|
|
46
|
-
signer: Jws.createSigner(alice),
|
|
47
|
-
action: 'root',
|
|
48
|
-
});
|
|
49
|
-
const reply = await dwn.processMessage(alice.did, message);
|
|
50
|
-
expect(reply.status.code).toBe(200);
|
|
51
|
-
expect(typeof reply.root).toBe('string');
|
|
52
|
-
expect(reply.root.length).toBe(64); // hex-encoded 32-byte hash
|
|
53
|
-
});
|
|
54
|
-
it('returns a different root hash after writing a message', async () => {
|
|
55
|
-
const alice = await TestDataGenerator.generateDidKeyPersona();
|
|
56
|
-
await TestDataGenerator.installDefaultTestProtocol(dwn, alice);
|
|
57
|
-
// get the empty root
|
|
58
|
-
const { message: rootMsg1 } = await MessagesSync.create({
|
|
59
|
-
signer: Jws.createSigner(alice),
|
|
60
|
-
action: 'root',
|
|
61
|
-
});
|
|
62
|
-
const reply1 = await dwn.processMessage(alice.did, rootMsg1);
|
|
63
|
-
expect(reply1.status.code).toBe(200);
|
|
64
|
-
const emptyRoot = reply1.root;
|
|
65
|
-
// write a record
|
|
66
|
-
const { message: recordMessage, dataStream } = await TestDataGenerator.generateRecordsWrite({ author: alice });
|
|
67
|
-
const writeReply = await dwn.processMessage(alice.did, recordMessage, { dataStream });
|
|
68
|
-
expect(writeReply.status.code).toBe(202);
|
|
69
|
-
// get the root again
|
|
70
|
-
const { message: rootMsg2 } = await MessagesSync.create({
|
|
71
|
-
signer: Jws.createSigner(alice),
|
|
72
|
-
action: 'root',
|
|
73
|
-
});
|
|
74
|
-
const reply2 = await dwn.processMessage(alice.did, rootMsg2);
|
|
75
|
-
expect(reply2.status.code).toBe(200);
|
|
76
|
-
expect(reply2.root).not.toBe(emptyRoot);
|
|
77
|
-
});
|
|
78
|
-
it('returns protocol-scoped root hash when protocol is specified', async () => {
|
|
79
|
-
const alice = await TestDataGenerator.generateDidKeyPersona();
|
|
80
|
-
await TestDataGenerator.installDefaultTestProtocol(dwn, alice);
|
|
81
|
-
const protocolDefinition = { ...freeForAll, published: true };
|
|
82
|
-
// configure the protocol
|
|
83
|
-
const { message: protocolMessage } = await TestDataGenerator.generateProtocolsConfigure({
|
|
84
|
-
author: alice,
|
|
85
|
-
protocolDefinition,
|
|
86
|
-
});
|
|
87
|
-
const configureReply = await dwn.processMessage(alice.did, protocolMessage);
|
|
88
|
-
expect(configureReply.status.code).toBe(202);
|
|
89
|
-
// write a record for this protocol
|
|
90
|
-
const { message: recordMessage, dataStream } = await TestDataGenerator.generateRecordsWrite({
|
|
91
|
-
author: alice,
|
|
92
|
-
protocol: protocolDefinition.protocol,
|
|
93
|
-
protocolPath: 'post',
|
|
94
|
-
schema: protocolDefinition.types.post.schema,
|
|
95
|
-
});
|
|
96
|
-
const writeReply = await dwn.processMessage(alice.did, recordMessage, { dataStream });
|
|
97
|
-
expect(writeReply.status.code).toBe(202);
|
|
98
|
-
// write a record under a different protocol to diverge the global root
|
|
99
|
-
const { message: otherRecord, dataStream: otherDataStream } = await TestDataGenerator.generateRecordsWrite({ author: alice });
|
|
100
|
-
const otherWriteReply = await dwn.processMessage(alice.did, otherRecord, { dataStream: otherDataStream });
|
|
101
|
-
expect(otherWriteReply.status.code).toBe(202);
|
|
102
|
-
// get the global root
|
|
103
|
-
const { message: globalRootMsg } = await MessagesSync.create({
|
|
104
|
-
signer: Jws.createSigner(alice),
|
|
105
|
-
action: 'root',
|
|
106
|
-
});
|
|
107
|
-
const globalReply = await dwn.processMessage(alice.did, globalRootMsg);
|
|
108
|
-
expect(globalReply.status.code).toBe(200);
|
|
109
|
-
// get the protocol-scoped root
|
|
110
|
-
const { message: protoRootMsg } = await MessagesSync.create({
|
|
111
|
-
signer: Jws.createSigner(alice),
|
|
112
|
-
action: 'root',
|
|
113
|
-
protocol: protocolDefinition.protocol,
|
|
114
|
-
});
|
|
115
|
-
const protoReply = await dwn.processMessage(alice.did, protoRootMsg);
|
|
116
|
-
expect(protoReply.status.code).toBe(200);
|
|
117
|
-
// global root and protocol root should be different
|
|
118
|
-
// (global includes the record from the other protocol)
|
|
119
|
-
expect(protoReply.root).not.toBe(globalReply.root);
|
|
120
|
-
// both should be non-empty roots
|
|
121
|
-
expect(protoReply.root.length).toBe(64);
|
|
122
|
-
});
|
|
123
|
-
});
|
|
124
|
-
describe('subtree action', () => {
|
|
125
|
-
it('returns a subtree hash for a given bit prefix', async () => {
|
|
126
|
-
const alice = await TestDataGenerator.generateDidKeyPersona();
|
|
127
|
-
await TestDataGenerator.installDefaultTestProtocol(dwn, alice);
|
|
128
|
-
// write a record so the tree is non-empty
|
|
129
|
-
const { message: recordMessage, dataStream } = await TestDataGenerator.generateRecordsWrite({ author: alice });
|
|
130
|
-
const writeReply = await dwn.processMessage(alice.did, recordMessage, { dataStream });
|
|
131
|
-
expect(writeReply.status.code).toBe(202);
|
|
132
|
-
const { message } = await MessagesSync.create({
|
|
133
|
-
signer: Jws.createSigner(alice),
|
|
134
|
-
action: 'subtree',
|
|
135
|
-
prefix: '0',
|
|
136
|
-
});
|
|
137
|
-
const reply = await dwn.processMessage(alice.did, message);
|
|
138
|
-
expect(reply.status.code).toBe(200);
|
|
139
|
-
expect(typeof reply.hash).toBe('string');
|
|
140
|
-
expect(reply.hash.length).toBe(64);
|
|
141
|
-
});
|
|
142
|
-
it('returns different hashes for different prefixes', async () => {
|
|
143
|
-
const alice = await TestDataGenerator.generateDidKeyPersona();
|
|
144
|
-
await TestDataGenerator.installDefaultTestProtocol(dwn, alice);
|
|
145
|
-
// write several records to populate various subtrees
|
|
146
|
-
for (let i = 0; i < 10; i++) {
|
|
147
|
-
const { message: recordMessage, dataStream } = await TestDataGenerator.generateRecordsWrite({ author: alice });
|
|
148
|
-
const writeReply = await dwn.processMessage(alice.did, recordMessage, { dataStream });
|
|
149
|
-
expect(writeReply.status.code).toBe(202);
|
|
150
|
-
}
|
|
151
|
-
const { message: msg0 } = await MessagesSync.create({
|
|
152
|
-
signer: Jws.createSigner(alice),
|
|
153
|
-
action: 'subtree',
|
|
154
|
-
prefix: '0',
|
|
155
|
-
});
|
|
156
|
-
const reply0 = await dwn.processMessage(alice.did, msg0);
|
|
157
|
-
const { message: msg1 } = await MessagesSync.create({
|
|
158
|
-
signer: Jws.createSigner(alice),
|
|
159
|
-
action: 'subtree',
|
|
160
|
-
prefix: '1',
|
|
161
|
-
});
|
|
162
|
-
const reply1 = await dwn.processMessage(alice.did, msg1);
|
|
163
|
-
expect(reply0.status.code).toBe(200);
|
|
164
|
-
expect(reply1.status.code).toBe(200);
|
|
165
|
-
// With 10 messages, it's very likely the two halves of the tree differ
|
|
166
|
-
// (not guaranteed but probabilistically near-certain)
|
|
167
|
-
});
|
|
168
|
-
});
|
|
169
|
-
describe('leaves action', () => {
|
|
170
|
-
it('returns all message CIDs for an empty prefix', async () => {
|
|
171
|
-
const alice = await TestDataGenerator.generateDidKeyPersona();
|
|
172
|
-
await TestDataGenerator.installDefaultTestProtocol(dwn, alice);
|
|
173
|
-
// write some messages
|
|
174
|
-
const expectedCids = [];
|
|
175
|
-
for (let i = 0; i < 3; i++) {
|
|
176
|
-
const { message: recordMessage, dataStream } = await TestDataGenerator.generateRecordsWrite({ author: alice });
|
|
177
|
-
const writeReply = await dwn.processMessage(alice.did, recordMessage, { dataStream });
|
|
178
|
-
expect(writeReply.status.code).toBe(202);
|
|
179
|
-
expectedCids.push(await Message.getCid(recordMessage));
|
|
180
|
-
}
|
|
181
|
-
const { message } = await MessagesSync.create({
|
|
182
|
-
signer: Jws.createSigner(alice),
|
|
183
|
-
action: 'leaves',
|
|
184
|
-
prefix: '',
|
|
185
|
-
});
|
|
186
|
-
const reply = await dwn.processMessage(alice.did, message);
|
|
187
|
-
expect(reply.status.code).toBe(200);
|
|
188
|
-
expect(Array.isArray(reply.entries)).toBe(true);
|
|
189
|
-
// 3 RecordsWrite messages + 1 ProtocolsConfigure for the default test protocol
|
|
190
|
-
expect(reply.entries.length).toBe(4);
|
|
191
|
-
for (const cid of expectedCids) {
|
|
192
|
-
expect(reply.entries).toContain(cid);
|
|
193
|
-
}
|
|
194
|
-
});
|
|
195
|
-
it('returns protocol-scoped leaves when protocol is specified', async () => {
|
|
196
|
-
const alice = await TestDataGenerator.generateDidKeyPersona();
|
|
197
|
-
await TestDataGenerator.installDefaultTestProtocol(dwn, alice);
|
|
198
|
-
const protocolDefinition = { ...freeForAll, published: true };
|
|
199
|
-
// configure the protocol
|
|
200
|
-
const { message: protocolMessage } = await TestDataGenerator.generateProtocolsConfigure({
|
|
201
|
-
author: alice,
|
|
202
|
-
protocolDefinition,
|
|
203
|
-
});
|
|
204
|
-
const configureReply = await dwn.processMessage(alice.did, protocolMessage);
|
|
205
|
-
expect(configureReply.status.code).toBe(202);
|
|
206
|
-
// write a protocol-scoped record
|
|
207
|
-
const { message: protoRecord, dataStream: protoDataStream } = await TestDataGenerator.generateRecordsWrite({
|
|
208
|
-
author: alice,
|
|
209
|
-
protocol: protocolDefinition.protocol,
|
|
210
|
-
protocolPath: 'post',
|
|
211
|
-
schema: protocolDefinition.types.post.schema,
|
|
212
|
-
});
|
|
213
|
-
const protoWriteReply = await dwn.processMessage(alice.did, protoRecord, { dataStream: protoDataStream });
|
|
214
|
-
expect(protoWriteReply.status.code).toBe(202);
|
|
215
|
-
// write a record under a different protocol
|
|
216
|
-
const { message: otherRecord, dataStream: otherDataStream } = await TestDataGenerator.generateRecordsWrite({ author: alice });
|
|
217
|
-
const otherWriteReply = await dwn.processMessage(alice.did, otherRecord, { dataStream: otherDataStream });
|
|
218
|
-
expect(otherWriteReply.status.code).toBe(202);
|
|
219
|
-
// query protocol-scoped leaves
|
|
220
|
-
const { message } = await MessagesSync.create({
|
|
221
|
-
signer: Jws.createSigner(alice),
|
|
222
|
-
action: 'leaves',
|
|
223
|
-
prefix: '',
|
|
224
|
-
protocol: protocolDefinition.protocol,
|
|
225
|
-
});
|
|
226
|
-
const reply = await dwn.processMessage(alice.did, message);
|
|
227
|
-
expect(reply.status.code).toBe(200);
|
|
228
|
-
// should contain the ProtocolsConfigure and the protocol-scoped record, but not the other-protocol record
|
|
229
|
-
expect(reply.entries.length).toBe(2);
|
|
230
|
-
const protocolCid = await Message.getCid(protocolMessage);
|
|
231
|
-
const recordCid = await Message.getCid(protoRecord);
|
|
232
|
-
expect(reply.entries).toContain(protocolCid);
|
|
233
|
-
expect(reply.entries).toContain(recordCid);
|
|
234
|
-
});
|
|
235
|
-
it('returns projected record leaves without protocol configs or out-of-path records', async () => {
|
|
236
|
-
const alice = await TestDataGenerator.generateDidKeyPersona();
|
|
237
|
-
const protocolDefinition = { ...freeForAll, published: true };
|
|
238
|
-
const protocol = protocolDefinition.protocol;
|
|
239
|
-
const { message: protocolMessage } = await TestDataGenerator.generateProtocolsConfigure({
|
|
240
|
-
author: alice,
|
|
241
|
-
protocolDefinition,
|
|
242
|
-
});
|
|
243
|
-
expect((await dwn.processMessage(alice.did, protocolMessage)).status.code).toBe(202);
|
|
244
|
-
const { message: postMessage, dataStream: postDataStream } = await TestDataGenerator.generateRecordsWrite({
|
|
245
|
-
author: alice,
|
|
246
|
-
protocol,
|
|
247
|
-
protocolPath: 'post',
|
|
248
|
-
schema: protocolDefinition.types.post.schema,
|
|
249
|
-
});
|
|
250
|
-
expect((await dwn.processMessage(alice.did, postMessage, { dataStream: postDataStream })).status.code).toBe(202);
|
|
251
|
-
const { message: attachmentMessage, dataStream: attachmentDataStream } = await TestDataGenerator.generateRecordsWrite({
|
|
252
|
-
author: alice,
|
|
253
|
-
protocol,
|
|
254
|
-
protocolPath: 'post/attachment',
|
|
255
|
-
parentContextId: postMessage.contextId,
|
|
256
|
-
});
|
|
257
|
-
expect((await dwn.processMessage(alice.did, attachmentMessage, { dataStream: attachmentDataStream })).status.code).toBe(202);
|
|
258
|
-
const { message } = await MessagesSync.create({
|
|
259
|
-
signer: Jws.createSigner(alice),
|
|
260
|
-
action: 'leaves',
|
|
261
|
-
prefix: '',
|
|
262
|
-
projectionRootVersion: RECORDS_PROJECTION_ROOT_VERSION,
|
|
263
|
-
projectionScopes: [{ protocol, protocolPath: 'post' }],
|
|
264
|
-
});
|
|
265
|
-
const reply = await dwn.processMessage(alice.did, message);
|
|
266
|
-
expect(reply.status.code).toBe(200);
|
|
267
|
-
expect(reply.entries).toEqual([await Message.getCid(postMessage)]);
|
|
268
|
-
expect(reply.entries).not.toContain(await Message.getCid(protocolMessage));
|
|
269
|
-
expect(reply.entries).not.toContain(await Message.getCid(attachmentMessage));
|
|
270
|
-
});
|
|
271
|
-
it('excludes infrastructure protocols from records-primary projection leaves', async () => {
|
|
272
|
-
const alice = await TestDataGenerator.generateDidKeyPersona();
|
|
273
|
-
const bob = await TestDataGenerator.generateDidKeyPersona();
|
|
274
|
-
const { message: permissionGrantMessage, dataStream: permissionGrantDataStream } = await TestDataGenerator.generateGrantCreate({
|
|
275
|
-
author: alice,
|
|
276
|
-
grantedTo: bob,
|
|
277
|
-
scope: {
|
|
278
|
-
interface: DwnInterfaceName.Messages,
|
|
279
|
-
method: DwnMethodName.Read,
|
|
280
|
-
protocol: 'http://projected-sync-app-protocol',
|
|
281
|
-
},
|
|
282
|
-
});
|
|
283
|
-
expect((await dwn.processMessage(alice.did, permissionGrantMessage, { dataStream: permissionGrantDataStream })).status.code).toBe(202);
|
|
284
|
-
const keyDeliverySchema = 'https://identity.foundation/schemas/key-delivery/context-key';
|
|
285
|
-
const keyDeliveryProtocolDefinition = {
|
|
286
|
-
protocol: KEY_DELIVERY_PROTOCOL_URI,
|
|
287
|
-
published: false,
|
|
288
|
-
types: {
|
|
289
|
-
contextKey: {
|
|
290
|
-
schema: keyDeliverySchema,
|
|
291
|
-
dataFormats: ['application/json'],
|
|
292
|
-
},
|
|
293
|
-
},
|
|
294
|
-
structure: {
|
|
295
|
-
contextKey: {},
|
|
296
|
-
},
|
|
297
|
-
};
|
|
298
|
-
const { message: keyDeliveryConfigureMessage } = await TestDataGenerator.generateProtocolsConfigure({
|
|
299
|
-
author: alice,
|
|
300
|
-
protocolDefinition: keyDeliveryProtocolDefinition,
|
|
301
|
-
});
|
|
302
|
-
expect((await dwn.processMessage(alice.did, keyDeliveryConfigureMessage)).status.code).toBe(202);
|
|
303
|
-
const { message: keyDeliveryMessage, dataStream: keyDeliveryDataStream } = await TestDataGenerator.generateRecordsWrite({
|
|
304
|
-
author: alice,
|
|
305
|
-
protocol: KEY_DELIVERY_PROTOCOL_URI,
|
|
306
|
-
protocolPath: 'contextKey',
|
|
307
|
-
schema: keyDeliverySchema,
|
|
308
|
-
});
|
|
309
|
-
expect((await dwn.processMessage(alice.did, keyDeliveryMessage, { dataStream: keyDeliveryDataStream })).status.code).toBe(202);
|
|
310
|
-
for (const protocol of [PermissionsProtocol.uri, KEY_DELIVERY_PROTOCOL_URI]) {
|
|
311
|
-
const { message } = await MessagesSync.create({
|
|
312
|
-
signer: Jws.createSigner(alice),
|
|
313
|
-
action: 'leaves',
|
|
314
|
-
prefix: '',
|
|
315
|
-
projectionRootVersion: RECORDS_PROJECTION_ROOT_VERSION,
|
|
316
|
-
projectionScopes: [{ protocol }],
|
|
317
|
-
});
|
|
318
|
-
const reply = await dwn.processMessage(alice.did, message);
|
|
319
|
-
expect(reply.status.code).toBe(200);
|
|
320
|
-
expect(reply.entries).toEqual([]);
|
|
321
|
-
}
|
|
322
|
-
});
|
|
323
|
-
it('returns projected roots from the Records projection algorithm', async () => {
|
|
324
|
-
const alice = await TestDataGenerator.generateDidKeyPersona();
|
|
325
|
-
const protocolDefinition = { ...freeForAll, published: true };
|
|
326
|
-
const protocol = protocolDefinition.protocol;
|
|
327
|
-
const projectionScopes = [{ protocol, protocolPath: 'post' }];
|
|
328
|
-
const { message: protocolMessage } = await TestDataGenerator.generateProtocolsConfigure({
|
|
329
|
-
author: alice,
|
|
330
|
-
protocolDefinition,
|
|
331
|
-
});
|
|
332
|
-
expect((await dwn.processMessage(alice.did, protocolMessage)).status.code).toBe(202);
|
|
333
|
-
const { message: postMessage, dataStream: postDataStream } = await TestDataGenerator.generateRecordsWrite({
|
|
334
|
-
author: alice,
|
|
335
|
-
protocol,
|
|
336
|
-
protocolPath: 'post',
|
|
337
|
-
schema: protocolDefinition.types.post.schema,
|
|
338
|
-
});
|
|
339
|
-
expect((await dwn.processMessage(alice.did, postMessage, { dataStream: postDataStream })).status.code).toBe(202);
|
|
340
|
-
const expectedRoot = await RecordsProjection.getRootHex({
|
|
341
|
-
tenant: alice.did,
|
|
342
|
-
messageStore: messageStore,
|
|
343
|
-
scopes: projectionScopes,
|
|
344
|
-
});
|
|
345
|
-
const { message } = await MessagesSync.create({
|
|
346
|
-
signer: Jws.createSigner(alice),
|
|
347
|
-
action: 'root',
|
|
348
|
-
projectionRootVersion: RECORDS_PROJECTION_ROOT_VERSION,
|
|
349
|
-
projectionScopes,
|
|
350
|
-
});
|
|
351
|
-
const reply = await dwn.processMessage(alice.did, message);
|
|
352
|
-
expect(reply.status.code).toBe(200);
|
|
353
|
-
expect(reply.root).toBe(expectedRoot);
|
|
354
|
-
});
|
|
355
|
-
});
|
|
356
|
-
describe('authorization', () => {
|
|
357
|
-
it('returns 401 if tenant is not the author', async () => {
|
|
358
|
-
const alice = await TestDataGenerator.generateDidKeyPersona();
|
|
359
|
-
const bob = await TestDataGenerator.generateDidKeyPersona();
|
|
360
|
-
const { message } = await MessagesSync.create({
|
|
361
|
-
signer: Jws.createSigner(alice),
|
|
362
|
-
action: 'root',
|
|
363
|
-
});
|
|
364
|
-
const reply = await dwn.processMessage(bob.did, message);
|
|
365
|
-
expect(reply.status.code).toBe(401);
|
|
366
|
-
});
|
|
367
|
-
it('returns 400 if message is invalid', async () => {
|
|
368
|
-
const alice = await TestDataGenerator.generateDidKeyPersona();
|
|
369
|
-
const { message } = await MessagesSync.create({
|
|
370
|
-
signer: Jws.createSigner(alice),
|
|
371
|
-
action: 'root',
|
|
372
|
-
});
|
|
373
|
-
message['descriptor']['troll'] = 'hehe';
|
|
374
|
-
const handler = new MessagesSyncHandler({ didResolver, messageStore, stateIndex });
|
|
375
|
-
const reply = await handler.handle({ tenant: alice.did, message });
|
|
376
|
-
expect(reply.status.code).toBe(400);
|
|
377
|
-
});
|
|
378
|
-
describe('grant-based sync', () => {
|
|
379
|
-
it('allows sync with a matching MessagesSync grant scope', async () => {
|
|
380
|
-
const alice = await TestDataGenerator.generateDidKeyPersona();
|
|
381
|
-
const bob = await TestDataGenerator.generateDidKeyPersona();
|
|
382
|
-
await TestDataGenerator.installDefaultTestProtocol(dwn, alice);
|
|
383
|
-
// write a record so the tree is non-empty
|
|
384
|
-
const { message: recordMessage, dataStream } = await TestDataGenerator.generateRecordsWrite({ author: alice });
|
|
385
|
-
const writeReply = await dwn.processMessage(alice.did, recordMessage, { dataStream });
|
|
386
|
-
expect(writeReply.status.code).toBe(202);
|
|
387
|
-
// grant bob permission to sync Alice's messages
|
|
388
|
-
const { message: grantMessage, dataStream: grantDataStream } = await TestDataGenerator.generateGrantCreate({
|
|
389
|
-
author: alice,
|
|
390
|
-
grantedTo: bob,
|
|
391
|
-
scope: {
|
|
392
|
-
interface: DwnInterfaceName.Messages,
|
|
393
|
-
method: DwnMethodName.Read,
|
|
394
|
-
},
|
|
395
|
-
});
|
|
396
|
-
const grantReply = await dwn.processMessage(alice.did, grantMessage, { dataStream: grantDataStream });
|
|
397
|
-
expect(grantReply.status.code).toBe(202);
|
|
398
|
-
// bob syncs using the grant — root action
|
|
399
|
-
const { message: syncMsg } = await MessagesSync.create({
|
|
400
|
-
signer: Jws.createSigner(bob),
|
|
401
|
-
action: 'root',
|
|
402
|
-
permissionGrantIds: [grantMessage.recordId],
|
|
403
|
-
});
|
|
404
|
-
const reply = await dwn.processMessage(alice.did, syncMsg);
|
|
405
|
-
expect(reply.status.code).toBe(200);
|
|
406
|
-
expect(typeof reply.root).toBe('string');
|
|
407
|
-
expect(reply.root.length).toBe(64);
|
|
408
|
-
});
|
|
409
|
-
it('allows sync with a unified MessagesRead grant scope', async () => {
|
|
410
|
-
// scenario: A Messages.Read grant should also authorize MessagesSync operations
|
|
411
|
-
const alice = await TestDataGenerator.generateDidKeyPersona();
|
|
412
|
-
const bob = await TestDataGenerator.generateDidKeyPersona();
|
|
413
|
-
await TestDataGenerator.installDefaultTestProtocol(dwn, alice);
|
|
414
|
-
// write a record so the tree is non-empty
|
|
415
|
-
const { message: recordMessage, dataStream } = await TestDataGenerator.generateRecordsWrite({ author: alice });
|
|
416
|
-
const writeReply = await dwn.processMessage(alice.did, recordMessage, { dataStream });
|
|
417
|
-
expect(writeReply.status.code).toBe(202);
|
|
418
|
-
// grant bob permission with Messages.Read scope (unified)
|
|
419
|
-
const { message: grantMessage, dataStream: grantDataStream } = await TestDataGenerator.generateGrantCreate({
|
|
420
|
-
author: alice,
|
|
421
|
-
grantedTo: bob,
|
|
422
|
-
scope: {
|
|
423
|
-
interface: DwnInterfaceName.Messages,
|
|
424
|
-
method: DwnMethodName.Read,
|
|
425
|
-
},
|
|
426
|
-
});
|
|
427
|
-
const grantReply = await dwn.processMessage(alice.did, grantMessage, { dataStream: grantDataStream });
|
|
428
|
-
expect(grantReply.status.code).toBe(202);
|
|
429
|
-
// bob syncs using the Messages.Read grant — root action
|
|
430
|
-
const { message: syncMsg } = await MessagesSync.create({
|
|
431
|
-
signer: Jws.createSigner(bob),
|
|
432
|
-
action: 'root',
|
|
433
|
-
permissionGrantIds: [grantMessage.recordId],
|
|
434
|
-
});
|
|
435
|
-
const reply2 = await dwn.processMessage(alice.did, syncMsg);
|
|
436
|
-
expect(reply2.status.code).toBe(200);
|
|
437
|
-
expect(typeof reply2.root).toBe('string');
|
|
438
|
-
expect(reply2.root.length).toBe(64);
|
|
439
|
-
});
|
|
440
|
-
it('allows sync with a protocol-scoped MessagesRead grant', async () => {
|
|
441
|
-
// scenario: A Messages.Read grant scoped to a protocol should authorize protocol-scoped MessagesSync
|
|
442
|
-
const alice = await TestDataGenerator.generateDidKeyPersona();
|
|
443
|
-
const bob = await TestDataGenerator.generateDidKeyPersona();
|
|
444
|
-
const protocolDefinition = { ...freeForAll, published: true };
|
|
445
|
-
// configure and write a protocol record
|
|
446
|
-
const { message: protocolMessage } = await TestDataGenerator.generateProtocolsConfigure({
|
|
447
|
-
author: alice,
|
|
448
|
-
protocolDefinition,
|
|
449
|
-
});
|
|
450
|
-
await dwn.processMessage(alice.did, protocolMessage);
|
|
451
|
-
const { message: recordMessage, dataStream } = await TestDataGenerator.generateRecordsWrite({
|
|
452
|
-
author: alice,
|
|
453
|
-
protocol: protocolDefinition.protocol,
|
|
454
|
-
protocolPath: 'post',
|
|
455
|
-
schema: protocolDefinition.types.post.schema,
|
|
456
|
-
});
|
|
457
|
-
await dwn.processMessage(alice.did, recordMessage, { dataStream });
|
|
458
|
-
// grant bob permission with Messages.Read scope scoped to this protocol
|
|
459
|
-
const { message: grantMessage, dataStream: grantDataStream } = await TestDataGenerator.generateGrantCreate({
|
|
460
|
-
author: alice,
|
|
461
|
-
grantedTo: bob,
|
|
462
|
-
scope: {
|
|
463
|
-
interface: DwnInterfaceName.Messages,
|
|
464
|
-
method: DwnMethodName.Read,
|
|
465
|
-
protocol: protocolDefinition.protocol,
|
|
466
|
-
},
|
|
467
|
-
});
|
|
468
|
-
const grantReply = await dwn.processMessage(alice.did, grantMessage, { dataStream: grantDataStream });
|
|
469
|
-
expect(grantReply.status.code).toBe(202);
|
|
470
|
-
// bob syncs leaves with the protocol-scoped Messages.Read grant
|
|
471
|
-
const { message: syncMsg } = await MessagesSync.create({
|
|
472
|
-
signer: Jws.createSigner(bob),
|
|
473
|
-
action: 'leaves',
|
|
474
|
-
prefix: '',
|
|
475
|
-
protocol: protocolDefinition.protocol,
|
|
476
|
-
permissionGrantIds: [grantMessage.recordId],
|
|
477
|
-
});
|
|
478
|
-
const reply2 = await dwn.processMessage(alice.did, syncMsg);
|
|
479
|
-
expect(reply2.status.code).toBe(200);
|
|
480
|
-
expect(Array.isArray(reply2.entries)).toBe(true);
|
|
481
|
-
expect(reply2.entries.length).toBe(2);
|
|
482
|
-
});
|
|
483
|
-
it('allows sync with a protocol-scoped grant', async () => {
|
|
484
|
-
const alice = await TestDataGenerator.generateDidKeyPersona();
|
|
485
|
-
const bob = await TestDataGenerator.generateDidKeyPersona();
|
|
486
|
-
const protocolDefinition = { ...freeForAll, published: true };
|
|
487
|
-
// configure and write a protocol record
|
|
488
|
-
const { message: protocolMessage } = await TestDataGenerator.generateProtocolsConfigure({
|
|
489
|
-
author: alice,
|
|
490
|
-
protocolDefinition,
|
|
491
|
-
});
|
|
492
|
-
await dwn.processMessage(alice.did, protocolMessage);
|
|
493
|
-
const { message: recordMessage, dataStream } = await TestDataGenerator.generateRecordsWrite({
|
|
494
|
-
author: alice,
|
|
495
|
-
protocol: protocolDefinition.protocol,
|
|
496
|
-
protocolPath: 'post',
|
|
497
|
-
schema: protocolDefinition.types.post.schema,
|
|
498
|
-
});
|
|
499
|
-
await dwn.processMessage(alice.did, recordMessage, { dataStream });
|
|
500
|
-
// grant bob permission to sync Alice's messages scoped to this protocol
|
|
501
|
-
const { message: grantMessage, dataStream: grantDataStream } = await TestDataGenerator.generateGrantCreate({
|
|
502
|
-
author: alice,
|
|
503
|
-
grantedTo: bob,
|
|
504
|
-
scope: {
|
|
505
|
-
interface: DwnInterfaceName.Messages,
|
|
506
|
-
method: DwnMethodName.Read,
|
|
507
|
-
protocol: protocolDefinition.protocol,
|
|
508
|
-
},
|
|
509
|
-
});
|
|
510
|
-
const grantReply = await dwn.processMessage(alice.did, grantMessage, { dataStream: grantDataStream });
|
|
511
|
-
expect(grantReply.status.code).toBe(202);
|
|
512
|
-
// bob syncs leaves with the protocol-scoped grant
|
|
513
|
-
const { message: syncMsg } = await MessagesSync.create({
|
|
514
|
-
signer: Jws.createSigner(bob),
|
|
515
|
-
action: 'leaves',
|
|
516
|
-
prefix: '',
|
|
517
|
-
protocol: protocolDefinition.protocol,
|
|
518
|
-
permissionGrantIds: [grantMessage.recordId],
|
|
519
|
-
});
|
|
520
|
-
const reply = await dwn.processMessage(alice.did, syncMsg);
|
|
521
|
-
expect(reply.status.code).toBe(200);
|
|
522
|
-
expect(Array.isArray(reply.entries)).toBe(true);
|
|
523
|
-
// includes both the ProtocolsConfigure and the RecordsWrite
|
|
524
|
-
expect(reply.entries.length).toBe(2);
|
|
525
|
-
const protocolCid = await Message.getCid(protocolMessage);
|
|
526
|
-
const recordCid = await Message.getCid(recordMessage);
|
|
527
|
-
expect(reply.entries).toContain(protocolCid);
|
|
528
|
-
expect(reply.entries).toContain(recordCid);
|
|
529
|
-
});
|
|
530
|
-
it('allows protocol sync when one grant in a plural grant set covers the protocol', async () => {
|
|
531
|
-
const alice = await TestDataGenerator.generateDidKeyPersona();
|
|
532
|
-
const bob = await TestDataGenerator.generateDidKeyPersona();
|
|
533
|
-
const protocol1 = { ...freeForAll, published: true, protocol: 'http://plural-grant-sync-1' };
|
|
534
|
-
const protocol2 = { ...freeForAll, published: true, protocol: 'http://plural-grant-sync-2' };
|
|
535
|
-
for (const protocolDefinition of [protocol1, protocol2]) {
|
|
536
|
-
const { message: protocolMessage } = await TestDataGenerator.generateProtocolsConfigure({
|
|
537
|
-
author: alice,
|
|
538
|
-
protocolDefinition,
|
|
539
|
-
});
|
|
540
|
-
await dwn.processMessage(alice.did, protocolMessage);
|
|
541
|
-
}
|
|
542
|
-
const { message: recordMessage, dataStream } = await TestDataGenerator.generateRecordsWrite({
|
|
543
|
-
author: alice,
|
|
544
|
-
protocol: protocol2.protocol,
|
|
545
|
-
protocolPath: 'post',
|
|
546
|
-
schema: protocol2.types.post.schema,
|
|
547
|
-
});
|
|
548
|
-
await dwn.processMessage(alice.did, recordMessage, { dataStream });
|
|
549
|
-
const grantIds = [];
|
|
550
|
-
for (const protocolDefinition of [protocol1, protocol2]) {
|
|
551
|
-
const { message: grantMessage, dataStream: grantDataStream } = await TestDataGenerator.generateGrantCreate({
|
|
552
|
-
author: alice,
|
|
553
|
-
grantedTo: bob,
|
|
554
|
-
scope: {
|
|
555
|
-
interface: DwnInterfaceName.Messages,
|
|
556
|
-
method: DwnMethodName.Read,
|
|
557
|
-
protocol: protocolDefinition.protocol,
|
|
558
|
-
},
|
|
559
|
-
});
|
|
560
|
-
const grantReply = await dwn.processMessage(alice.did, grantMessage, { dataStream: grantDataStream });
|
|
561
|
-
expect(grantReply.status.code).toBe(202);
|
|
562
|
-
grantIds.push(grantMessage.recordId);
|
|
563
|
-
}
|
|
564
|
-
const { message: syncMsg } = await MessagesSync.create({
|
|
565
|
-
signer: Jws.createSigner(bob),
|
|
566
|
-
action: 'leaves',
|
|
567
|
-
prefix: '',
|
|
568
|
-
protocol: protocol2.protocol,
|
|
569
|
-
permissionGrantIds: grantIds.reverse(),
|
|
570
|
-
});
|
|
571
|
-
const reply = await dwn.processMessage(alice.did, syncMsg);
|
|
572
|
-
expect(reply.status.code).toBe(200);
|
|
573
|
-
expect(reply.entries).toContain(await Message.getCid(recordMessage));
|
|
574
|
-
expect(syncMsg.descriptor.permissionGrantIds).toEqual([...grantIds].sort());
|
|
575
|
-
});
|
|
576
|
-
it('rejects sync when any grant in a plural grant set is expired', async () => {
|
|
577
|
-
const alice = await TestDataGenerator.generateDidKeyPersona();
|
|
578
|
-
const bob = await TestDataGenerator.generateDidKeyPersona();
|
|
579
|
-
const protocol = 'http://plural-grant-sync-expired';
|
|
580
|
-
const now = Time.getCurrentTimestamp();
|
|
581
|
-
const { message: activeGrantMessage, dataStream: activeGrantDataStream } = await TestDataGenerator.generateGrantCreate({
|
|
582
|
-
author: alice,
|
|
583
|
-
grantedTo: bob,
|
|
584
|
-
dateExpires: Time.createOffsetTimestamp({ seconds: 60 * 60 }, now),
|
|
585
|
-
scope: {
|
|
586
|
-
interface: DwnInterfaceName.Messages,
|
|
587
|
-
method: DwnMethodName.Read,
|
|
588
|
-
protocol,
|
|
589
|
-
},
|
|
590
|
-
});
|
|
591
|
-
expect((await dwn.processMessage(alice.did, activeGrantMessage, { dataStream: activeGrantDataStream })).status.code).toBe(202);
|
|
592
|
-
const { message: expiredGrantMessage, dataStream: expiredGrantDataStream } = await TestDataGenerator.generateGrantCreate({
|
|
593
|
-
author: alice,
|
|
594
|
-
grantedTo: bob,
|
|
595
|
-
dateGranted: Time.createOffsetTimestamp({ seconds: -120 }, now),
|
|
596
|
-
dateExpires: Time.createOffsetTimestamp({ seconds: -60 }, now),
|
|
597
|
-
scope: {
|
|
598
|
-
interface: DwnInterfaceName.Messages,
|
|
599
|
-
method: DwnMethodName.Read,
|
|
600
|
-
protocol,
|
|
601
|
-
},
|
|
602
|
-
});
|
|
603
|
-
expect((await dwn.processMessage(alice.did, expiredGrantMessage, { dataStream: expiredGrantDataStream })).status.code).toBe(202);
|
|
604
|
-
const { message: syncMsg } = await MessagesSync.create({
|
|
605
|
-
signer: Jws.createSigner(bob),
|
|
606
|
-
action: 'root',
|
|
607
|
-
protocol,
|
|
608
|
-
permissionGrantIds: [activeGrantMessage.recordId, expiredGrantMessage.recordId],
|
|
609
|
-
});
|
|
610
|
-
const reply = await dwn.processMessage(alice.did, syncMsg);
|
|
611
|
-
expect(reply.status.code).toBe(401);
|
|
612
|
-
expect(reply.status.detail).toContain(DwnErrorCode.GrantAuthorizationGrantExpired);
|
|
613
|
-
});
|
|
614
|
-
it('rejects sync when any grant in a plural grant set is revoked', async () => {
|
|
615
|
-
const alice = await TestDataGenerator.generateDidKeyPersona();
|
|
616
|
-
const bob = await TestDataGenerator.generateDidKeyPersona();
|
|
617
|
-
const protocol = 'http://plural-grant-sync-revoked';
|
|
618
|
-
const { message: activeGrantMessage, dataStream: activeGrantDataStream } = await TestDataGenerator.generateGrantCreate({
|
|
619
|
-
author: alice,
|
|
620
|
-
grantedTo: bob,
|
|
621
|
-
scope: {
|
|
622
|
-
interface: DwnInterfaceName.Messages,
|
|
623
|
-
method: DwnMethodName.Read,
|
|
624
|
-
protocol,
|
|
625
|
-
},
|
|
626
|
-
});
|
|
627
|
-
expect((await dwn.processMessage(alice.did, activeGrantMessage, { dataStream: activeGrantDataStream })).status.code).toBe(202);
|
|
628
|
-
const revokedGrant = await TestDataGenerator.generateGrantCreate({
|
|
629
|
-
author: alice,
|
|
630
|
-
grantedTo: bob,
|
|
631
|
-
scope: {
|
|
632
|
-
interface: DwnInterfaceName.Messages,
|
|
633
|
-
method: DwnMethodName.Read,
|
|
634
|
-
protocol,
|
|
635
|
-
},
|
|
636
|
-
});
|
|
637
|
-
expect((await dwn.processMessage(alice.did, revokedGrant.message, { dataStream: revokedGrant.dataStream })).status.code).toBe(202);
|
|
638
|
-
const revocation = await PermissionsProtocol.createRevocation({
|
|
639
|
-
signer: Jws.createSigner(alice),
|
|
640
|
-
grant: PermissionGrant.parse(revokedGrant.dataEncodedMessage),
|
|
641
|
-
});
|
|
642
|
-
const revocationReply = await dwn.processMessage(alice.did, revocation.recordsWrite.message, { dataStream: DataStream.fromBytes(revocation.permissionRevocationBytes) });
|
|
643
|
-
expect(revocationReply.status.code).toBe(202);
|
|
644
|
-
await Time.minimalSleep();
|
|
645
|
-
const { message: syncMsg } = await MessagesSync.create({
|
|
646
|
-
signer: Jws.createSigner(bob),
|
|
647
|
-
action: 'root',
|
|
648
|
-
protocol,
|
|
649
|
-
permissionGrantIds: [activeGrantMessage.recordId, revokedGrant.message.recordId],
|
|
650
|
-
});
|
|
651
|
-
const reply = await dwn.processMessage(alice.did, syncMsg);
|
|
652
|
-
expect(reply.status.code).toBe(401);
|
|
653
|
-
expect(reply.status.detail).toContain(DwnErrorCode.GrantAuthorizationGrantRevoked);
|
|
654
|
-
});
|
|
655
|
-
it('rejects sync with mismatching interface grant scope', async () => {
|
|
656
|
-
const alice = await TestDataGenerator.generateDidKeyPersona();
|
|
657
|
-
const bob = await TestDataGenerator.generateDidKeyPersona();
|
|
658
|
-
// create a RecordsWrite grant (wrong interface for MessagesSync)
|
|
659
|
-
const { message: grantMessage, dataStream } = await TestDataGenerator.generateGrantCreate({
|
|
660
|
-
author: alice,
|
|
661
|
-
grantedTo: bob,
|
|
662
|
-
scope: {
|
|
663
|
-
interface: DwnInterfaceName.Records,
|
|
664
|
-
method: DwnMethodName.Write,
|
|
665
|
-
protocol: freeForAll.protocol,
|
|
666
|
-
},
|
|
667
|
-
});
|
|
668
|
-
const grantReply = await dwn.processMessage(alice.did, grantMessage, { dataStream });
|
|
669
|
-
expect(grantReply.status.code).toBe(202);
|
|
670
|
-
const { message: syncMsg } = await MessagesSync.create({
|
|
671
|
-
signer: Jws.createSigner(bob),
|
|
672
|
-
action: 'root',
|
|
673
|
-
permissionGrantIds: [grantMessage.recordId],
|
|
674
|
-
});
|
|
675
|
-
const reply = await dwn.processMessage(alice.did, syncMsg);
|
|
676
|
-
expect(reply.status.code).toBe(401);
|
|
677
|
-
expect(reply.status.detail).toContain(DwnErrorCode.GrantAuthorizationInterfaceMismatch);
|
|
678
|
-
});
|
|
679
|
-
it('rejects sync with mismatching protocol grant scope', async () => {
|
|
680
|
-
const alice = await TestDataGenerator.generateDidKeyPersona();
|
|
681
|
-
const bob = await TestDataGenerator.generateDidKeyPersona();
|
|
682
|
-
// grant bob permission to sync protocol1
|
|
683
|
-
const { message: grantMessage, dataStream } = await TestDataGenerator.generateGrantCreate({
|
|
684
|
-
author: alice,
|
|
685
|
-
grantedTo: bob,
|
|
686
|
-
scope: {
|
|
687
|
-
interface: DwnInterfaceName.Messages,
|
|
688
|
-
method: DwnMethodName.Read,
|
|
689
|
-
protocol: 'http://protocol1',
|
|
690
|
-
},
|
|
691
|
-
});
|
|
692
|
-
const grantReply = await dwn.processMessage(alice.did, grantMessage, { dataStream });
|
|
693
|
-
expect(grantReply.status.code).toBe(202);
|
|
694
|
-
// bob attempts to sync protocol2 using the protocol1 grant
|
|
695
|
-
const { message: syncMsg } = await MessagesSync.create({
|
|
696
|
-
signer: Jws.createSigner(bob),
|
|
697
|
-
action: 'root',
|
|
698
|
-
protocol: 'http://protocol2',
|
|
699
|
-
permissionGrantIds: [grantMessage.recordId],
|
|
700
|
-
});
|
|
701
|
-
const reply = await dwn.processMessage(alice.did, syncMsg);
|
|
702
|
-
expect(reply.status.code).toBe(401);
|
|
703
|
-
expect(reply.status.detail).toContain(DwnErrorCode.MessagesGrantAuthorizationMismatchedProtocol);
|
|
704
|
-
});
|
|
705
|
-
it('rejects full-tenant sync actions with a protocol-scoped grant', async () => {
|
|
706
|
-
const alice = await TestDataGenerator.generateDidKeyPersona();
|
|
707
|
-
const bob = await TestDataGenerator.generateDidKeyPersona();
|
|
708
|
-
const { message: grantMessage, dataStream } = await TestDataGenerator.generateGrantCreate({
|
|
709
|
-
author: alice,
|
|
710
|
-
grantedTo: bob,
|
|
711
|
-
scope: {
|
|
712
|
-
interface: DwnInterfaceName.Messages,
|
|
713
|
-
method: DwnMethodName.Read,
|
|
714
|
-
protocol: 'http://protocol-scoped-sync',
|
|
715
|
-
},
|
|
716
|
-
});
|
|
717
|
-
const grantReply = await dwn.processMessage(alice.did, grantMessage, { dataStream });
|
|
718
|
-
expect(grantReply.status.code).toBe(202);
|
|
719
|
-
const syncActions = [
|
|
720
|
-
{ action: 'root' },
|
|
721
|
-
{ action: 'subtree', prefix: '' },
|
|
722
|
-
{ action: 'leaves', prefix: '' },
|
|
723
|
-
{ action: 'diff', hashes: {}, depth: 2 },
|
|
724
|
-
];
|
|
725
|
-
for (const syncAction of syncActions) {
|
|
726
|
-
const { message: syncMsg } = await MessagesSync.create({
|
|
727
|
-
signer: Jws.createSigner(bob),
|
|
728
|
-
...syncAction,
|
|
729
|
-
permissionGrantIds: [grantMessage.recordId],
|
|
730
|
-
});
|
|
731
|
-
const reply = await dwn.processMessage(alice.did, syncMsg);
|
|
732
|
-
expect(reply.status.code).toBe(401);
|
|
733
|
-
expect(reply.status.detail).toContain(DwnErrorCode.MessagesGrantAuthorizationMismatchedProtocol);
|
|
734
|
-
}
|
|
735
|
-
});
|
|
736
|
-
it('rejects protocol sync with subtree-scoped grants', async () => {
|
|
737
|
-
const alice = await TestDataGenerator.generateDidKeyPersona();
|
|
738
|
-
const bob = await TestDataGenerator.generateDidKeyPersona();
|
|
739
|
-
const protocol = 'http://subtree-scoped-sync';
|
|
740
|
-
const scopedGrants = [
|
|
741
|
-
{
|
|
742
|
-
interface: DwnInterfaceName.Messages,
|
|
743
|
-
method: DwnMethodName.Read,
|
|
744
|
-
protocol,
|
|
745
|
-
protocolPath: 'post',
|
|
746
|
-
},
|
|
747
|
-
{
|
|
748
|
-
interface: DwnInterfaceName.Messages,
|
|
749
|
-
method: DwnMethodName.Read,
|
|
750
|
-
protocol,
|
|
751
|
-
contextId: 'root',
|
|
752
|
-
},
|
|
753
|
-
];
|
|
754
|
-
for (const scope of scopedGrants) {
|
|
755
|
-
const { message: grantMessage, dataStream } = await TestDataGenerator.generateGrantCreate({
|
|
756
|
-
author: alice,
|
|
757
|
-
grantedTo: bob,
|
|
758
|
-
scope,
|
|
759
|
-
});
|
|
760
|
-
const grantReply = await dwn.processMessage(alice.did, grantMessage, { dataStream });
|
|
761
|
-
expect(grantReply.status.code).toBe(202);
|
|
762
|
-
const { message: syncMsg } = await MessagesSync.create({
|
|
763
|
-
signer: Jws.createSigner(bob),
|
|
764
|
-
action: 'root',
|
|
765
|
-
protocol,
|
|
766
|
-
permissionGrantIds: [grantMessage.recordId],
|
|
767
|
-
});
|
|
768
|
-
const reply = await dwn.processMessage(alice.did, syncMsg);
|
|
769
|
-
expect(reply.status.code).toBe(401);
|
|
770
|
-
expect(reply.status.detail).toContain(DwnErrorCode.MessagesGrantAuthorizationMismatchedProtocol);
|
|
771
|
-
}
|
|
772
|
-
});
|
|
773
|
-
it('allows projected sync with a matching protocolPath Messages.Read grant', async () => {
|
|
774
|
-
const alice = await TestDataGenerator.generateDidKeyPersona();
|
|
775
|
-
const bob = await TestDataGenerator.generateDidKeyPersona();
|
|
776
|
-
const protocolDefinition = { ...freeForAll, published: true };
|
|
777
|
-
const protocol = protocolDefinition.protocol;
|
|
778
|
-
const { message: protocolMessage } = await TestDataGenerator.generateProtocolsConfigure({
|
|
779
|
-
author: alice,
|
|
780
|
-
protocolDefinition,
|
|
781
|
-
});
|
|
782
|
-
expect((await dwn.processMessage(alice.did, protocolMessage)).status.code).toBe(202);
|
|
783
|
-
const { message: postMessage, dataStream: postDataStream } = await TestDataGenerator.generateRecordsWrite({
|
|
784
|
-
author: alice,
|
|
785
|
-
protocol,
|
|
786
|
-
protocolPath: 'post',
|
|
787
|
-
schema: protocolDefinition.types.post.schema,
|
|
788
|
-
});
|
|
789
|
-
expect((await dwn.processMessage(alice.did, postMessage, { dataStream: postDataStream })).status.code).toBe(202);
|
|
790
|
-
const { message: attachmentMessage, dataStream: attachmentDataStream } = await TestDataGenerator.generateRecordsWrite({
|
|
791
|
-
author: alice,
|
|
792
|
-
protocol,
|
|
793
|
-
protocolPath: 'post/attachment',
|
|
794
|
-
parentContextId: postMessage.contextId,
|
|
795
|
-
});
|
|
796
|
-
expect((await dwn.processMessage(alice.did, attachmentMessage, { dataStream: attachmentDataStream })).status.code).toBe(202);
|
|
797
|
-
const { message: grantMessage, dataStream: grantDataStream } = await TestDataGenerator.generateGrantCreate({
|
|
798
|
-
author: alice,
|
|
799
|
-
grantedTo: bob,
|
|
800
|
-
scope: {
|
|
801
|
-
interface: DwnInterfaceName.Messages,
|
|
802
|
-
method: DwnMethodName.Read,
|
|
803
|
-
protocol,
|
|
804
|
-
protocolPath: 'post',
|
|
805
|
-
},
|
|
806
|
-
});
|
|
807
|
-
expect((await dwn.processMessage(alice.did, grantMessage, { dataStream: grantDataStream })).status.code).toBe(202);
|
|
808
|
-
const { message: diffMsg } = await MessagesSync.create({
|
|
809
|
-
signer: Jws.createSigner(bob),
|
|
810
|
-
action: 'diff',
|
|
811
|
-
hashes: {},
|
|
812
|
-
depth: 2,
|
|
813
|
-
projectionRootVersion: RECORDS_PROJECTION_ROOT_VERSION,
|
|
814
|
-
projectionScopes: [{ protocol, protocolPath: 'post' }],
|
|
815
|
-
permissionGrantIds: [grantMessage.recordId],
|
|
816
|
-
});
|
|
817
|
-
const reply = await dwn.processMessage(alice.did, diffMsg);
|
|
818
|
-
expect(reply.status.code).toBe(200);
|
|
819
|
-
const protocolCid = await Message.getCid(protocolMessage);
|
|
820
|
-
const postCid = await Message.getCid(postMessage);
|
|
821
|
-
const remoteCids = reply.onlyRemote.map(entry => entry.messageCid);
|
|
822
|
-
expect(remoteCids).toContain(postCid);
|
|
823
|
-
expect(remoteCids).not.toContain(protocolCid);
|
|
824
|
-
expect(remoteCids).not.toContain(await Message.getCid(attachmentMessage));
|
|
825
|
-
expect(reply.dependencies).toEqual([{
|
|
826
|
-
dependencyClass: 'protocolsConfigure',
|
|
827
|
-
messageCid: protocolCid,
|
|
828
|
-
message: protocolMessage,
|
|
829
|
-
rootMessageCid: postCid,
|
|
830
|
-
}]);
|
|
831
|
-
});
|
|
832
|
-
it('returns the governing protocol config dependency for projected sync', async () => {
|
|
833
|
-
const alice = await TestDataGenerator.generateDidKeyPersona();
|
|
834
|
-
const bob = await TestDataGenerator.generateDidKeyPersona();
|
|
835
|
-
const protocol = 'http://projected-sync-config-history';
|
|
836
|
-
const protocolDefinition = {
|
|
837
|
-
...freeForAll,
|
|
838
|
-
protocol,
|
|
839
|
-
published: true,
|
|
840
|
-
};
|
|
841
|
-
const { message: firstProtocolMessage } = await TestDataGenerator.generateProtocolsConfigure({
|
|
842
|
-
author: alice,
|
|
843
|
-
messageTimestamp: '2026-01-01T00:00:00.000000Z',
|
|
844
|
-
protocolDefinition,
|
|
845
|
-
});
|
|
846
|
-
expect((await dwn.processMessage(alice.did, firstProtocolMessage)).status.code).toBe(202);
|
|
847
|
-
const { message: secondProtocolMessage } = await TestDataGenerator.generateProtocolsConfigure({
|
|
848
|
-
author: alice,
|
|
849
|
-
messageTimestamp: '2026-01-02T00:00:00.000000Z',
|
|
850
|
-
protocolDefinition,
|
|
851
|
-
});
|
|
852
|
-
expect((await dwn.processMessage(alice.did, secondProtocolMessage)).status.code).toBe(202);
|
|
853
|
-
const { message: postMessage, dataStream: postDataStream } = await TestDataGenerator.generateRecordsWrite({
|
|
854
|
-
author: alice,
|
|
855
|
-
dateCreated: '2026-01-03T00:00:00.000000Z',
|
|
856
|
-
messageTimestamp: '2026-01-03T00:00:00.000000Z',
|
|
857
|
-
protocol,
|
|
858
|
-
protocolPath: 'post',
|
|
859
|
-
schema: protocolDefinition.types.post.schema,
|
|
860
|
-
});
|
|
861
|
-
expect((await dwn.processMessage(alice.did, postMessage, { dataStream: postDataStream })).status.code).toBe(202);
|
|
862
|
-
const { message: futureProtocolMessage } = await TestDataGenerator.generateProtocolsConfigure({
|
|
863
|
-
author: alice,
|
|
864
|
-
messageTimestamp: '2026-01-04T00:00:00.000000Z',
|
|
865
|
-
protocolDefinition,
|
|
866
|
-
});
|
|
867
|
-
expect((await dwn.processMessage(alice.did, futureProtocolMessage)).status.code).toBe(202);
|
|
868
|
-
const { message: grantMessage, dataStream: grantDataStream } = await TestDataGenerator.generateGrantCreate({
|
|
869
|
-
author: alice,
|
|
870
|
-
grantedTo: bob,
|
|
871
|
-
scope: {
|
|
872
|
-
interface: DwnInterfaceName.Messages,
|
|
873
|
-
method: DwnMethodName.Read,
|
|
874
|
-
protocol,
|
|
875
|
-
protocolPath: 'post',
|
|
876
|
-
},
|
|
877
|
-
});
|
|
878
|
-
expect((await dwn.processMessage(alice.did, grantMessage, { dataStream: grantDataStream })).status.code).toBe(202);
|
|
879
|
-
const { message: diffMsg } = await MessagesSync.create({
|
|
880
|
-
signer: Jws.createSigner(bob),
|
|
881
|
-
action: 'diff',
|
|
882
|
-
hashes: {},
|
|
883
|
-
depth: 2,
|
|
884
|
-
projectionRootVersion: RECORDS_PROJECTION_ROOT_VERSION,
|
|
885
|
-
projectionScopes: [{ protocol, protocolPath: 'post' }],
|
|
886
|
-
permissionGrantIds: [grantMessage.recordId],
|
|
887
|
-
});
|
|
888
|
-
const reply = await dwn.processMessage(alice.did, diffMsg);
|
|
889
|
-
expect(reply.status.code).toBe(200);
|
|
890
|
-
const postCid = await Message.getCid(postMessage);
|
|
891
|
-
expect(reply.onlyRemote.map(entry => entry.messageCid)).toContain(postCid);
|
|
892
|
-
expect(reply.dependencies.map(entry => entry.messageCid)).not.toContain(await Message.getCid(firstProtocolMessage));
|
|
893
|
-
expect(reply.dependencies.map(entry => entry.messageCid)).not.toContain(await Message.getCid(futureProtocolMessage));
|
|
894
|
-
expect(reply.dependencies).toEqual([
|
|
895
|
-
{
|
|
896
|
-
dependencyClass: 'protocolsConfigure',
|
|
897
|
-
messageCid: await Message.getCid(secondProtocolMessage),
|
|
898
|
-
message: secondProtocolMessage,
|
|
899
|
-
rootMessageCid: postCid,
|
|
900
|
-
},
|
|
901
|
-
]);
|
|
902
|
-
});
|
|
903
|
-
it('returns initial write and protocol config dependencies for projected delete tombstones', async () => {
|
|
904
|
-
const alice = await TestDataGenerator.generateDidKeyPersona();
|
|
905
|
-
const bob = await TestDataGenerator.generateDidKeyPersona();
|
|
906
|
-
const protocolDefinition = { ...freeForAll, protocol: 'http://projected-sync-delete-hints', published: true };
|
|
907
|
-
const unrelatedDefinition = { ...freeForAll, protocol: 'http://projected-sync-delete-hints-unrelated', published: true };
|
|
908
|
-
const protocol = protocolDefinition.protocol;
|
|
909
|
-
const { message: protocolMessage } = await TestDataGenerator.generateProtocolsConfigure({
|
|
910
|
-
author: alice,
|
|
911
|
-
protocolDefinition,
|
|
912
|
-
});
|
|
913
|
-
expect((await dwn.processMessage(alice.did, protocolMessage)).status.code).toBe(202);
|
|
914
|
-
const { message: unrelatedProtocolMessage } = await TestDataGenerator.generateProtocolsConfigure({
|
|
915
|
-
author: alice,
|
|
916
|
-
protocolDefinition: unrelatedDefinition,
|
|
917
|
-
});
|
|
918
|
-
expect((await dwn.processMessage(alice.did, unrelatedProtocolMessage)).status.code).toBe(202);
|
|
919
|
-
const { message: postMessage, dataStream: postDataStream } = await TestDataGenerator.generateRecordsWrite({
|
|
920
|
-
author: alice,
|
|
921
|
-
protocol,
|
|
922
|
-
protocolPath: 'post',
|
|
923
|
-
schema: protocolDefinition.types.post.schema,
|
|
924
|
-
});
|
|
925
|
-
expect((await dwn.processMessage(alice.did, postMessage, { dataStream: postDataStream })).status.code).toBe(202);
|
|
926
|
-
const { message: deleteMessage } = await TestDataGenerator.generateRecordsDelete({
|
|
927
|
-
author: alice,
|
|
928
|
-
recordId: postMessage.recordId,
|
|
929
|
-
});
|
|
930
|
-
expect((await dwn.processMessage(alice.did, deleteMessage)).status.code).toBe(202);
|
|
931
|
-
const { message: grantMessage, dataStream: grantDataStream } = await TestDataGenerator.generateGrantCreate({
|
|
932
|
-
author: alice,
|
|
933
|
-
grantedTo: bob,
|
|
934
|
-
scope: {
|
|
935
|
-
interface: DwnInterfaceName.Messages,
|
|
936
|
-
method: DwnMethodName.Read,
|
|
937
|
-
protocol,
|
|
938
|
-
protocolPath: 'post',
|
|
939
|
-
},
|
|
940
|
-
});
|
|
941
|
-
expect((await dwn.processMessage(alice.did, grantMessage, { dataStream: grantDataStream })).status.code).toBe(202);
|
|
942
|
-
const { message: diffMsg } = await MessagesSync.create({
|
|
943
|
-
signer: Jws.createSigner(bob),
|
|
944
|
-
action: 'diff',
|
|
945
|
-
hashes: {},
|
|
946
|
-
depth: 2,
|
|
947
|
-
projectionRootVersion: RECORDS_PROJECTION_ROOT_VERSION,
|
|
948
|
-
projectionScopes: [{ protocol, protocolPath: 'post' }],
|
|
949
|
-
permissionGrantIds: [grantMessage.recordId],
|
|
950
|
-
});
|
|
951
|
-
const reply = await dwn.processMessage(alice.did, diffMsg);
|
|
952
|
-
expect(reply.status.code).toBe(200);
|
|
953
|
-
const deleteCid = await Message.getCid(deleteMessage);
|
|
954
|
-
const postCid = await Message.getCid(postMessage);
|
|
955
|
-
const protocolCid = await Message.getCid(protocolMessage);
|
|
956
|
-
expect(reply.onlyRemote.map(entry => entry.messageCid)).toEqual([deleteCid]);
|
|
957
|
-
expect(reply.dependencies.map(entry => entry.messageCid)).not.toContain(await Message.getCid(unrelatedProtocolMessage));
|
|
958
|
-
expect(reply.dependencies).toEqual([
|
|
959
|
-
{
|
|
960
|
-
dependencyClass: 'recordsInitialWrite',
|
|
961
|
-
messageCid: postCid,
|
|
962
|
-
message: postMessage,
|
|
963
|
-
rootMessageCid: deleteCid,
|
|
964
|
-
},
|
|
965
|
-
{
|
|
966
|
-
dependencyClass: 'protocolsConfigure',
|
|
967
|
-
messageCid: protocolCid,
|
|
968
|
-
message: protocolMessage,
|
|
969
|
-
rootMessageCid: deleteCid,
|
|
970
|
-
},
|
|
971
|
-
]);
|
|
972
|
-
const initialWriteDependency = reply.dependencies.find(entry => entry.dependencyClass === 'recordsInitialWrite');
|
|
973
|
-
expect(initialWriteDependency.encodedData).toBeUndefined();
|
|
974
|
-
expect('encodedData' in initialWriteDependency.message).toBe(false);
|
|
975
|
-
});
|
|
976
|
-
it('returns composed protocol config dependencies for projected sync', async () => {
|
|
977
|
-
const alice = await TestDataGenerator.generateDidKeyPersona();
|
|
978
|
-
const bob = await TestDataGenerator.generateDidKeyPersona();
|
|
979
|
-
const socialDefinition = {
|
|
980
|
-
...freeForAll,
|
|
981
|
-
protocol: 'http://projected-sync-composed-social',
|
|
982
|
-
published: true,
|
|
983
|
-
};
|
|
984
|
-
const profileDefinition = {
|
|
985
|
-
...freeForAll,
|
|
986
|
-
protocol: 'http://projected-sync-composed-profile',
|
|
987
|
-
published: true,
|
|
988
|
-
uses: { social: socialDefinition.protocol },
|
|
989
|
-
};
|
|
990
|
-
const unrelatedDefinition = {
|
|
991
|
-
...freeForAll,
|
|
992
|
-
protocol: 'http://projected-sync-composed-unrelated',
|
|
993
|
-
published: true,
|
|
994
|
-
};
|
|
995
|
-
const { message: socialProtocolMessage } = await TestDataGenerator.generateProtocolsConfigure({
|
|
996
|
-
author: alice,
|
|
997
|
-
protocolDefinition: socialDefinition,
|
|
998
|
-
});
|
|
999
|
-
expect((await dwn.processMessage(alice.did, socialProtocolMessage)).status.code).toBe(202);
|
|
1000
|
-
const { message: profileProtocolMessage } = await TestDataGenerator.generateProtocolsConfigure({
|
|
1001
|
-
author: alice,
|
|
1002
|
-
protocolDefinition: profileDefinition,
|
|
1003
|
-
});
|
|
1004
|
-
expect((await dwn.processMessage(alice.did, profileProtocolMessage)).status.code).toBe(202);
|
|
1005
|
-
const { message: unrelatedProtocolMessage } = await TestDataGenerator.generateProtocolsConfigure({
|
|
1006
|
-
author: alice,
|
|
1007
|
-
protocolDefinition: unrelatedDefinition,
|
|
1008
|
-
});
|
|
1009
|
-
expect((await dwn.processMessage(alice.did, unrelatedProtocolMessage)).status.code).toBe(202);
|
|
1010
|
-
const { message: postMessage, dataStream: postDataStream } = await TestDataGenerator.generateRecordsWrite({
|
|
1011
|
-
author: alice,
|
|
1012
|
-
protocol: profileDefinition.protocol,
|
|
1013
|
-
protocolPath: 'post',
|
|
1014
|
-
schema: profileDefinition.types.post.schema,
|
|
1015
|
-
});
|
|
1016
|
-
expect((await dwn.processMessage(alice.did, postMessage, { dataStream: postDataStream })).status.code).toBe(202);
|
|
1017
|
-
const { message: grantMessage, dataStream: grantDataStream } = await TestDataGenerator.generateGrantCreate({
|
|
1018
|
-
author: alice,
|
|
1019
|
-
grantedTo: bob,
|
|
1020
|
-
scope: {
|
|
1021
|
-
interface: DwnInterfaceName.Messages,
|
|
1022
|
-
method: DwnMethodName.Read,
|
|
1023
|
-
protocol: profileDefinition.protocol,
|
|
1024
|
-
protocolPath: 'post',
|
|
1025
|
-
},
|
|
1026
|
-
});
|
|
1027
|
-
expect((await dwn.processMessage(alice.did, grantMessage, { dataStream: grantDataStream })).status.code).toBe(202);
|
|
1028
|
-
const { message: diffMsg } = await MessagesSync.create({
|
|
1029
|
-
signer: Jws.createSigner(bob),
|
|
1030
|
-
action: 'diff',
|
|
1031
|
-
hashes: {},
|
|
1032
|
-
depth: 2,
|
|
1033
|
-
projectionRootVersion: RECORDS_PROJECTION_ROOT_VERSION,
|
|
1034
|
-
projectionScopes: [{ protocol: profileDefinition.protocol, protocolPath: 'post' }],
|
|
1035
|
-
permissionGrantIds: [grantMessage.recordId],
|
|
1036
|
-
});
|
|
1037
|
-
const reply = await dwn.processMessage(alice.did, diffMsg);
|
|
1038
|
-
expect(reply.status.code).toBe(200);
|
|
1039
|
-
const postCid = await Message.getCid(postMessage);
|
|
1040
|
-
expect(reply.onlyRemote.map(entry => entry.messageCid)).toContain(postCid);
|
|
1041
|
-
expect(reply.dependencies.map(entry => entry.messageCid)).not.toContain(await Message.getCid(unrelatedProtocolMessage));
|
|
1042
|
-
expect(reply.dependencies).toEqual([
|
|
1043
|
-
{
|
|
1044
|
-
dependencyClass: 'protocolsConfigure',
|
|
1045
|
-
messageCid: await Message.getCid(profileProtocolMessage),
|
|
1046
|
-
message: profileProtocolMessage,
|
|
1047
|
-
rootMessageCid: postCid,
|
|
1048
|
-
},
|
|
1049
|
-
{
|
|
1050
|
-
dependencyClass: 'protocolsConfigure',
|
|
1051
|
-
messageCid: await Message.getCid(socialProtocolMessage),
|
|
1052
|
-
message: socialProtocolMessage,
|
|
1053
|
-
rootMessageCid: postCid,
|
|
1054
|
-
},
|
|
1055
|
-
]);
|
|
1056
|
-
});
|
|
1057
|
-
it('terminates cyclic composed protocol dependencies for projected sync', async () => {
|
|
1058
|
-
const alice = await TestDataGenerator.generateDidKeyPersona();
|
|
1059
|
-
const bob = await TestDataGenerator.generateDidKeyPersona();
|
|
1060
|
-
const protocolA = 'http://projected-sync-cycle-a';
|
|
1061
|
-
const protocolB = 'http://projected-sync-cycle-b';
|
|
1062
|
-
const protocolBBaseDefinition = {
|
|
1063
|
-
...freeForAll,
|
|
1064
|
-
protocol: protocolB,
|
|
1065
|
-
published: true,
|
|
1066
|
-
};
|
|
1067
|
-
const protocolADefinition = {
|
|
1068
|
-
...freeForAll,
|
|
1069
|
-
protocol: protocolA,
|
|
1070
|
-
published: true,
|
|
1071
|
-
uses: { b: protocolB },
|
|
1072
|
-
};
|
|
1073
|
-
const protocolBCycleDefinition = {
|
|
1074
|
-
...freeForAll,
|
|
1075
|
-
protocol: protocolB,
|
|
1076
|
-
published: true,
|
|
1077
|
-
uses: { a: protocolA },
|
|
1078
|
-
};
|
|
1079
|
-
const { message: protocolBBaseMessage } = await TestDataGenerator.generateProtocolsConfigure({
|
|
1080
|
-
author: alice,
|
|
1081
|
-
messageTimestamp: '2026-01-01T00:00:00.000000Z',
|
|
1082
|
-
protocolDefinition: protocolBBaseDefinition,
|
|
1083
|
-
});
|
|
1084
|
-
expect((await dwn.processMessage(alice.did, protocolBBaseMessage)).status.code).toBe(202);
|
|
1085
|
-
const { message: protocolAMessage } = await TestDataGenerator.generateProtocolsConfigure({
|
|
1086
|
-
author: alice,
|
|
1087
|
-
messageTimestamp: '2026-01-02T00:00:00.000000Z',
|
|
1088
|
-
protocolDefinition: protocolADefinition,
|
|
1089
|
-
});
|
|
1090
|
-
expect((await dwn.processMessage(alice.did, protocolAMessage)).status.code).toBe(202);
|
|
1091
|
-
const { message: protocolBCycleMessage } = await TestDataGenerator.generateProtocolsConfigure({
|
|
1092
|
-
author: alice,
|
|
1093
|
-
messageTimestamp: '2026-01-03T00:00:00.000000Z',
|
|
1094
|
-
protocolDefinition: protocolBCycleDefinition,
|
|
1095
|
-
});
|
|
1096
|
-
expect((await dwn.processMessage(alice.did, protocolBCycleMessage)).status.code).toBe(202);
|
|
1097
|
-
const { message: postMessage, dataStream: postDataStream } = await TestDataGenerator.generateRecordsWrite({
|
|
1098
|
-
author: alice,
|
|
1099
|
-
dateCreated: '2026-01-04T00:00:00.000000Z',
|
|
1100
|
-
messageTimestamp: '2026-01-04T00:00:00.000000Z',
|
|
1101
|
-
protocol: protocolA,
|
|
1102
|
-
protocolPath: 'post',
|
|
1103
|
-
schema: protocolADefinition.types.post.schema,
|
|
1104
|
-
});
|
|
1105
|
-
expect((await dwn.processMessage(alice.did, postMessage, { dataStream: postDataStream })).status.code).toBe(202);
|
|
1106
|
-
const { message: grantMessage, dataStream: grantDataStream } = await TestDataGenerator.generateGrantCreate({
|
|
1107
|
-
author: alice,
|
|
1108
|
-
grantedTo: bob,
|
|
1109
|
-
scope: {
|
|
1110
|
-
interface: DwnInterfaceName.Messages,
|
|
1111
|
-
method: DwnMethodName.Read,
|
|
1112
|
-
protocol: protocolA,
|
|
1113
|
-
protocolPath: 'post',
|
|
1114
|
-
},
|
|
1115
|
-
});
|
|
1116
|
-
expect((await dwn.processMessage(alice.did, grantMessage, { dataStream: grantDataStream })).status.code).toBe(202);
|
|
1117
|
-
const { message: diffMsg } = await MessagesSync.create({
|
|
1118
|
-
signer: Jws.createSigner(bob),
|
|
1119
|
-
action: 'diff',
|
|
1120
|
-
hashes: {},
|
|
1121
|
-
depth: 2,
|
|
1122
|
-
projectionRootVersion: RECORDS_PROJECTION_ROOT_VERSION,
|
|
1123
|
-
projectionScopes: [{ protocol: protocolA, protocolPath: 'post' }],
|
|
1124
|
-
permissionGrantIds: [grantMessage.recordId],
|
|
1125
|
-
});
|
|
1126
|
-
const reply = await dwn.processMessage(alice.did, diffMsg);
|
|
1127
|
-
expect(reply.status.code).toBe(200);
|
|
1128
|
-
const postCid = await Message.getCid(postMessage);
|
|
1129
|
-
const dependencyCids = reply.dependencies.map(entry => entry.messageCid);
|
|
1130
|
-
expect(reply.onlyRemote.map(entry => entry.messageCid)).toContain(postCid);
|
|
1131
|
-
expect(dependencyCids.sort()).toEqual([
|
|
1132
|
-
await Message.getCid(protocolAMessage),
|
|
1133
|
-
await Message.getCid(protocolBCycleMessage),
|
|
1134
|
-
].sort());
|
|
1135
|
-
expect(dependencyCids).not.toContain(await Message.getCid(protocolBBaseMessage));
|
|
1136
|
-
expect(reply.dependencies.every(entry => entry.rootMessageCid === postCid)).toBe(true);
|
|
1137
|
-
});
|
|
1138
|
-
it('rejects projected sync when no grant covers a requested projection scope', async () => {
|
|
1139
|
-
const alice = await TestDataGenerator.generateDidKeyPersona();
|
|
1140
|
-
const bob = await TestDataGenerator.generateDidKeyPersona();
|
|
1141
|
-
const protocol = 'http://projected-sync-scope-mismatch';
|
|
1142
|
-
const { message: grantMessage, dataStream: grantDataStream } = await TestDataGenerator.generateGrantCreate({
|
|
1143
|
-
author: alice,
|
|
1144
|
-
grantedTo: bob,
|
|
1145
|
-
scope: {
|
|
1146
|
-
interface: DwnInterfaceName.Messages,
|
|
1147
|
-
method: DwnMethodName.Read,
|
|
1148
|
-
protocol,
|
|
1149
|
-
protocolPath: 'post',
|
|
1150
|
-
},
|
|
1151
|
-
});
|
|
1152
|
-
expect((await dwn.processMessage(alice.did, grantMessage, { dataStream: grantDataStream })).status.code).toBe(202);
|
|
1153
|
-
const { message: syncMsg } = await MessagesSync.create({
|
|
1154
|
-
signer: Jws.createSigner(bob),
|
|
1155
|
-
action: 'root',
|
|
1156
|
-
projectionRootVersion: RECORDS_PROJECTION_ROOT_VERSION,
|
|
1157
|
-
projectionScopes: [{ protocol }],
|
|
1158
|
-
permissionGrantIds: [grantMessage.recordId],
|
|
1159
|
-
});
|
|
1160
|
-
const reply = await dwn.processMessage(alice.did, syncMsg);
|
|
1161
|
-
expect(reply.status.code).toBe(401);
|
|
1162
|
-
expect(reply.status.detail).toContain(DwnErrorCode.MessagesGrantAuthorizationProjectionScopeMismatch);
|
|
1163
|
-
});
|
|
1164
|
-
it('rejects projected sync when any requested projection scope is uncovered', async () => {
|
|
1165
|
-
const alice = await TestDataGenerator.generateDidKeyPersona();
|
|
1166
|
-
const bob = await TestDataGenerator.generateDidKeyPersona();
|
|
1167
|
-
const coveredProtocol = 'http://projected-sync-covered-scope';
|
|
1168
|
-
const uncoveredProtocol = 'http://projected-sync-uncovered-scope';
|
|
1169
|
-
const { message: grantMessage, dataStream: grantDataStream } = await TestDataGenerator.generateGrantCreate({
|
|
1170
|
-
author: alice,
|
|
1171
|
-
grantedTo: bob,
|
|
1172
|
-
scope: {
|
|
1173
|
-
interface: DwnInterfaceName.Messages,
|
|
1174
|
-
method: DwnMethodName.Read,
|
|
1175
|
-
protocol: coveredProtocol,
|
|
1176
|
-
protocolPath: 'post',
|
|
1177
|
-
},
|
|
1178
|
-
});
|
|
1179
|
-
expect((await dwn.processMessage(alice.did, grantMessage, { dataStream: grantDataStream })).status.code).toBe(202);
|
|
1180
|
-
const { message: syncMsg } = await MessagesSync.create({
|
|
1181
|
-
signer: Jws.createSigner(bob),
|
|
1182
|
-
action: 'root',
|
|
1183
|
-
projectionRootVersion: RECORDS_PROJECTION_ROOT_VERSION,
|
|
1184
|
-
projectionScopes: [
|
|
1185
|
-
{ protocol: coveredProtocol, protocolPath: 'post' },
|
|
1186
|
-
{ protocol: uncoveredProtocol, protocolPath: 'post' },
|
|
1187
|
-
],
|
|
1188
|
-
permissionGrantIds: [grantMessage.recordId],
|
|
1189
|
-
});
|
|
1190
|
-
const reply = await dwn.processMessage(alice.did, syncMsg);
|
|
1191
|
-
expect(reply.status.code).toBe(401);
|
|
1192
|
-
expect(reply.status.detail).toContain(DwnErrorCode.MessagesGrantAuthorizationProjectionScopeMismatch);
|
|
1193
|
-
});
|
|
1194
|
-
it('rejects delegated MessagesSync of infrastructure protocols', async () => {
|
|
1195
|
-
const alice = await TestDataGenerator.generateDidKeyPersona();
|
|
1196
|
-
const bob = await TestDataGenerator.generateDidKeyPersona();
|
|
1197
|
-
const carol = await TestDataGenerator.generateDidKeyPersona();
|
|
1198
|
-
const { message: permissionsGrantMessage, dataStream: permissionsGrantDataStream } = await TestDataGenerator.generateGrantCreate({
|
|
1199
|
-
author: alice,
|
|
1200
|
-
grantedTo: bob,
|
|
1201
|
-
scope: {
|
|
1202
|
-
interface: DwnInterfaceName.Messages,
|
|
1203
|
-
method: DwnMethodName.Read,
|
|
1204
|
-
protocol: PermissionsProtocol.uri,
|
|
1205
|
-
},
|
|
1206
|
-
});
|
|
1207
|
-
expect((await dwn.processMessage(alice.did, permissionsGrantMessage, { dataStream: permissionsGrantDataStream })).status.code).toBe(202);
|
|
1208
|
-
const { message: keyDeliveryGrantMessage, dataStream: keyDeliveryGrantDataStream } = await TestDataGenerator.generateGrantCreate({
|
|
1209
|
-
author: alice,
|
|
1210
|
-
grantedTo: bob,
|
|
1211
|
-
scope: {
|
|
1212
|
-
interface: DwnInterfaceName.Messages,
|
|
1213
|
-
method: DwnMethodName.Read,
|
|
1214
|
-
protocol: KEY_DELIVERY_PROTOCOL_URI,
|
|
1215
|
-
},
|
|
1216
|
-
});
|
|
1217
|
-
expect((await dwn.processMessage(alice.did, keyDeliveryGrantMessage, { dataStream: keyDeliveryGrantDataStream })).status.code).toBe(202);
|
|
1218
|
-
const { message: carolGrantMessage, dataStream: carolGrantDataStream } = await TestDataGenerator.generateGrantCreate({
|
|
1219
|
-
author: alice,
|
|
1220
|
-
grantedTo: carol,
|
|
1221
|
-
scope: {
|
|
1222
|
-
interface: DwnInterfaceName.Messages,
|
|
1223
|
-
method: DwnMethodName.Read,
|
|
1224
|
-
protocol: 'http://private-delegate-protocol',
|
|
1225
|
-
},
|
|
1226
|
-
});
|
|
1227
|
-
expect((await dwn.processMessage(alice.did, carolGrantMessage, { dataStream: carolGrantDataStream })).status.code).toBe(202);
|
|
1228
|
-
const { message: readMessage } = await TestDataGenerator.generateMessagesRead({
|
|
1229
|
-
author: bob,
|
|
1230
|
-
messageCid: await Message.getCid(carolGrantMessage),
|
|
1231
|
-
permissionGrantIds: [permissionsGrantMessage.recordId],
|
|
1232
|
-
});
|
|
1233
|
-
const readReply = await dwn.processMessage(alice.did, readMessage);
|
|
1234
|
-
expect(readReply.status.code).toBe(401);
|
|
1235
|
-
expect(readReply.status.detail).toContain(DwnErrorCode.MessagesReadVerifyScopeFailed);
|
|
1236
|
-
const stateIndexSyncActions = [
|
|
1237
|
-
{ action: 'root' },
|
|
1238
|
-
{ action: 'subtree', prefix: '' },
|
|
1239
|
-
{ action: 'leaves', prefix: '' },
|
|
1240
|
-
{ action: 'diff', hashes: {}, depth: 2 },
|
|
1241
|
-
];
|
|
1242
|
-
const infrastructureProtocolGrants = [
|
|
1243
|
-
{ protocol: PermissionsProtocol.uri, grantId: permissionsGrantMessage.recordId },
|
|
1244
|
-
{ protocol: KEY_DELIVERY_PROTOCOL_URI, grantId: keyDeliveryGrantMessage.recordId },
|
|
1245
|
-
];
|
|
1246
|
-
for (const { protocol, grantId } of infrastructureProtocolGrants) {
|
|
1247
|
-
for (const syncAction of stateIndexSyncActions) {
|
|
1248
|
-
const { message: syncMessage } = await MessagesSync.create({
|
|
1249
|
-
signer: Jws.createSigner(bob),
|
|
1250
|
-
...syncAction,
|
|
1251
|
-
protocol,
|
|
1252
|
-
permissionGrantIds: [grantId],
|
|
1253
|
-
});
|
|
1254
|
-
const syncReply = await dwn.processMessage(alice.did, syncMessage);
|
|
1255
|
-
expect(syncReply.status.code).toBe(401);
|
|
1256
|
-
expect(syncReply.status.detail).toContain(DwnErrorCode.MessagesGrantAuthorizationProtocolSyncInfrastructureProtocol);
|
|
1257
|
-
}
|
|
1258
|
-
}
|
|
1259
|
-
for (const { protocol, grantId } of infrastructureProtocolGrants) {
|
|
1260
|
-
const { message: projectedSyncMessage } = await MessagesSync.create({
|
|
1261
|
-
signer: Jws.createSigner(bob),
|
|
1262
|
-
action: 'diff',
|
|
1263
|
-
hashes: {},
|
|
1264
|
-
depth: 2,
|
|
1265
|
-
projectionRootVersion: RECORDS_PROJECTION_ROOT_VERSION,
|
|
1266
|
-
projectionScopes: [{ protocol }],
|
|
1267
|
-
permissionGrantIds: [grantId],
|
|
1268
|
-
});
|
|
1269
|
-
const projectedSyncReply = await dwn.processMessage(alice.did, projectedSyncMessage);
|
|
1270
|
-
expect(projectedSyncReply.status.code).toBe(401);
|
|
1271
|
-
expect(projectedSyncReply.status.detail).toContain(DwnErrorCode.MessagesGrantAuthorizationProjectionInfrastructureProtocol);
|
|
1272
|
-
}
|
|
1273
|
-
});
|
|
1274
|
-
it('allows projected sync with a matching contextId Messages.Read grant', async () => {
|
|
1275
|
-
const alice = await TestDataGenerator.generateDidKeyPersona();
|
|
1276
|
-
const bob = await TestDataGenerator.generateDidKeyPersona();
|
|
1277
|
-
const protocolDefinition = { ...freeForAll, published: true };
|
|
1278
|
-
const protocol = protocolDefinition.protocol;
|
|
1279
|
-
const { message: protocolMessage } = await TestDataGenerator.generateProtocolsConfigure({
|
|
1280
|
-
author: alice,
|
|
1281
|
-
protocolDefinition,
|
|
1282
|
-
});
|
|
1283
|
-
expect((await dwn.processMessage(alice.did, protocolMessage)).status.code).toBe(202);
|
|
1284
|
-
const { message: rootMessage, dataStream: rootDataStream } = await TestDataGenerator.generateRecordsWrite({
|
|
1285
|
-
author: alice,
|
|
1286
|
-
protocol,
|
|
1287
|
-
protocolPath: 'post',
|
|
1288
|
-
schema: protocolDefinition.types.post.schema,
|
|
1289
|
-
});
|
|
1290
|
-
expect((await dwn.processMessage(alice.did, rootMessage, { dataStream: rootDataStream })).status.code).toBe(202);
|
|
1291
|
-
const { message: childMessage, dataStream: childDataStream } = await TestDataGenerator.generateRecordsWrite({
|
|
1292
|
-
author: alice,
|
|
1293
|
-
protocol,
|
|
1294
|
-
protocolPath: 'post/attachment',
|
|
1295
|
-
parentContextId: rootMessage.contextId,
|
|
1296
|
-
});
|
|
1297
|
-
expect((await dwn.processMessage(alice.did, childMessage, { dataStream: childDataStream })).status.code).toBe(202);
|
|
1298
|
-
const { message: siblingMessage, dataStream: siblingDataStream } = await TestDataGenerator.generateRecordsWrite({
|
|
1299
|
-
author: alice,
|
|
1300
|
-
protocol,
|
|
1301
|
-
protocolPath: 'post',
|
|
1302
|
-
schema: protocolDefinition.types.post.schema,
|
|
1303
|
-
});
|
|
1304
|
-
expect((await dwn.processMessage(alice.did, siblingMessage, { dataStream: siblingDataStream })).status.code).toBe(202);
|
|
1305
|
-
const { message: grantMessage, dataStream: grantDataStream } = await TestDataGenerator.generateGrantCreate({
|
|
1306
|
-
author: alice,
|
|
1307
|
-
grantedTo: bob,
|
|
1308
|
-
scope: {
|
|
1309
|
-
interface: DwnInterfaceName.Messages,
|
|
1310
|
-
method: DwnMethodName.Read,
|
|
1311
|
-
protocol,
|
|
1312
|
-
contextId: rootMessage.contextId,
|
|
1313
|
-
},
|
|
1314
|
-
});
|
|
1315
|
-
expect((await dwn.processMessage(alice.did, grantMessage, { dataStream: grantDataStream })).status.code).toBe(202);
|
|
1316
|
-
const { message: syncMsg } = await MessagesSync.create({
|
|
1317
|
-
signer: Jws.createSigner(bob),
|
|
1318
|
-
action: 'leaves',
|
|
1319
|
-
prefix: '',
|
|
1320
|
-
projectionRootVersion: RECORDS_PROJECTION_ROOT_VERSION,
|
|
1321
|
-
projectionScopes: [{ protocol, contextId: rootMessage.contextId }],
|
|
1322
|
-
permissionGrantIds: [grantMessage.recordId],
|
|
1323
|
-
});
|
|
1324
|
-
const reply = await dwn.processMessage(alice.did, syncMsg);
|
|
1325
|
-
expect(reply.status.code).toBe(200);
|
|
1326
|
-
expect(reply.entries).toContain(await Message.getCid(rootMessage));
|
|
1327
|
-
expect(reply.entries).toContain(await Message.getCid(childMessage));
|
|
1328
|
-
expect(reply.entries).not.toContain(await Message.getCid(protocolMessage));
|
|
1329
|
-
expect(reply.entries).not.toContain(await Message.getCid(siblingMessage));
|
|
1330
|
-
});
|
|
1331
|
-
it('returns only protocol-scoped diff entries for a delegated protocol grant', async () => {
|
|
1332
|
-
const alice = await TestDataGenerator.generateDidKeyPersona();
|
|
1333
|
-
const bob = await TestDataGenerator.generateDidKeyPersona();
|
|
1334
|
-
const protocolA = { ...freeForAll, protocol: 'http://delegated-diff-protocol-a' };
|
|
1335
|
-
const protocolB = { ...freeForAll, protocol: 'http://delegated-diff-protocol-b' };
|
|
1336
|
-
for (const protocolDefinition of [protocolA, protocolB]) {
|
|
1337
|
-
const { message: protocolMessage } = await TestDataGenerator.generateProtocolsConfigure({
|
|
1338
|
-
author: alice,
|
|
1339
|
-
protocolDefinition,
|
|
1340
|
-
});
|
|
1341
|
-
expect((await dwn.processMessage(alice.did, protocolMessage)).status.code).toBe(202);
|
|
1342
|
-
}
|
|
1343
|
-
const { message: recordA, dataStream: dataStreamA } = await TestDataGenerator.generateRecordsWrite({
|
|
1344
|
-
author: alice,
|
|
1345
|
-
protocol: protocolA.protocol,
|
|
1346
|
-
protocolPath: 'post',
|
|
1347
|
-
schema: protocolA.types.post.schema,
|
|
1348
|
-
});
|
|
1349
|
-
expect((await dwn.processMessage(alice.did, recordA, { dataStream: dataStreamA })).status.code).toBe(202);
|
|
1350
|
-
const { message: recordB, dataStream: dataStreamB } = await TestDataGenerator.generateRecordsWrite({
|
|
1351
|
-
author: alice,
|
|
1352
|
-
protocol: protocolB.protocol,
|
|
1353
|
-
protocolPath: 'post',
|
|
1354
|
-
schema: protocolB.types.post.schema,
|
|
1355
|
-
});
|
|
1356
|
-
expect((await dwn.processMessage(alice.did, recordB, { dataStream: dataStreamB })).status.code).toBe(202);
|
|
1357
|
-
const { message: grantMessage, dataStream: grantDataStream } = await TestDataGenerator.generateGrantCreate({
|
|
1358
|
-
author: alice,
|
|
1359
|
-
grantedTo: bob,
|
|
1360
|
-
scope: {
|
|
1361
|
-
interface: DwnInterfaceName.Messages,
|
|
1362
|
-
method: DwnMethodName.Read,
|
|
1363
|
-
protocol: protocolA.protocol,
|
|
1364
|
-
},
|
|
1365
|
-
});
|
|
1366
|
-
expect((await dwn.processMessage(alice.did, grantMessage, { dataStream: grantDataStream })).status.code).toBe(202);
|
|
1367
|
-
const { message: diffMsg } = await MessagesSync.create({
|
|
1368
|
-
signer: Jws.createSigner(bob),
|
|
1369
|
-
action: 'diff',
|
|
1370
|
-
hashes: {},
|
|
1371
|
-
depth: 2,
|
|
1372
|
-
protocol: protocolA.protocol,
|
|
1373
|
-
permissionGrantIds: [grantMessage.recordId],
|
|
1374
|
-
});
|
|
1375
|
-
const reply = await dwn.processMessage(alice.did, diffMsg);
|
|
1376
|
-
expect(reply.status.code).toBe(200);
|
|
1377
|
-
const remoteCids = reply.onlyRemote.map(entry => entry.messageCid);
|
|
1378
|
-
expect(remoteCids).toContain(await Message.getCid(recordA));
|
|
1379
|
-
expect(remoteCids).not.toContain(await Message.getCid(recordB));
|
|
1380
|
-
expect(reply.onlyRemote.every(entry => {
|
|
1381
|
-
if (entry.message?.descriptor.interface !== DwnInterfaceName.Records) {
|
|
1382
|
-
return true;
|
|
1383
|
-
}
|
|
1384
|
-
const recordsMessage = entry.message;
|
|
1385
|
-
return recordsMessage.descriptor.protocol === protocolA.protocol;
|
|
1386
|
-
})).toBe(true);
|
|
1387
|
-
});
|
|
1388
|
-
});
|
|
1389
|
-
});
|
|
1390
|
-
describe('input validation', () => {
|
|
1391
|
-
it('returns 400 for an unknown action', async () => {
|
|
1392
|
-
const alice = await TestDataGenerator.generateDidKeyPersona();
|
|
1393
|
-
const { message } = await MessagesSync.create({
|
|
1394
|
-
signer: Jws.createSigner(alice),
|
|
1395
|
-
action: 'root',
|
|
1396
|
-
});
|
|
1397
|
-
// manually override to an invalid action
|
|
1398
|
-
message.descriptor.action = 'invalid';
|
|
1399
|
-
const handler = new MessagesSyncHandler({ didResolver, messageStore, stateIndex });
|
|
1400
|
-
const reply = await handler.handle({ tenant: alice.did, message });
|
|
1401
|
-
expect(reply.status.code).toBe(400);
|
|
1402
|
-
// the JSON schema validator catches the invalid action before the handler switch/case
|
|
1403
|
-
expect(reply.status.detail).toContain('SchemaValidatorFailure');
|
|
1404
|
-
});
|
|
1405
|
-
it('returns 400 for unknown action that bypasses schema validation (default case)', async () => {
|
|
1406
|
-
const alice = await TestDataGenerator.generateDidKeyPersona();
|
|
1407
|
-
const { message } = await MessagesSync.create({
|
|
1408
|
-
signer: Jws.createSigner(alice),
|
|
1409
|
-
action: 'root',
|
|
1410
|
-
});
|
|
1411
|
-
// Stub MessagesSync.parse to skip schema validation
|
|
1412
|
-
const parseStub = sinon.stub(MessagesSync, 'parse').resolves({
|
|
1413
|
-
author: alice.did,
|
|
1414
|
-
message: message,
|
|
1415
|
-
signaturePayload: { descriptorCid: 'test' },
|
|
1416
|
-
});
|
|
1417
|
-
try {
|
|
1418
|
-
// Override action to something that passes the stub but hits the default case
|
|
1419
|
-
message.descriptor.action = 'bogusAction';
|
|
1420
|
-
const handler = new MessagesSyncHandler({ didResolver, messageStore, stateIndex });
|
|
1421
|
-
const reply = await handler.handle({ tenant: alice.did, message });
|
|
1422
|
-
expect(reply.status.code).toBe(400);
|
|
1423
|
-
expect(reply.status.detail).toContain('Unknown action');
|
|
1424
|
-
}
|
|
1425
|
-
finally {
|
|
1426
|
-
parseStub.restore();
|
|
1427
|
-
}
|
|
1428
|
-
});
|
|
1429
|
-
it('returns 500 for invalid prefix with non-binary characters (via stubbed parse)', async () => {
|
|
1430
|
-
const alice = await TestDataGenerator.generateDidKeyPersona();
|
|
1431
|
-
const { message } = await MessagesSync.create({
|
|
1432
|
-
signer: Jws.createSigner(alice),
|
|
1433
|
-
action: 'subtree',
|
|
1434
|
-
prefix: '0',
|
|
1435
|
-
});
|
|
1436
|
-
// Stub parse to skip schema validation
|
|
1437
|
-
const parseStub = sinon.stub(MessagesSync, 'parse').resolves({
|
|
1438
|
-
author: alice.did,
|
|
1439
|
-
message: message,
|
|
1440
|
-
signaturePayload: { descriptorCid: 'test' },
|
|
1441
|
-
});
|
|
1442
|
-
try {
|
|
1443
|
-
// Override prefix to contain invalid characters
|
|
1444
|
-
message.descriptor.prefix = 'abc';
|
|
1445
|
-
const handler = new MessagesSyncHandler({ didResolver, messageStore, stateIndex });
|
|
1446
|
-
const reply = await handler.handle({ tenant: alice.did, message });
|
|
1447
|
-
expect(reply.status.code).toBe(500);
|
|
1448
|
-
expect(reply.status.detail).toContain('MessagesSyncInvalidPrefix');
|
|
1449
|
-
}
|
|
1450
|
-
finally {
|
|
1451
|
-
parseStub.restore();
|
|
1452
|
-
}
|
|
1453
|
-
});
|
|
1454
|
-
it('returns 500 for prefix exceeding 256 characters (via stubbed parse)', async () => {
|
|
1455
|
-
const alice = await TestDataGenerator.generateDidKeyPersona();
|
|
1456
|
-
const { message } = await MessagesSync.create({
|
|
1457
|
-
signer: Jws.createSigner(alice),
|
|
1458
|
-
action: 'subtree',
|
|
1459
|
-
prefix: '0',
|
|
1460
|
-
});
|
|
1461
|
-
// Stub parse to skip schema validation
|
|
1462
|
-
const parseStub = sinon.stub(MessagesSync, 'parse').resolves({
|
|
1463
|
-
author: alice.did,
|
|
1464
|
-
message: message,
|
|
1465
|
-
signaturePayload: { descriptorCid: 'test' },
|
|
1466
|
-
});
|
|
1467
|
-
try {
|
|
1468
|
-
// Override prefix to be too long
|
|
1469
|
-
message.descriptor.prefix = '0'.repeat(257);
|
|
1470
|
-
const handler = new MessagesSyncHandler({ didResolver, messageStore, stateIndex });
|
|
1471
|
-
const reply = await handler.handle({ tenant: alice.did, message });
|
|
1472
|
-
expect(reply.status.code).toBe(500);
|
|
1473
|
-
expect(reply.status.detail).toContain('MessagesSyncInvalidPrefix');
|
|
1474
|
-
}
|
|
1475
|
-
finally {
|
|
1476
|
-
parseStub.restore();
|
|
1477
|
-
}
|
|
1478
|
-
});
|
|
1479
|
-
it('returns 400 when projection scopes are provided without a projection root version', async () => {
|
|
1480
|
-
const alice = await TestDataGenerator.generateDidKeyPersona();
|
|
1481
|
-
const { message } = await MessagesSync.create({
|
|
1482
|
-
signer: Jws.createSigner(alice),
|
|
1483
|
-
action: 'root',
|
|
1484
|
-
projectionRootVersion: RECORDS_PROJECTION_ROOT_VERSION,
|
|
1485
|
-
projectionScopes: [{ protocol: 'http://projected-sync-schema' }],
|
|
1486
|
-
});
|
|
1487
|
-
delete message.descriptor.projectionRootVersion;
|
|
1488
|
-
const reply = await dwn.processMessage(alice.did, message);
|
|
1489
|
-
expect(reply.status.code).toBe(400);
|
|
1490
|
-
expect(reply.status.detail).toContain('SchemaValidatorFailure');
|
|
1491
|
-
});
|
|
1492
|
-
it('returns 400 when projection root version is provided without projection scopes', async () => {
|
|
1493
|
-
const alice = await TestDataGenerator.generateDidKeyPersona();
|
|
1494
|
-
const { message } = await MessagesSync.create({
|
|
1495
|
-
signer: Jws.createSigner(alice),
|
|
1496
|
-
action: 'root',
|
|
1497
|
-
projectionRootVersion: RECORDS_PROJECTION_ROOT_VERSION,
|
|
1498
|
-
projectionScopes: [{ protocol: 'http://projected-sync-schema' }],
|
|
1499
|
-
});
|
|
1500
|
-
delete message.descriptor.projectionScopes;
|
|
1501
|
-
const reply = await dwn.processMessage(alice.did, message);
|
|
1502
|
-
expect(reply.status.code).toBe(400);
|
|
1503
|
-
expect(reply.status.detail).toContain('SchemaValidatorFailure');
|
|
1504
|
-
});
|
|
1505
|
-
it('returns 400 when projection root version is unsupported', async () => {
|
|
1506
|
-
const alice = await TestDataGenerator.generateDidKeyPersona();
|
|
1507
|
-
const { message } = await MessagesSync.create({
|
|
1508
|
-
signer: Jws.createSigner(alice),
|
|
1509
|
-
action: 'root',
|
|
1510
|
-
projectionRootVersion: RECORDS_PROJECTION_ROOT_VERSION,
|
|
1511
|
-
projectionScopes: [{ protocol: 'http://projected-sync-schema' }],
|
|
1512
|
-
});
|
|
1513
|
-
const unsupportedProjectionRootVersion = 'records-primary-scope-root-v2';
|
|
1514
|
-
const descriptor = {
|
|
1515
|
-
...message.descriptor,
|
|
1516
|
-
projectionRootVersion: unsupportedProjectionRootVersion,
|
|
1517
|
-
};
|
|
1518
|
-
const authorization = await Message.createAuthorization({
|
|
1519
|
-
descriptor,
|
|
1520
|
-
signer: Jws.createSigner(alice),
|
|
1521
|
-
});
|
|
1522
|
-
const unsupportedVersionMessage = {
|
|
1523
|
-
...message,
|
|
1524
|
-
descriptor,
|
|
1525
|
-
authorization,
|
|
1526
|
-
};
|
|
1527
|
-
const validateJsonSchemaStub = sinon.stub(Message, 'validateJsonSchema');
|
|
1528
|
-
try {
|
|
1529
|
-
const reply = await dwn.processMessage(alice.did, unsupportedVersionMessage);
|
|
1530
|
-
expect(reply.status.code).toBe(400);
|
|
1531
|
-
expect(reply.status.detail).toContain(DwnErrorCode.MessagesSyncUnsupportedProjectionRootVersion);
|
|
1532
|
-
}
|
|
1533
|
-
finally {
|
|
1534
|
-
validateJsonSchemaStub.restore();
|
|
1535
|
-
}
|
|
1536
|
-
});
|
|
1537
|
-
it('returns 400 when projected sync also specifies a protocol root', async () => {
|
|
1538
|
-
const alice = await TestDataGenerator.generateDidKeyPersona();
|
|
1539
|
-
const { message } = await MessagesSync.create({
|
|
1540
|
-
signer: Jws.createSigner(alice),
|
|
1541
|
-
action: 'root',
|
|
1542
|
-
projectionRootVersion: RECORDS_PROJECTION_ROOT_VERSION,
|
|
1543
|
-
projectionScopes: [{ protocol: 'http://projected-sync-schema' }],
|
|
1544
|
-
});
|
|
1545
|
-
message.descriptor.protocol = 'http://projected-sync-schema';
|
|
1546
|
-
const reply = await dwn.processMessage(alice.did, message);
|
|
1547
|
-
expect(reply.status.code).toBe(400);
|
|
1548
|
-
expect(reply.status.detail).toContain('SchemaValidatorFailure');
|
|
1549
|
-
});
|
|
1550
|
-
});
|
|
1551
|
-
describe('diff action', () => {
|
|
1552
|
-
it('returns empty diff when client hashes match server hashes', async () => {
|
|
1553
|
-
const alice = await TestDataGenerator.generateDidKeyPersona();
|
|
1554
|
-
await TestDataGenerator.installDefaultTestProtocol(dwn, alice);
|
|
1555
|
-
// write a record
|
|
1556
|
-
const { message: recordMessage, dataStream } = await TestDataGenerator.generateRecordsWrite({ author: alice });
|
|
1557
|
-
const writeReply = await dwn.processMessage(alice.did, recordMessage, { dataStream });
|
|
1558
|
-
expect(writeReply.status.code).toBe(202);
|
|
1559
|
-
// get the server's subtree hashes at depth 2 via individual subtree requests
|
|
1560
|
-
const serverHashes = {};
|
|
1561
|
-
for (const prefix of ['00', '01', '10', '11']) {
|
|
1562
|
-
const { message: subtreeMsg } = await MessagesSync.create({
|
|
1563
|
-
signer: Jws.createSigner(alice),
|
|
1564
|
-
action: 'subtree',
|
|
1565
|
-
prefix,
|
|
1566
|
-
});
|
|
1567
|
-
const subtreeReply = await dwn.processMessage(alice.did, subtreeMsg);
|
|
1568
|
-
if (subtreeReply.hash) {
|
|
1569
|
-
serverHashes[prefix] = subtreeReply.hash;
|
|
1570
|
-
}
|
|
1571
|
-
}
|
|
1572
|
-
// send diff with matching hashes — should get empty diff
|
|
1573
|
-
const { message: diffMsg } = await MessagesSync.create({
|
|
1574
|
-
signer: Jws.createSigner(alice),
|
|
1575
|
-
action: 'diff',
|
|
1576
|
-
hashes: serverHashes,
|
|
1577
|
-
depth: 2,
|
|
1578
|
-
});
|
|
1579
|
-
const reply = await dwn.processMessage(alice.did, diffMsg);
|
|
1580
|
-
expect(reply.status.code).toBe(200);
|
|
1581
|
-
expect(reply.onlyRemote).toEqual([]);
|
|
1582
|
-
expect(reply.onlyLocal).toEqual([]);
|
|
1583
|
-
});
|
|
1584
|
-
it('returns onlyRemote entries when client sends empty hashes', async () => {
|
|
1585
|
-
const alice = await TestDataGenerator.generateDidKeyPersona();
|
|
1586
|
-
await TestDataGenerator.installDefaultTestProtocol(dwn, alice);
|
|
1587
|
-
// write a record
|
|
1588
|
-
const { message: recordMessage, dataStream } = await TestDataGenerator.generateRecordsWrite({ author: alice });
|
|
1589
|
-
const writeReply = await dwn.processMessage(alice.did, recordMessage, { dataStream });
|
|
1590
|
-
expect(writeReply.status.code).toBe(202);
|
|
1591
|
-
// send diff with empty hashes — everything on the server is onlyRemote
|
|
1592
|
-
const { message: diffMsg } = await MessagesSync.create({
|
|
1593
|
-
signer: Jws.createSigner(alice),
|
|
1594
|
-
action: 'diff',
|
|
1595
|
-
hashes: {},
|
|
1596
|
-
depth: 2,
|
|
1597
|
-
});
|
|
1598
|
-
const reply = await dwn.processMessage(alice.did, diffMsg);
|
|
1599
|
-
expect(reply.status.code).toBe(200);
|
|
1600
|
-
expect(reply.onlyRemote.length).toBeGreaterThan(0);
|
|
1601
|
-
// each entry should have a messageCid and message
|
|
1602
|
-
for (const entry of reply.onlyRemote) {
|
|
1603
|
-
expect(typeof entry.messageCid).toBe('string');
|
|
1604
|
-
expect(entry.message).toBeDefined();
|
|
1605
|
-
}
|
|
1606
|
-
});
|
|
1607
|
-
it('returns onlyLocal prefixes when server has empty subtrees', async () => {
|
|
1608
|
-
const alice = await TestDataGenerator.generateDidKeyPersona();
|
|
1609
|
-
// send diff with non-empty client hashes against an empty server
|
|
1610
|
-
const { message: diffMsg } = await MessagesSync.create({
|
|
1611
|
-
signer: Jws.createSigner(alice),
|
|
1612
|
-
action: 'diff',
|
|
1613
|
-
hashes: { '00': 'aabbccdd', '01': '11223344' },
|
|
1614
|
-
depth: 2,
|
|
1615
|
-
});
|
|
1616
|
-
const reply = await dwn.processMessage(alice.did, diffMsg);
|
|
1617
|
-
expect(reply.status.code).toBe(200);
|
|
1618
|
-
expect(reply.onlyRemote).toEqual([]);
|
|
1619
|
-
expect(reply.onlyLocal).toContain('00');
|
|
1620
|
-
expect(reply.onlyLocal).toContain('01');
|
|
1621
|
-
});
|
|
1622
|
-
it('prunes empty server subtrees at the current diff depth', async () => {
|
|
1623
|
-
const alice = await TestDataGenerator.generateDidKeyPersona();
|
|
1624
|
-
const getSubtreeHashSpy = sinon.spy(stateIndex, 'getSubtreeHash');
|
|
1625
|
-
try {
|
|
1626
|
-
const { message: diffMsg } = await MessagesSync.create({
|
|
1627
|
-
signer: Jws.createSigner(alice),
|
|
1628
|
-
action: 'diff',
|
|
1629
|
-
hashes: {},
|
|
1630
|
-
depth: 64,
|
|
1631
|
-
});
|
|
1632
|
-
const reply = await dwn.processMessage(alice.did, diffMsg);
|
|
1633
|
-
expect(reply.status.code).toBe(200);
|
|
1634
|
-
expect(reply.onlyRemote).toEqual([]);
|
|
1635
|
-
expect(reply.onlyLocal).toEqual([]);
|
|
1636
|
-
expect(getSubtreeHashSpy.callCount).toBe(1);
|
|
1637
|
-
}
|
|
1638
|
-
finally {
|
|
1639
|
-
getSubtreeHashSpy.restore();
|
|
1640
|
-
}
|
|
1641
|
-
});
|
|
1642
|
-
it('inlines small data payloads as encodedData in onlyRemote entries', async () => {
|
|
1643
|
-
const alice = await TestDataGenerator.generateDidKeyPersona();
|
|
1644
|
-
await TestDataGenerator.installDefaultTestProtocol(dwn, alice);
|
|
1645
|
-
// write a small record
|
|
1646
|
-
const { message: recordMessage, dataStream } = await TestDataGenerator.generateRecordsWrite({
|
|
1647
|
-
author: alice,
|
|
1648
|
-
data: new TextEncoder().encode('small payload'),
|
|
1649
|
-
dataFormat: 'text/plain',
|
|
1650
|
-
});
|
|
1651
|
-
const writeReply = await dwn.processMessage(alice.did, recordMessage, { dataStream });
|
|
1652
|
-
expect(writeReply.status.code).toBe(202);
|
|
1653
|
-
// diff with empty client — server should inline the data
|
|
1654
|
-
const { message: diffMsg } = await MessagesSync.create({
|
|
1655
|
-
signer: Jws.createSigner(alice),
|
|
1656
|
-
action: 'diff',
|
|
1657
|
-
hashes: {},
|
|
1658
|
-
depth: 2,
|
|
1659
|
-
});
|
|
1660
|
-
const reply = await dwn.processMessage(alice.did, diffMsg);
|
|
1661
|
-
expect(reply.status.code).toBe(200);
|
|
1662
|
-
// find the RecordsWrite entry
|
|
1663
|
-
const recordEntry = reply.onlyRemote.find((e) => e.message?.descriptor.interface === 'Records' && e.message?.descriptor.method === 'Write');
|
|
1664
|
-
expect(recordEntry).toBeDefined();
|
|
1665
|
-
expect(recordEntry.encodedData).toBeDefined();
|
|
1666
|
-
expect(typeof recordEntry.encodedData).toBe('string');
|
|
1667
|
-
});
|
|
1668
|
-
it('inlines small dataStore-backed payloads using the RecordsWrite recordId', async () => {
|
|
1669
|
-
const alice = await TestDataGenerator.generateDidKeyPersona();
|
|
1670
|
-
await TestDataGenerator.installDefaultTestProtocol(dwn, alice);
|
|
1671
|
-
const dataBytes = new TextEncoder().encode('small data-store payload');
|
|
1672
|
-
const { message: recordMessage, recordsWrite } = await TestDataGenerator.generateRecordsWrite({
|
|
1673
|
-
author: alice,
|
|
1674
|
-
data: dataBytes,
|
|
1675
|
-
dataFormat: 'text/plain',
|
|
1676
|
-
});
|
|
1677
|
-
const indexes = await recordsWrite.constructIndexes(true);
|
|
1678
|
-
const messageCid = await Message.getCid(recordMessage);
|
|
1679
|
-
await dataStore.put(alice.did, recordMessage.recordId, recordMessage.descriptor.dataCid, DataStream.fromBytes(dataBytes));
|
|
1680
|
-
await messageStore.put(alice.did, recordMessage, indexes);
|
|
1681
|
-
await stateIndex.insert(alice.did, messageCid, indexes);
|
|
1682
|
-
const { message: diffMsg } = await MessagesSync.create({
|
|
1683
|
-
signer: Jws.createSigner(alice),
|
|
1684
|
-
action: 'diff',
|
|
1685
|
-
hashes: {},
|
|
1686
|
-
depth: 2,
|
|
1687
|
-
});
|
|
1688
|
-
const reply = await dwn.processMessage(alice.did, diffMsg);
|
|
1689
|
-
expect(reply.status.code).toBe(200);
|
|
1690
|
-
const recordEntry = reply.onlyRemote.find(entry => entry.messageCid === messageCid);
|
|
1691
|
-
expect(recordEntry).toBeDefined();
|
|
1692
|
-
expect(recordEntry.encodedData).toBe(Encoder.bytesToBase64Url(dataBytes));
|
|
1693
|
-
});
|
|
1694
|
-
it('returns 400 when hashes or depth are missing', async () => {
|
|
1695
|
-
const alice = await TestDataGenerator.generateDidKeyPersona();
|
|
1696
|
-
// create a valid diff message then strip required fields
|
|
1697
|
-
const { message } = await MessagesSync.create({
|
|
1698
|
-
signer: Jws.createSigner(alice),
|
|
1699
|
-
action: 'diff',
|
|
1700
|
-
hashes: {},
|
|
1701
|
-
depth: 2,
|
|
1702
|
-
});
|
|
1703
|
-
// stub parse to skip schema validation
|
|
1704
|
-
const parseStub = sinon.stub(MessagesSync, 'parse').resolves({
|
|
1705
|
-
author: alice.did,
|
|
1706
|
-
message,
|
|
1707
|
-
signaturePayload: { descriptorCid: 'test' },
|
|
1708
|
-
});
|
|
1709
|
-
try {
|
|
1710
|
-
// remove hashes to trigger the guard
|
|
1711
|
-
delete message.descriptor.hashes;
|
|
1712
|
-
const handler = new MessagesSyncHandler({ didResolver, messageStore, stateIndex, dataStore });
|
|
1713
|
-
const reply = await handler.handle({ tenant: alice.did, message });
|
|
1714
|
-
expect(reply.status.code).toBe(400);
|
|
1715
|
-
expect(reply.status.detail).toContain('diff action requires hashes and depth');
|
|
1716
|
-
}
|
|
1717
|
-
finally {
|
|
1718
|
-
parseStub.restore();
|
|
1719
|
-
}
|
|
1720
|
-
});
|
|
1721
|
-
it('handles protocol-scoped diff', async () => {
|
|
1722
|
-
const alice = await TestDataGenerator.generateDidKeyPersona();
|
|
1723
|
-
await TestDataGenerator.installDefaultTestProtocol(dwn, alice);
|
|
1724
|
-
// write a record using the default test protocol
|
|
1725
|
-
const { message: recordMessage, dataStream } = await TestDataGenerator.generateRecordsWrite({
|
|
1726
|
-
author: alice,
|
|
1727
|
-
});
|
|
1728
|
-
const writeReply = await dwn.processMessage(alice.did, recordMessage, { dataStream });
|
|
1729
|
-
expect(writeReply.status.code).toBe(202);
|
|
1730
|
-
// diff scoped to the default test protocol
|
|
1731
|
-
const protocol = recordMessage.descriptor.protocol;
|
|
1732
|
-
const { message: diffMsg } = await MessagesSync.create({
|
|
1733
|
-
signer: Jws.createSigner(alice),
|
|
1734
|
-
action: 'diff',
|
|
1735
|
-
hashes: {},
|
|
1736
|
-
depth: 2,
|
|
1737
|
-
protocol,
|
|
1738
|
-
});
|
|
1739
|
-
const reply = await dwn.processMessage(alice.did, diffMsg);
|
|
1740
|
-
expect(reply.status.code).toBe(200);
|
|
1741
|
-
expect(reply.onlyRemote.length).toBeGreaterThan(0);
|
|
1742
|
-
});
|
|
1743
|
-
});
|
|
1744
|
-
describe('error handling', () => {
|
|
1745
|
-
it('returns 500 when stateIndex throws an unexpected error', async () => {
|
|
1746
|
-
const alice = await TestDataGenerator.generateDidKeyPersona();
|
|
1747
|
-
const failingStateIndex = {
|
|
1748
|
-
open: async () => { },
|
|
1749
|
-
close: async () => { },
|
|
1750
|
-
clear: async () => { },
|
|
1751
|
-
insert: async () => { },
|
|
1752
|
-
delete: async () => { },
|
|
1753
|
-
getRoot: async () => { throw new Error('Unexpected DB failure'); },
|
|
1754
|
-
getProtocolRoot: async () => { throw new Error('Unexpected DB failure'); },
|
|
1755
|
-
getSubtreeHash: async () => { throw new Error('Unexpected DB failure'); },
|
|
1756
|
-
getProtocolSubtreeHash: async () => { throw new Error('Unexpected DB failure'); },
|
|
1757
|
-
getLeaves: async () => { throw new Error('Unexpected DB failure'); },
|
|
1758
|
-
getProtocolLeaves: async () => { throw new Error('Unexpected DB failure'); },
|
|
1759
|
-
};
|
|
1760
|
-
const handler = new MessagesSyncHandler({ didResolver, messageStore, stateIndex: failingStateIndex });
|
|
1761
|
-
const { message } = await MessagesSync.create({
|
|
1762
|
-
signer: Jws.createSigner(alice),
|
|
1763
|
-
action: 'root',
|
|
1764
|
-
});
|
|
1765
|
-
const reply = await handler.handle({ tenant: alice.did, message });
|
|
1766
|
-
expect(reply.status.code).toBe(500);
|
|
1767
|
-
});
|
|
1768
|
-
});
|
|
1769
|
-
});
|
|
1770
|
-
}
|
|
1771
|
-
//# sourceMappingURL=messages-sync.spec.js.map
|