@enbox/dwn-sdk-js 0.0.6 → 0.0.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/browser.mjs +8 -8
- package/dist/browser.mjs.map +4 -4
- package/dist/esm/generated/precompiled-validators.js +762 -911
- package/dist/esm/generated/precompiled-validators.js.map +1 -1
- package/dist/esm/src/core/abstract-message.js +4 -0
- package/dist/esm/src/core/abstract-message.js.map +1 -1
- package/dist/esm/src/core/auth.js +22 -33
- package/dist/esm/src/core/auth.js.map +1 -1
- package/dist/esm/src/core/constants.js +11 -0
- package/dist/esm/src/core/constants.js.map +1 -0
- package/dist/esm/src/core/core-protocol.js +44 -0
- package/dist/esm/src/core/core-protocol.js.map +1 -0
- package/dist/esm/src/core/dwn-constant.js +7 -7
- package/dist/esm/src/core/dwn-constant.js.map +1 -1
- package/dist/esm/src/core/dwn-error.js +10 -12
- package/dist/esm/src/core/dwn-error.js.map +1 -1
- package/dist/esm/src/core/grant-authorization.js +50 -52
- package/dist/esm/src/core/grant-authorization.js.map +1 -1
- package/dist/esm/src/core/message.js +85 -116
- package/dist/esm/src/core/message.js.map +1 -1
- package/dist/esm/src/core/messages-grant-authorization.js +63 -78
- package/dist/esm/src/core/messages-grant-authorization.js.map +1 -1
- package/dist/esm/src/core/protocol-authorization-action.js +266 -0
- package/dist/esm/src/core/protocol-authorization-action.js.map +1 -0
- package/dist/esm/src/core/protocol-authorization-validation.js +321 -0
- package/dist/esm/src/core/protocol-authorization-validation.js.map +1 -0
- package/dist/esm/src/core/protocol-authorization.js +144 -741
- package/dist/esm/src/core/protocol-authorization.js.map +1 -1
- package/dist/esm/src/core/protocols-grant-authorization.js +24 -38
- package/dist/esm/src/core/protocols-grant-authorization.js.map +1 -1
- package/dist/esm/src/core/record-chain.js +64 -0
- package/dist/esm/src/core/record-chain.js.map +1 -0
- package/dist/esm/src/core/records-grant-authorization.js +53 -72
- package/dist/esm/src/core/records-grant-authorization.js.map +1 -1
- package/dist/esm/src/core/resumable-task-manager.js +50 -65
- package/dist/esm/src/core/resumable-task-manager.js.map +1 -1
- package/dist/esm/src/core/tenant-gate.js +2 -13
- package/dist/esm/src/core/tenant-gate.js.map +1 -1
- package/dist/esm/src/dwn.js +108 -101
- package/dist/esm/src/dwn.js.map +1 -1
- package/dist/esm/src/event-stream/event-emitter-event-log.js +204 -0
- package/dist/esm/src/event-stream/event-emitter-event-log.js.map +1 -0
- package/dist/esm/src/handlers/messages-read.js +67 -81
- package/dist/esm/src/handlers/messages-read.js.map +1 -1
- package/dist/esm/src/handlers/messages-subscribe.js +51 -63
- package/dist/esm/src/handlers/messages-subscribe.js.map +1 -1
- package/dist/esm/src/handlers/messages-sync.js +75 -89
- package/dist/esm/src/handlers/messages-sync.js.map +1 -1
- package/dist/esm/src/handlers/protocols-configure.js +153 -163
- package/dist/esm/src/handlers/protocols-configure.js.map +1 -1
- package/dist/esm/src/handlers/protocols-query.js +52 -55
- package/dist/esm/src/handlers/protocols-query.js.map +1 -1
- package/dist/esm/src/handlers/records-count.js +97 -85
- package/dist/esm/src/handlers/records-count.js.map +1 -1
- package/dist/esm/src/handlers/records-delete.js +75 -93
- package/dist/esm/src/handlers/records-delete.js.map +1 -1
- package/dist/esm/src/handlers/records-query.js +116 -105
- package/dist/esm/src/handlers/records-query.js.map +1 -1
- package/dist/esm/src/handlers/records-read.js +130 -132
- package/dist/esm/src/handlers/records-read.js.map +1 -1
- package/dist/esm/src/handlers/records-subscribe.js +164 -104
- package/dist/esm/src/handlers/records-subscribe.js.map +1 -1
- package/dist/esm/src/handlers/records-write.js +213 -280
- package/dist/esm/src/handlers/records-write.js.map +1 -1
- package/dist/esm/src/index.js +5 -2
- package/dist/esm/src/index.js.map +1 -1
- package/dist/esm/src/interfaces/messages-read.js +24 -32
- package/dist/esm/src/interfaces/messages-read.js.map +1 -1
- package/dist/esm/src/interfaces/messages-subscribe.js +28 -41
- package/dist/esm/src/interfaces/messages-subscribe.js.map +1 -1
- package/dist/esm/src/interfaces/messages-sync.js +26 -40
- package/dist/esm/src/interfaces/messages-sync.js.map +1 -1
- package/dist/esm/src/interfaces/protocols-configure.js +87 -65
- package/dist/esm/src/interfaces/protocols-configure.js.map +1 -1
- package/dist/esm/src/interfaces/protocols-query.js +55 -68
- package/dist/esm/src/interfaces/protocols-query.js.map +1 -1
- package/dist/esm/src/interfaces/records-count.js +50 -66
- package/dist/esm/src/interfaces/records-count.js.map +1 -1
- package/dist/esm/src/interfaces/records-delete.js +45 -55
- package/dist/esm/src/interfaces/records-delete.js.map +1 -1
- package/dist/esm/src/interfaces/records-query.js +60 -76
- package/dist/esm/src/interfaces/records-query.js.map +1 -1
- package/dist/esm/src/interfaces/records-read.js +51 -67
- package/dist/esm/src/interfaces/records-read.js.map +1 -1
- package/dist/esm/src/interfaces/records-subscribe.js +53 -68
- package/dist/esm/src/interfaces/records-subscribe.js.map +1 -1
- package/dist/esm/src/interfaces/records-write-query.js +102 -0
- package/dist/esm/src/interfaces/records-write-query.js.map +1 -0
- package/dist/esm/src/interfaces/records-write-signing.js +81 -0
- package/dist/esm/src/interfaces/records-write-signing.js.map +1 -0
- package/dist/esm/src/interfaces/records-write.js +396 -610
- package/dist/esm/src/interfaces/records-write.js.map +1 -1
- package/dist/esm/src/jose/algorithms/signing/ed25519.js +10 -19
- package/dist/esm/src/jose/algorithms/signing/ed25519.js.map +1 -1
- package/dist/esm/src/jose/jws/general/builder.js +23 -35
- package/dist/esm/src/jose/jws/general/builder.js.map +1 -1
- package/dist/esm/src/jose/jws/general/verifier.js +56 -69
- package/dist/esm/src/jose/jws/general/verifier.js.map +1 -1
- package/dist/esm/src/protocols/permission-grant.js +43 -14
- package/dist/esm/src/protocols/permission-grant.js.map +1 -1
- package/dist/esm/src/protocols/permission-request.js +28 -14
- package/dist/esm/src/protocols/permission-request.js.map +1 -1
- package/dist/esm/src/protocols/permissions.js +325 -227
- package/dist/esm/src/protocols/permissions.js.map +1 -1
- package/dist/esm/src/smt/smt-store-level.js +42 -64
- package/dist/esm/src/smt/smt-store-level.js.map +1 -1
- package/dist/esm/src/smt/smt-store-memory.js +19 -45
- package/dist/esm/src/smt/smt-store-memory.js.map +1 -1
- package/dist/esm/src/smt/smt-utils.js +28 -45
- package/dist/esm/src/smt/smt-utils.js.map +1 -1
- package/dist/esm/src/smt/sparse-merkle-tree.js +426 -471
- package/dist/esm/src/smt/sparse-merkle-tree.js.map +1 -1
- package/dist/esm/src/state-index/state-index-level.js +113 -150
- package/dist/esm/src/state-index/state-index-level.js.map +1 -1
- package/dist/esm/src/store/blockstore-level.js +54 -156
- package/dist/esm/src/store/blockstore-level.js.map +1 -1
- package/dist/esm/src/store/blockstore-mock.js +48 -153
- package/dist/esm/src/store/blockstore-mock.js.map +1 -1
- package/dist/esm/src/store/data-store-level.js +137 -100
- package/dist/esm/src/store/data-store-level.js.map +1 -1
- package/dist/esm/src/store/index-level-compound.js +246 -0
- package/dist/esm/src/store/index-level-compound.js.map +1 -0
- package/dist/esm/src/store/index-level.js +307 -715
- package/dist/esm/src/store/index-level.js.map +1 -1
- package/dist/esm/src/store/level-wrapper.js +143 -244
- package/dist/esm/src/store/level-wrapper.js.map +1 -1
- package/dist/esm/src/store/message-store-level.js +71 -94
- package/dist/esm/src/store/message-store-level.js.map +1 -1
- package/dist/esm/src/store/resumable-task-store-level.js +62 -101
- package/dist/esm/src/store/resumable-task-store-level.js.map +1 -1
- package/dist/esm/src/store/storage-controller.js +131 -146
- package/dist/esm/src/store/storage-controller.js.map +1 -1
- package/dist/esm/src/types/permission-types.js.map +1 -1
- package/dist/esm/src/types/protocols-types.js +10 -0
- package/dist/esm/src/types/protocols-types.js.map +1 -1
- package/dist/esm/src/types/records-types.js.map +1 -1
- package/dist/esm/src/utils/abort.js +8 -19
- package/dist/esm/src/utils/abort.js.map +1 -1
- package/dist/esm/src/utils/array.js +15 -49
- package/dist/esm/src/utils/array.js.map +1 -1
- package/dist/esm/src/utils/cid.js +29 -77
- package/dist/esm/src/utils/cid.js.map +1 -1
- package/dist/esm/src/utils/data-stream.js +37 -65
- package/dist/esm/src/utils/data-stream.js.map +1 -1
- package/dist/esm/src/utils/encryption.js +136 -162
- package/dist/esm/src/utils/encryption.js.map +1 -1
- package/dist/esm/src/utils/filter.js +1 -12
- package/dist/esm/src/utils/filter.js.map +1 -1
- package/dist/esm/src/utils/hd-key.js +45 -71
- package/dist/esm/src/utils/hd-key.js.map +1 -1
- package/dist/esm/src/utils/jws.js +9 -20
- package/dist/esm/src/utils/jws.js.map +1 -1
- package/dist/esm/src/utils/memory-cache.js +12 -23
- package/dist/esm/src/utils/memory-cache.js.map +1 -1
- package/dist/esm/src/utils/messages.js +21 -33
- package/dist/esm/src/utils/messages.js.map +1 -1
- package/dist/esm/src/utils/private-key-signer.js +9 -17
- package/dist/esm/src/utils/private-key-signer.js.map +1 -1
- package/dist/esm/src/utils/protocols.js +62 -70
- package/dist/esm/src/utils/protocols.js.map +1 -1
- package/dist/esm/src/utils/records.js +103 -166
- package/dist/esm/src/utils/records.js.map +1 -1
- package/dist/esm/src/utils/secp256k1.js +60 -96
- package/dist/esm/src/utils/secp256k1.js.map +1 -1
- package/dist/esm/src/utils/secp256r1.js +54 -71
- package/dist/esm/src/utils/secp256r1.js.map +1 -1
- package/dist/esm/src/utils/time.js +5 -18
- package/dist/esm/src/utils/time.js.map +1 -1
- package/dist/esm/src/utils/url.js +3 -3
- package/dist/esm/src/utils/url.js.map +1 -1
- package/dist/esm/tests/core/auth.spec.js +3 -12
- package/dist/esm/tests/core/auth.spec.js.map +1 -1
- package/dist/esm/tests/core/message.spec.js +50 -59
- package/dist/esm/tests/core/message.spec.js.map +1 -1
- package/dist/esm/tests/core/protocol-authorization.spec.js +10 -18
- package/dist/esm/tests/core/protocol-authorization.spec.js.map +1 -1
- package/dist/esm/tests/dwn.spec.js +65 -89
- package/dist/esm/tests/dwn.spec.js.map +1 -1
- package/dist/esm/tests/event-emitter-event-log.spec.js +305 -0
- package/dist/esm/tests/event-emitter-event-log.spec.js.map +1 -0
- package/dist/esm/tests/features/author-delegated-grant.spec.js +337 -347
- package/dist/esm/tests/features/author-delegated-grant.spec.js.map +1 -1
- package/dist/esm/tests/features/owner-delegated-grant.spec.js +160 -172
- package/dist/esm/tests/features/owner-delegated-grant.spec.js.map +1 -1
- package/dist/esm/tests/features/owner-signature.spec.js +78 -82
- package/dist/esm/tests/features/owner-signature.spec.js.map +1 -1
- package/dist/esm/tests/features/permissions.spec.js +449 -184
- package/dist/esm/tests/features/permissions.spec.js.map +1 -1
- package/dist/esm/tests/features/protocol-composition.spec.js +981 -360
- package/dist/esm/tests/features/protocol-composition.spec.js.map +1 -1
- package/dist/esm/tests/features/protocol-create-action.spec.js +45 -54
- package/dist/esm/tests/features/protocol-create-action.spec.js.map +1 -1
- package/dist/esm/tests/features/protocol-delete-action.spec.js +99 -108
- package/dist/esm/tests/features/protocol-delete-action.spec.js.map +1 -1
- package/dist/esm/tests/features/protocol-update-action.spec.js +108 -117
- package/dist/esm/tests/features/protocol-update-action.spec.js.map +1 -1
- package/dist/esm/tests/features/records-immutable.spec.js +315 -0
- package/dist/esm/tests/features/records-immutable.spec.js.map +1 -0
- package/dist/esm/tests/features/records-prune.spec.js +178 -194
- package/dist/esm/tests/features/records-prune.spec.js.map +1 -1
- package/dist/esm/tests/features/records-record-limit.spec.js +542 -0
- package/dist/esm/tests/features/records-record-limit.spec.js.map +1 -0
- package/dist/esm/tests/features/records-tags.spec.js +456 -463
- package/dist/esm/tests/features/records-tags.spec.js.map +1 -1
- package/dist/esm/tests/features/resumable-tasks.spec.js +88 -98
- package/dist/esm/tests/features/resumable-tasks.spec.js.map +1 -1
- package/dist/esm/tests/handlers/messages-read.spec.js +215 -210
- package/dist/esm/tests/handlers/messages-read.spec.js.map +1 -1
- package/dist/esm/tests/handlers/messages-subscribe.spec.js +309 -171
- package/dist/esm/tests/handlers/messages-subscribe.spec.js.map +1 -1
- package/dist/esm/tests/handlers/messages-sync.spec.js +272 -199
- package/dist/esm/tests/handlers/messages-sync.spec.js.map +1 -1
- package/dist/esm/tests/handlers/protocols-configure.spec.js +247 -241
- package/dist/esm/tests/handlers/protocols-configure.spec.js.map +1 -1
- package/dist/esm/tests/handlers/protocols-query.spec.js +159 -172
- package/dist/esm/tests/handlers/protocols-query.spec.js.map +1 -1
- package/dist/esm/tests/handlers/records-count.spec.js +101 -105
- package/dist/esm/tests/handlers/records-count.spec.js.map +1 -1
- package/dist/esm/tests/handlers/records-delete.spec.js +266 -279
- package/dist/esm/tests/handlers/records-delete.spec.js.map +1 -1
- package/dist/esm/tests/handlers/records-query.spec.js +984 -996
- package/dist/esm/tests/handlers/records-query.spec.js.map +1 -1
- package/dist/esm/tests/handlers/records-read.spec.js +542 -671
- package/dist/esm/tests/handlers/records-read.spec.js.map +1 -1
- package/dist/esm/tests/handlers/records-subscribe.spec.js +433 -302
- package/dist/esm/tests/handlers/records-subscribe.spec.js.map +1 -1
- package/dist/esm/tests/handlers/records-write.spec.js +1216 -1140
- package/dist/esm/tests/handlers/records-write.spec.js.map +1 -1
- package/dist/esm/tests/interfaces/messages-get.spec.js +39 -48
- package/dist/esm/tests/interfaces/messages-get.spec.js.map +1 -1
- package/dist/esm/tests/interfaces/messages-subscribe.spec.js +4 -13
- package/dist/esm/tests/interfaces/messages-subscribe.spec.js.map +1 -1
- package/dist/esm/tests/interfaces/protocols-configure.spec.js +212 -88
- package/dist/esm/tests/interfaces/protocols-configure.spec.js.map +1 -1
- package/dist/esm/tests/interfaces/protocols-query.spec.js +8 -17
- package/dist/esm/tests/interfaces/protocols-query.spec.js.map +1 -1
- package/dist/esm/tests/interfaces/records-delete.spec.js +8 -17
- package/dist/esm/tests/interfaces/records-delete.spec.js.map +1 -1
- package/dist/esm/tests/interfaces/records-query.spec.js +20 -29
- package/dist/esm/tests/interfaces/records-query.spec.js.map +1 -1
- package/dist/esm/tests/interfaces/records-read.spec.js +42 -51
- package/dist/esm/tests/interfaces/records-read.spec.js.map +1 -1
- package/dist/esm/tests/interfaces/records-subscribe.spec.js +16 -25
- package/dist/esm/tests/interfaces/records-subscribe.spec.js.map +1 -1
- package/dist/esm/tests/interfaces/records-write.spec.js +190 -219
- package/dist/esm/tests/interfaces/records-write.spec.js.map +1 -1
- package/dist/esm/tests/jose/jws/general.spec.js +36 -45
- package/dist/esm/tests/jose/jws/general.spec.js.map +1 -1
- package/dist/esm/tests/protocols/permission-grant.spec.js +44 -50
- package/dist/esm/tests/protocols/permission-grant.spec.js.map +1 -1
- package/dist/esm/tests/protocols/permission-request.spec.js +23 -32
- package/dist/esm/tests/protocols/permission-request.spec.js.map +1 -1
- package/dist/esm/tests/protocols/permissions.spec.js +49 -55
- package/dist/esm/tests/protocols/permissions.spec.js.map +1 -1
- package/dist/esm/tests/scenarios/aggregator.spec.js +127 -138
- package/dist/esm/tests/scenarios/aggregator.spec.js.map +1 -1
- package/dist/esm/tests/scenarios/deleted-record.spec.js +372 -36
- package/dist/esm/tests/scenarios/deleted-record.spec.js.map +1 -1
- package/dist/esm/tests/scenarios/end-to-end-tests.spec.js +55 -64
- package/dist/esm/tests/scenarios/end-to-end-tests.spec.js.map +1 -1
- package/dist/esm/tests/scenarios/nested-roles.spec.js +66 -76
- package/dist/esm/tests/scenarios/nested-roles.spec.js.map +1 -1
- package/dist/esm/tests/scenarios/subscriptions.spec.js +451 -354
- package/dist/esm/tests/scenarios/subscriptions.spec.js.map +1 -1
- package/dist/esm/tests/smt/smt-store-level.spec.js +76 -87
- package/dist/esm/tests/smt/smt-store-level.spec.js.map +1 -1
- package/dist/esm/tests/smt/sparse-merkle-tree.spec.js +344 -353
- package/dist/esm/tests/smt/sparse-merkle-tree.spec.js.map +1 -1
- package/dist/esm/tests/state-index/state-index-level.spec.js +117 -126
- package/dist/esm/tests/state-index/state-index-level.spec.js.map +1 -1
- package/dist/esm/tests/store/blockstore-level.spec.js +44 -99
- package/dist/esm/tests/store/blockstore-level.spec.js.map +1 -1
- package/dist/esm/tests/store/blockstore-mock.spec.js +40 -120
- package/dist/esm/tests/store/blockstore-mock.spec.js.map +1 -1
- package/dist/esm/tests/store/data-store-level.spec.js +160 -108
- package/dist/esm/tests/store/data-store-level.spec.js.map +1 -1
- package/dist/esm/tests/store/index-level.spec.js +404 -414
- package/dist/esm/tests/store/index-level.spec.js.map +1 -1
- package/dist/esm/tests/store/message-store-level.spec.js +13 -22
- package/dist/esm/tests/store/message-store-level.spec.js.map +1 -1
- package/dist/esm/tests/store/message-store.spec.js +229 -238
- package/dist/esm/tests/store/message-store.spec.js.map +1 -1
- package/dist/esm/tests/test-event-stream.js +12 -13
- package/dist/esm/tests/test-event-stream.js.map +1 -1
- package/dist/esm/tests/test-stores.js +16 -13
- package/dist/esm/tests/test-stores.js.map +1 -1
- package/dist/esm/tests/test-suite.js +8 -15
- package/dist/esm/tests/test-suite.js.map +1 -1
- package/dist/esm/tests/utils/cid.spec.js +24 -33
- package/dist/esm/tests/utils/cid.spec.js.map +1 -1
- package/dist/esm/tests/utils/data-stream.spec.js +48 -57
- package/dist/esm/tests/utils/data-stream.spec.js.map +1 -1
- package/dist/esm/tests/utils/encryption-callbacks.spec.js +45 -54
- package/dist/esm/tests/utils/encryption-callbacks.spec.js.map +1 -1
- package/dist/esm/tests/utils/encryption.spec.js +229 -82
- package/dist/esm/tests/utils/encryption.spec.js.map +1 -1
- package/dist/esm/tests/utils/filters.spec.js +46 -55
- package/dist/esm/tests/utils/filters.spec.js.map +1 -1
- package/dist/esm/tests/utils/hd-key.spec.js +10 -19
- package/dist/esm/tests/utils/hd-key.spec.js.map +1 -1
- package/dist/esm/tests/utils/jws.spec.js +3 -12
- package/dist/esm/tests/utils/jws.spec.js.map +1 -1
- package/dist/esm/tests/utils/memory-cache.spec.js +9 -18
- package/dist/esm/tests/utils/memory-cache.spec.js.map +1 -1
- package/dist/esm/tests/utils/messages.spec.js +18 -20
- package/dist/esm/tests/utils/messages.spec.js.map +1 -1
- package/dist/esm/tests/utils/poller.js +22 -33
- package/dist/esm/tests/utils/poller.js.map +1 -1
- package/dist/esm/tests/utils/private-key-signer.spec.js +15 -24
- package/dist/esm/tests/utils/private-key-signer.spec.js.map +1 -1
- package/dist/esm/tests/utils/records.spec.js +14 -27
- package/dist/esm/tests/utils/records.spec.js.map +1 -1
- package/dist/esm/tests/utils/secp256k1.spec.js +16 -25
- package/dist/esm/tests/utils/secp256k1.spec.js.map +1 -1
- package/dist/esm/tests/utils/secp256r1.spec.js +18 -27
- package/dist/esm/tests/utils/secp256r1.spec.js.map +1 -1
- package/dist/esm/tests/utils/test-data-generator.js +446 -467
- package/dist/esm/tests/utils/test-data-generator.js.map +1 -1
- package/dist/esm/tests/validation/json-schemas/definitions.spec.js +2 -11
- package/dist/esm/tests/validation/json-schemas/definitions.spec.js.map +1 -1
- package/dist/esm/tests/validation/json-schemas/jwk/general-jwk.spec.js +4 -13
- package/dist/esm/tests/validation/json-schemas/jwk/general-jwk.spec.js.map +1 -1
- package/dist/esm/tests/validation/json-schemas/jwk/public-jwk.spec.js +8 -17
- package/dist/esm/tests/validation/json-schemas/jwk/public-jwk.spec.js.map +1 -1
- package/dist/esm/tests/validation/json-schemas/jwk-verification-method.spec.js +3 -12
- package/dist/esm/tests/validation/json-schemas/jwk-verification-method.spec.js.map +1 -1
- package/dist/esm/tests/validation/json-schemas/protocols/protocols-configure.spec.js +4 -13
- package/dist/esm/tests/validation/json-schemas/protocols/protocols-configure.spec.js.map +1 -1
- package/dist/esm/tests/validation/json-schemas/records/records-query.spec.js +2 -11
- package/dist/esm/tests/validation/json-schemas/records/records-query.spec.js.map +1 -1
- package/dist/esm/tests/validation/json-schemas/records/records-read.spec.js +2 -11
- package/dist/esm/tests/validation/json-schemas/records/records-read.spec.js.map +1 -1
- package/dist/esm/tests/validation/json-schemas/records/records-write.spec.js +44 -24
- package/dist/esm/tests/validation/json-schemas/records/records-write.spec.js.map +1 -1
- package/dist/types/generated/precompiled-validators.d.ts +49 -40
- package/dist/types/generated/precompiled-validators.d.ts.map +1 -1
- package/dist/types/src/core/constants.d.ts +11 -0
- package/dist/types/src/core/constants.d.ts.map +1 -0
- package/dist/types/src/core/core-protocol.d.ts +89 -0
- package/dist/types/src/core/core-protocol.d.ts.map +1 -0
- package/dist/types/src/core/dwn-error.d.ts +9 -12
- package/dist/types/src/core/dwn-error.d.ts.map +1 -1
- package/dist/types/src/core/grant-authorization.d.ts +6 -2
- package/dist/types/src/core/grant-authorization.d.ts.map +1 -1
- package/dist/types/src/core/protocol-authorization-action.d.ts +42 -0
- package/dist/types/src/core/protocol-authorization-action.d.ts.map +1 -0
- package/dist/types/src/core/protocol-authorization-validation.d.ts +81 -0
- package/dist/types/src/core/protocol-authorization-validation.d.ts.map +1 -0
- package/dist/types/src/core/protocol-authorization.d.ts +24 -106
- package/dist/types/src/core/protocol-authorization.d.ts.map +1 -1
- package/dist/types/src/core/record-chain.d.ts +24 -0
- package/dist/types/src/core/record-chain.d.ts.map +1 -0
- package/dist/types/src/core/records-grant-authorization.d.ts.map +1 -1
- package/dist/types/src/dwn.d.ts +19 -7
- package/dist/types/src/dwn.d.ts.map +1 -1
- package/dist/types/src/event-stream/event-emitter-event-log.d.ts +50 -0
- package/dist/types/src/event-stream/event-emitter-event-log.d.ts.map +1 -0
- package/dist/types/src/handlers/messages-read.d.ts +3 -8
- package/dist/types/src/handlers/messages-read.d.ts.map +1 -1
- package/dist/types/src/handlers/messages-subscribe.d.ts +6 -10
- package/dist/types/src/handlers/messages-subscribe.d.ts.map +1 -1
- package/dist/types/src/handlers/messages-sync.d.ts +3 -8
- package/dist/types/src/handlers/messages-sync.d.ts.map +1 -1
- package/dist/types/src/handlers/protocols-configure.d.ts +3 -10
- package/dist/types/src/handlers/protocols-configure.d.ts.map +1 -1
- package/dist/types/src/handlers/protocols-query.d.ts +3 -8
- package/dist/types/src/handlers/protocols-query.d.ts.map +1 -1
- package/dist/types/src/handlers/records-count.d.ts +3 -6
- package/dist/types/src/handlers/records-count.d.ts.map +1 -1
- package/dist/types/src/handlers/records-delete.d.ts +3 -8
- package/dist/types/src/handlers/records-delete.d.ts.map +1 -1
- package/dist/types/src/handlers/records-query.d.ts +3 -8
- package/dist/types/src/handlers/records-query.d.ts.map +1 -1
- package/dist/types/src/handlers/records-read.d.ts +3 -8
- package/dist/types/src/handlers/records-read.d.ts.map +1 -1
- package/dist/types/src/handlers/records-subscribe.d.ts +8 -10
- package/dist/types/src/handlers/records-subscribe.d.ts.map +1 -1
- package/dist/types/src/handlers/records-write.d.ts +4 -24
- package/dist/types/src/handlers/records-write.d.ts.map +1 -1
- package/dist/types/src/index.d.ts +8 -4
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/interfaces/messages-subscribe.d.ts +5 -0
- package/dist/types/src/interfaces/messages-subscribe.d.ts.map +1 -1
- package/dist/types/src/interfaces/protocols-configure.d.ts.map +1 -1
- package/dist/types/src/interfaces/records-subscribe.d.ts +5 -0
- package/dist/types/src/interfaces/records-subscribe.d.ts.map +1 -1
- package/dist/types/src/interfaces/records-write-query.d.ts +33 -0
- package/dist/types/src/interfaces/records-write-query.d.ts.map +1 -0
- package/dist/types/src/interfaces/records-write-signing.d.ts +34 -0
- package/dist/types/src/interfaces/records-write-signing.d.ts.map +1 -0
- package/dist/types/src/interfaces/records-write.d.ts +13 -53
- package/dist/types/src/interfaces/records-write.d.ts.map +1 -1
- package/dist/types/src/protocols/permission-grant.d.ts +1 -1
- package/dist/types/src/protocols/permission-grant.d.ts.map +1 -1
- package/dist/types/src/protocols/permission-request.d.ts +1 -1
- package/dist/types/src/protocols/permission-request.d.ts.map +1 -1
- package/dist/types/src/protocols/permissions.d.ts +40 -3
- package/dist/types/src/protocols/permissions.d.ts.map +1 -1
- package/dist/types/src/state-index/state-index-level.d.ts.map +1 -1
- package/dist/types/src/store/data-store-level.d.ts +20 -4
- package/dist/types/src/store/data-store-level.d.ts.map +1 -1
- package/dist/types/src/store/index-level-compound.d.ts +70 -0
- package/dist/types/src/store/index-level-compound.d.ts.map +1 -0
- package/dist/types/src/store/index-level.d.ts +4 -58
- package/dist/types/src/store/index-level.d.ts.map +1 -1
- package/dist/types/src/store/storage-controller.d.ts +4 -4
- package/dist/types/src/store/storage-controller.d.ts.map +1 -1
- package/dist/types/src/types/message-types.d.ts +3 -3
- package/dist/types/src/types/message-types.d.ts.map +1 -1
- package/dist/types/src/types/messages-types.d.ts +12 -3
- package/dist/types/src/types/messages-types.d.ts.map +1 -1
- package/dist/types/src/types/method-handler.d.ts +24 -3
- package/dist/types/src/types/method-handler.d.ts.map +1 -1
- package/dist/types/src/types/permission-types.d.ts +7 -0
- package/dist/types/src/types/permission-types.d.ts.map +1 -1
- package/dist/types/src/types/protocols-types.d.ts +41 -1
- package/dist/types/src/types/protocols-types.d.ts.map +1 -1
- package/dist/types/src/types/records-types.d.ts +16 -6
- package/dist/types/src/types/records-types.d.ts.map +1 -1
- package/dist/types/src/types/subscriptions.d.ts +151 -13
- package/dist/types/src/types/subscriptions.d.ts.map +1 -1
- package/dist/types/src/utils/hd-key.d.ts +1 -9
- package/dist/types/src/utils/hd-key.d.ts.map +1 -1
- package/dist/types/src/utils/messages.d.ts +7 -5
- package/dist/types/src/utils/messages.d.ts.map +1 -1
- package/dist/types/src/utils/protocols.d.ts +5 -0
- package/dist/types/src/utils/protocols.d.ts.map +1 -1
- package/dist/types/src/utils/records.d.ts +1 -11
- package/dist/types/src/utils/records.d.ts.map +1 -1
- package/dist/types/tests/dwn.spec.d.ts.map +1 -1
- package/dist/types/tests/event-emitter-event-log.spec.d.ts +2 -0
- package/dist/types/tests/event-emitter-event-log.spec.d.ts.map +1 -0
- package/dist/types/tests/features/author-delegated-grant.spec.d.ts.map +1 -1
- package/dist/types/tests/features/owner-delegated-grant.spec.d.ts.map +1 -1
- package/dist/types/tests/features/owner-signature.spec.d.ts.map +1 -1
- package/dist/types/tests/features/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/records-immutable.spec.d.ts +2 -0
- package/dist/types/tests/features/records-immutable.spec.d.ts.map +1 -0
- package/dist/types/tests/features/records-record-limit.spec.d.ts +2 -0
- package/dist/types/tests/features/records-record-limit.spec.d.ts.map +1 -0
- package/dist/types/tests/features/records-tags.spec.d.ts.map +1 -1
- package/dist/types/tests/features/resumable-tasks.spec.d.ts.map +1 -1
- package/dist/types/tests/handlers/messages-read.spec.d.ts.map +1 -1
- package/dist/types/tests/handlers/messages-subscribe.spec.d.ts.map +1 -1
- package/dist/types/tests/handlers/messages-sync.spec.d.ts.map +1 -1
- package/dist/types/tests/handlers/records-count.spec.d.ts.map +1 -1
- package/dist/types/tests/handlers/records-delete.spec.d.ts.map +1 -1
- package/dist/types/tests/handlers/records-query.spec.d.ts.map +1 -1
- package/dist/types/tests/handlers/records-read.spec.d.ts.map +1 -1
- package/dist/types/tests/handlers/records-subscribe.spec.d.ts.map +1 -1
- package/dist/types/tests/handlers/records-write.spec.d.ts.map +1 -1
- package/dist/types/tests/scenarios/deleted-record.spec.d.ts.map +1 -1
- package/dist/types/tests/scenarios/subscriptions.spec.d.ts.map +1 -1
- package/dist/types/tests/test-event-stream.d.ts +11 -12
- package/dist/types/tests/test-event-stream.d.ts.map +1 -1
- package/dist/types/tests/test-suite.d.ts +2 -2
- package/dist/types/tests/test-suite.d.ts.map +1 -1
- package/dist/types/tests/utils/test-data-generator.d.ts +18 -0
- package/dist/types/tests/utils/test-data-generator.d.ts.map +1 -1
- package/package.json +5 -4
- package/src/core/constants.ts +11 -0
- package/src/core/core-protocol.ts +129 -0
- package/src/core/dwn-error.ts +15 -12
- package/src/core/grant-authorization.ts +20 -3
- package/src/core/protocol-authorization-action.ts +377 -0
- package/src/core/protocol-authorization-validation.ts +487 -0
- package/src/core/protocol-authorization.ts +111 -856
- package/src/core/record-chain.ts +99 -0
- package/src/core/records-grant-authorization.ts +6 -8
- package/src/dwn.ts +58 -73
- package/src/event-stream/event-emitter-event-log.ts +283 -0
- package/src/handlers/messages-read.ts +8 -9
- package/src/handlers/messages-subscribe.ts +24 -28
- package/src/handlers/messages-sync.ts +10 -16
- package/src/handlers/protocols-configure.ts +47 -32
- package/src/handlers/protocols-query.ts +6 -9
- package/src/handlers/records-count.ts +11 -10
- package/src/handlers/records-delete.ts +12 -21
- package/src/handlers/records-query.ts +12 -12
- package/src/handlers/records-read.ts +34 -22
- package/src/handlers/records-subscribe.ts +47 -26
- package/src/handlers/records-write.ts +47 -104
- package/src/index.ts +9 -5
- package/src/interfaces/messages-subscribe.ts +7 -1
- package/src/interfaces/protocols-configure.ts +73 -8
- package/src/interfaces/records-count.ts +1 -1
- package/src/interfaces/records-delete.ts +1 -1
- package/src/interfaces/records-query.ts +1 -1
- package/src/interfaces/records-read.ts +1 -1
- package/src/interfaces/records-subscribe.ts +8 -1
- package/src/interfaces/records-write-query.ts +139 -0
- package/src/interfaces/records-write-signing.ts +123 -0
- package/src/interfaces/records-write.ts +66 -261
- package/src/protocols/permission-grant.ts +1 -1
- package/src/protocols/permission-request.ts +1 -1
- package/src/protocols/permissions.ts +148 -6
- package/src/state-index/state-index-level.ts +5 -7
- package/src/store/data-store-level.ts +124 -34
- package/src/store/index-level-compound.ts +324 -0
- package/src/store/index-level.ts +68 -341
- package/src/store/storage-controller.ts +11 -11
- package/src/types/message-types.ts +3 -3
- package/src/types/messages-types.ts +12 -3
- package/src/types/method-handler.ts +26 -4
- package/src/types/mitt.d.ts +28 -0
- package/src/types/permission-types.ts +7 -0
- package/src/types/protocols-types.ts +46 -0
- package/src/types/records-types.ts +16 -6
- package/src/types/subscriptions.ts +178 -14
- package/src/utils/hd-key.ts +0 -9
- package/src/utils/messages.ts +17 -37
- package/src/utils/protocols.ts +8 -0
- package/src/utils/records.ts +8 -59
- package/dist/esm/src/event-stream/event-emitter-stream.js +0 -60
- package/dist/esm/src/event-stream/event-emitter-stream.js.map +0 -1
- package/dist/esm/tests/event-stream/event-emitter-stream.spec.js +0 -77
- package/dist/esm/tests/event-stream/event-emitter-stream.spec.js.map +0 -1
- package/dist/esm/tests/event-stream/event-stream.spec.js +0 -123
- package/dist/esm/tests/event-stream/event-stream.spec.js.map +0 -1
- package/dist/types/src/event-stream/event-emitter-stream.d.ts +0 -23
- package/dist/types/src/event-stream/event-emitter-stream.d.ts.map +0 -1
- package/dist/types/tests/event-stream/event-emitter-stream.spec.d.ts +0 -2
- package/dist/types/tests/event-stream/event-emitter-stream.spec.d.ts.map +0 -1
- package/dist/types/tests/event-stream/event-stream.spec.d.ts +0 -2
- package/dist/types/tests/event-stream/event-stream.spec.d.ts.map +0 -1
- package/src/event-stream/event-emitter-stream.ts +0 -69
|
@@ -1,12 +1,3 @@
|
|
|
1
|
-
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
-
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
-
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
-
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
-
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
-
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
-
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
-
});
|
|
9
|
-
};
|
|
10
1
|
import sinon from 'sinon';
|
|
11
2
|
import { DataStream } from '../../src/utils/data-stream.js';
|
|
12
3
|
import { Dwn } from '../../src/dwn.js';
|
|
@@ -15,13 +6,13 @@ import { Jws } from '../../src/utils/jws.js';
|
|
|
15
6
|
import { Protocols } from '../../src/utils/protocols.js';
|
|
16
7
|
import { Records } from '../../src/utils/records.js';
|
|
17
8
|
import { TestDataGenerator } from '../utils/test-data-generator.js';
|
|
18
|
-
import {
|
|
9
|
+
import { TestEventLog } from '../test-event-stream.js';
|
|
19
10
|
import { TestStores } from '../test-stores.js';
|
|
20
11
|
import { TestStubGenerator } from '../utils/test-stub-generator.js';
|
|
21
12
|
import { X25519 } from '@enbox/crypto';
|
|
22
13
|
import { afterAll, beforeAll, beforeEach, describe, expect, it } from 'bun:test';
|
|
23
14
|
import { DidKey, UniversalResolver } from '@enbox/dids';
|
|
24
|
-
import { DwnErrorCode, Message, ProtocolsConfigure, RecordsDelete, RecordsQuery, RecordsRead, Time } from '../../src/index.js';
|
|
15
|
+
import { DwnErrorCode, Message, ProtocolsConfigure, RecordsCount, RecordsDelete, RecordsQuery, RecordsRead, RecordsSubscribe, Time } from '../../src/index.js';
|
|
25
16
|
import { HdKey, KeyDerivationScheme } from '../../src/utils/hd-key.js';
|
|
26
17
|
/**
|
|
27
18
|
* Tests for protocol composition using `uses` + `$ref`.
|
|
@@ -33,28 +24,28 @@ export function testProtocolComposition() {
|
|
|
33
24
|
let dataStore;
|
|
34
25
|
let resumableTaskStore;
|
|
35
26
|
let stateIndex;
|
|
36
|
-
let
|
|
27
|
+
let eventLog;
|
|
37
28
|
let dwn;
|
|
38
|
-
beforeAll(() =>
|
|
29
|
+
beforeAll(async () => {
|
|
39
30
|
didResolver = new UniversalResolver({ didResolvers: [DidKey] });
|
|
40
31
|
const stores = TestStores.get();
|
|
41
32
|
messageStore = stores.messageStore;
|
|
42
33
|
dataStore = stores.dataStore;
|
|
43
34
|
resumableTaskStore = stores.resumableTaskStore;
|
|
44
35
|
stateIndex = stores.stateIndex;
|
|
45
|
-
|
|
46
|
-
dwn =
|
|
47
|
-
})
|
|
48
|
-
beforeEach(() =>
|
|
36
|
+
eventLog = TestEventLog.get();
|
|
37
|
+
dwn = await Dwn.create({ didResolver, messageStore, dataStore, stateIndex, eventLog, resumableTaskStore });
|
|
38
|
+
});
|
|
39
|
+
beforeEach(async () => {
|
|
49
40
|
sinon.restore();
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
})
|
|
55
|
-
afterAll(() =>
|
|
56
|
-
|
|
57
|
-
})
|
|
41
|
+
await messageStore.clear();
|
|
42
|
+
await dataStore.clear();
|
|
43
|
+
await resumableTaskStore.clear();
|
|
44
|
+
await stateIndex.clear();
|
|
45
|
+
});
|
|
46
|
+
afterAll(async () => {
|
|
47
|
+
await dwn.close();
|
|
48
|
+
});
|
|
58
49
|
// =========================================================================
|
|
59
50
|
// Protocol definitions for tests
|
|
60
51
|
// =========================================================================
|
|
@@ -117,18 +108,18 @@ export function testProtocolComposition() {
|
|
|
117
108
|
// Validation tests (ProtocolsConfigure)
|
|
118
109
|
// =========================================================================
|
|
119
110
|
describe('ProtocolsConfigure validation', () => {
|
|
120
|
-
it('should accept a valid protocol definition with `uses` and `$ref`', () =>
|
|
121
|
-
const alice =
|
|
122
|
-
const protocolsConfigure =
|
|
111
|
+
it('should accept a valid protocol definition with `uses` and `$ref`', async () => {
|
|
112
|
+
const alice = await TestDataGenerator.generateDidKeyPersona();
|
|
113
|
+
const protocolsConfigure = await ProtocolsConfigure.create({
|
|
123
114
|
definition: commentsProtocol,
|
|
124
115
|
signer: Jws.createSigner(alice),
|
|
125
116
|
});
|
|
126
117
|
expect(protocolsConfigure.message.descriptor.definition.uses).toEqual({
|
|
127
118
|
threads: 'https://threads.example.com',
|
|
128
119
|
});
|
|
129
|
-
})
|
|
130
|
-
it('should reject `uses` alias that does not match naming pattern (bypassing JSON schema)', () =>
|
|
131
|
-
const alice =
|
|
120
|
+
});
|
|
121
|
+
it('should reject `uses` alias that does not match naming pattern (bypassing JSON schema)', async () => {
|
|
122
|
+
const alice = await TestDataGenerator.generateDidKeyPersona();
|
|
132
123
|
// The JSON schema enforces alias naming via `patternProperties` + `additionalProperties: false`,
|
|
133
124
|
// which means invalid aliases like '123invalid' are caught by JSON schema before the code-level
|
|
134
125
|
// `validateUses()` check. To exercise the code-level `ProtocolsConfigureInvalidUsesAlias` error,
|
|
@@ -142,7 +133,7 @@ export function testProtocolComposition() {
|
|
|
142
133
|
structure: {},
|
|
143
134
|
};
|
|
144
135
|
try {
|
|
145
|
-
|
|
136
|
+
await ProtocolsConfigure.create({
|
|
146
137
|
definition: badDefinition,
|
|
147
138
|
signer: Jws.createSigner(alice),
|
|
148
139
|
});
|
|
@@ -151,9 +142,9 @@ export function testProtocolComposition() {
|
|
|
151
142
|
catch (error) {
|
|
152
143
|
expect(error.message).toContain(DwnErrorCode.ProtocolsConfigureInvalidUsesAlias);
|
|
153
144
|
}
|
|
154
|
-
})
|
|
155
|
-
it('should reject `$ref` with alias not in `uses`', () =>
|
|
156
|
-
const alice =
|
|
145
|
+
});
|
|
146
|
+
it('should reject `$ref` with alias not in `uses`', async () => {
|
|
147
|
+
const alice = await TestDataGenerator.generateDidKeyPersona();
|
|
157
148
|
const badDefinition = {
|
|
158
149
|
protocol: 'https://bad.example.com',
|
|
159
150
|
published: true,
|
|
@@ -171,7 +162,7 @@ export function testProtocolComposition() {
|
|
|
171
162
|
},
|
|
172
163
|
};
|
|
173
164
|
try {
|
|
174
|
-
|
|
165
|
+
await ProtocolsConfigure.create({
|
|
175
166
|
definition: badDefinition,
|
|
176
167
|
signer: Jws.createSigner(alice),
|
|
177
168
|
});
|
|
@@ -180,9 +171,9 @@ export function testProtocolComposition() {
|
|
|
180
171
|
catch (error) {
|
|
181
172
|
expect(error.message).toContain(DwnErrorCode.ProtocolsConfigureInvalidRefAlias);
|
|
182
173
|
}
|
|
183
|
-
})
|
|
184
|
-
it('should reject `$ref` node with `$actions`', () =>
|
|
185
|
-
const alice =
|
|
174
|
+
});
|
|
175
|
+
it('should reject `$ref` node with `$actions`', async () => {
|
|
176
|
+
const alice = await TestDataGenerator.generateDidKeyPersona();
|
|
186
177
|
const badDefinition = {
|
|
187
178
|
protocol: 'https://bad.example.com',
|
|
188
179
|
published: true,
|
|
@@ -199,7 +190,7 @@ export function testProtocolComposition() {
|
|
|
199
190
|
},
|
|
200
191
|
};
|
|
201
192
|
try {
|
|
202
|
-
|
|
193
|
+
await ProtocolsConfigure.create({
|
|
203
194
|
definition: badDefinition,
|
|
204
195
|
signer: Jws.createSigner(alice),
|
|
205
196
|
});
|
|
@@ -208,9 +199,9 @@ export function testProtocolComposition() {
|
|
|
208
199
|
catch (error) {
|
|
209
200
|
expect(error.message).toContain(DwnErrorCode.ProtocolsConfigureInvalidRefNodeHasDirectives);
|
|
210
201
|
}
|
|
211
|
-
})
|
|
212
|
-
it('should reject `$ref` node with `$role`', () =>
|
|
213
|
-
const alice =
|
|
202
|
+
});
|
|
203
|
+
it('should reject `$ref` node with `$role`', async () => {
|
|
204
|
+
const alice = await TestDataGenerator.generateDidKeyPersona();
|
|
214
205
|
const badDefinition = {
|
|
215
206
|
protocol: 'https://bad.example.com',
|
|
216
207
|
published: true,
|
|
@@ -224,7 +215,7 @@ export function testProtocolComposition() {
|
|
|
224
215
|
},
|
|
225
216
|
};
|
|
226
217
|
try {
|
|
227
|
-
|
|
218
|
+
await ProtocolsConfigure.create({
|
|
228
219
|
definition: badDefinition,
|
|
229
220
|
signer: Jws.createSigner(alice),
|
|
230
221
|
});
|
|
@@ -233,9 +224,9 @@ export function testProtocolComposition() {
|
|
|
233
224
|
catch (error) {
|
|
234
225
|
expect(error.message).toContain(DwnErrorCode.ProtocolsConfigureInvalidRefNodeHasDirectives);
|
|
235
226
|
}
|
|
236
|
-
})
|
|
237
|
-
it('should reject cross-protocol `role` with alias not in `uses`', () =>
|
|
238
|
-
const alice =
|
|
227
|
+
});
|
|
228
|
+
it('should reject cross-protocol `role` with alias not in `uses`', async () => {
|
|
229
|
+
const alice = await TestDataGenerator.generateDidKeyPersona();
|
|
239
230
|
const badDefinition = {
|
|
240
231
|
protocol: 'https://bad.example.com',
|
|
241
232
|
published: true,
|
|
@@ -253,7 +244,7 @@ export function testProtocolComposition() {
|
|
|
253
244
|
},
|
|
254
245
|
};
|
|
255
246
|
try {
|
|
256
|
-
|
|
247
|
+
await ProtocolsConfigure.create({
|
|
257
248
|
definition: badDefinition,
|
|
258
249
|
signer: Jws.createSigner(alice),
|
|
259
250
|
});
|
|
@@ -262,9 +253,9 @@ export function testProtocolComposition() {
|
|
|
262
253
|
catch (error) {
|
|
263
254
|
expect(error.message).toContain(DwnErrorCode.ProtocolsConfigureInvalidCrossProtocolRole);
|
|
264
255
|
}
|
|
265
|
-
})
|
|
266
|
-
it('should reject `uses` with invalid protocol URL', () =>
|
|
267
|
-
const alice =
|
|
256
|
+
});
|
|
257
|
+
it('should reject `uses` with invalid protocol URL', async () => {
|
|
258
|
+
const alice = await TestDataGenerator.generateDidKeyPersona();
|
|
268
259
|
const badDefinition = {
|
|
269
260
|
protocol: 'https://bad.example.com',
|
|
270
261
|
published: true,
|
|
@@ -273,7 +264,7 @@ export function testProtocolComposition() {
|
|
|
273
264
|
structure: {},
|
|
274
265
|
};
|
|
275
266
|
try {
|
|
276
|
-
|
|
267
|
+
await ProtocolsConfigure.create({
|
|
277
268
|
definition: badDefinition,
|
|
278
269
|
signer: Jws.createSigner(alice),
|
|
279
270
|
});
|
|
@@ -282,9 +273,9 @@ export function testProtocolComposition() {
|
|
|
282
273
|
catch (error) {
|
|
283
274
|
expect(error.message).toContain(DwnErrorCode.ProtocolsConfigureInvalidUsesProtocolUrl);
|
|
284
275
|
}
|
|
285
|
-
})
|
|
286
|
-
it('should accept `$ref` referencing a multi-segment path in the referenced protocol', () =>
|
|
287
|
-
const alice =
|
|
276
|
+
});
|
|
277
|
+
it('should accept `$ref` referencing a multi-segment path in the referenced protocol', async () => {
|
|
278
|
+
const alice = await TestDataGenerator.generateDidKeyPersona();
|
|
288
279
|
// The root-only constraint means `$ref` must appear at the root of the composing structure,
|
|
289
280
|
// but the REFERENCED path can be multi-segment (e.g., 'thread/participant').
|
|
290
281
|
const definition = {
|
|
@@ -304,22 +295,22 @@ export function testProtocolComposition() {
|
|
|
304
295
|
},
|
|
305
296
|
};
|
|
306
297
|
// ProtocolsConfigure.create() should succeed — the $ref is at root level
|
|
307
|
-
const protocolsConfigure =
|
|
298
|
+
const protocolsConfigure = await ProtocolsConfigure.create({
|
|
308
299
|
definition,
|
|
309
300
|
signer: Jws.createSigner(alice),
|
|
310
301
|
});
|
|
311
302
|
expect(protocolsConfigure.message.descriptor.definition.uses).toBeDefined();
|
|
312
303
|
// Install-time validation should also succeed when the referenced protocol is installed
|
|
313
|
-
const threadsConfigure =
|
|
304
|
+
const threadsConfigure = await ProtocolsConfigure.create({
|
|
314
305
|
definition: threadsProtocol,
|
|
315
306
|
signer: Jws.createSigner(alice),
|
|
316
307
|
});
|
|
317
|
-
|
|
318
|
-
const installReply =
|
|
308
|
+
await dwn.processMessage(alice.did, threadsConfigure.message);
|
|
309
|
+
const installReply = await dwn.processMessage(alice.did, protocolsConfigure.message);
|
|
319
310
|
expect(installReply.status.code).toBe(202);
|
|
320
|
-
})
|
|
321
|
-
it('should not require `$ref` types in local types map', () =>
|
|
322
|
-
const alice =
|
|
311
|
+
});
|
|
312
|
+
it('should not require `$ref` types in local types map', async () => {
|
|
313
|
+
const alice = await TestDataGenerator.generateDidKeyPersona();
|
|
323
314
|
// 'thread' is NOT in the local `types` — it comes from the $ref
|
|
324
315
|
const definition = {
|
|
325
316
|
protocol: 'https://comments.example.com',
|
|
@@ -336,53 +327,53 @@ export function testProtocolComposition() {
|
|
|
336
327
|
},
|
|
337
328
|
};
|
|
338
329
|
// should succeed without error
|
|
339
|
-
const protocolsConfigure =
|
|
330
|
+
const protocolsConfigure = await ProtocolsConfigure.create({
|
|
340
331
|
definition,
|
|
341
332
|
signer: Jws.createSigner(alice),
|
|
342
333
|
});
|
|
343
334
|
expect(protocolsConfigure.message.descriptor.definition.uses).toBeDefined();
|
|
344
|
-
})
|
|
335
|
+
});
|
|
345
336
|
});
|
|
346
337
|
// =========================================================================
|
|
347
338
|
// Install-time validation tests (handler)
|
|
348
339
|
// =========================================================================
|
|
349
340
|
describe('install-time composition dependency validation', () => {
|
|
350
|
-
it('should reject composing protocol if `uses` protocol is not installed', () =>
|
|
351
|
-
const alice =
|
|
341
|
+
it('should reject composing protocol if `uses` protocol is not installed', async () => {
|
|
342
|
+
const alice = await TestDataGenerator.generateDidKeyPersona();
|
|
352
343
|
// Try to install comments protocol WITHOUT installing threads first
|
|
353
|
-
const commentsConfigure =
|
|
344
|
+
const commentsConfigure = await ProtocolsConfigure.create({
|
|
354
345
|
definition: commentsProtocol,
|
|
355
346
|
signer: Jws.createSigner(alice),
|
|
356
347
|
});
|
|
357
|
-
const reply =
|
|
348
|
+
const reply = await dwn.processMessage(alice.did, commentsConfigure.message);
|
|
358
349
|
expect(reply.status.code).toBe(400);
|
|
359
350
|
expect(reply.status.detail).toContain(DwnErrorCode.ProtocolsConfigureComposedProtocolNotInstalled);
|
|
360
|
-
})
|
|
361
|
-
it('should accept composing protocol when `uses` protocol is already installed', () =>
|
|
362
|
-
const alice =
|
|
351
|
+
});
|
|
352
|
+
it('should accept composing protocol when `uses` protocol is already installed', async () => {
|
|
353
|
+
const alice = await TestDataGenerator.generateDidKeyPersona();
|
|
363
354
|
// Install threads protocol first
|
|
364
|
-
const threadsConfigure =
|
|
355
|
+
const threadsConfigure = await ProtocolsConfigure.create({
|
|
365
356
|
definition: threadsProtocol,
|
|
366
357
|
signer: Jws.createSigner(alice),
|
|
367
358
|
});
|
|
368
|
-
const threadsReply =
|
|
359
|
+
const threadsReply = await dwn.processMessage(alice.did, threadsConfigure.message);
|
|
369
360
|
expect(threadsReply.status.code).toBe(202);
|
|
370
361
|
// Now install comments protocol — should succeed
|
|
371
|
-
const commentsConfigure =
|
|
362
|
+
const commentsConfigure = await ProtocolsConfigure.create({
|
|
372
363
|
definition: commentsProtocol,
|
|
373
364
|
signer: Jws.createSigner(alice),
|
|
374
365
|
});
|
|
375
|
-
const commentsReply =
|
|
366
|
+
const commentsReply = await dwn.processMessage(alice.did, commentsConfigure.message);
|
|
376
367
|
expect(commentsReply.status.code).toBe(202);
|
|
377
|
-
})
|
|
378
|
-
it('should reject composing protocol if `$ref` path does not exist in referenced protocol', () =>
|
|
379
|
-
const alice =
|
|
368
|
+
});
|
|
369
|
+
it('should reject composing protocol if `$ref` path does not exist in referenced protocol', async () => {
|
|
370
|
+
const alice = await TestDataGenerator.generateDidKeyPersona();
|
|
380
371
|
// Install threads protocol first
|
|
381
|
-
const threadsConfigure =
|
|
372
|
+
const threadsConfigure = await ProtocolsConfigure.create({
|
|
382
373
|
definition: threadsProtocol,
|
|
383
374
|
signer: Jws.createSigner(alice),
|
|
384
375
|
});
|
|
385
|
-
|
|
376
|
+
await dwn.processMessage(alice.did, threadsConfigure.message);
|
|
386
377
|
// Install a composing protocol that references a non-existent type path
|
|
387
378
|
const badDefinition = {
|
|
388
379
|
protocol: 'https://bad.example.com',
|
|
@@ -398,22 +389,22 @@ export function testProtocolComposition() {
|
|
|
398
389
|
},
|
|
399
390
|
},
|
|
400
391
|
};
|
|
401
|
-
const badConfigure =
|
|
392
|
+
const badConfigure = await ProtocolsConfigure.create({
|
|
402
393
|
definition: badDefinition,
|
|
403
394
|
signer: Jws.createSigner(alice),
|
|
404
395
|
});
|
|
405
|
-
const reply =
|
|
396
|
+
const reply = await dwn.processMessage(alice.did, badConfigure.message);
|
|
406
397
|
expect(reply.status.code).toBe(400);
|
|
407
398
|
expect(reply.status.detail).toContain(DwnErrorCode.ProtocolsConfigureInvalidRefProtocolPath);
|
|
408
|
-
})
|
|
409
|
-
it('should reject composing protocol if cross-protocol `of` path does not exist in referenced protocol', () =>
|
|
410
|
-
const alice =
|
|
399
|
+
});
|
|
400
|
+
it('should reject composing protocol if cross-protocol `of` path does not exist in referenced protocol', async () => {
|
|
401
|
+
const alice = await TestDataGenerator.generateDidKeyPersona();
|
|
411
402
|
// Install threads protocol first
|
|
412
|
-
const threadsConfigure =
|
|
403
|
+
const threadsConfigure = await ProtocolsConfigure.create({
|
|
413
404
|
definition: threadsProtocol,
|
|
414
405
|
signer: Jws.createSigner(alice),
|
|
415
406
|
});
|
|
416
|
-
|
|
407
|
+
await dwn.processMessage(alice.did, threadsConfigure.message);
|
|
417
408
|
// Install a composing protocol with an `of` reference to a non-existent path
|
|
418
409
|
const badDefinition = {
|
|
419
410
|
protocol: 'https://bad.example.com',
|
|
@@ -431,22 +422,22 @@ export function testProtocolComposition() {
|
|
|
431
422
|
},
|
|
432
423
|
},
|
|
433
424
|
};
|
|
434
|
-
const badConfigure =
|
|
425
|
+
const badConfigure = await ProtocolsConfigure.create({
|
|
435
426
|
definition: badDefinition,
|
|
436
427
|
signer: Jws.createSigner(alice),
|
|
437
428
|
});
|
|
438
|
-
const reply =
|
|
429
|
+
const reply = await dwn.processMessage(alice.did, badConfigure.message);
|
|
439
430
|
expect(reply.status.code).toBe(400);
|
|
440
431
|
expect(reply.status.detail).toContain(DwnErrorCode.ProtocolsConfigureInvalidCrossProtocolOf);
|
|
441
|
-
})
|
|
442
|
-
it('should reject composing protocol if cross-protocol role does not exist in referenced protocol', () =>
|
|
443
|
-
const alice =
|
|
432
|
+
});
|
|
433
|
+
it('should reject composing protocol if cross-protocol role does not exist in referenced protocol', async () => {
|
|
434
|
+
const alice = await TestDataGenerator.generateDidKeyPersona();
|
|
444
435
|
// Install threads protocol first
|
|
445
|
-
const threadsConfigure =
|
|
436
|
+
const threadsConfigure = await ProtocolsConfigure.create({
|
|
446
437
|
definition: threadsProtocol,
|
|
447
438
|
signer: Jws.createSigner(alice),
|
|
448
439
|
});
|
|
449
|
-
|
|
440
|
+
await dwn.processMessage(alice.did, threadsConfigure.message);
|
|
450
441
|
// Install a composing protocol with invalid cross-protocol role
|
|
451
442
|
const badDefinition = {
|
|
452
443
|
protocol: 'https://bad.example.com',
|
|
@@ -465,45 +456,45 @@ export function testProtocolComposition() {
|
|
|
465
456
|
},
|
|
466
457
|
},
|
|
467
458
|
};
|
|
468
|
-
const badConfigure =
|
|
459
|
+
const badConfigure = await ProtocolsConfigure.create({
|
|
469
460
|
definition: badDefinition,
|
|
470
461
|
signer: Jws.createSigner(alice),
|
|
471
462
|
});
|
|
472
|
-
const reply =
|
|
463
|
+
const reply = await dwn.processMessage(alice.did, badConfigure.message);
|
|
473
464
|
expect(reply.status.code).toBe(400);
|
|
474
465
|
expect(reply.status.detail).toContain(DwnErrorCode.ProtocolsConfigureInvalidCrossProtocolRole);
|
|
475
|
-
})
|
|
466
|
+
});
|
|
476
467
|
});
|
|
477
468
|
// =========================================================================
|
|
478
469
|
// Runtime cross-protocol record creation tests
|
|
479
470
|
// =========================================================================
|
|
480
471
|
describe('cross-protocol record creation', () => {
|
|
481
|
-
it('should allow creating a child record in a composing protocol under a parent from a different protocol', () =>
|
|
482
|
-
const alice =
|
|
472
|
+
it('should allow creating a child record in a composing protocol under a parent from a different protocol', async () => {
|
|
473
|
+
const alice = await TestDataGenerator.generateDidKeyPersona();
|
|
483
474
|
// Install both protocols
|
|
484
|
-
const threadsConfigure =
|
|
475
|
+
const threadsConfigure = await ProtocolsConfigure.create({
|
|
485
476
|
definition: threadsProtocol,
|
|
486
477
|
signer: Jws.createSigner(alice),
|
|
487
478
|
});
|
|
488
|
-
|
|
489
|
-
const commentsConfigure =
|
|
479
|
+
await dwn.processMessage(alice.did, threadsConfigure.message);
|
|
480
|
+
const commentsConfigure = await ProtocolsConfigure.create({
|
|
490
481
|
definition: commentsProtocol,
|
|
491
482
|
signer: Jws.createSigner(alice),
|
|
492
483
|
});
|
|
493
|
-
|
|
484
|
+
await dwn.processMessage(alice.did, commentsConfigure.message);
|
|
494
485
|
// Create a thread record in the threads protocol
|
|
495
|
-
const threadWrite =
|
|
486
|
+
const threadWrite = await TestDataGenerator.generateRecordsWrite({
|
|
496
487
|
author: alice,
|
|
497
488
|
protocol: threadsProtocol.protocol,
|
|
498
489
|
protocolPath: 'thread',
|
|
499
490
|
schema: 'https://threads.example.com/schemas/thread',
|
|
500
491
|
dataFormat: 'application/json',
|
|
501
492
|
});
|
|
502
|
-
const threadReply =
|
|
493
|
+
const threadReply = await dwn.processMessage(alice.did, threadWrite.message, { dataStream: threadWrite.dataStream });
|
|
503
494
|
expect(threadReply.status.code).toBe(202);
|
|
504
495
|
const threadContextId = threadWrite.message.contextId;
|
|
505
496
|
// Create a comment record in the comments protocol, parented under the thread
|
|
506
|
-
const commentWrite =
|
|
497
|
+
const commentWrite = await TestDataGenerator.generateRecordsWrite({
|
|
507
498
|
author: alice,
|
|
508
499
|
protocol: commentsProtocol.protocol,
|
|
509
500
|
protocolPath: 'thread/comment',
|
|
@@ -511,35 +502,35 @@ export function testProtocolComposition() {
|
|
|
511
502
|
dataFormat: 'application/json',
|
|
512
503
|
parentContextId: threadContextId,
|
|
513
504
|
});
|
|
514
|
-
const commentReply =
|
|
505
|
+
const commentReply = await dwn.processMessage(alice.did, commentWrite.message, { dataStream: commentWrite.dataStream });
|
|
515
506
|
expect(commentReply.status.code).toBe(202);
|
|
516
507
|
// The comment's contextId should chain from the thread's contextId
|
|
517
508
|
expect(commentWrite.message.contextId).toBe(`${threadContextId}/${commentWrite.message.recordId}`);
|
|
518
|
-
})
|
|
519
|
-
it('should allow creating a grandchild in the composing protocol under a cross-protocol child', () =>
|
|
520
|
-
const alice =
|
|
509
|
+
});
|
|
510
|
+
it('should allow creating a grandchild in the composing protocol under a cross-protocol child', async () => {
|
|
511
|
+
const alice = await TestDataGenerator.generateDidKeyPersona();
|
|
521
512
|
// Install both protocols
|
|
522
|
-
const threadsConfigure =
|
|
513
|
+
const threadsConfigure = await ProtocolsConfigure.create({
|
|
523
514
|
definition: threadsProtocol,
|
|
524
515
|
signer: Jws.createSigner(alice),
|
|
525
516
|
});
|
|
526
|
-
|
|
527
|
-
const commentsConfigure =
|
|
517
|
+
await dwn.processMessage(alice.did, threadsConfigure.message);
|
|
518
|
+
const commentsConfigure = await ProtocolsConfigure.create({
|
|
528
519
|
definition: commentsProtocol,
|
|
529
520
|
signer: Jws.createSigner(alice),
|
|
530
521
|
});
|
|
531
|
-
|
|
522
|
+
await dwn.processMessage(alice.did, commentsConfigure.message);
|
|
532
523
|
// Create thread -> comment -> reaction chain
|
|
533
|
-
const threadWrite =
|
|
524
|
+
const threadWrite = await TestDataGenerator.generateRecordsWrite({
|
|
534
525
|
author: alice,
|
|
535
526
|
protocol: threadsProtocol.protocol,
|
|
536
527
|
protocolPath: 'thread',
|
|
537
528
|
schema: 'https://threads.example.com/schemas/thread',
|
|
538
529
|
dataFormat: 'application/json',
|
|
539
530
|
});
|
|
540
|
-
|
|
531
|
+
await dwn.processMessage(alice.did, threadWrite.message, { dataStream: threadWrite.dataStream });
|
|
541
532
|
const threadContextId = threadWrite.message.contextId;
|
|
542
|
-
const commentWrite =
|
|
533
|
+
const commentWrite = await TestDataGenerator.generateRecordsWrite({
|
|
543
534
|
author: alice,
|
|
544
535
|
protocol: commentsProtocol.protocol,
|
|
545
536
|
protocolPath: 'thread/comment',
|
|
@@ -547,10 +538,10 @@ export function testProtocolComposition() {
|
|
|
547
538
|
dataFormat: 'application/json',
|
|
548
539
|
parentContextId: threadContextId,
|
|
549
540
|
});
|
|
550
|
-
|
|
541
|
+
await dwn.processMessage(alice.did, commentWrite.message, { dataStream: commentWrite.dataStream });
|
|
551
542
|
const commentContextId = commentWrite.message.contextId;
|
|
552
543
|
// Create a reaction under the comment (grandchild, same composing protocol)
|
|
553
|
-
const reactionWrite =
|
|
544
|
+
const reactionWrite = await TestDataGenerator.generateRecordsWrite({
|
|
554
545
|
author: alice,
|
|
555
546
|
protocol: commentsProtocol.protocol,
|
|
556
547
|
protocolPath: 'thread/comment/reaction',
|
|
@@ -558,104 +549,104 @@ export function testProtocolComposition() {
|
|
|
558
549
|
dataFormat: 'application/json',
|
|
559
550
|
parentContextId: commentContextId,
|
|
560
551
|
});
|
|
561
|
-
const reactionReply =
|
|
552
|
+
const reactionReply = await dwn.processMessage(alice.did, reactionWrite.message, { dataStream: reactionWrite.dataStream });
|
|
562
553
|
expect(reactionReply.status.code).toBe(202);
|
|
563
|
-
})
|
|
554
|
+
});
|
|
564
555
|
});
|
|
565
556
|
// =========================================================================
|
|
566
557
|
// Writing at the $ref position
|
|
567
558
|
// =========================================================================
|
|
568
559
|
describe('writing at the $ref position', () => {
|
|
569
|
-
it('should reject a non-tenant writing at the `$ref` position because `$ref` nodes have no `$actions`', () =>
|
|
570
|
-
const alice =
|
|
571
|
-
const bob =
|
|
560
|
+
it('should reject a non-tenant writing at the `$ref` position because `$ref` nodes have no `$actions`', async () => {
|
|
561
|
+
const alice = await TestDataGenerator.generateDidKeyPersona();
|
|
562
|
+
const bob = await TestDataGenerator.generateDidKeyPersona();
|
|
572
563
|
// Install both protocols on Alice's DWN
|
|
573
|
-
const threadsConfigure =
|
|
564
|
+
const threadsConfigure = await ProtocolsConfigure.create({
|
|
574
565
|
definition: threadsProtocol,
|
|
575
566
|
signer: Jws.createSigner(alice),
|
|
576
567
|
});
|
|
577
|
-
|
|
578
|
-
const commentsConfigure =
|
|
568
|
+
await dwn.processMessage(alice.did, threadsConfigure.message);
|
|
569
|
+
const commentsConfigure = await ProtocolsConfigure.create({
|
|
579
570
|
definition: commentsProtocol,
|
|
580
571
|
signer: Jws.createSigner(alice),
|
|
581
572
|
});
|
|
582
|
-
|
|
573
|
+
await dwn.processMessage(alice.did, commentsConfigure.message);
|
|
583
574
|
// Bob tries to write a record at the `$ref` position (protocolPath: 'thread')
|
|
584
575
|
// through the composing protocol. The `$ref` node has no `$actions`, so this should
|
|
585
576
|
// be rejected for any non-tenant author.
|
|
586
|
-
const threadWriteAtRef =
|
|
577
|
+
const threadWriteAtRef = await TestDataGenerator.generateRecordsWrite({
|
|
587
578
|
author: bob,
|
|
588
579
|
protocol: commentsProtocol.protocol,
|
|
589
580
|
protocolPath: 'thread',
|
|
590
581
|
schema: 'https://threads.example.com/schemas/thread',
|
|
591
582
|
dataFormat: 'application/json',
|
|
592
583
|
});
|
|
593
|
-
const reply =
|
|
584
|
+
const reply = await dwn.processMessage(alice.did, threadWriteAtRef.message, { dataStream: threadWriteAtRef.dataStream });
|
|
594
585
|
expect(reply.status.code).toBe(401);
|
|
595
586
|
expect(reply.status.detail).toContain(DwnErrorCode.ProtocolAuthorizationActionRulesNotFound);
|
|
596
|
-
})
|
|
597
|
-
it('should resolve type from the referenced protocol for records at the `$ref` position', () =>
|
|
598
|
-
const alice =
|
|
587
|
+
});
|
|
588
|
+
it('should resolve type from the referenced protocol for records at the `$ref` position', async () => {
|
|
589
|
+
const alice = await TestDataGenerator.generateDidKeyPersona();
|
|
599
590
|
// Install both protocols on Alice's DWN
|
|
600
|
-
const threadsConfigure =
|
|
591
|
+
const threadsConfigure = await ProtocolsConfigure.create({
|
|
601
592
|
definition: threadsProtocol,
|
|
602
593
|
signer: Jws.createSigner(alice),
|
|
603
594
|
});
|
|
604
|
-
|
|
605
|
-
const commentsConfigure =
|
|
595
|
+
await dwn.processMessage(alice.did, threadsConfigure.message);
|
|
596
|
+
const commentsConfigure = await ProtocolsConfigure.create({
|
|
606
597
|
definition: commentsProtocol,
|
|
607
598
|
signer: Jws.createSigner(alice),
|
|
608
599
|
});
|
|
609
|
-
|
|
600
|
+
await dwn.processMessage(alice.did, commentsConfigure.message);
|
|
610
601
|
// Alice (tenant) writes at the `$ref` position using the WRONG schema.
|
|
611
602
|
// The type at position 'thread' should resolve from the threads protocol, which expects
|
|
612
603
|
// schema 'https://threads.example.com/schemas/thread'. Using a different schema should fail.
|
|
613
|
-
const wrongSchemaWrite =
|
|
604
|
+
const wrongSchemaWrite = await TestDataGenerator.generateRecordsWrite({
|
|
614
605
|
author: alice,
|
|
615
606
|
protocol: commentsProtocol.protocol,
|
|
616
607
|
protocolPath: 'thread',
|
|
617
608
|
schema: 'https://comments.example.com/schemas/comment', // wrong schema
|
|
618
609
|
dataFormat: 'application/json',
|
|
619
610
|
});
|
|
620
|
-
const reply =
|
|
611
|
+
const reply = await dwn.processMessage(alice.did, wrongSchemaWrite.message, { dataStream: wrongSchemaWrite.dataStream });
|
|
621
612
|
expect(reply.status.code).toBe(400);
|
|
622
613
|
// Type verification should fail because the schema doesn't match the referenced protocol's type
|
|
623
|
-
})
|
|
614
|
+
});
|
|
624
615
|
});
|
|
625
616
|
// =========================================================================
|
|
626
617
|
// Cross-protocol role invocation tests
|
|
627
618
|
// =========================================================================
|
|
628
619
|
describe('cross-protocol role invocation', () => {
|
|
629
|
-
it('should allow a cross-protocol role holder to perform actions in the composing protocol', () =>
|
|
630
|
-
const alice =
|
|
631
|
-
const bob =
|
|
620
|
+
it('should allow a cross-protocol role holder to perform actions in the composing protocol', async () => {
|
|
621
|
+
const alice = await TestDataGenerator.generateDidKeyPersona();
|
|
622
|
+
const bob = await TestDataGenerator.generateDidKeyPersona();
|
|
632
623
|
// Install threads protocol on Alice's DWN
|
|
633
|
-
const threadsConfigure =
|
|
624
|
+
const threadsConfigure = await ProtocolsConfigure.create({
|
|
634
625
|
definition: threadsProtocol,
|
|
635
626
|
signer: Jws.createSigner(alice),
|
|
636
627
|
});
|
|
637
|
-
const threadsReply =
|
|
628
|
+
const threadsReply = await dwn.processMessage(alice.did, threadsConfigure.message);
|
|
638
629
|
expect(threadsReply.status.code).toBe(202);
|
|
639
630
|
// Install comments protocol on Alice's DWN
|
|
640
|
-
const commentsConfigure =
|
|
631
|
+
const commentsConfigure = await ProtocolsConfigure.create({
|
|
641
632
|
definition: commentsProtocol,
|
|
642
633
|
signer: Jws.createSigner(alice),
|
|
643
634
|
});
|
|
644
|
-
const commentsReply =
|
|
635
|
+
const commentsReply = await dwn.processMessage(alice.did, commentsConfigure.message);
|
|
645
636
|
expect(commentsReply.status.code).toBe(202);
|
|
646
637
|
// Alice creates a thread
|
|
647
|
-
const threadWrite =
|
|
638
|
+
const threadWrite = await TestDataGenerator.generateRecordsWrite({
|
|
648
639
|
author: alice,
|
|
649
640
|
protocol: threadsProtocol.protocol,
|
|
650
641
|
protocolPath: 'thread',
|
|
651
642
|
schema: 'https://threads.example.com/schemas/thread',
|
|
652
643
|
dataFormat: 'application/json',
|
|
653
644
|
});
|
|
654
|
-
const threadReply =
|
|
645
|
+
const threadReply = await dwn.processMessage(alice.did, threadWrite.message, { dataStream: threadWrite.dataStream });
|
|
655
646
|
expect(threadReply.status.code).toBe(202);
|
|
656
647
|
const threadContextId = threadWrite.message.contextId;
|
|
657
648
|
// Alice assigns Bob as a participant in the thread (role record in threads protocol)
|
|
658
|
-
const participantWrite =
|
|
649
|
+
const participantWrite = await TestDataGenerator.generateRecordsWrite({
|
|
659
650
|
author: alice,
|
|
660
651
|
recipient: bob.did,
|
|
661
652
|
protocol: threadsProtocol.protocol,
|
|
@@ -664,10 +655,10 @@ export function testProtocolComposition() {
|
|
|
664
655
|
dataFormat: 'application/json',
|
|
665
656
|
parentContextId: threadContextId,
|
|
666
657
|
});
|
|
667
|
-
const participantReply =
|
|
658
|
+
const participantReply = await dwn.processMessage(alice.did, participantWrite.message, { dataStream: participantWrite.dataStream });
|
|
668
659
|
expect(participantReply.status.code).toBe(202);
|
|
669
660
|
// Bob invokes the cross-protocol role to read comments
|
|
670
|
-
const commentWrite =
|
|
661
|
+
const commentWrite = await TestDataGenerator.generateRecordsWrite({
|
|
671
662
|
author: alice,
|
|
672
663
|
protocol: commentsProtocol.protocol,
|
|
673
664
|
protocolPath: 'thread/comment',
|
|
@@ -675,10 +666,10 @@ export function testProtocolComposition() {
|
|
|
675
666
|
dataFormat: 'application/json',
|
|
676
667
|
parentContextId: threadContextId,
|
|
677
668
|
});
|
|
678
|
-
const commentReply =
|
|
669
|
+
const commentReply = await dwn.processMessage(alice.did, commentWrite.message, { dataStream: commentWrite.dataStream });
|
|
679
670
|
expect(commentReply.status.code).toBe(202);
|
|
680
671
|
// Bob reads the comment using the cross-protocol role
|
|
681
|
-
const bobRead =
|
|
672
|
+
const bobRead = await RecordsRead.create({
|
|
682
673
|
signer: Jws.createSigner(bob),
|
|
683
674
|
protocolRole: 'threads:thread/participant',
|
|
684
675
|
filter: {
|
|
@@ -687,36 +678,35 @@ export function testProtocolComposition() {
|
|
|
687
678
|
contextId: threadContextId,
|
|
688
679
|
},
|
|
689
680
|
});
|
|
690
|
-
const bobReadReply =
|
|
681
|
+
const bobReadReply = await dwn.processMessage(alice.did, bobRead.message);
|
|
691
682
|
expect(bobReadReply.status.code).toBe(200);
|
|
692
|
-
})
|
|
693
|
-
it('should allow a cross-protocol role holder to query records in the composing protocol', () =>
|
|
694
|
-
|
|
695
|
-
const
|
|
696
|
-
const bob = yield TestDataGenerator.generateDidKeyPersona();
|
|
683
|
+
});
|
|
684
|
+
it('should allow a cross-protocol role holder to query records in the composing protocol', async () => {
|
|
685
|
+
const alice = await TestDataGenerator.generateDidKeyPersona();
|
|
686
|
+
const bob = await TestDataGenerator.generateDidKeyPersona();
|
|
697
687
|
// Install both protocols on Alice's DWN
|
|
698
|
-
const threadsConfigure =
|
|
688
|
+
const threadsConfigure = await ProtocolsConfigure.create({
|
|
699
689
|
definition: threadsProtocol,
|
|
700
690
|
signer: Jws.createSigner(alice),
|
|
701
691
|
});
|
|
702
|
-
|
|
703
|
-
const commentsConfigure =
|
|
692
|
+
await dwn.processMessage(alice.did, threadsConfigure.message);
|
|
693
|
+
const commentsConfigure = await ProtocolsConfigure.create({
|
|
704
694
|
definition: commentsProtocol,
|
|
705
695
|
signer: Jws.createSigner(alice),
|
|
706
696
|
});
|
|
707
|
-
|
|
697
|
+
await dwn.processMessage(alice.did, commentsConfigure.message);
|
|
708
698
|
// Alice creates a thread
|
|
709
|
-
const threadWrite =
|
|
699
|
+
const threadWrite = await TestDataGenerator.generateRecordsWrite({
|
|
710
700
|
author: alice,
|
|
711
701
|
protocol: threadsProtocol.protocol,
|
|
712
702
|
protocolPath: 'thread',
|
|
713
703
|
schema: 'https://threads.example.com/schemas/thread',
|
|
714
704
|
dataFormat: 'application/json',
|
|
715
705
|
});
|
|
716
|
-
|
|
706
|
+
await dwn.processMessage(alice.did, threadWrite.message, { dataStream: threadWrite.dataStream });
|
|
717
707
|
const threadContextId = threadWrite.message.contextId;
|
|
718
708
|
// Alice assigns Bob as a participant
|
|
719
|
-
const participantWrite =
|
|
709
|
+
const participantWrite = await TestDataGenerator.generateRecordsWrite({
|
|
720
710
|
author: alice,
|
|
721
711
|
recipient: bob.did,
|
|
722
712
|
protocol: threadsProtocol.protocol,
|
|
@@ -725,9 +715,9 @@ export function testProtocolComposition() {
|
|
|
725
715
|
dataFormat: 'application/json',
|
|
726
716
|
parentContextId: threadContextId,
|
|
727
717
|
});
|
|
728
|
-
|
|
718
|
+
await dwn.processMessage(alice.did, participantWrite.message, { dataStream: participantWrite.dataStream });
|
|
729
719
|
// Alice creates two comments
|
|
730
|
-
const comment1 =
|
|
720
|
+
const comment1 = await TestDataGenerator.generateRecordsWrite({
|
|
731
721
|
author: alice,
|
|
732
722
|
protocol: commentsProtocol.protocol,
|
|
733
723
|
protocolPath: 'thread/comment',
|
|
@@ -735,8 +725,8 @@ export function testProtocolComposition() {
|
|
|
735
725
|
dataFormat: 'application/json',
|
|
736
726
|
parentContextId: threadContextId,
|
|
737
727
|
});
|
|
738
|
-
|
|
739
|
-
const comment2 =
|
|
728
|
+
await dwn.processMessage(alice.did, comment1.message, { dataStream: comment1.dataStream });
|
|
729
|
+
const comment2 = await TestDataGenerator.generateRecordsWrite({
|
|
740
730
|
author: alice,
|
|
741
731
|
protocol: commentsProtocol.protocol,
|
|
742
732
|
protocolPath: 'thread/comment',
|
|
@@ -744,9 +734,9 @@ export function testProtocolComposition() {
|
|
|
744
734
|
dataFormat: 'application/json',
|
|
745
735
|
parentContextId: threadContextId,
|
|
746
736
|
});
|
|
747
|
-
|
|
737
|
+
await dwn.processMessage(alice.did, comment2.message, { dataStream: comment2.dataStream });
|
|
748
738
|
// Bob queries comments using the cross-protocol role
|
|
749
|
-
const bobQuery =
|
|
739
|
+
const bobQuery = await RecordsQuery.create({
|
|
750
740
|
signer: Jws.createSigner(bob),
|
|
751
741
|
protocolRole: 'threads:thread/participant',
|
|
752
742
|
filter: {
|
|
@@ -755,37 +745,37 @@ export function testProtocolComposition() {
|
|
|
755
745
|
contextId: threadContextId,
|
|
756
746
|
},
|
|
757
747
|
});
|
|
758
|
-
const bobQueryReply =
|
|
748
|
+
const bobQueryReply = await dwn.processMessage(alice.did, bobQuery.message);
|
|
759
749
|
expect(bobQueryReply.status.code).toBe(200);
|
|
760
|
-
expect(
|
|
761
|
-
})
|
|
762
|
-
it('should allow a cross-protocol role holder to co-delete records in the composing protocol', () =>
|
|
763
|
-
const alice =
|
|
764
|
-
const bob =
|
|
765
|
-
const carol =
|
|
750
|
+
expect(bobQueryReply.entries?.length).toBe(2);
|
|
751
|
+
});
|
|
752
|
+
it('should allow a cross-protocol role holder to co-delete records in the composing protocol', async () => {
|
|
753
|
+
const alice = await TestDataGenerator.generateDidKeyPersona();
|
|
754
|
+
const bob = await TestDataGenerator.generateDidKeyPersona();
|
|
755
|
+
const carol = await TestDataGenerator.generateDidKeyPersona();
|
|
766
756
|
// Install both protocols on Alice's DWN
|
|
767
|
-
const threadsConfigure =
|
|
757
|
+
const threadsConfigure = await ProtocolsConfigure.create({
|
|
768
758
|
definition: threadsProtocol,
|
|
769
759
|
signer: Jws.createSigner(alice),
|
|
770
760
|
});
|
|
771
|
-
|
|
772
|
-
const commentsConfigure =
|
|
761
|
+
await dwn.processMessage(alice.did, threadsConfigure.message);
|
|
762
|
+
const commentsConfigure = await ProtocolsConfigure.create({
|
|
773
763
|
definition: commentsProtocol,
|
|
774
764
|
signer: Jws.createSigner(alice),
|
|
775
765
|
});
|
|
776
|
-
|
|
766
|
+
await dwn.processMessage(alice.did, commentsConfigure.message);
|
|
777
767
|
// Alice creates a thread
|
|
778
|
-
const threadWrite =
|
|
768
|
+
const threadWrite = await TestDataGenerator.generateRecordsWrite({
|
|
779
769
|
author: alice,
|
|
780
770
|
protocol: threadsProtocol.protocol,
|
|
781
771
|
protocolPath: 'thread',
|
|
782
772
|
schema: 'https://threads.example.com/schemas/thread',
|
|
783
773
|
dataFormat: 'application/json',
|
|
784
774
|
});
|
|
785
|
-
|
|
775
|
+
await dwn.processMessage(alice.did, threadWrite.message, { dataStream: threadWrite.dataStream });
|
|
786
776
|
const threadContextId = threadWrite.message.contextId;
|
|
787
777
|
// Alice assigns Bob as a participant (role record)
|
|
788
|
-
const participantWrite =
|
|
778
|
+
const participantWrite = await TestDataGenerator.generateRecordsWrite({
|
|
789
779
|
author: alice,
|
|
790
780
|
recipient: bob.did,
|
|
791
781
|
protocol: threadsProtocol.protocol,
|
|
@@ -794,9 +784,9 @@ export function testProtocolComposition() {
|
|
|
794
784
|
dataFormat: 'application/json',
|
|
795
785
|
parentContextId: threadContextId,
|
|
796
786
|
});
|
|
797
|
-
|
|
787
|
+
await dwn.processMessage(alice.did, participantWrite.message, { dataStream: participantWrite.dataStream });
|
|
798
788
|
// Carol creates a comment (via 'anyone' can 'create')
|
|
799
|
-
const carolComment =
|
|
789
|
+
const carolComment = await TestDataGenerator.generateRecordsWrite({
|
|
800
790
|
author: carol,
|
|
801
791
|
protocol: commentsProtocol.protocol,
|
|
802
792
|
protocolPath: 'thread/comment',
|
|
@@ -804,43 +794,43 @@ export function testProtocolComposition() {
|
|
|
804
794
|
dataFormat: 'application/json',
|
|
805
795
|
parentContextId: threadContextId,
|
|
806
796
|
});
|
|
807
|
-
|
|
797
|
+
await dwn.processMessage(alice.did, carolComment.message, { dataStream: carolComment.dataStream });
|
|
808
798
|
// Bob (participant) co-deletes Carol's comment using the cross-protocol role
|
|
809
799
|
// The commentsProtocol grants 'threads:thread/participant' the 'co-delete' action on comments
|
|
810
|
-
const bobDelete =
|
|
800
|
+
const bobDelete = await RecordsDelete.create({
|
|
811
801
|
protocolRole: 'threads:thread/participant',
|
|
812
802
|
recordId: carolComment.message.recordId,
|
|
813
803
|
signer: Jws.createSigner(bob),
|
|
814
804
|
});
|
|
815
|
-
const deleteReply =
|
|
805
|
+
const deleteReply = await dwn.processMessage(alice.did, bobDelete.message);
|
|
816
806
|
expect(deleteReply.status.code).toBe(202);
|
|
817
|
-
})
|
|
818
|
-
it('should deny cross-protocol role access when the role record has been deleted (revocation)', () =>
|
|
819
|
-
const alice =
|
|
820
|
-
const bob =
|
|
807
|
+
});
|
|
808
|
+
it('should deny cross-protocol role access when the role record has been deleted (revocation)', async () => {
|
|
809
|
+
const alice = await TestDataGenerator.generateDidKeyPersona();
|
|
810
|
+
const bob = await TestDataGenerator.generateDidKeyPersona();
|
|
821
811
|
// Install both protocols on Alice's DWN
|
|
822
|
-
const threadsConfigure =
|
|
812
|
+
const threadsConfigure = await ProtocolsConfigure.create({
|
|
823
813
|
definition: threadsProtocol,
|
|
824
814
|
signer: Jws.createSigner(alice),
|
|
825
815
|
});
|
|
826
|
-
|
|
827
|
-
const commentsConfigure =
|
|
816
|
+
await dwn.processMessage(alice.did, threadsConfigure.message);
|
|
817
|
+
const commentsConfigure = await ProtocolsConfigure.create({
|
|
828
818
|
definition: commentsProtocol,
|
|
829
819
|
signer: Jws.createSigner(alice),
|
|
830
820
|
});
|
|
831
|
-
|
|
821
|
+
await dwn.processMessage(alice.did, commentsConfigure.message);
|
|
832
822
|
// Alice creates a thread
|
|
833
|
-
const threadWrite =
|
|
823
|
+
const threadWrite = await TestDataGenerator.generateRecordsWrite({
|
|
834
824
|
author: alice,
|
|
835
825
|
protocol: threadsProtocol.protocol,
|
|
836
826
|
protocolPath: 'thread',
|
|
837
827
|
schema: 'https://threads.example.com/schemas/thread',
|
|
838
828
|
dataFormat: 'application/json',
|
|
839
829
|
});
|
|
840
|
-
|
|
830
|
+
await dwn.processMessage(alice.did, threadWrite.message, { dataStream: threadWrite.dataStream });
|
|
841
831
|
const threadContextId = threadWrite.message.contextId;
|
|
842
832
|
// Alice assigns Bob as a participant
|
|
843
|
-
const participantWrite =
|
|
833
|
+
const participantWrite = await TestDataGenerator.generateRecordsWrite({
|
|
844
834
|
author: alice,
|
|
845
835
|
recipient: bob.did,
|
|
846
836
|
protocol: threadsProtocol.protocol,
|
|
@@ -849,9 +839,9 @@ export function testProtocolComposition() {
|
|
|
849
839
|
dataFormat: 'application/json',
|
|
850
840
|
parentContextId: threadContextId,
|
|
851
841
|
});
|
|
852
|
-
|
|
842
|
+
await dwn.processMessage(alice.did, participantWrite.message, { dataStream: participantWrite.dataStream });
|
|
853
843
|
// Alice creates a comment
|
|
854
|
-
const commentWrite =
|
|
844
|
+
const commentWrite = await TestDataGenerator.generateRecordsWrite({
|
|
855
845
|
author: alice,
|
|
856
846
|
protocol: commentsProtocol.protocol,
|
|
857
847
|
protocolPath: 'thread/comment',
|
|
@@ -859,9 +849,9 @@ export function testProtocolComposition() {
|
|
|
859
849
|
dataFormat: 'application/json',
|
|
860
850
|
parentContextId: threadContextId,
|
|
861
851
|
});
|
|
862
|
-
|
|
852
|
+
await dwn.processMessage(alice.did, commentWrite.message, { dataStream: commentWrite.dataStream });
|
|
863
853
|
// Verify Bob CAN read the comment (role is active)
|
|
864
|
-
const bobRead =
|
|
854
|
+
const bobRead = await RecordsRead.create({
|
|
865
855
|
signer: Jws.createSigner(bob),
|
|
866
856
|
protocolRole: 'threads:thread/participant',
|
|
867
857
|
filter: {
|
|
@@ -870,17 +860,17 @@ export function testProtocolComposition() {
|
|
|
870
860
|
contextId: threadContextId,
|
|
871
861
|
},
|
|
872
862
|
});
|
|
873
|
-
const bobReadReply =
|
|
863
|
+
const bobReadReply = await dwn.processMessage(alice.did, bobRead.message);
|
|
874
864
|
expect(bobReadReply.status.code).toBe(200);
|
|
875
865
|
// Alice deletes Bob's participant role record (revocation)
|
|
876
|
-
const deleteRole =
|
|
866
|
+
const deleteRole = await RecordsDelete.create({
|
|
877
867
|
recordId: participantWrite.message.recordId,
|
|
878
868
|
signer: Jws.createSigner(alice),
|
|
879
869
|
});
|
|
880
|
-
const deleteRoleReply =
|
|
870
|
+
const deleteRoleReply = await dwn.processMessage(alice.did, deleteRole.message);
|
|
881
871
|
expect(deleteRoleReply.status.code).toBe(202);
|
|
882
872
|
// Bob tries to read the comment again — should be denied because role was deleted
|
|
883
|
-
const bobRead2 =
|
|
873
|
+
const bobRead2 = await RecordsRead.create({
|
|
884
874
|
signer: Jws.createSigner(bob),
|
|
885
875
|
protocolRole: 'threads:thread/participant',
|
|
886
876
|
filter: {
|
|
@@ -889,36 +879,36 @@ export function testProtocolComposition() {
|
|
|
889
879
|
contextId: threadContextId,
|
|
890
880
|
},
|
|
891
881
|
});
|
|
892
|
-
const bobRead2Reply =
|
|
882
|
+
const bobRead2Reply = await dwn.processMessage(alice.did, bobRead2.message);
|
|
893
883
|
expect(bobRead2Reply.status.code).toBe(401);
|
|
894
884
|
expect(bobRead2Reply.status.detail).toContain(DwnErrorCode.ProtocolAuthorizationMatchingRoleRecordNotFound);
|
|
895
|
-
})
|
|
896
|
-
it('should reject a cross-protocol role invocation if the invoker lacks the role record', () =>
|
|
897
|
-
const alice =
|
|
898
|
-
const carol =
|
|
885
|
+
});
|
|
886
|
+
it('should reject a cross-protocol role invocation if the invoker lacks the role record', async () => {
|
|
887
|
+
const alice = await TestDataGenerator.generateDidKeyPersona();
|
|
888
|
+
const carol = await TestDataGenerator.generateDidKeyPersona();
|
|
899
889
|
// Install both protocols
|
|
900
|
-
const threadsConfigure =
|
|
890
|
+
const threadsConfigure = await ProtocolsConfigure.create({
|
|
901
891
|
definition: threadsProtocol,
|
|
902
892
|
signer: Jws.createSigner(alice),
|
|
903
893
|
});
|
|
904
|
-
|
|
905
|
-
const commentsConfigure =
|
|
894
|
+
await dwn.processMessage(alice.did, threadsConfigure.message);
|
|
895
|
+
const commentsConfigure = await ProtocolsConfigure.create({
|
|
906
896
|
definition: commentsProtocol,
|
|
907
897
|
signer: Jws.createSigner(alice),
|
|
908
898
|
});
|
|
909
|
-
|
|
899
|
+
await dwn.processMessage(alice.did, commentsConfigure.message);
|
|
910
900
|
// Alice creates a thread
|
|
911
|
-
const threadWrite =
|
|
901
|
+
const threadWrite = await TestDataGenerator.generateRecordsWrite({
|
|
912
902
|
author: alice,
|
|
913
903
|
protocol: threadsProtocol.protocol,
|
|
914
904
|
protocolPath: 'thread',
|
|
915
905
|
schema: 'https://threads.example.com/schemas/thread',
|
|
916
906
|
dataFormat: 'application/json',
|
|
917
907
|
});
|
|
918
|
-
|
|
908
|
+
await dwn.processMessage(alice.did, threadWrite.message, { dataStream: threadWrite.dataStream });
|
|
919
909
|
const threadContextId = threadWrite.message.contextId;
|
|
920
910
|
// Alice creates a comment
|
|
921
|
-
const commentWrite =
|
|
911
|
+
const commentWrite = await TestDataGenerator.generateRecordsWrite({
|
|
922
912
|
author: alice,
|
|
923
913
|
protocol: commentsProtocol.protocol,
|
|
924
914
|
protocolPath: 'thread/comment',
|
|
@@ -926,9 +916,9 @@ export function testProtocolComposition() {
|
|
|
926
916
|
dataFormat: 'application/json',
|
|
927
917
|
parentContextId: threadContextId,
|
|
928
918
|
});
|
|
929
|
-
|
|
919
|
+
await dwn.processMessage(alice.did, commentWrite.message, { dataStream: commentWrite.dataStream });
|
|
930
920
|
// Carol (who has NO participant role) tries to read the comment using the cross-protocol role
|
|
931
|
-
const carolRead =
|
|
921
|
+
const carolRead = await RecordsRead.create({
|
|
932
922
|
signer: Jws.createSigner(carol),
|
|
933
923
|
protocolRole: 'threads:thread/participant',
|
|
934
924
|
filter: {
|
|
@@ -937,42 +927,42 @@ export function testProtocolComposition() {
|
|
|
937
927
|
contextId: threadContextId,
|
|
938
928
|
},
|
|
939
929
|
});
|
|
940
|
-
const carolReadReply =
|
|
930
|
+
const carolReadReply = await dwn.processMessage(alice.did, carolRead.message);
|
|
941
931
|
expect(carolReadReply.status.code).toBe(401);
|
|
942
932
|
expect(carolReadReply.status.detail).toContain(DwnErrorCode.ProtocolAuthorizationMatchingRoleRecordNotFound);
|
|
943
|
-
})
|
|
933
|
+
});
|
|
944
934
|
});
|
|
945
935
|
// =========================================================================
|
|
946
936
|
// Cross-protocol `who`/`of` actor checks
|
|
947
937
|
// =========================================================================
|
|
948
938
|
describe('cross-protocol `who`/`of` actor checks', () => {
|
|
949
|
-
it('should allow `author` of a cross-protocol parent to perform actions in the composing protocol', () =>
|
|
950
|
-
const alice =
|
|
951
|
-
const bob =
|
|
939
|
+
it('should allow `author` of a cross-protocol parent to perform actions in the composing protocol', async () => {
|
|
940
|
+
const alice = await TestDataGenerator.generateDidKeyPersona();
|
|
941
|
+
const bob = await TestDataGenerator.generateDidKeyPersona();
|
|
952
942
|
// Install both protocols
|
|
953
|
-
const threadsConfigure =
|
|
943
|
+
const threadsConfigure = await ProtocolsConfigure.create({
|
|
954
944
|
definition: threadsProtocol,
|
|
955
945
|
signer: Jws.createSigner(alice),
|
|
956
946
|
});
|
|
957
|
-
|
|
958
|
-
const commentsConfigure =
|
|
947
|
+
await dwn.processMessage(alice.did, threadsConfigure.message);
|
|
948
|
+
const commentsConfigure = await ProtocolsConfigure.create({
|
|
959
949
|
definition: commentsProtocol,
|
|
960
950
|
signer: Jws.createSigner(alice),
|
|
961
951
|
});
|
|
962
|
-
|
|
952
|
+
await dwn.processMessage(alice.did, commentsConfigure.message);
|
|
963
953
|
// Bob creates a thread (via the 'anyone' can 'create' rule)
|
|
964
|
-
const threadWrite =
|
|
954
|
+
const threadWrite = await TestDataGenerator.generateRecordsWrite({
|
|
965
955
|
author: bob,
|
|
966
956
|
protocol: threadsProtocol.protocol,
|
|
967
957
|
protocolPath: 'thread',
|
|
968
958
|
schema: 'https://threads.example.com/schemas/thread',
|
|
969
959
|
dataFormat: 'application/json',
|
|
970
960
|
});
|
|
971
|
-
const threadReply =
|
|
961
|
+
const threadReply = await dwn.processMessage(alice.did, threadWrite.message, { dataStream: threadWrite.dataStream });
|
|
972
962
|
expect(threadReply.status.code).toBe(202);
|
|
973
963
|
const threadContextId = threadWrite.message.contextId;
|
|
974
964
|
// Anyone can create a comment under the thread (via 'anyone' can 'create' rule)
|
|
975
|
-
const commentWrite =
|
|
965
|
+
const commentWrite = await TestDataGenerator.generateRecordsWrite({
|
|
976
966
|
author: alice,
|
|
977
967
|
protocol: commentsProtocol.protocol,
|
|
978
968
|
protocolPath: 'thread/comment',
|
|
@@ -980,10 +970,10 @@ export function testProtocolComposition() {
|
|
|
980
970
|
dataFormat: 'application/json',
|
|
981
971
|
parentContextId: threadContextId,
|
|
982
972
|
});
|
|
983
|
-
const commentReply =
|
|
973
|
+
const commentReply = await dwn.processMessage(alice.did, commentWrite.message, { dataStream: commentWrite.dataStream });
|
|
984
974
|
expect(commentReply.status.code).toBe(202);
|
|
985
975
|
// Anyone can also read (via 'anyone' can 'read' rule)
|
|
986
|
-
const readComment =
|
|
976
|
+
const readComment = await RecordsRead.create({
|
|
987
977
|
signer: Jws.createSigner(bob),
|
|
988
978
|
filter: {
|
|
989
979
|
protocol: commentsProtocol.protocol,
|
|
@@ -991,18 +981,18 @@ export function testProtocolComposition() {
|
|
|
991
981
|
contextId: threadContextId,
|
|
992
982
|
},
|
|
993
983
|
});
|
|
994
|
-
const readReply =
|
|
984
|
+
const readReply = await dwn.processMessage(alice.did, readComment.message);
|
|
995
985
|
expect(readReply.status.code).toBe(200);
|
|
996
|
-
})
|
|
986
|
+
});
|
|
997
987
|
});
|
|
998
988
|
// =========================================================================
|
|
999
989
|
// Cross-protocol `of` actor check — happy path
|
|
1000
990
|
// =========================================================================
|
|
1001
991
|
describe('cross-protocol `of` actor check — happy path', () => {
|
|
1002
|
-
it('should allow author of cross-protocol parent to create records via `who: author, of: alias:path` rule', () =>
|
|
1003
|
-
const alice =
|
|
1004
|
-
const bob =
|
|
1005
|
-
const carol =
|
|
992
|
+
it('should allow author of cross-protocol parent to create records via `who: author, of: alias:path` rule', async () => {
|
|
993
|
+
const alice = await TestDataGenerator.generateDidKeyPersona();
|
|
994
|
+
const bob = await TestDataGenerator.generateDidKeyPersona();
|
|
995
|
+
const carol = await TestDataGenerator.generateDidKeyPersona();
|
|
1006
996
|
// A protocol that grants the author of a thread (from the threads protocol) the ability to create moderation actions
|
|
1007
997
|
const moderationProtocol = {
|
|
1008
998
|
protocol: 'https://moderation.example.com',
|
|
@@ -1024,30 +1014,30 @@ export function testProtocolComposition() {
|
|
|
1024
1014
|
},
|
|
1025
1015
|
};
|
|
1026
1016
|
// Install both protocols on Alice's DWN
|
|
1027
|
-
const threadsConfigure =
|
|
1017
|
+
const threadsConfigure = await ProtocolsConfigure.create({
|
|
1028
1018
|
definition: threadsProtocol,
|
|
1029
1019
|
signer: Jws.createSigner(alice),
|
|
1030
1020
|
});
|
|
1031
|
-
|
|
1032
|
-
const moderationConfigure =
|
|
1021
|
+
await dwn.processMessage(alice.did, threadsConfigure.message);
|
|
1022
|
+
const moderationConfigure = await ProtocolsConfigure.create({
|
|
1033
1023
|
definition: moderationProtocol,
|
|
1034
1024
|
signer: Jws.createSigner(alice),
|
|
1035
1025
|
});
|
|
1036
|
-
const modReply =
|
|
1026
|
+
const modReply = await dwn.processMessage(alice.did, moderationConfigure.message);
|
|
1037
1027
|
expect(modReply.status.code).toBe(202);
|
|
1038
1028
|
// Bob creates a thread (via 'anyone' can 'create' in threadsProtocol)
|
|
1039
|
-
const threadWrite =
|
|
1029
|
+
const threadWrite = await TestDataGenerator.generateRecordsWrite({
|
|
1040
1030
|
author: bob,
|
|
1041
1031
|
protocol: threadsProtocol.protocol,
|
|
1042
1032
|
protocolPath: 'thread',
|
|
1043
1033
|
schema: 'https://threads.example.com/schemas/thread',
|
|
1044
1034
|
dataFormat: 'application/json',
|
|
1045
1035
|
});
|
|
1046
|
-
const threadReply =
|
|
1036
|
+
const threadReply = await dwn.processMessage(alice.did, threadWrite.message, { dataStream: threadWrite.dataStream });
|
|
1047
1037
|
expect(threadReply.status.code).toBe(202);
|
|
1048
1038
|
const threadContextId = threadWrite.message.contextId;
|
|
1049
1039
|
// Bob (as author of the thread) creates a moderation action — should succeed
|
|
1050
|
-
const actionWrite =
|
|
1040
|
+
const actionWrite = await TestDataGenerator.generateRecordsWrite({
|
|
1051
1041
|
author: bob,
|
|
1052
1042
|
protocol: moderationProtocol.protocol,
|
|
1053
1043
|
protocolPath: 'thread/action',
|
|
@@ -1055,10 +1045,10 @@ export function testProtocolComposition() {
|
|
|
1055
1045
|
dataFormat: 'application/json',
|
|
1056
1046
|
parentContextId: threadContextId,
|
|
1057
1047
|
});
|
|
1058
|
-
const actionReply =
|
|
1048
|
+
const actionReply = await dwn.processMessage(alice.did, actionWrite.message, { dataStream: actionWrite.dataStream });
|
|
1059
1049
|
expect(actionReply.status.code).toBe(202);
|
|
1060
1050
|
// Carol (NOT author of the thread, NOT the tenant) tries to create a moderation action — should fail
|
|
1061
|
-
const carolActionWrite =
|
|
1051
|
+
const carolActionWrite = await TestDataGenerator.generateRecordsWrite({
|
|
1062
1052
|
author: carol,
|
|
1063
1053
|
protocol: moderationProtocol.protocol,
|
|
1064
1054
|
protocolPath: 'thread/action',
|
|
@@ -1066,17 +1056,17 @@ export function testProtocolComposition() {
|
|
|
1066
1056
|
dataFormat: 'application/json',
|
|
1067
1057
|
parentContextId: threadContextId,
|
|
1068
1058
|
});
|
|
1069
|
-
const carolActionReply =
|
|
1059
|
+
const carolActionReply = await dwn.processMessage(alice.did, carolActionWrite.message, { dataStream: carolActionWrite.dataStream });
|
|
1070
1060
|
expect(carolActionReply.status.code).toBe(401);
|
|
1071
1061
|
expect(carolActionReply.status.detail).toContain(DwnErrorCode.ProtocolAuthorizationActionNotAllowed);
|
|
1072
|
-
})
|
|
1062
|
+
});
|
|
1073
1063
|
});
|
|
1074
1064
|
// =========================================================================
|
|
1075
1065
|
// Edge case and error tests
|
|
1076
1066
|
// =========================================================================
|
|
1077
1067
|
describe('composition error cases', () => {
|
|
1078
|
-
it('should reject `$ref` with malformed format (no colon separator)', () =>
|
|
1079
|
-
const alice =
|
|
1068
|
+
it('should reject `$ref` with malformed format (no colon separator)', async () => {
|
|
1069
|
+
const alice = await TestDataGenerator.generateDidKeyPersona();
|
|
1080
1070
|
const badDefinition = {
|
|
1081
1071
|
protocol: 'https://bad.example.com',
|
|
1082
1072
|
published: true,
|
|
@@ -1092,7 +1082,7 @@ export function testProtocolComposition() {
|
|
|
1092
1082
|
},
|
|
1093
1083
|
};
|
|
1094
1084
|
try {
|
|
1095
|
-
|
|
1085
|
+
await ProtocolsConfigure.create({
|
|
1096
1086
|
definition: badDefinition,
|
|
1097
1087
|
signer: Jws.createSigner(alice),
|
|
1098
1088
|
});
|
|
@@ -1102,9 +1092,9 @@ export function testProtocolComposition() {
|
|
|
1102
1092
|
// JSON schema enforces `$ref` must match pattern `^[a-zA-Z][a-zA-Z0-9_-]*:.+$`
|
|
1103
1093
|
expect(error.message).toContain('must match pattern');
|
|
1104
1094
|
}
|
|
1105
|
-
})
|
|
1106
|
-
it('should reject `$ref` that references own protocol', () =>
|
|
1107
|
-
const alice =
|
|
1095
|
+
});
|
|
1096
|
+
it('should reject `$ref` that references own protocol', async () => {
|
|
1097
|
+
const alice = await TestDataGenerator.generateDidKeyPersona();
|
|
1108
1098
|
const badDefinition = {
|
|
1109
1099
|
protocol: 'https://self.example.com',
|
|
1110
1100
|
published: true,
|
|
@@ -1120,7 +1110,7 @@ export function testProtocolComposition() {
|
|
|
1120
1110
|
},
|
|
1121
1111
|
};
|
|
1122
1112
|
try {
|
|
1123
|
-
|
|
1113
|
+
await ProtocolsConfigure.create({
|
|
1124
1114
|
definition: badDefinition,
|
|
1125
1115
|
signer: Jws.createSigner(alice),
|
|
1126
1116
|
});
|
|
@@ -1129,9 +1119,9 @@ export function testProtocolComposition() {
|
|
|
1129
1119
|
catch (error) {
|
|
1130
1120
|
expect(error.message).toContain(DwnErrorCode.ProtocolsConfigureInvalidUsesSelfReference);
|
|
1131
1121
|
}
|
|
1132
|
-
})
|
|
1133
|
-
it('should reject `uses` with invalid alias name (starts with a number)', () =>
|
|
1134
|
-
const alice =
|
|
1122
|
+
});
|
|
1123
|
+
it('should reject `uses` with invalid alias name (starts with a number)', async () => {
|
|
1124
|
+
const alice = await TestDataGenerator.generateDidKeyPersona();
|
|
1135
1125
|
const badDefinition = {
|
|
1136
1126
|
protocol: 'https://bad.example.com',
|
|
1137
1127
|
published: true,
|
|
@@ -1140,7 +1130,7 @@ export function testProtocolComposition() {
|
|
|
1140
1130
|
structure: {},
|
|
1141
1131
|
};
|
|
1142
1132
|
try {
|
|
1143
|
-
|
|
1133
|
+
await ProtocolsConfigure.create({
|
|
1144
1134
|
definition: badDefinition,
|
|
1145
1135
|
signer: Jws.createSigner(alice),
|
|
1146
1136
|
});
|
|
@@ -1150,9 +1140,9 @@ export function testProtocolComposition() {
|
|
|
1150
1140
|
// JSON schema enforces alias pattern `^[a-zA-Z][a-zA-Z0-9_-]*$` via additionalProperties: false
|
|
1151
1141
|
expect(error.message).toContain('must NOT have additional properties');
|
|
1152
1142
|
}
|
|
1153
|
-
})
|
|
1154
|
-
it('should reject cross-protocol `of` with alias not in `uses`', () =>
|
|
1155
|
-
const alice =
|
|
1143
|
+
});
|
|
1144
|
+
it('should reject cross-protocol `of` with alias not in `uses`', async () => {
|
|
1145
|
+
const alice = await TestDataGenerator.generateDidKeyPersona();
|
|
1156
1146
|
const badDefinition = {
|
|
1157
1147
|
protocol: 'https://bad.example.com',
|
|
1158
1148
|
published: true,
|
|
@@ -1170,7 +1160,7 @@ export function testProtocolComposition() {
|
|
|
1170
1160
|
},
|
|
1171
1161
|
};
|
|
1172
1162
|
try {
|
|
1173
|
-
|
|
1163
|
+
await ProtocolsConfigure.create({
|
|
1174
1164
|
definition: badDefinition,
|
|
1175
1165
|
signer: Jws.createSigner(alice),
|
|
1176
1166
|
});
|
|
@@ -1179,23 +1169,23 @@ export function testProtocolComposition() {
|
|
|
1179
1169
|
catch (error) {
|
|
1180
1170
|
expect(error.message).toContain(DwnErrorCode.ProtocolsConfigureInvalidCrossProtocolOf);
|
|
1181
1171
|
}
|
|
1182
|
-
})
|
|
1183
|
-
it('should return 400 when cross-protocol parent record does not exist at runtime', () =>
|
|
1184
|
-
const alice =
|
|
1172
|
+
});
|
|
1173
|
+
it('should return 400 when cross-protocol parent record does not exist at runtime', async () => {
|
|
1174
|
+
const alice = await TestDataGenerator.generateDidKeyPersona();
|
|
1185
1175
|
// Install both protocols
|
|
1186
|
-
const threadsConfigure =
|
|
1176
|
+
const threadsConfigure = await ProtocolsConfigure.create({
|
|
1187
1177
|
definition: threadsProtocol,
|
|
1188
1178
|
signer: Jws.createSigner(alice),
|
|
1189
1179
|
});
|
|
1190
|
-
|
|
1191
|
-
const commentsConfigure =
|
|
1180
|
+
await dwn.processMessage(alice.did, threadsConfigure.message);
|
|
1181
|
+
const commentsConfigure = await ProtocolsConfigure.create({
|
|
1192
1182
|
definition: commentsProtocol,
|
|
1193
1183
|
signer: Jws.createSigner(alice),
|
|
1194
1184
|
});
|
|
1195
|
-
|
|
1185
|
+
await dwn.processMessage(alice.did, commentsConfigure.message);
|
|
1196
1186
|
// Try to create a comment under a non-existent thread (fabricate a contextId)
|
|
1197
1187
|
const fakeThreadContextId = 'bafybeifake1234567890';
|
|
1198
|
-
const commentWrite =
|
|
1188
|
+
const commentWrite = await TestDataGenerator.generateRecordsWrite({
|
|
1199
1189
|
author: alice,
|
|
1200
1190
|
protocol: commentsProtocol.protocol,
|
|
1201
1191
|
protocolPath: 'thread/comment',
|
|
@@ -1203,12 +1193,12 @@ export function testProtocolComposition() {
|
|
|
1203
1193
|
dataFormat: 'application/json',
|
|
1204
1194
|
parentContextId: fakeThreadContextId,
|
|
1205
1195
|
});
|
|
1206
|
-
const reply =
|
|
1196
|
+
const reply = await dwn.processMessage(alice.did, commentWrite.message, { dataStream: commentWrite.dataStream });
|
|
1207
1197
|
expect(reply.status.code).toBe(400);
|
|
1208
1198
|
expect(reply.status.detail).toContain(DwnErrorCode.ProtocolAuthorizationCrossProtocolParentNotFound);
|
|
1209
|
-
})
|
|
1210
|
-
it('should reject `$ref` at non-root protocol path', () =>
|
|
1211
|
-
const alice =
|
|
1199
|
+
});
|
|
1200
|
+
it('should reject `$ref` at non-root protocol path', async () => {
|
|
1201
|
+
const alice = await TestDataGenerator.generateDidKeyPersona();
|
|
1212
1202
|
const badDefinition = {
|
|
1213
1203
|
protocol: 'https://bad.example.com',
|
|
1214
1204
|
published: true,
|
|
@@ -1223,7 +1213,7 @@ export function testProtocolComposition() {
|
|
|
1223
1213
|
},
|
|
1224
1214
|
};
|
|
1225
1215
|
try {
|
|
1226
|
-
|
|
1216
|
+
await ProtocolsConfigure.create({
|
|
1227
1217
|
definition: badDefinition,
|
|
1228
1218
|
signer: Jws.createSigner(alice),
|
|
1229
1219
|
});
|
|
@@ -1232,9 +1222,9 @@ export function testProtocolComposition() {
|
|
|
1232
1222
|
catch (error) {
|
|
1233
1223
|
expect(error.message).toContain(DwnErrorCode.ProtocolsConfigureInvalidRefNotAtRoot);
|
|
1234
1224
|
}
|
|
1235
|
-
})
|
|
1236
|
-
it('should reject `uses` with empty alias map', () =>
|
|
1237
|
-
const alice =
|
|
1225
|
+
});
|
|
1226
|
+
it('should reject `uses` with empty alias map', async () => {
|
|
1227
|
+
const alice = await TestDataGenerator.generateDidKeyPersona();
|
|
1238
1228
|
const badDefinition = {
|
|
1239
1229
|
protocol: 'https://bad.example.com',
|
|
1240
1230
|
published: true,
|
|
@@ -1243,7 +1233,7 @@ export function testProtocolComposition() {
|
|
|
1243
1233
|
structure: {},
|
|
1244
1234
|
};
|
|
1245
1235
|
try {
|
|
1246
|
-
|
|
1236
|
+
await ProtocolsConfigure.create({
|
|
1247
1237
|
definition: badDefinition,
|
|
1248
1238
|
signer: Jws.createSigner(alice),
|
|
1249
1239
|
});
|
|
@@ -1253,13 +1243,644 @@ export function testProtocolComposition() {
|
|
|
1253
1243
|
// JSON schema enforces minProperties: 1 on `uses`
|
|
1254
1244
|
expect(error.message).toContain('must NOT have fewer than 1 properties');
|
|
1255
1245
|
}
|
|
1256
|
-
})
|
|
1246
|
+
});
|
|
1247
|
+
});
|
|
1248
|
+
// =========================================================================
|
|
1249
|
+
// Multi-level composition rejection (#299)
|
|
1250
|
+
// =========================================================================
|
|
1251
|
+
describe('multi-level composition rejection', () => {
|
|
1252
|
+
it('should reject `$ref` that targets a node which is itself a `$ref` composition point', async () => {
|
|
1253
|
+
const alice = await TestDataGenerator.generateDidKeyPersona();
|
|
1254
|
+
// Protocol B uses Protocol A (threads)
|
|
1255
|
+
const protocolB = {
|
|
1256
|
+
protocol: 'https://protocol-b.example.com',
|
|
1257
|
+
published: true,
|
|
1258
|
+
uses: { threads: 'https://threads.example.com' },
|
|
1259
|
+
types: {
|
|
1260
|
+
annotation: { schema: 'https://protocol-b.example.com/schemas/annotation', dataFormats: ['application/json'] },
|
|
1261
|
+
},
|
|
1262
|
+
structure: {
|
|
1263
|
+
thread: {
|
|
1264
|
+
$ref: 'threads:thread',
|
|
1265
|
+
annotation: {
|
|
1266
|
+
$actions: [{ who: 'anyone', can: ['create', 'read'] }],
|
|
1267
|
+
},
|
|
1268
|
+
},
|
|
1269
|
+
},
|
|
1270
|
+
};
|
|
1271
|
+
// Protocol C uses Protocol B — attempts to $ref through B's $ref node
|
|
1272
|
+
const protocolC = {
|
|
1273
|
+
protocol: 'https://protocol-c.example.com',
|
|
1274
|
+
published: true,
|
|
1275
|
+
uses: { b: 'https://protocol-b.example.com' },
|
|
1276
|
+
types: {
|
|
1277
|
+
tag: { schema: 'https://protocol-c.example.com/schemas/tag', dataFormats: ['application/json'] },
|
|
1278
|
+
},
|
|
1279
|
+
structure: {
|
|
1280
|
+
thread: {
|
|
1281
|
+
$ref: 'b:thread', // 'thread' in protocol B IS a $ref node — multi-level, should be rejected
|
|
1282
|
+
tag: {
|
|
1283
|
+
$actions: [{ who: 'anyone', can: ['create', 'read'] }],
|
|
1284
|
+
},
|
|
1285
|
+
},
|
|
1286
|
+
},
|
|
1287
|
+
};
|
|
1288
|
+
// Install threads protocol (protocol A)
|
|
1289
|
+
const threadsConfigure = await ProtocolsConfigure.create({
|
|
1290
|
+
definition: threadsProtocol,
|
|
1291
|
+
signer: Jws.createSigner(alice),
|
|
1292
|
+
});
|
|
1293
|
+
await dwn.processMessage(alice.did, threadsConfigure.message);
|
|
1294
|
+
// Install protocol B (depends on threads)
|
|
1295
|
+
const bConfigure = await ProtocolsConfigure.create({
|
|
1296
|
+
definition: protocolB,
|
|
1297
|
+
signer: Jws.createSigner(alice),
|
|
1298
|
+
});
|
|
1299
|
+
const bReply = await dwn.processMessage(alice.did, bConfigure.message);
|
|
1300
|
+
expect(bReply.status.code).toBe(202);
|
|
1301
|
+
// Install protocol C (depends on B) — should be REJECTED
|
|
1302
|
+
const cConfigure = await ProtocolsConfigure.create({
|
|
1303
|
+
definition: protocolC,
|
|
1304
|
+
signer: Jws.createSigner(alice),
|
|
1305
|
+
});
|
|
1306
|
+
const cReply = await dwn.processMessage(alice.did, cConfigure.message);
|
|
1307
|
+
expect(cReply.status.code).toBe(400);
|
|
1308
|
+
expect(cReply.status.detail).toContain(DwnErrorCode.ProtocolsConfigureInvalidRefTargetThroughRef);
|
|
1309
|
+
});
|
|
1310
|
+
it('should reject `$ref` that targets a multi-segment path traversing through an intermediate `$ref` node', async () => {
|
|
1311
|
+
const alice = await TestDataGenerator.generateDidKeyPersona();
|
|
1312
|
+
// Protocol B uses threads — has $ref at 'thread' and local children under it
|
|
1313
|
+
const protocolB = {
|
|
1314
|
+
protocol: 'https://protocol-b.example.com',
|
|
1315
|
+
published: true,
|
|
1316
|
+
uses: { threads: 'https://threads.example.com' },
|
|
1317
|
+
types: {
|
|
1318
|
+
annotation: { schema: 'https://protocol-b.example.com/schemas/annotation', dataFormats: ['application/json'] },
|
|
1319
|
+
},
|
|
1320
|
+
structure: {
|
|
1321
|
+
thread: {
|
|
1322
|
+
$ref: 'threads:thread',
|
|
1323
|
+
annotation: {
|
|
1324
|
+
$actions: [{ who: 'anyone', can: ['create', 'read'] }],
|
|
1325
|
+
},
|
|
1326
|
+
},
|
|
1327
|
+
},
|
|
1328
|
+
};
|
|
1329
|
+
// Protocol C references a path that traverses through B's $ref node: 'thread/annotation'
|
|
1330
|
+
// 'thread' is a $ref node in protocol B, so this should be rejected even though 'annotation' is a real node
|
|
1331
|
+
const protocolC = {
|
|
1332
|
+
protocol: 'https://protocol-c.example.com',
|
|
1333
|
+
published: true,
|
|
1334
|
+
uses: { b: 'https://protocol-b.example.com' },
|
|
1335
|
+
types: {
|
|
1336
|
+
tag: { schema: 'https://protocol-c.example.com/schemas/tag', dataFormats: ['application/json'] },
|
|
1337
|
+
},
|
|
1338
|
+
structure: {
|
|
1339
|
+
annotation: {
|
|
1340
|
+
$ref: 'b:thread/annotation', // traverses through 'thread' which is a $ref node
|
|
1341
|
+
tag: {
|
|
1342
|
+
$actions: [{ who: 'anyone', can: ['create', 'read'] }],
|
|
1343
|
+
},
|
|
1344
|
+
},
|
|
1345
|
+
},
|
|
1346
|
+
};
|
|
1347
|
+
// Install threads protocol
|
|
1348
|
+
const threadsConfigure = await ProtocolsConfigure.create({
|
|
1349
|
+
definition: threadsProtocol,
|
|
1350
|
+
signer: Jws.createSigner(alice),
|
|
1351
|
+
});
|
|
1352
|
+
await dwn.processMessage(alice.did, threadsConfigure.message);
|
|
1353
|
+
// Install protocol B
|
|
1354
|
+
const bConfigure = await ProtocolsConfigure.create({
|
|
1355
|
+
definition: protocolB,
|
|
1356
|
+
signer: Jws.createSigner(alice),
|
|
1357
|
+
});
|
|
1358
|
+
const bReply = await dwn.processMessage(alice.did, bConfigure.message);
|
|
1359
|
+
expect(bReply.status.code).toBe(202);
|
|
1360
|
+
// Install protocol C — should be REJECTED because 'thread' (intermediate) is a $ref node
|
|
1361
|
+
const cConfigure = await ProtocolsConfigure.create({
|
|
1362
|
+
definition: protocolC,
|
|
1363
|
+
signer: Jws.createSigner(alice),
|
|
1364
|
+
});
|
|
1365
|
+
const cReply = await dwn.processMessage(alice.did, cConfigure.message);
|
|
1366
|
+
expect(cReply.status.code).toBe(400);
|
|
1367
|
+
expect(cReply.status.detail).toContain(DwnErrorCode.ProtocolsConfigureInvalidRefTargetThroughRef);
|
|
1368
|
+
});
|
|
1369
|
+
it('should accept `$ref` to a node that does NOT have a `$ref` directive', async () => {
|
|
1370
|
+
const alice = await TestDataGenerator.generateDidKeyPersona();
|
|
1371
|
+
// Protocol B has a normal (non-$ref) 'item' type with children
|
|
1372
|
+
const protocolB = {
|
|
1373
|
+
protocol: 'https://protocol-b.example.com',
|
|
1374
|
+
published: true,
|
|
1375
|
+
types: {
|
|
1376
|
+
item: { schema: 'https://protocol-b.example.com/schemas/item', dataFormats: ['application/json'] },
|
|
1377
|
+
sub: { schema: 'https://protocol-b.example.com/schemas/sub', dataFormats: ['application/json'] },
|
|
1378
|
+
},
|
|
1379
|
+
structure: {
|
|
1380
|
+
item: {
|
|
1381
|
+
$actions: [{ who: 'anyone', can: ['create', 'read'] }],
|
|
1382
|
+
sub: {
|
|
1383
|
+
$actions: [{ who: 'anyone', can: ['create', 'read'] }],
|
|
1384
|
+
},
|
|
1385
|
+
},
|
|
1386
|
+
},
|
|
1387
|
+
};
|
|
1388
|
+
// Protocol C references 'item' in B — this is single-level composition (no $ref in target), should succeed
|
|
1389
|
+
const protocolC = {
|
|
1390
|
+
protocol: 'https://protocol-c.example.com',
|
|
1391
|
+
published: true,
|
|
1392
|
+
uses: { b: 'https://protocol-b.example.com' },
|
|
1393
|
+
types: {
|
|
1394
|
+
extra: { schema: 'https://protocol-c.example.com/schemas/extra', dataFormats: ['application/json'] },
|
|
1395
|
+
},
|
|
1396
|
+
structure: {
|
|
1397
|
+
item: {
|
|
1398
|
+
$ref: 'b:item',
|
|
1399
|
+
extra: {
|
|
1400
|
+
$actions: [{ who: 'anyone', can: ['create', 'read'] }],
|
|
1401
|
+
},
|
|
1402
|
+
},
|
|
1403
|
+
},
|
|
1404
|
+
};
|
|
1405
|
+
// Install protocol B
|
|
1406
|
+
const bConfigure = await ProtocolsConfigure.create({
|
|
1407
|
+
definition: protocolB,
|
|
1408
|
+
signer: Jws.createSigner(alice),
|
|
1409
|
+
});
|
|
1410
|
+
const bReply = await dwn.processMessage(alice.did, bConfigure.message);
|
|
1411
|
+
expect(bReply.status.code).toBe(202);
|
|
1412
|
+
// Install protocol C — should SUCCEED
|
|
1413
|
+
const cConfigure = await ProtocolsConfigure.create({
|
|
1414
|
+
definition: protocolC,
|
|
1415
|
+
signer: Jws.createSigner(alice),
|
|
1416
|
+
});
|
|
1417
|
+
const cReply = await dwn.processMessage(alice.did, cConfigure.message);
|
|
1418
|
+
expect(cReply.status.code).toBe(202);
|
|
1419
|
+
});
|
|
1420
|
+
});
|
|
1421
|
+
// =========================================================================
|
|
1422
|
+
// RecordsSubscribe with cross-protocol role
|
|
1423
|
+
// =========================================================================
|
|
1424
|
+
describe('cross-protocol RecordsSubscribe', () => {
|
|
1425
|
+
it('should allow a cross-protocol role holder to subscribe to records in the composing protocol', async () => {
|
|
1426
|
+
const alice = await TestDataGenerator.generateDidKeyPersona();
|
|
1427
|
+
const bob = await TestDataGenerator.generateDidKeyPersona();
|
|
1428
|
+
// Install both protocols on Alice's DWN
|
|
1429
|
+
const threadsConfigure = await ProtocolsConfigure.create({
|
|
1430
|
+
definition: threadsProtocol,
|
|
1431
|
+
signer: Jws.createSigner(alice),
|
|
1432
|
+
});
|
|
1433
|
+
await dwn.processMessage(alice.did, threadsConfigure.message);
|
|
1434
|
+
const commentsConfigure = await ProtocolsConfigure.create({
|
|
1435
|
+
definition: commentsProtocol,
|
|
1436
|
+
signer: Jws.createSigner(alice),
|
|
1437
|
+
});
|
|
1438
|
+
await dwn.processMessage(alice.did, commentsConfigure.message);
|
|
1439
|
+
// Alice creates a thread
|
|
1440
|
+
const threadWrite = await TestDataGenerator.generateRecordsWrite({
|
|
1441
|
+
author: alice,
|
|
1442
|
+
protocol: threadsProtocol.protocol,
|
|
1443
|
+
protocolPath: 'thread',
|
|
1444
|
+
schema: 'https://threads.example.com/schemas/thread',
|
|
1445
|
+
dataFormat: 'application/json',
|
|
1446
|
+
});
|
|
1447
|
+
await dwn.processMessage(alice.did, threadWrite.message, { dataStream: threadWrite.dataStream });
|
|
1448
|
+
const threadContextId = threadWrite.message.contextId;
|
|
1449
|
+
// Alice assigns Bob as a participant
|
|
1450
|
+
const participantWrite = await TestDataGenerator.generateRecordsWrite({
|
|
1451
|
+
author: alice,
|
|
1452
|
+
recipient: bob.did,
|
|
1453
|
+
protocol: threadsProtocol.protocol,
|
|
1454
|
+
protocolPath: 'thread/participant',
|
|
1455
|
+
schema: 'https://threads.example.com/schemas/participant',
|
|
1456
|
+
dataFormat: 'application/json',
|
|
1457
|
+
parentContextId: threadContextId,
|
|
1458
|
+
});
|
|
1459
|
+
await dwn.processMessage(alice.did, participantWrite.message, { dataStream: participantWrite.dataStream });
|
|
1460
|
+
// Bob subscribes to comments using the cross-protocol role
|
|
1461
|
+
const bobSubscribe = await RecordsSubscribe.create({
|
|
1462
|
+
signer: Jws.createSigner(bob),
|
|
1463
|
+
protocolRole: 'threads:thread/participant',
|
|
1464
|
+
filter: {
|
|
1465
|
+
protocol: commentsProtocol.protocol,
|
|
1466
|
+
protocolPath: 'thread/comment',
|
|
1467
|
+
contextId: threadContextId,
|
|
1468
|
+
},
|
|
1469
|
+
});
|
|
1470
|
+
const subReply = await dwn.processMessage(alice.did, bobSubscribe.message, { subscriptionHandler: () => { } });
|
|
1471
|
+
expect(subReply.status.code).toBe(200);
|
|
1472
|
+
expect(subReply.subscription).toBeDefined();
|
|
1473
|
+
// Clean up subscription
|
|
1474
|
+
await subReply.subscription.close();
|
|
1475
|
+
});
|
|
1476
|
+
});
|
|
1477
|
+
// =========================================================================
|
|
1478
|
+
// RecordsCount with cross-protocol role
|
|
1479
|
+
// =========================================================================
|
|
1480
|
+
describe('cross-protocol RecordsCount', () => {
|
|
1481
|
+
it('should allow a cross-protocol role holder to count records in the composing protocol', async () => {
|
|
1482
|
+
const alice = await TestDataGenerator.generateDidKeyPersona();
|
|
1483
|
+
const bob = await TestDataGenerator.generateDidKeyPersona();
|
|
1484
|
+
// Install both protocols on Alice's DWN
|
|
1485
|
+
const threadsConfigure = await ProtocolsConfigure.create({
|
|
1486
|
+
definition: threadsProtocol,
|
|
1487
|
+
signer: Jws.createSigner(alice),
|
|
1488
|
+
});
|
|
1489
|
+
await dwn.processMessage(alice.did, threadsConfigure.message);
|
|
1490
|
+
const commentsConfigure = await ProtocolsConfigure.create({
|
|
1491
|
+
definition: commentsProtocol,
|
|
1492
|
+
signer: Jws.createSigner(alice),
|
|
1493
|
+
});
|
|
1494
|
+
await dwn.processMessage(alice.did, commentsConfigure.message);
|
|
1495
|
+
// Alice creates a thread
|
|
1496
|
+
const threadWrite = await TestDataGenerator.generateRecordsWrite({
|
|
1497
|
+
author: alice,
|
|
1498
|
+
protocol: threadsProtocol.protocol,
|
|
1499
|
+
protocolPath: 'thread',
|
|
1500
|
+
schema: 'https://threads.example.com/schemas/thread',
|
|
1501
|
+
dataFormat: 'application/json',
|
|
1502
|
+
});
|
|
1503
|
+
await dwn.processMessage(alice.did, threadWrite.message, { dataStream: threadWrite.dataStream });
|
|
1504
|
+
const threadContextId = threadWrite.message.contextId;
|
|
1505
|
+
// Alice assigns Bob as a participant
|
|
1506
|
+
const participantWrite = await TestDataGenerator.generateRecordsWrite({
|
|
1507
|
+
author: alice,
|
|
1508
|
+
recipient: bob.did,
|
|
1509
|
+
protocol: threadsProtocol.protocol,
|
|
1510
|
+
protocolPath: 'thread/participant',
|
|
1511
|
+
schema: 'https://threads.example.com/schemas/participant',
|
|
1512
|
+
dataFormat: 'application/json',
|
|
1513
|
+
parentContextId: threadContextId,
|
|
1514
|
+
});
|
|
1515
|
+
await dwn.processMessage(alice.did, participantWrite.message, { dataStream: participantWrite.dataStream });
|
|
1516
|
+
// Alice creates 3 comments
|
|
1517
|
+
for (let i = 0; i < 3; i++) {
|
|
1518
|
+
const commentWrite = await TestDataGenerator.generateRecordsWrite({
|
|
1519
|
+
author: alice,
|
|
1520
|
+
protocol: commentsProtocol.protocol,
|
|
1521
|
+
protocolPath: 'thread/comment',
|
|
1522
|
+
schema: 'https://comments.example.com/schemas/comment',
|
|
1523
|
+
dataFormat: 'application/json',
|
|
1524
|
+
parentContextId: threadContextId,
|
|
1525
|
+
});
|
|
1526
|
+
await dwn.processMessage(alice.did, commentWrite.message, { dataStream: commentWrite.dataStream });
|
|
1527
|
+
}
|
|
1528
|
+
// Bob counts comments using the cross-protocol role
|
|
1529
|
+
const bobCount = await RecordsCount.create({
|
|
1530
|
+
signer: Jws.createSigner(bob),
|
|
1531
|
+
protocolRole: 'threads:thread/participant',
|
|
1532
|
+
filter: {
|
|
1533
|
+
protocol: commentsProtocol.protocol,
|
|
1534
|
+
protocolPath: 'thread/comment',
|
|
1535
|
+
contextId: threadContextId,
|
|
1536
|
+
},
|
|
1537
|
+
});
|
|
1538
|
+
const countReply = await dwn.processMessage(alice.did, bobCount.message);
|
|
1539
|
+
expect(countReply.status.code).toBe(200);
|
|
1540
|
+
expect(countReply.count).toBe(3);
|
|
1541
|
+
});
|
|
1542
|
+
});
|
|
1543
|
+
// =========================================================================
|
|
1544
|
+
// Cross-protocol co-update via role
|
|
1545
|
+
// =========================================================================
|
|
1546
|
+
describe('cross-protocol co-update via role', () => {
|
|
1547
|
+
it('should allow a cross-protocol role holder to co-update records in the composing protocol', async () => {
|
|
1548
|
+
const alice = await TestDataGenerator.generateDidKeyPersona();
|
|
1549
|
+
const bob = await TestDataGenerator.generateDidKeyPersona();
|
|
1550
|
+
// A variant of the comments protocol where participants can also co-update
|
|
1551
|
+
const commentsCoUpdateProtocol = {
|
|
1552
|
+
protocol: 'https://comments-coupdate.example.com',
|
|
1553
|
+
published: true,
|
|
1554
|
+
uses: { threads: 'https://threads.example.com' },
|
|
1555
|
+
types: {
|
|
1556
|
+
comment: { schema: 'https://comments-coupdate.example.com/schemas/comment', dataFormats: ['application/json'] },
|
|
1557
|
+
},
|
|
1558
|
+
structure: {
|
|
1559
|
+
thread: {
|
|
1560
|
+
$ref: 'threads:thread',
|
|
1561
|
+
comment: {
|
|
1562
|
+
$actions: [
|
|
1563
|
+
{ who: 'anyone', can: ['create', 'read'] },
|
|
1564
|
+
{ role: 'threads:thread/participant', can: ['co-update'] },
|
|
1565
|
+
],
|
|
1566
|
+
},
|
|
1567
|
+
},
|
|
1568
|
+
},
|
|
1569
|
+
};
|
|
1570
|
+
// Install both protocols on Alice's DWN
|
|
1571
|
+
const threadsConfigure = await ProtocolsConfigure.create({
|
|
1572
|
+
definition: threadsProtocol,
|
|
1573
|
+
signer: Jws.createSigner(alice),
|
|
1574
|
+
});
|
|
1575
|
+
await dwn.processMessage(alice.did, threadsConfigure.message);
|
|
1576
|
+
const commentsConfigure = await ProtocolsConfigure.create({
|
|
1577
|
+
definition: commentsCoUpdateProtocol,
|
|
1578
|
+
signer: Jws.createSigner(alice),
|
|
1579
|
+
});
|
|
1580
|
+
const commentsReply = await dwn.processMessage(alice.did, commentsConfigure.message);
|
|
1581
|
+
expect(commentsReply.status.code).toBe(202);
|
|
1582
|
+
// Alice creates a thread
|
|
1583
|
+
const threadWrite = await TestDataGenerator.generateRecordsWrite({
|
|
1584
|
+
author: alice,
|
|
1585
|
+
protocol: threadsProtocol.protocol,
|
|
1586
|
+
protocolPath: 'thread',
|
|
1587
|
+
schema: 'https://threads.example.com/schemas/thread',
|
|
1588
|
+
dataFormat: 'application/json',
|
|
1589
|
+
});
|
|
1590
|
+
await dwn.processMessage(alice.did, threadWrite.message, { dataStream: threadWrite.dataStream });
|
|
1591
|
+
const threadContextId = threadWrite.message.contextId;
|
|
1592
|
+
// Alice assigns Bob as a participant
|
|
1593
|
+
const participantWrite = await TestDataGenerator.generateRecordsWrite({
|
|
1594
|
+
author: alice,
|
|
1595
|
+
recipient: bob.did,
|
|
1596
|
+
protocol: threadsProtocol.protocol,
|
|
1597
|
+
protocolPath: 'thread/participant',
|
|
1598
|
+
schema: 'https://threads.example.com/schemas/participant',
|
|
1599
|
+
dataFormat: 'application/json',
|
|
1600
|
+
parentContextId: threadContextId,
|
|
1601
|
+
});
|
|
1602
|
+
await dwn.processMessage(alice.did, participantWrite.message, { dataStream: participantWrite.dataStream });
|
|
1603
|
+
// Alice creates a comment (via 'anyone' can 'create')
|
|
1604
|
+
const commentWrite = await TestDataGenerator.generateRecordsWrite({
|
|
1605
|
+
author: alice,
|
|
1606
|
+
protocol: commentsCoUpdateProtocol.protocol,
|
|
1607
|
+
protocolPath: 'thread/comment',
|
|
1608
|
+
schema: 'https://comments-coupdate.example.com/schemas/comment',
|
|
1609
|
+
dataFormat: 'application/json',
|
|
1610
|
+
parentContextId: threadContextId,
|
|
1611
|
+
});
|
|
1612
|
+
await dwn.processMessage(alice.did, commentWrite.message, { dataStream: commentWrite.dataStream });
|
|
1613
|
+
// Bob (participant) co-updates Alice's comment using the cross-protocol role
|
|
1614
|
+
const bobUpdate = await TestDataGenerator.generateFromRecordsWrite({
|
|
1615
|
+
author: bob,
|
|
1616
|
+
existingWrite: commentWrite.recordsWrite,
|
|
1617
|
+
protocolRole: 'threads:thread/participant',
|
|
1618
|
+
});
|
|
1619
|
+
const updateReply = await dwn.processMessage(alice.did, bobUpdate.message, { dataStream: bobUpdate.dataStream });
|
|
1620
|
+
expect(updateReply.status.code).toBe(202);
|
|
1621
|
+
});
|
|
1622
|
+
});
|
|
1623
|
+
// =========================================================================
|
|
1624
|
+
// Query isolation — subscribing to referenced protocol does NOT yield composing protocol events
|
|
1625
|
+
// =========================================================================
|
|
1626
|
+
describe('query isolation between protocols', () => {
|
|
1627
|
+
it('should not return composing protocol records when querying the referenced protocol', async () => {
|
|
1628
|
+
const alice = await TestDataGenerator.generateDidKeyPersona();
|
|
1629
|
+
// Install both protocols
|
|
1630
|
+
const threadsConfigure = await ProtocolsConfigure.create({
|
|
1631
|
+
definition: threadsProtocol,
|
|
1632
|
+
signer: Jws.createSigner(alice),
|
|
1633
|
+
});
|
|
1634
|
+
await dwn.processMessage(alice.did, threadsConfigure.message);
|
|
1635
|
+
const commentsConfigure = await ProtocolsConfigure.create({
|
|
1636
|
+
definition: commentsProtocol,
|
|
1637
|
+
signer: Jws.createSigner(alice),
|
|
1638
|
+
});
|
|
1639
|
+
await dwn.processMessage(alice.did, commentsConfigure.message);
|
|
1640
|
+
// Alice creates a thread in the threads protocol
|
|
1641
|
+
const threadWrite = await TestDataGenerator.generateRecordsWrite({
|
|
1642
|
+
author: alice,
|
|
1643
|
+
protocol: threadsProtocol.protocol,
|
|
1644
|
+
protocolPath: 'thread',
|
|
1645
|
+
schema: 'https://threads.example.com/schemas/thread',
|
|
1646
|
+
dataFormat: 'application/json',
|
|
1647
|
+
});
|
|
1648
|
+
await dwn.processMessage(alice.did, threadWrite.message, { dataStream: threadWrite.dataStream });
|
|
1649
|
+
const threadContextId = threadWrite.message.contextId;
|
|
1650
|
+
// Alice creates comments in the composing protocol
|
|
1651
|
+
const commentWrite = await TestDataGenerator.generateRecordsWrite({
|
|
1652
|
+
author: alice,
|
|
1653
|
+
protocol: commentsProtocol.protocol,
|
|
1654
|
+
protocolPath: 'thread/comment',
|
|
1655
|
+
schema: 'https://comments.example.com/schemas/comment',
|
|
1656
|
+
dataFormat: 'application/json',
|
|
1657
|
+
parentContextId: threadContextId,
|
|
1658
|
+
});
|
|
1659
|
+
await dwn.processMessage(alice.did, commentWrite.message, { dataStream: commentWrite.dataStream });
|
|
1660
|
+
// Query the THREADS protocol for all records — should NOT include the comment
|
|
1661
|
+
const threadsQuery = await RecordsQuery.create({
|
|
1662
|
+
signer: Jws.createSigner(alice),
|
|
1663
|
+
filter: {
|
|
1664
|
+
protocol: threadsProtocol.protocol,
|
|
1665
|
+
},
|
|
1666
|
+
});
|
|
1667
|
+
const threadsQueryReply = await dwn.processMessage(alice.did, threadsQuery.message);
|
|
1668
|
+
expect(threadsQueryReply.status.code).toBe(200);
|
|
1669
|
+
// Only the thread record itself should be returned
|
|
1670
|
+
const threadsRecordIds = threadsQueryReply.entries.map((e) => e.recordId);
|
|
1671
|
+
expect(threadsRecordIds).toContain(threadWrite.message.recordId);
|
|
1672
|
+
expect(threadsRecordIds).not.toContain(commentWrite.message.recordId);
|
|
1673
|
+
// Query the COMMENTS protocol — should include the comment
|
|
1674
|
+
const commentsQuery = await RecordsQuery.create({
|
|
1675
|
+
signer: Jws.createSigner(alice),
|
|
1676
|
+
filter: {
|
|
1677
|
+
protocol: commentsProtocol.protocol,
|
|
1678
|
+
},
|
|
1679
|
+
});
|
|
1680
|
+
const commentsQueryReply = await dwn.processMessage(alice.did, commentsQuery.message);
|
|
1681
|
+
expect(commentsQueryReply.status.code).toBe(200);
|
|
1682
|
+
const commentsRecordIds = commentsQueryReply.entries.map((e) => e.recordId);
|
|
1683
|
+
expect(commentsRecordIds).toContain(commentWrite.message.recordId);
|
|
1684
|
+
expect(commentsRecordIds).not.toContain(threadWrite.message.recordId);
|
|
1685
|
+
});
|
|
1686
|
+
});
|
|
1687
|
+
// =========================================================================
|
|
1688
|
+
// Multiple composing protocols on the same $ref target
|
|
1689
|
+
// =========================================================================
|
|
1690
|
+
describe('multiple composing protocols on the same $ref target', () => {
|
|
1691
|
+
it('should allow two different composing protocols to independently reference the same type in a base protocol', async () => {
|
|
1692
|
+
const alice = await TestDataGenerator.generateDidKeyPersona();
|
|
1693
|
+
// commentsProtocol already references threads:thread
|
|
1694
|
+
// Create a second composing protocol that also references threads:thread
|
|
1695
|
+
const ratingsProtocol = {
|
|
1696
|
+
protocol: 'https://ratings.example.com',
|
|
1697
|
+
published: true,
|
|
1698
|
+
uses: { threads: 'https://threads.example.com' },
|
|
1699
|
+
types: {
|
|
1700
|
+
rating: { schema: 'https://ratings.example.com/schemas/rating', dataFormats: ['application/json'] },
|
|
1701
|
+
},
|
|
1702
|
+
structure: {
|
|
1703
|
+
thread: {
|
|
1704
|
+
$ref: 'threads:thread',
|
|
1705
|
+
rating: {
|
|
1706
|
+
$actions: [
|
|
1707
|
+
{ who: 'anyone', can: ['create', 'read'] },
|
|
1708
|
+
],
|
|
1709
|
+
},
|
|
1710
|
+
},
|
|
1711
|
+
},
|
|
1712
|
+
};
|
|
1713
|
+
// Install threads (base), comments, and ratings
|
|
1714
|
+
const threadsConfigure = await ProtocolsConfigure.create({
|
|
1715
|
+
definition: threadsProtocol,
|
|
1716
|
+
signer: Jws.createSigner(alice),
|
|
1717
|
+
});
|
|
1718
|
+
await dwn.processMessage(alice.did, threadsConfigure.message);
|
|
1719
|
+
const commentsConfigure = await ProtocolsConfigure.create({
|
|
1720
|
+
definition: commentsProtocol,
|
|
1721
|
+
signer: Jws.createSigner(alice),
|
|
1722
|
+
});
|
|
1723
|
+
const commentsReply = await dwn.processMessage(alice.did, commentsConfigure.message);
|
|
1724
|
+
expect(commentsReply.status.code).toBe(202);
|
|
1725
|
+
const ratingsConfigure = await ProtocolsConfigure.create({
|
|
1726
|
+
definition: ratingsProtocol,
|
|
1727
|
+
signer: Jws.createSigner(alice),
|
|
1728
|
+
});
|
|
1729
|
+
const ratingsReply = await dwn.processMessage(alice.did, ratingsConfigure.message);
|
|
1730
|
+
expect(ratingsReply.status.code).toBe(202);
|
|
1731
|
+
// Create a thread
|
|
1732
|
+
const threadWrite = await TestDataGenerator.generateRecordsWrite({
|
|
1733
|
+
author: alice,
|
|
1734
|
+
protocol: threadsProtocol.protocol,
|
|
1735
|
+
protocolPath: 'thread',
|
|
1736
|
+
schema: 'https://threads.example.com/schemas/thread',
|
|
1737
|
+
dataFormat: 'application/json',
|
|
1738
|
+
});
|
|
1739
|
+
await dwn.processMessage(alice.did, threadWrite.message, { dataStream: threadWrite.dataStream });
|
|
1740
|
+
const threadContextId = threadWrite.message.contextId;
|
|
1741
|
+
// Create a comment under the thread via comments protocol
|
|
1742
|
+
const commentWrite = await TestDataGenerator.generateRecordsWrite({
|
|
1743
|
+
author: alice,
|
|
1744
|
+
protocol: commentsProtocol.protocol,
|
|
1745
|
+
protocolPath: 'thread/comment',
|
|
1746
|
+
schema: 'https://comments.example.com/schemas/comment',
|
|
1747
|
+
dataFormat: 'application/json',
|
|
1748
|
+
parentContextId: threadContextId,
|
|
1749
|
+
});
|
|
1750
|
+
const commentReply = await dwn.processMessage(alice.did, commentWrite.message, { dataStream: commentWrite.dataStream });
|
|
1751
|
+
expect(commentReply.status.code).toBe(202);
|
|
1752
|
+
// Create a rating under the same thread via ratings protocol
|
|
1753
|
+
const ratingWrite = await TestDataGenerator.generateRecordsWrite({
|
|
1754
|
+
author: alice,
|
|
1755
|
+
protocol: ratingsProtocol.protocol,
|
|
1756
|
+
protocolPath: 'thread/rating',
|
|
1757
|
+
schema: 'https://ratings.example.com/schemas/rating',
|
|
1758
|
+
dataFormat: 'application/json',
|
|
1759
|
+
parentContextId: threadContextId,
|
|
1760
|
+
});
|
|
1761
|
+
const ratingReply = await dwn.processMessage(alice.did, ratingWrite.message, { dataStream: ratingWrite.dataStream });
|
|
1762
|
+
expect(ratingReply.status.code).toBe(202);
|
|
1763
|
+
// Each protocol's records are isolated — querying comments returns only comments
|
|
1764
|
+
const commentsQuery = await RecordsQuery.create({
|
|
1765
|
+
signer: Jws.createSigner(alice),
|
|
1766
|
+
filter: { protocol: commentsProtocol.protocol },
|
|
1767
|
+
});
|
|
1768
|
+
const commentsQueryReply = await dwn.processMessage(alice.did, commentsQuery.message);
|
|
1769
|
+
expect(commentsQueryReply.entries.length).toBe(1);
|
|
1770
|
+
expect(commentsQueryReply.entries[0].recordId).toBe(commentWrite.message.recordId);
|
|
1771
|
+
// Querying ratings returns only ratings
|
|
1772
|
+
const ratingsQuery = await RecordsQuery.create({
|
|
1773
|
+
signer: Jws.createSigner(alice),
|
|
1774
|
+
filter: { protocol: ratingsProtocol.protocol },
|
|
1775
|
+
});
|
|
1776
|
+
const ratingsQueryReply = await dwn.processMessage(alice.did, ratingsQuery.message);
|
|
1777
|
+
expect(ratingsQueryReply.entries.length).toBe(1);
|
|
1778
|
+
expect(ratingsQueryReply.entries[0].recordId).toBe(ratingWrite.message.recordId);
|
|
1779
|
+
});
|
|
1780
|
+
});
|
|
1781
|
+
// =========================================================================
|
|
1782
|
+
// Composing protocol definition upgrade (adding children under $ref in V2)
|
|
1783
|
+
// =========================================================================
|
|
1784
|
+
describe('composing protocol definition upgrade', () => {
|
|
1785
|
+
it('should allow upgrading a composing protocol to add new child types under a `$ref` node', async () => {
|
|
1786
|
+
const alice = await TestDataGenerator.generateDidKeyPersona();
|
|
1787
|
+
// Install threads protocol
|
|
1788
|
+
const threadsConfigure = await ProtocolsConfigure.create({
|
|
1789
|
+
definition: threadsProtocol,
|
|
1790
|
+
signer: Jws.createSigner(alice),
|
|
1791
|
+
});
|
|
1792
|
+
await dwn.processMessage(alice.did, threadsConfigure.message);
|
|
1793
|
+
// Comments V1 — only has 'comment' under 'thread'
|
|
1794
|
+
const commentsV1 = {
|
|
1795
|
+
protocol: 'https://comments-upgrade.example.com',
|
|
1796
|
+
published: true,
|
|
1797
|
+
uses: { threads: 'https://threads.example.com' },
|
|
1798
|
+
types: {
|
|
1799
|
+
comment: { schema: 'https://comments-upgrade.example.com/schemas/comment', dataFormats: ['application/json'] },
|
|
1800
|
+
},
|
|
1801
|
+
structure: {
|
|
1802
|
+
thread: {
|
|
1803
|
+
$ref: 'threads:thread',
|
|
1804
|
+
comment: {
|
|
1805
|
+
$actions: [{ who: 'anyone', can: ['create', 'read'] }],
|
|
1806
|
+
},
|
|
1807
|
+
},
|
|
1808
|
+
},
|
|
1809
|
+
};
|
|
1810
|
+
const v1Configure = await ProtocolsConfigure.create({
|
|
1811
|
+
definition: commentsV1,
|
|
1812
|
+
signer: Jws.createSigner(alice),
|
|
1813
|
+
});
|
|
1814
|
+
const v1Reply = await dwn.processMessage(alice.did, v1Configure.message);
|
|
1815
|
+
expect(v1Reply.status.code).toBe(202);
|
|
1816
|
+
// Create a thread and a V1 comment
|
|
1817
|
+
const threadWrite = await TestDataGenerator.generateRecordsWrite({
|
|
1818
|
+
author: alice,
|
|
1819
|
+
protocol: threadsProtocol.protocol,
|
|
1820
|
+
protocolPath: 'thread',
|
|
1821
|
+
schema: 'https://threads.example.com/schemas/thread',
|
|
1822
|
+
dataFormat: 'application/json',
|
|
1823
|
+
});
|
|
1824
|
+
await dwn.processMessage(alice.did, threadWrite.message, { dataStream: threadWrite.dataStream });
|
|
1825
|
+
const threadContextId = threadWrite.message.contextId;
|
|
1826
|
+
const commentWrite = await TestDataGenerator.generateRecordsWrite({
|
|
1827
|
+
author: alice,
|
|
1828
|
+
protocol: commentsV1.protocol,
|
|
1829
|
+
protocolPath: 'thread/comment',
|
|
1830
|
+
schema: 'https://comments-upgrade.example.com/schemas/comment',
|
|
1831
|
+
dataFormat: 'application/json',
|
|
1832
|
+
parentContextId: threadContextId,
|
|
1833
|
+
});
|
|
1834
|
+
const commentReply = await dwn.processMessage(alice.did, commentWrite.message, { dataStream: commentWrite.dataStream });
|
|
1835
|
+
expect(commentReply.status.code).toBe(202);
|
|
1836
|
+
// Wait to ensure V2 gets a later timestamp
|
|
1837
|
+
await Time.minimalSleep();
|
|
1838
|
+
// Comments V2 — adds 'reaction' type under 'thread/comment'
|
|
1839
|
+
const commentsV2 = {
|
|
1840
|
+
protocol: 'https://comments-upgrade.example.com',
|
|
1841
|
+
published: true,
|
|
1842
|
+
uses: { threads: 'https://threads.example.com' },
|
|
1843
|
+
types: {
|
|
1844
|
+
comment: { schema: 'https://comments-upgrade.example.com/schemas/comment', dataFormats: ['application/json'] },
|
|
1845
|
+
reaction: { schema: 'https://comments-upgrade.example.com/schemas/reaction', dataFormats: ['application/json'] },
|
|
1846
|
+
},
|
|
1847
|
+
structure: {
|
|
1848
|
+
thread: {
|
|
1849
|
+
$ref: 'threads:thread',
|
|
1850
|
+
comment: {
|
|
1851
|
+
$actions: [{ who: 'anyone', can: ['create', 'read'] }],
|
|
1852
|
+
reaction: {
|
|
1853
|
+
$actions: [{ who: 'anyone', can: ['create', 'read'] }],
|
|
1854
|
+
},
|
|
1855
|
+
},
|
|
1856
|
+
},
|
|
1857
|
+
},
|
|
1858
|
+
};
|
|
1859
|
+
const v2Configure = await ProtocolsConfigure.create({
|
|
1860
|
+
definition: commentsV2,
|
|
1861
|
+
signer: Jws.createSigner(alice),
|
|
1862
|
+
});
|
|
1863
|
+
const v2Reply = await dwn.processMessage(alice.did, v2Configure.message);
|
|
1864
|
+
expect(v2Reply.status.code).toBe(202);
|
|
1865
|
+
// Now create a reaction under the comment using V2's structure
|
|
1866
|
+
const commentContextId = commentWrite.message.contextId;
|
|
1867
|
+
const reactionWrite = await TestDataGenerator.generateRecordsWrite({
|
|
1868
|
+
author: alice,
|
|
1869
|
+
protocol: commentsV2.protocol,
|
|
1870
|
+
protocolPath: 'thread/comment/reaction',
|
|
1871
|
+
schema: 'https://comments-upgrade.example.com/schemas/reaction',
|
|
1872
|
+
dataFormat: 'application/json',
|
|
1873
|
+
parentContextId: commentContextId,
|
|
1874
|
+
});
|
|
1875
|
+
const reactionReply = await dwn.processMessage(alice.did, reactionWrite.message, { dataStream: reactionWrite.dataStream });
|
|
1876
|
+
expect(reactionReply.status.code).toBe(202);
|
|
1877
|
+
});
|
|
1257
1878
|
});
|
|
1258
1879
|
// =========================================================================
|
|
1259
1880
|
// Temporal correctness — governing timestamp for cross-protocol lookups
|
|
1260
1881
|
// =========================================================================
|
|
1261
1882
|
describe('temporal correctness — governing timestamp', () => {
|
|
1262
|
-
it('should use the governing timestamp when fetching the referenced protocol for role verification', () =>
|
|
1883
|
+
it('should use the governing timestamp when fetching the referenced protocol for role verification', async () => {
|
|
1263
1884
|
// Scenario:
|
|
1264
1885
|
// 1. Install threads V1 (participant has $role: true)
|
|
1265
1886
|
// 2. Install comments protocol (uses threads, references threads:thread/participant role)
|
|
@@ -1267,8 +1888,8 @@ export function testProtocolComposition() {
|
|
|
1267
1888
|
// 4. Update threads to V2 where participant is no longer a role ($role removed)
|
|
1268
1889
|
// 5. Bob reads the comment using the cross-protocol role
|
|
1269
1890
|
// → should SUCCEED because the comment's governing timestamp pins to threads V1
|
|
1270
|
-
const alice =
|
|
1271
|
-
const bob =
|
|
1891
|
+
const alice = await TestDataGenerator.generateDidKeyPersona();
|
|
1892
|
+
const bob = await TestDataGenerator.generateDidKeyPersona();
|
|
1272
1893
|
// threads V1: participant is a role
|
|
1273
1894
|
const threadsV1 = {
|
|
1274
1895
|
protocol: 'https://threads-versioned.example.com',
|
|
@@ -1311,31 +1932,31 @@ export function testProtocolComposition() {
|
|
|
1311
1932
|
},
|
|
1312
1933
|
};
|
|
1313
1934
|
// Install threads V1
|
|
1314
|
-
const threadsV1Configure =
|
|
1935
|
+
const threadsV1Configure = await ProtocolsConfigure.create({
|
|
1315
1936
|
definition: threadsV1,
|
|
1316
1937
|
signer: Jws.createSigner(alice),
|
|
1317
1938
|
});
|
|
1318
|
-
const threadsV1Reply =
|
|
1939
|
+
const threadsV1Reply = await dwn.processMessage(alice.did, threadsV1Configure.message);
|
|
1319
1940
|
expect(threadsV1Reply.status.code).toBe(202);
|
|
1320
1941
|
// Install comments protocol (depends on threads)
|
|
1321
|
-
const commentsConfigure =
|
|
1942
|
+
const commentsConfigure = await ProtocolsConfigure.create({
|
|
1322
1943
|
definition: commentsVersioned,
|
|
1323
1944
|
signer: Jws.createSigner(alice),
|
|
1324
1945
|
});
|
|
1325
|
-
const commentsReply =
|
|
1946
|
+
const commentsReply = await dwn.processMessage(alice.did, commentsConfigure.message);
|
|
1326
1947
|
expect(commentsReply.status.code).toBe(202);
|
|
1327
1948
|
// Alice creates a thread
|
|
1328
|
-
const threadWrite =
|
|
1949
|
+
const threadWrite = await TestDataGenerator.generateRecordsWrite({
|
|
1329
1950
|
author: alice,
|
|
1330
1951
|
protocol: threadsV1.protocol,
|
|
1331
1952
|
protocolPath: 'thread',
|
|
1332
1953
|
schema: 'https://threads-versioned.example.com/schemas/thread',
|
|
1333
1954
|
dataFormat: 'application/json',
|
|
1334
1955
|
});
|
|
1335
|
-
|
|
1956
|
+
await dwn.processMessage(alice.did, threadWrite.message, { dataStream: threadWrite.dataStream });
|
|
1336
1957
|
const threadContextId = threadWrite.message.contextId;
|
|
1337
1958
|
// Alice assigns Bob as participant (role record in threads V1)
|
|
1338
|
-
const participantWrite =
|
|
1959
|
+
const participantWrite = await TestDataGenerator.generateRecordsWrite({
|
|
1339
1960
|
author: alice,
|
|
1340
1961
|
recipient: bob.did,
|
|
1341
1962
|
protocol: threadsV1.protocol,
|
|
@@ -1344,9 +1965,9 @@ export function testProtocolComposition() {
|
|
|
1344
1965
|
dataFormat: 'application/json',
|
|
1345
1966
|
parentContextId: threadContextId,
|
|
1346
1967
|
});
|
|
1347
|
-
|
|
1968
|
+
await dwn.processMessage(alice.did, participantWrite.message, { dataStream: participantWrite.dataStream });
|
|
1348
1969
|
// Alice creates a comment (under V1)
|
|
1349
|
-
const commentWrite =
|
|
1970
|
+
const commentWrite = await TestDataGenerator.generateRecordsWrite({
|
|
1350
1971
|
author: alice,
|
|
1351
1972
|
protocol: commentsVersioned.protocol,
|
|
1352
1973
|
protocolPath: 'thread/comment',
|
|
@@ -1354,9 +1975,9 @@ export function testProtocolComposition() {
|
|
|
1354
1975
|
dataFormat: 'application/json',
|
|
1355
1976
|
parentContextId: threadContextId,
|
|
1356
1977
|
});
|
|
1357
|
-
|
|
1978
|
+
await dwn.processMessage(alice.did, commentWrite.message, { dataStream: commentWrite.dataStream });
|
|
1358
1979
|
// Wait to ensure V2 gets a later timestamp
|
|
1359
|
-
|
|
1980
|
+
await Time.minimalSleep();
|
|
1360
1981
|
// threads V2: participant is NO LONGER a role ($role removed)
|
|
1361
1982
|
const threadsV2 = {
|
|
1362
1983
|
protocol: 'https://threads-versioned.example.com',
|
|
@@ -1378,16 +1999,16 @@ export function testProtocolComposition() {
|
|
|
1378
1999
|
},
|
|
1379
2000
|
},
|
|
1380
2001
|
};
|
|
1381
|
-
const threadsV2Configure =
|
|
2002
|
+
const threadsV2Configure = await ProtocolsConfigure.create({
|
|
1382
2003
|
definition: threadsV2,
|
|
1383
2004
|
signer: Jws.createSigner(alice),
|
|
1384
2005
|
});
|
|
1385
|
-
const threadsV2Reply =
|
|
2006
|
+
const threadsV2Reply = await dwn.processMessage(alice.did, threadsV2Configure.message);
|
|
1386
2007
|
expect(threadsV2Reply.status.code).toBe(202);
|
|
1387
2008
|
// Bob reads the comment using the cross-protocol role.
|
|
1388
2009
|
// The comment was created under V1. The governing timestamp pins the referenced protocol
|
|
1389
2010
|
// lookup to V1 where participant IS a valid role. This should SUCCEED.
|
|
1390
|
-
const bobRead =
|
|
2011
|
+
const bobRead = await RecordsRead.create({
|
|
1391
2012
|
signer: Jws.createSigner(bob),
|
|
1392
2013
|
protocolRole: 'threads:thread/participant',
|
|
1393
2014
|
filter: {
|
|
@@ -1396,9 +2017,9 @@ export function testProtocolComposition() {
|
|
|
1396
2017
|
contextId: threadContextId,
|
|
1397
2018
|
},
|
|
1398
2019
|
});
|
|
1399
|
-
const bobReadReply =
|
|
2020
|
+
const bobReadReply = await dwn.processMessage(alice.did, bobRead.message);
|
|
1400
2021
|
expect(bobReadReply.status.code).toBe(200);
|
|
1401
|
-
})
|
|
2022
|
+
});
|
|
1402
2023
|
});
|
|
1403
2024
|
// =========================================================================
|
|
1404
2025
|
// Encryption + composition tests
|
|
@@ -1406,12 +2027,12 @@ export function testProtocolComposition() {
|
|
|
1406
2027
|
describe('encryption with protocol composition', () => {
|
|
1407
2028
|
let encryptionPrivateJwk;
|
|
1408
2029
|
let encryptionRootKeyId;
|
|
1409
|
-
beforeAll(() =>
|
|
1410
|
-
const privateKey =
|
|
2030
|
+
beforeAll(async () => {
|
|
2031
|
+
const privateKey = await X25519.generateKey();
|
|
1411
2032
|
encryptionPrivateJwk = privateKey;
|
|
1412
2033
|
encryptionRootKeyId = 'did:example:alice#enc';
|
|
1413
|
-
})
|
|
1414
|
-
it('should skip `$encryption` injection on `$ref` nodes but inject on their children (raw-key path)', () =>
|
|
2034
|
+
});
|
|
2035
|
+
it('should skip `$encryption` injection on `$ref` nodes but inject on their children (raw-key path)', async () => {
|
|
1415
2036
|
const composingProtocol = {
|
|
1416
2037
|
protocol: 'https://comments.example.com',
|
|
1417
2038
|
published: true,
|
|
@@ -1432,7 +2053,7 @@ export function testProtocolComposition() {
|
|
|
1432
2053
|
},
|
|
1433
2054
|
},
|
|
1434
2055
|
};
|
|
1435
|
-
const result =
|
|
2056
|
+
const result = await Protocols.deriveAndInjectPublicEncryptionKeys(composingProtocol, encryptionRootKeyId, encryptionPrivateJwk);
|
|
1436
2057
|
// $ref node must NOT have $encryption
|
|
1437
2058
|
expect(result.structure.thread.$encryption).toBeUndefined();
|
|
1438
2059
|
// Children of $ref node MUST have $encryption
|
|
@@ -1444,8 +2065,8 @@ export function testProtocolComposition() {
|
|
|
1444
2065
|
const reactionRuleSet = commentRuleSet.reaction;
|
|
1445
2066
|
expect(reactionRuleSet.$encryption).toBeDefined();
|
|
1446
2067
|
expect(reactionRuleSet.$encryption.rootKeyId).toBe(encryptionRootKeyId);
|
|
1447
|
-
})
|
|
1448
|
-
it('should skip `$encryption` injection on `$ref` nodes but inject on their children (callback path)', () =>
|
|
2068
|
+
});
|
|
2069
|
+
it('should skip `$encryption` injection on `$ref` nodes but inject on their children (callback path)', async () => {
|
|
1449
2070
|
const composingProtocol = {
|
|
1450
2071
|
protocol: 'https://comments.example.com',
|
|
1451
2072
|
published: true,
|
|
@@ -1470,15 +2091,15 @@ export function testProtocolComposition() {
|
|
|
1470
2091
|
const keyDeriver = {
|
|
1471
2092
|
rootKeyId: encryptionRootKeyId,
|
|
1472
2093
|
derivationScheme: KeyDerivationScheme.ProtocolPath,
|
|
1473
|
-
derivePublicKey: (fullDerivationPath) =>
|
|
2094
|
+
derivePublicKey: async (fullDerivationPath) => {
|
|
1474
2095
|
calledPaths.push([...fullDerivationPath]);
|
|
1475
|
-
const privateKeyBytes =
|
|
1476
|
-
const derivedPrivateKeyBytes =
|
|
1477
|
-
const derivedPrivateKeyJwk =
|
|
1478
|
-
return
|
|
1479
|
-
}
|
|
2096
|
+
const privateKeyBytes = await X25519.privateKeyToBytes({ privateKey: encryptionPrivateJwk });
|
|
2097
|
+
const derivedPrivateKeyBytes = await HdKey.derivePrivateKeyBytes(privateKeyBytes, fullDerivationPath);
|
|
2098
|
+
const derivedPrivateKeyJwk = await X25519.bytesToPrivateKey({ privateKeyBytes: derivedPrivateKeyBytes });
|
|
2099
|
+
return await X25519.getPublicKey({ key: derivedPrivateKeyJwk });
|
|
2100
|
+
},
|
|
1480
2101
|
};
|
|
1481
|
-
const result =
|
|
2102
|
+
const result = await Protocols.deriveAndInjectPublicEncryptionKeys(composingProtocol, keyDeriver);
|
|
1482
2103
|
// $ref node must NOT have $encryption
|
|
1483
2104
|
expect(result.structure.thread.$encryption).toBeUndefined();
|
|
1484
2105
|
// Children of $ref node MUST have $encryption
|
|
@@ -1493,8 +2114,8 @@ export function testProtocolComposition() {
|
|
|
1493
2114
|
const reactionPath = [KeyDerivationScheme.ProtocolPath, 'https://comments.example.com', 'thread', 'comment', 'reaction'];
|
|
1494
2115
|
expect(calledPaths).toContainEqual(commentPath);
|
|
1495
2116
|
expect(calledPaths).toContainEqual(reactionPath);
|
|
1496
|
-
})
|
|
1497
|
-
it('should produce identical $encryption for children across both overloads', () =>
|
|
2117
|
+
});
|
|
2118
|
+
it('should produce identical $encryption for children across both overloads', async () => {
|
|
1498
2119
|
const composingProtocol = {
|
|
1499
2120
|
protocol: 'https://comments.example.com',
|
|
1500
2121
|
published: true,
|
|
@@ -1510,19 +2131,19 @@ export function testProtocolComposition() {
|
|
|
1510
2131
|
},
|
|
1511
2132
|
};
|
|
1512
2133
|
// Raw-key path
|
|
1513
|
-
const resultA =
|
|
2134
|
+
const resultA = await Protocols.deriveAndInjectPublicEncryptionKeys(composingProtocol, encryptionRootKeyId, encryptionPrivateJwk);
|
|
1514
2135
|
// Callback path
|
|
1515
2136
|
const keyDeriver = {
|
|
1516
2137
|
rootKeyId: encryptionRootKeyId,
|
|
1517
2138
|
derivationScheme: KeyDerivationScheme.ProtocolPath,
|
|
1518
|
-
derivePublicKey: (fullDerivationPath) =>
|
|
1519
|
-
const privateKeyBytes =
|
|
1520
|
-
const derivedPrivateKeyBytes =
|
|
1521
|
-
const derivedPrivateKeyJwk =
|
|
1522
|
-
return
|
|
1523
|
-
}
|
|
2139
|
+
derivePublicKey: async (fullDerivationPath) => {
|
|
2140
|
+
const privateKeyBytes = await X25519.privateKeyToBytes({ privateKey: encryptionPrivateJwk });
|
|
2141
|
+
const derivedPrivateKeyBytes = await HdKey.derivePrivateKeyBytes(privateKeyBytes, fullDerivationPath);
|
|
2142
|
+
const derivedPrivateKeyJwk = await X25519.bytesToPrivateKey({ privateKeyBytes: derivedPrivateKeyBytes });
|
|
2143
|
+
return await X25519.getPublicKey({ key: derivedPrivateKeyJwk });
|
|
2144
|
+
},
|
|
1524
2145
|
};
|
|
1525
|
-
const resultB =
|
|
2146
|
+
const resultB = await Protocols.deriveAndInjectPublicEncryptionKeys(composingProtocol, keyDeriver);
|
|
1526
2147
|
// Both paths must skip $ref
|
|
1527
2148
|
expect(resultA.structure.thread.$encryption).toBeUndefined();
|
|
1528
2149
|
expect(resultB.structure.thread.$encryption).toBeUndefined();
|
|
@@ -1531,69 +2152,69 @@ export function testProtocolComposition() {
|
|
|
1531
2152
|
const commentB = resultB.structure.thread.comment;
|
|
1532
2153
|
expect(commentA.$encryption.publicKeyJwk).toEqual(commentB.$encryption.publicKeyJwk);
|
|
1533
2154
|
expect(commentA.$encryption.rootKeyId).toBe(commentB.$encryption.rootKeyId);
|
|
1534
|
-
})
|
|
1535
|
-
it('should successfully install a composing protocol with encryption after $ref skip', () =>
|
|
2155
|
+
});
|
|
2156
|
+
it('should successfully install a composing protocol with encryption after $ref skip', async () => {
|
|
1536
2157
|
// This test verifies the full pipeline: inject encryption keys → ProtocolsConfigure.create()
|
|
1537
2158
|
// → validateRefNode() passes because $ref node has no $encryption.
|
|
1538
|
-
const alice =
|
|
2159
|
+
const alice = await TestDataGenerator.generatePersona();
|
|
1539
2160
|
TestStubGenerator.stubDidResolver(didResolver, [alice]);
|
|
1540
2161
|
// Install the threads protocol first (required dependency)
|
|
1541
|
-
const threadsConfigure =
|
|
2162
|
+
const threadsConfigure = await ProtocolsConfigure.create({
|
|
1542
2163
|
definition: threadsProtocol,
|
|
1543
2164
|
signer: Jws.createSigner(alice),
|
|
1544
2165
|
});
|
|
1545
|
-
const threadsInstallReply =
|
|
2166
|
+
const threadsInstallReply = await dwn.processMessage(alice.did, threadsConfigure.message);
|
|
1546
2167
|
expect(threadsInstallReply.status.code).toBe(202);
|
|
1547
2168
|
// Inject encryption keys into the composing protocol
|
|
1548
|
-
const encryptedComments =
|
|
2169
|
+
const encryptedComments = await Protocols.deriveAndInjectPublicEncryptionKeys(commentsProtocol, alice.keyId, alice.encryptionKeyPair.privateJwk);
|
|
1549
2170
|
// $ref node should not have $encryption
|
|
1550
2171
|
expect(encryptedComments.structure.thread.$encryption).toBeUndefined();
|
|
1551
2172
|
// Children should have $encryption
|
|
1552
2173
|
expect(encryptedComments.structure.thread.comment.$encryption).toBeDefined();
|
|
1553
2174
|
// ProtocolsConfigure.create() should NOT throw — validateRefNode() will pass
|
|
1554
2175
|
// because $ref node has no forbidden directives
|
|
1555
|
-
const commentsConfigure =
|
|
2176
|
+
const commentsConfigure = await ProtocolsConfigure.create({
|
|
1556
2177
|
definition: encryptedComments,
|
|
1557
2178
|
signer: Jws.createSigner(alice),
|
|
1558
2179
|
});
|
|
1559
2180
|
// Install should succeed
|
|
1560
|
-
const commentsInstallReply =
|
|
2181
|
+
const commentsInstallReply = await dwn.processMessage(alice.did, commentsConfigure.message);
|
|
1561
2182
|
expect(commentsInstallReply.status.code).toBe(202);
|
|
1562
|
-
})
|
|
1563
|
-
it('should encrypt and decrypt a child record written under a `$ref` parent', () =>
|
|
2183
|
+
});
|
|
2184
|
+
it('should encrypt and decrypt a child record written under a `$ref` parent', async () => {
|
|
1564
2185
|
// Full round-trip: install both protocols → write parent → write encrypted child → read and decrypt
|
|
1565
|
-
const alice =
|
|
2186
|
+
const alice = await TestDataGenerator.generatePersona();
|
|
1566
2187
|
TestStubGenerator.stubDidResolver(didResolver, [alice]);
|
|
1567
2188
|
// 1. Install the threads protocol (parent)
|
|
1568
|
-
const threadsConfigure =
|
|
2189
|
+
const threadsConfigure = await ProtocolsConfigure.create({
|
|
1569
2190
|
definition: threadsProtocol,
|
|
1570
2191
|
signer: Jws.createSigner(alice),
|
|
1571
2192
|
});
|
|
1572
|
-
const threadsReply =
|
|
2193
|
+
const threadsReply = await dwn.processMessage(alice.did, threadsConfigure.message);
|
|
1573
2194
|
expect(threadsReply.status.code).toBe(202);
|
|
1574
2195
|
// 2. Install the comments protocol with encryption keys
|
|
1575
|
-
const encryptedComments =
|
|
1576
|
-
const commentsConfigure =
|
|
2196
|
+
const encryptedComments = await Protocols.deriveAndInjectPublicEncryptionKeys(commentsProtocol, alice.keyId, alice.encryptionKeyPair.privateJwk);
|
|
2197
|
+
const commentsConfigure = await ProtocolsConfigure.create({
|
|
1577
2198
|
definition: encryptedComments,
|
|
1578
2199
|
signer: Jws.createSigner(alice),
|
|
1579
2200
|
});
|
|
1580
|
-
const commentsReply =
|
|
2201
|
+
const commentsReply = await dwn.processMessage(alice.did, commentsConfigure.message);
|
|
1581
2202
|
expect(commentsReply.status.code).toBe(202);
|
|
1582
2203
|
// 3. Write a thread record (in the threads protocol — the $ref parent)
|
|
1583
|
-
const threadWrite =
|
|
2204
|
+
const threadWrite = await TestDataGenerator.generateRecordsWrite({
|
|
1584
2205
|
author: alice,
|
|
1585
2206
|
protocol: threadsProtocol.protocol,
|
|
1586
2207
|
protocolPath: 'thread',
|
|
1587
2208
|
schema: 'https://threads.example.com/schemas/thread',
|
|
1588
2209
|
dataFormat: 'application/json',
|
|
1589
2210
|
});
|
|
1590
|
-
const threadWriteReply =
|
|
2211
|
+
const threadWriteReply = await dwn.processMessage(alice.did, threadWrite.message, { dataStream: threadWrite.dataStream });
|
|
1591
2212
|
expect(threadWriteReply.status.code).toBe(202);
|
|
1592
2213
|
const threadContextId = threadWrite.message.contextId;
|
|
1593
2214
|
// 4. Write an encrypted comment (child of $ref parent, in the comments protocol)
|
|
1594
2215
|
const plaintext = 'This is a secret comment';
|
|
1595
2216
|
const plaintextBytes = Encoder.stringToBytes(plaintext);
|
|
1596
|
-
const encryptedComment =
|
|
2217
|
+
const encryptedComment = await TestDataGenerator.generateProtocolEncryptedRecordsWrite({
|
|
1597
2218
|
plaintextBytes,
|
|
1598
2219
|
author: alice,
|
|
1599
2220
|
protocolDefinition: encryptedComments,
|
|
@@ -1602,14 +2223,14 @@ export function testProtocolComposition() {
|
|
|
1602
2223
|
encryptSymmetricKeyWithProtocolPathDerivedKey: true,
|
|
1603
2224
|
encryptSymmetricKeyWithProtocolContextDerivedKey: false,
|
|
1604
2225
|
});
|
|
1605
|
-
const commentWriteReply =
|
|
2226
|
+
const commentWriteReply = await dwn.processMessage(alice.did, encryptedComment.message, { dataStream: DataStream.fromBytes(encryptedComment.encryptedDataBytes) });
|
|
1606
2227
|
expect(commentWriteReply.status.code).toBe(202);
|
|
1607
2228
|
// 5. Read the encrypted comment back
|
|
1608
|
-
const readMessage =
|
|
2229
|
+
const readMessage = await RecordsRead.create({
|
|
1609
2230
|
signer: Jws.createSigner(alice),
|
|
1610
2231
|
filter: { recordId: encryptedComment.message.recordId },
|
|
1611
2232
|
});
|
|
1612
|
-
const readReply =
|
|
2233
|
+
const readReply = await dwn.processMessage(alice.did, readMessage.message);
|
|
1613
2234
|
expect(readReply.status.code).toBe(200);
|
|
1614
2235
|
// 6. Decrypt using the composing protocol's key hierarchy.
|
|
1615
2236
|
// The key derivation path is [protocolPath, commentsProtocol.protocol, 'thread', 'comment']
|
|
@@ -1620,11 +2241,11 @@ export function testProtocolComposition() {
|
|
|
1620
2241
|
derivationScheme: KeyDerivationScheme.ProtocolPath,
|
|
1621
2242
|
derivedPrivateKey: alice.encryptionKeyPair.privateJwk,
|
|
1622
2243
|
};
|
|
1623
|
-
const decryptedStream =
|
|
1624
|
-
const decryptedBytes =
|
|
2244
|
+
const decryptedStream = await Records.decrypt(readReply.entry.recordsWrite, rootKey, readReply.entry.data);
|
|
2245
|
+
const decryptedBytes = await DataStream.toBytes(decryptedStream);
|
|
1625
2246
|
expect(Encoder.bytesToString(decryptedBytes)).toBe(plaintext);
|
|
1626
|
-
})
|
|
1627
|
-
it('should not inject `$encryption` on the original protocol definition (immutability)', () =>
|
|
2247
|
+
});
|
|
2248
|
+
it('should not inject `$encryption` on the original protocol definition (immutability)', async () => {
|
|
1628
2249
|
const composingProtocol = {
|
|
1629
2250
|
protocol: 'https://comments.example.com',
|
|
1630
2251
|
published: true,
|
|
@@ -1639,11 +2260,11 @@ export function testProtocolComposition() {
|
|
|
1639
2260
|
},
|
|
1640
2261
|
},
|
|
1641
2262
|
};
|
|
1642
|
-
|
|
2263
|
+
await Protocols.deriveAndInjectPublicEncryptionKeys(composingProtocol, encryptionRootKeyId, encryptionPrivateJwk);
|
|
1643
2264
|
// Original must be unmodified
|
|
1644
2265
|
expect(composingProtocol.structure.thread.$encryption).toBeUndefined();
|
|
1645
2266
|
expect(composingProtocol.structure.thread.comment.$encryption).toBeUndefined();
|
|
1646
|
-
})
|
|
2267
|
+
});
|
|
1647
2268
|
});
|
|
1648
2269
|
});
|
|
1649
2270
|
}
|