@enbox/dwn-sdk-js 0.0.2 → 0.0.3

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.
Files changed (601) hide show
  1. package/README.md +52 -301
  2. package/dist/bundles/dwn.js +19 -21
  3. package/dist/esm/generated/precompiled-validators.js +2764 -1773
  4. package/dist/esm/generated/precompiled-validators.js.map +1 -1
  5. package/dist/esm/src/core/dwn-error.js +27 -3
  6. package/dist/esm/src/core/dwn-error.js.map +1 -1
  7. package/dist/esm/src/core/message.js.map +1 -1
  8. package/dist/esm/src/core/messages-grant-authorization.js +17 -6
  9. package/dist/esm/src/core/messages-grant-authorization.js.map +1 -1
  10. package/dist/esm/src/core/protocol-authorization.js +245 -69
  11. package/dist/esm/src/core/protocol-authorization.js.map +1 -1
  12. package/dist/esm/src/core/resumable-task-manager.js +4 -4
  13. package/dist/esm/src/core/resumable-task-manager.js.map +1 -1
  14. package/dist/esm/src/dwn.js +10 -8
  15. package/dist/esm/src/dwn.js.map +1 -1
  16. package/dist/esm/src/enums/dwn-interface-method.js +4 -2
  17. package/dist/esm/src/enums/dwn-interface-method.js.map +1 -1
  18. package/dist/esm/src/event-stream/event-emitter-stream.js.map +1 -0
  19. package/dist/esm/src/handlers/messages-subscribe.js +1 -1
  20. package/dist/esm/src/handlers/messages-subscribe.js.map +1 -1
  21. package/dist/esm/src/handlers/messages-sync.js +116 -0
  22. package/dist/esm/src/handlers/messages-sync.js.map +1 -0
  23. package/dist/esm/src/handlers/protocols-configure.js +149 -16
  24. package/dist/esm/src/handlers/protocols-configure.js.map +1 -1
  25. package/dist/esm/src/handlers/protocols-query.js +2 -2
  26. package/dist/esm/src/handlers/protocols-query.js.map +1 -1
  27. package/dist/esm/src/handlers/records-count.js +143 -0
  28. package/dist/esm/src/handlers/records-count.js.map +1 -0
  29. package/dist/esm/src/handlers/records-query.js +4 -0
  30. package/dist/esm/src/handlers/records-query.js.map +1 -1
  31. package/dist/esm/src/handlers/records-read.js +4 -6
  32. package/dist/esm/src/handlers/records-read.js.map +1 -1
  33. package/dist/esm/src/handlers/records-write.js +17 -18
  34. package/dist/esm/src/handlers/records-write.js.map +1 -1
  35. package/dist/esm/src/index.js +9 -5
  36. package/dist/esm/src/index.js.map +1 -1
  37. package/dist/esm/src/interfaces/messages-read.js +2 -7
  38. package/dist/esm/src/interfaces/messages-read.js.map +1 -1
  39. package/dist/esm/src/interfaces/messages-subscribe.js +1 -0
  40. package/dist/esm/src/interfaces/messages-subscribe.js.map +1 -1
  41. package/dist/esm/src/interfaces/{messages-query.js → messages-sync.js} +11 -12
  42. package/dist/esm/src/interfaces/messages-sync.js.map +1 -0
  43. package/dist/esm/src/interfaces/protocols-configure.js +153 -30
  44. package/dist/esm/src/interfaces/protocols-configure.js.map +1 -1
  45. package/dist/esm/src/interfaces/protocols-query.js +1 -0
  46. package/dist/esm/src/interfaces/protocols-query.js.map +1 -1
  47. package/dist/esm/src/interfaces/records-count.js +91 -0
  48. package/dist/esm/src/interfaces/records-count.js.map +1 -0
  49. package/dist/esm/src/interfaces/records-read.js +15 -1
  50. package/dist/esm/src/interfaces/records-read.js.map +1 -1
  51. package/dist/esm/src/interfaces/records-write.js +64 -15
  52. package/dist/esm/src/interfaces/records-write.js.map +1 -1
  53. package/dist/esm/src/jose/algorithms/signing/ed25519.js.map +1 -1
  54. package/dist/esm/src/jose/algorithms/signing/signature-algorithms.js.map +1 -1
  55. package/dist/esm/src/jose/jws/general/builder.js.map +1 -1
  56. package/dist/esm/src/jose/jws/general/verifier.js.map +1 -1
  57. package/dist/esm/src/protocols/permission-grant.js +30 -0
  58. package/dist/esm/src/protocols/permission-grant.js.map +1 -1
  59. package/dist/esm/src/protocols/permission-request.js +24 -0
  60. package/dist/esm/src/protocols/permission-request.js.map +1 -1
  61. package/dist/esm/src/protocols/permissions.js +1 -1
  62. package/dist/esm/src/protocols/permissions.js.map +1 -1
  63. package/dist/esm/src/schema-validator.js +0 -1
  64. package/dist/esm/src/schema-validator.js.map +1 -1
  65. package/dist/esm/src/smt/smt-store-level.js +125 -0
  66. package/dist/esm/src/smt/smt-store-level.js.map +1 -0
  67. package/dist/esm/src/smt/smt-store-memory.js +67 -0
  68. package/dist/esm/src/smt/smt-store-memory.js.map +1 -0
  69. package/dist/esm/src/smt/smt-utils.js +146 -0
  70. package/dist/esm/src/smt/smt-utils.js.map +1 -0
  71. package/dist/esm/src/smt/sparse-merkle-tree.js +622 -0
  72. package/dist/esm/src/smt/sparse-merkle-tree.js.map +1 -0
  73. package/dist/esm/src/state-index/state-index-level.js +228 -0
  74. package/dist/esm/src/state-index/state-index-level.js.map +1 -0
  75. package/dist/esm/src/store/data-store-level.js +6 -6
  76. package/dist/esm/src/store/data-store-level.js.map +1 -1
  77. package/dist/esm/src/store/index-level.js +375 -17
  78. package/dist/esm/src/store/index-level.js.map +1 -1
  79. package/dist/esm/src/store/message-store-level.js +56 -0
  80. package/dist/esm/src/store/message-store-level.js.map +1 -1
  81. package/dist/esm/src/store/storage-controller.js +19 -16
  82. package/dist/esm/src/store/storage-controller.js.map +1 -1
  83. package/dist/esm/src/types/encryption-types.js +2 -0
  84. package/dist/esm/src/types/encryption-types.js.map +1 -0
  85. package/dist/esm/src/types/message-types.js.map +1 -1
  86. package/dist/esm/src/types/protocols-types.js +0 -2
  87. package/dist/esm/src/types/protocols-types.js.map +1 -1
  88. package/dist/esm/src/types/records-types.js +2 -0
  89. package/dist/esm/src/types/records-types.js.map +1 -1
  90. package/dist/esm/src/types/smt-types.js +5 -0
  91. package/dist/esm/src/types/smt-types.js.map +1 -0
  92. package/dist/esm/src/types/state-index.js +2 -0
  93. package/dist/esm/src/types/state-index.js.map +1 -0
  94. package/dist/esm/src/utils/cid.js +2 -1
  95. package/dist/esm/src/utils/cid.js.map +1 -1
  96. package/dist/esm/src/utils/data-stream.js +84 -29
  97. package/dist/esm/src/utils/data-stream.js.map +1 -1
  98. package/dist/esm/src/utils/encryption.js +22 -31
  99. package/dist/esm/src/utils/encryption.js.map +1 -1
  100. package/dist/esm/src/utils/hd-key.js +3 -3
  101. package/dist/esm/src/utils/hd-key.js.map +1 -1
  102. package/dist/esm/src/utils/jws.js +4 -4
  103. package/dist/esm/src/utils/jws.js.map +1 -1
  104. package/dist/esm/src/utils/private-key-signer.js +4 -3
  105. package/dist/esm/src/utils/private-key-signer.js.map +1 -1
  106. package/dist/esm/src/utils/protocols.js +82 -9
  107. package/dist/esm/src/utils/protocols.js.map +1 -1
  108. package/dist/esm/src/utils/records.js +82 -26
  109. package/dist/esm/src/utils/records.js.map +1 -1
  110. package/dist/esm/src/utils/secp256k1.js +4 -3
  111. package/dist/esm/src/utils/secp256k1.js.map +1 -1
  112. package/dist/esm/src/utils/secp256r1.js +3 -2
  113. package/dist/esm/src/utils/secp256r1.js.map +1 -1
  114. package/dist/esm/src/utils/time.js +1 -1
  115. package/dist/esm/src/utils/url.js +1 -1
  116. package/dist/esm/src/utils/url.js.map +1 -1
  117. package/dist/esm/tests/core/auth.spec.js +2 -2
  118. package/dist/esm/tests/core/auth.spec.js.map +1 -1
  119. package/dist/esm/tests/core/message-reply.spec.js +3 -3
  120. package/dist/esm/tests/core/message-reply.spec.js.map +1 -1
  121. package/dist/esm/tests/core/message.spec.js +13 -13
  122. package/dist/esm/tests/core/message.spec.js.map +1 -1
  123. package/dist/esm/tests/core/protocol-authorization.spec.js +3 -3
  124. package/dist/esm/tests/core/protocol-authorization.spec.js.map +1 -1
  125. package/dist/esm/tests/dwn.spec.js +27 -37
  126. package/dist/esm/tests/dwn.spec.js.map +1 -1
  127. package/dist/esm/tests/{event-log → event-stream}/event-emitter-stream.spec.js +14 -15
  128. package/dist/esm/tests/event-stream/event-emitter-stream.spec.js.map +1 -0
  129. package/dist/esm/tests/{event-log → event-stream}/event-stream.spec.js +13 -15
  130. package/dist/esm/tests/event-stream/event-stream.spec.js.map +1 -0
  131. package/dist/esm/tests/features/author-delegated-grant.spec.js +281 -135
  132. package/dist/esm/tests/features/author-delegated-grant.spec.js.map +1 -1
  133. package/dist/esm/tests/features/owner-delegated-grant.spec.js +57 -59
  134. package/dist/esm/tests/features/owner-delegated-grant.spec.js.map +1 -1
  135. package/dist/esm/tests/features/owner-signature.spec.js +32 -34
  136. package/dist/esm/tests/features/owner-signature.spec.js.map +1 -1
  137. package/dist/esm/tests/features/permissions.spec.js +73 -95
  138. package/dist/esm/tests/features/permissions.spec.js.map +1 -1
  139. package/dist/esm/tests/features/protocol-composition.spec.js +1645 -0
  140. package/dist/esm/tests/features/protocol-composition.spec.js.map +1 -0
  141. package/dist/esm/tests/features/protocol-create-action.spec.js +25 -27
  142. package/dist/esm/tests/features/protocol-create-action.spec.js.map +1 -1
  143. package/dist/esm/tests/features/protocol-delete-action.spec.js +42 -44
  144. package/dist/esm/tests/features/protocol-delete-action.spec.js.map +1 -1
  145. package/dist/esm/tests/features/protocol-update-action.spec.js +53 -55
  146. package/dist/esm/tests/features/protocol-update-action.spec.js.map +1 -1
  147. package/dist/esm/tests/features/records-prune.spec.js +126 -100
  148. package/dist/esm/tests/features/records-prune.spec.js.map +1 -1
  149. package/dist/esm/tests/features/records-tags.spec.js +272 -272
  150. package/dist/esm/tests/features/records-tags.spec.js.map +1 -1
  151. package/dist/esm/tests/features/resumable-tasks.spec.js +35 -37
  152. package/dist/esm/tests/features/resumable-tasks.spec.js.map +1 -1
  153. package/dist/esm/tests/handlers/messages-read.spec.js +112 -112
  154. package/dist/esm/tests/handlers/messages-read.spec.js.map +1 -1
  155. package/dist/esm/tests/handlers/messages-subscribe.spec.js +78 -76
  156. package/dist/esm/tests/handlers/messages-subscribe.spec.js.map +1 -1
  157. package/dist/esm/tests/handlers/messages-sync.spec.js +528 -0
  158. package/dist/esm/tests/handlers/messages-sync.spec.js.map +1 -0
  159. package/dist/esm/tests/handlers/protocols-configure.spec.js +545 -152
  160. package/dist/esm/tests/handlers/protocols-configure.spec.js.map +1 -1
  161. package/dist/esm/tests/handlers/protocols-query.spec.js +70 -72
  162. package/dist/esm/tests/handlers/protocols-query.spec.js.map +1 -1
  163. package/dist/esm/tests/handlers/records-count.spec.js +313 -0
  164. package/dist/esm/tests/handlers/records-count.spec.js.map +1 -0
  165. package/dist/esm/tests/handlers/records-delete.spec.js +106 -109
  166. package/dist/esm/tests/handlers/records-delete.spec.js.map +1 -1
  167. package/dist/esm/tests/handlers/records-query.spec.js +863 -463
  168. package/dist/esm/tests/handlers/records-query.spec.js.map +1 -1
  169. package/dist/esm/tests/handlers/records-read.spec.js +439 -209
  170. package/dist/esm/tests/handlers/records-read.spec.js.map +1 -1
  171. package/dist/esm/tests/handlers/records-subscribe.spec.js +292 -97
  172. package/dist/esm/tests/handlers/records-subscribe.spec.js.map +1 -1
  173. package/dist/esm/tests/handlers/records-write.spec.js +481 -483
  174. package/dist/esm/tests/handlers/records-write.spec.js.map +1 -1
  175. package/dist/esm/tests/interfaces/messages-get.spec.js +31 -11
  176. package/dist/esm/tests/interfaces/messages-get.spec.js.map +1 -1
  177. package/dist/esm/tests/interfaces/messages-subscribe.spec.js +5 -5
  178. package/dist/esm/tests/interfaces/messages-subscribe.spec.js.map +1 -1
  179. package/dist/esm/tests/interfaces/protocols-configure.spec.js +64 -134
  180. package/dist/esm/tests/interfaces/protocols-configure.spec.js.map +1 -1
  181. package/dist/esm/tests/interfaces/protocols-query.spec.js +4 -6
  182. package/dist/esm/tests/interfaces/protocols-query.spec.js.map +1 -1
  183. package/dist/esm/tests/interfaces/records-delete.spec.js +3 -5
  184. package/dist/esm/tests/interfaces/records-delete.spec.js.map +1 -1
  185. package/dist/esm/tests/interfaces/records-query.spec.js +9 -11
  186. package/dist/esm/tests/interfaces/records-query.spec.js.map +1 -1
  187. package/dist/esm/tests/interfaces/records-read.spec.js +76 -7
  188. package/dist/esm/tests/interfaces/records-read.spec.js.map +1 -1
  189. package/dist/esm/tests/interfaces/records-subscribe.spec.js +7 -9
  190. package/dist/esm/tests/interfaces/records-subscribe.spec.js.map +1 -1
  191. package/dist/esm/tests/interfaces/records-write.spec.js +244 -48
  192. package/dist/esm/tests/interfaces/records-write.spec.js.map +1 -1
  193. package/dist/esm/tests/jose/jws/general.spec.js +15 -18
  194. package/dist/esm/tests/jose/jws/general.spec.js.map +1 -1
  195. package/dist/esm/tests/protocols/permission-grant.spec.js +114 -0
  196. package/dist/esm/tests/protocols/permission-grant.spec.js.map +1 -0
  197. package/dist/esm/tests/protocols/permission-request.spec.js +43 -7
  198. package/dist/esm/tests/protocols/permission-request.spec.js.map +1 -1
  199. package/dist/esm/tests/protocols/permissions.spec.js +9 -11
  200. package/dist/esm/tests/protocols/permissions.spec.js.map +1 -1
  201. package/dist/esm/tests/scenarios/aggregator.spec.js +90 -92
  202. package/dist/esm/tests/scenarios/aggregator.spec.js.map +1 -1
  203. package/dist/esm/tests/scenarios/deleted-record.spec.js +17 -19
  204. package/dist/esm/tests/scenarios/deleted-record.spec.js.map +1 -1
  205. package/dist/esm/tests/scenarios/end-to-end-tests.spec.js +27 -29
  206. package/dist/esm/tests/scenarios/end-to-end-tests.spec.js.map +1 -1
  207. package/dist/esm/tests/scenarios/nested-roles.spec.js +37 -39
  208. package/dist/esm/tests/scenarios/nested-roles.spec.js.map +1 -1
  209. package/dist/esm/tests/scenarios/subscriptions.spec.js +163 -163
  210. package/dist/esm/tests/scenarios/subscriptions.spec.js.map +1 -1
  211. package/dist/esm/tests/smt/smt-store-level.spec.js +143 -0
  212. package/dist/esm/tests/smt/smt-store-level.spec.js.map +1 -0
  213. package/dist/esm/tests/smt/sparse-merkle-tree.spec.js +741 -0
  214. package/dist/esm/tests/smt/sparse-merkle-tree.spec.js.map +1 -0
  215. package/dist/esm/tests/state-index/state-index-level.spec.js +254 -0
  216. package/dist/esm/tests/state-index/state-index-level.spec.js.map +1 -0
  217. package/dist/esm/tests/store/blockstore-level.spec.js +136 -0
  218. package/dist/esm/tests/store/blockstore-level.spec.js.map +1 -0
  219. package/dist/esm/tests/store/blockstore-mock.spec.js +29 -28
  220. package/dist/esm/tests/store/blockstore-mock.spec.js.map +1 -1
  221. package/dist/esm/tests/store/data-store-level.spec.js +23 -25
  222. package/dist/esm/tests/store/data-store-level.spec.js.map +1 -1
  223. package/dist/esm/tests/store/index-level.spec.js +544 -194
  224. package/dist/esm/tests/store/index-level.spec.js.map +1 -1
  225. package/dist/esm/tests/store/message-store-level.spec.js +4 -4
  226. package/dist/esm/tests/store/message-store-level.spec.js.map +1 -1
  227. package/dist/esm/tests/store/message-store.spec.js +147 -73
  228. package/dist/esm/tests/store/message-store.spec.js.map +1 -1
  229. package/dist/esm/tests/store-dependent-tests.spec.js +1 -0
  230. package/dist/esm/tests/store-dependent-tests.spec.js.map +1 -1
  231. package/dist/esm/tests/test-stores.js +5 -5
  232. package/dist/esm/tests/test-stores.js.map +1 -1
  233. package/dist/esm/tests/test-suite.js +9 -8
  234. package/dist/esm/tests/test-suite.js.map +1 -1
  235. package/dist/esm/tests/utils/cid.spec.js +8 -11
  236. package/dist/esm/tests/utils/cid.spec.js.map +1 -1
  237. package/dist/esm/tests/utils/data-stream.spec.js +167 -13
  238. package/dist/esm/tests/utils/data-stream.spec.js.map +1 -1
  239. package/dist/esm/tests/utils/encryption-callbacks.spec.js +233 -0
  240. package/dist/esm/tests/utils/encryption-callbacks.spec.js.map +1 -0
  241. package/dist/esm/tests/utils/encryption.spec.js +34 -85
  242. package/dist/esm/tests/utils/encryption.spec.js.map +1 -1
  243. package/dist/esm/tests/utils/filters.spec.js +67 -69
  244. package/dist/esm/tests/utils/filters.spec.js.map +1 -1
  245. package/dist/esm/tests/utils/hd-key.spec.js +3 -3
  246. package/dist/esm/tests/utils/hd-key.spec.js.map +1 -1
  247. package/dist/esm/tests/utils/jws.spec.js +54 -3
  248. package/dist/esm/tests/utils/jws.spec.js.map +1 -1
  249. package/dist/esm/tests/utils/memory-cache.spec.js +6 -9
  250. package/dist/esm/tests/utils/memory-cache.spec.js.map +1 -1
  251. package/dist/esm/tests/utils/messages.spec.js +63 -29
  252. package/dist/esm/tests/utils/messages.spec.js.map +1 -1
  253. package/dist/esm/tests/utils/object.spec.js +3 -3
  254. package/dist/esm/tests/utils/object.spec.js.map +1 -1
  255. package/dist/esm/tests/utils/poller.js +1 -1
  256. package/dist/esm/tests/utils/poller.js.map +1 -1
  257. package/dist/esm/tests/utils/private-key-signer.spec.js +6 -6
  258. package/dist/esm/tests/utils/private-key-signer.spec.js.map +1 -1
  259. package/dist/esm/tests/utils/records.spec.js +37 -5
  260. package/dist/esm/tests/utils/records.spec.js.map +1 -1
  261. package/dist/esm/tests/utils/secp256k1.spec.js +7 -7
  262. package/dist/esm/tests/utils/secp256k1.spec.js.map +1 -1
  263. package/dist/esm/tests/utils/secp256r1.spec.js +7 -7
  264. package/dist/esm/tests/utils/secp256r1.spec.js.map +1 -1
  265. package/dist/esm/tests/utils/test-data-generator.js +47 -28
  266. package/dist/esm/tests/utils/test-data-generator.js.map +1 -1
  267. package/dist/esm/tests/utils/time.spec.js +7 -7
  268. package/dist/esm/tests/utils/time.spec.js.map +1 -1
  269. package/dist/esm/tests/utils/url.spec.js +25 -27
  270. package/dist/esm/tests/utils/url.spec.js.map +1 -1
  271. package/dist/esm/tests/validation/json-schemas/definitions.spec.js +4 -4
  272. package/dist/esm/tests/validation/json-schemas/definitions.spec.js.map +1 -1
  273. package/dist/esm/tests/validation/json-schemas/jwk/general-jwk.spec.js +15 -3
  274. package/dist/esm/tests/validation/json-schemas/jwk/general-jwk.spec.js.map +1 -1
  275. package/dist/esm/tests/validation/json-schemas/jwk/public-jwk.spec.js +8 -8
  276. package/dist/esm/tests/validation/json-schemas/jwk/public-jwk.spec.js.map +1 -1
  277. package/dist/esm/tests/validation/json-schemas/jwk-verification-method.spec.js +8 -18
  278. package/dist/esm/tests/validation/json-schemas/jwk-verification-method.spec.js.map +1 -1
  279. package/dist/esm/tests/validation/json-schemas/protocols/protocols-configure.spec.js +3 -3
  280. package/dist/esm/tests/validation/json-schemas/protocols/protocols-configure.spec.js.map +1 -1
  281. package/dist/esm/tests/validation/json-schemas/records/records-query.spec.js +9 -9
  282. package/dist/esm/tests/validation/json-schemas/records/records-query.spec.js.map +1 -1
  283. package/dist/esm/tests/validation/json-schemas/records/records-read.spec.js +106 -0
  284. package/dist/esm/tests/validation/json-schemas/records/records-read.spec.js.map +1 -0
  285. package/dist/esm/tests/validation/json-schemas/records/records-write.spec.js +18 -18
  286. package/dist/esm/tests/validation/json-schemas/records/records-write.spec.js.map +1 -1
  287. package/dist/esm/tests/vectors/protocol-definitions/email.json +1 -1
  288. package/dist/esm/tests/vectors/protocol-definitions/friend-role.json +2 -4
  289. package/dist/esm/tests/vectors/protocol-definitions/slack.json +2 -6
  290. package/dist/esm/tests/vectors/protocol-definitions/thread-role.json +2 -6
  291. package/dist/types/generated/precompiled-validators.d.ts +82 -64
  292. package/dist/types/generated/precompiled-validators.d.ts.map +1 -1
  293. package/dist/types/src/core/dwn-error.d.ts +27 -3
  294. package/dist/types/src/core/dwn-error.d.ts.map +1 -1
  295. package/dist/types/src/core/message-reply.d.ts +1 -1
  296. package/dist/types/src/core/message.d.ts +3 -3
  297. package/dist/types/src/core/message.d.ts.map +1 -1
  298. package/dist/types/src/core/messages-grant-authorization.d.ts +4 -4
  299. package/dist/types/src/core/messages-grant-authorization.d.ts.map +1 -1
  300. package/dist/types/src/core/protocol-authorization.d.ts +43 -2
  301. package/dist/types/src/core/protocol-authorization.d.ts.map +1 -1
  302. package/dist/types/src/core/records-grant-authorization.d.ts +2 -2
  303. package/dist/types/src/core/records-grant-authorization.d.ts.map +1 -1
  304. package/dist/types/src/core/resumable-task-manager.d.ts +1 -0
  305. package/dist/types/src/core/resumable-task-manager.d.ts.map +1 -1
  306. package/dist/types/src/dwn.d.ts +8 -8
  307. package/dist/types/src/dwn.d.ts.map +1 -1
  308. package/dist/types/src/enums/dwn-interface-method.d.ts +5 -3
  309. package/dist/types/src/enums/dwn-interface-method.d.ts.map +1 -1
  310. package/dist/types/src/event-stream/event-emitter-stream.d.ts.map +1 -0
  311. package/dist/types/src/handlers/messages-sync.d.ts +21 -0
  312. package/dist/types/src/handlers/messages-sync.d.ts.map +1 -0
  313. package/dist/types/src/handlers/protocols-configure.d.ts +24 -4
  314. package/dist/types/src/handlers/protocols-configure.d.ts.map +1 -1
  315. package/dist/types/src/handlers/protocols-query.d.ts.map +1 -1
  316. package/dist/types/src/handlers/records-count.d.ts +43 -0
  317. package/dist/types/src/handlers/records-count.d.ts.map +1 -0
  318. package/dist/types/src/handlers/records-query.d.ts.map +1 -1
  319. package/dist/types/src/handlers/records-read.d.ts.map +1 -1
  320. package/dist/types/src/handlers/records-write.d.ts +5 -5
  321. package/dist/types/src/handlers/records-write.d.ts.map +1 -1
  322. package/dist/types/src/index.d.ts +72 -37
  323. package/dist/types/src/index.d.ts.map +1 -1
  324. package/dist/types/src/interfaces/messages-read.d.ts +2 -2
  325. package/dist/types/src/interfaces/messages-read.d.ts.map +1 -1
  326. package/dist/types/src/interfaces/messages-subscribe.d.ts +2 -2
  327. package/dist/types/src/interfaces/messages-subscribe.d.ts.map +1 -1
  328. package/dist/types/src/interfaces/messages-sync.d.ts +16 -0
  329. package/dist/types/src/interfaces/messages-sync.d.ts.map +1 -0
  330. package/dist/types/src/interfaces/protocols-configure.d.ts +22 -2
  331. package/dist/types/src/interfaces/protocols-configure.d.ts.map +1 -1
  332. package/dist/types/src/interfaces/protocols-query.d.ts +2 -2
  333. package/dist/types/src/interfaces/protocols-query.d.ts.map +1 -1
  334. package/dist/types/src/interfaces/records-count.d.ts +27 -0
  335. package/dist/types/src/interfaces/records-count.d.ts.map +1 -0
  336. package/dist/types/src/interfaces/records-delete.d.ts +2 -2
  337. package/dist/types/src/interfaces/records-delete.d.ts.map +1 -1
  338. package/dist/types/src/interfaces/records-query.d.ts +2 -2
  339. package/dist/types/src/interfaces/records-query.d.ts.map +1 -1
  340. package/dist/types/src/interfaces/records-read.d.ts +4 -2
  341. package/dist/types/src/interfaces/records-read.d.ts.map +1 -1
  342. package/dist/types/src/interfaces/records-subscribe.d.ts +2 -2
  343. package/dist/types/src/interfaces/records-subscribe.d.ts.map +1 -1
  344. package/dist/types/src/interfaces/records-write.d.ts +37 -15
  345. package/dist/types/src/interfaces/records-write.d.ts.map +1 -1
  346. package/dist/types/src/jose/algorithms/signing/ed25519.d.ts.map +1 -1
  347. package/dist/types/src/jose/algorithms/signing/signature-algorithms.d.ts +5 -1
  348. package/dist/types/src/jose/algorithms/signing/signature-algorithms.d.ts.map +1 -1
  349. package/dist/types/src/jose/jws/general/builder.d.ts +3 -3
  350. package/dist/types/src/jose/jws/general/builder.d.ts.map +1 -1
  351. package/dist/types/src/protocols/permission-grant.d.ts +11 -0
  352. package/dist/types/src/protocols/permission-grant.d.ts.map +1 -1
  353. package/dist/types/src/protocols/permission-request.d.ts +11 -0
  354. package/dist/types/src/protocols/permission-request.d.ts.map +1 -1
  355. package/dist/types/src/protocols/permissions.d.ts +4 -4
  356. package/dist/types/src/protocols/permissions.d.ts.map +1 -1
  357. package/dist/types/src/schema-validator.d.ts +1 -1
  358. package/dist/types/src/schema-validator.d.ts.map +1 -1
  359. package/dist/types/src/smt/smt-store-level.d.ts +32 -0
  360. package/dist/types/src/smt/smt-store-level.d.ts.map +1 -0
  361. package/dist/types/src/smt/smt-store-memory.d.ts +22 -0
  362. package/dist/types/src/smt/smt-store-memory.d.ts.map +1 -0
  363. package/dist/types/src/smt/smt-utils.d.ts +58 -0
  364. package/dist/types/src/smt/smt-utils.d.ts.map +1 -0
  365. package/dist/types/src/smt/sparse-merkle-tree.d.ts +124 -0
  366. package/dist/types/src/smt/sparse-merkle-tree.d.ts.map +1 -0
  367. package/dist/types/src/state-index/state-index-level.d.ts +83 -0
  368. package/dist/types/src/state-index/state-index-level.d.ts.map +1 -0
  369. package/dist/types/src/store/data-store-level.d.ts +1 -2
  370. package/dist/types/src/store/data-store-level.d.ts.map +1 -1
  371. package/dist/types/src/store/index-level.d.ts +98 -2
  372. package/dist/types/src/store/index-level.d.ts.map +1 -1
  373. package/dist/types/src/store/level-wrapper.d.ts.map +1 -1
  374. package/dist/types/src/store/message-store-level.d.ts +5 -0
  375. package/dist/types/src/store/message-store-level.d.ts.map +1 -1
  376. package/dist/types/src/store/storage-controller.d.ts +7 -7
  377. package/dist/types/src/store/storage-controller.d.ts.map +1 -1
  378. package/dist/types/src/types/data-store.d.ts +2 -3
  379. package/dist/types/src/types/data-store.d.ts.map +1 -1
  380. package/dist/types/src/types/encryption-types.d.ts +48 -0
  381. package/dist/types/src/types/encryption-types.d.ts.map +1 -0
  382. package/dist/types/src/types/jose-types.d.ts +9 -40
  383. package/dist/types/src/types/jose-types.d.ts.map +1 -1
  384. package/dist/types/src/types/message-store.d.ts +5 -0
  385. package/dist/types/src/types/message-store.d.ts.map +1 -1
  386. package/dist/types/src/types/message-types.d.ts +19 -0
  387. package/dist/types/src/types/message-types.d.ts.map +1 -1
  388. package/dist/types/src/types/messages-types.d.ts +16 -11
  389. package/dist/types/src/types/messages-types.d.ts.map +1 -1
  390. package/dist/types/src/types/method-handler.d.ts +1 -2
  391. package/dist/types/src/types/method-handler.d.ts.map +1 -1
  392. package/dist/types/src/types/permission-types.d.ts +2 -2
  393. package/dist/types/src/types/permission-types.d.ts.map +1 -1
  394. package/dist/types/src/types/protocols-types.d.ts +49 -5
  395. package/dist/types/src/types/protocols-types.d.ts.map +1 -1
  396. package/dist/types/src/types/records-types.d.ts +23 -7
  397. package/dist/types/src/types/records-types.d.ts.map +1 -1
  398. package/dist/types/src/types/signer.d.ts +1 -1
  399. package/dist/types/src/types/signer.d.ts.map +1 -1
  400. package/dist/types/src/types/smt-types.d.ts +81 -0
  401. package/dist/types/src/types/smt-types.d.ts.map +1 -0
  402. package/dist/types/src/types/state-index.d.ts +90 -0
  403. package/dist/types/src/types/state-index.d.ts.map +1 -0
  404. package/dist/types/src/utils/cid.d.ts +1 -2
  405. package/dist/types/src/utils/cid.d.ts.map +1 -1
  406. package/dist/types/src/utils/data-stream.d.ts +14 -7
  407. package/dist/types/src/utils/data-stream.d.ts.map +1 -1
  408. package/dist/types/src/utils/encryption.d.ts +2 -3
  409. package/dist/types/src/utils/encryption.d.ts.map +1 -1
  410. package/dist/types/src/utils/hd-key.d.ts +4 -4
  411. package/dist/types/src/utils/hd-key.d.ts.map +1 -1
  412. package/dist/types/src/utils/jws.d.ts +7 -7
  413. package/dist/types/src/utils/jws.d.ts.map +1 -1
  414. package/dist/types/src/utils/private-key-signer.d.ts +4 -4
  415. package/dist/types/src/utils/private-key-signer.d.ts.map +1 -1
  416. package/dist/types/src/utils/protocols.d.ts +46 -3
  417. package/dist/types/src/utils/protocols.d.ts.map +1 -1
  418. package/dist/types/src/utils/records.d.ts +33 -6
  419. package/dist/types/src/utils/records.d.ts.map +1 -1
  420. package/dist/types/src/utils/secp256k1.d.ts +11 -11
  421. package/dist/types/src/utils/secp256k1.d.ts.map +1 -1
  422. package/dist/types/src/utils/secp256r1.d.ts +8 -8
  423. package/dist/types/src/utils/secp256r1.d.ts.map +1 -1
  424. package/dist/types/src/utils/time.d.ts +1 -1
  425. package/dist/types/tests/dwn.spec.d.ts.map +1 -1
  426. package/dist/types/tests/event-stream/event-emitter-stream.spec.d.ts.map +1 -0
  427. package/dist/types/tests/event-stream/event-stream.spec.d.ts.map +1 -0
  428. package/dist/types/tests/features/author-delegated-grant.spec.d.ts.map +1 -1
  429. package/dist/types/tests/features/owner-delegated-grant.spec.d.ts.map +1 -1
  430. package/dist/types/tests/features/owner-signature.spec.d.ts.map +1 -1
  431. package/dist/types/tests/features/permissions.spec.d.ts.map +1 -1
  432. package/dist/types/tests/features/protocol-composition.spec.d.ts +5 -0
  433. package/dist/types/tests/features/protocol-composition.spec.d.ts.map +1 -0
  434. package/dist/types/tests/features/protocol-create-action.spec.d.ts.map +1 -1
  435. package/dist/types/tests/features/protocol-delete-action.spec.d.ts.map +1 -1
  436. package/dist/types/tests/features/protocol-update-action.spec.d.ts.map +1 -1
  437. package/dist/types/tests/features/records-prune.spec.d.ts.map +1 -1
  438. package/dist/types/tests/features/records-tags.spec.d.ts.map +1 -1
  439. package/dist/types/tests/features/resumable-tasks.spec.d.ts.map +1 -1
  440. package/dist/types/tests/handlers/messages-read.spec.d.ts.map +1 -1
  441. package/dist/types/tests/handlers/messages-subscribe.spec.d.ts.map +1 -1
  442. package/dist/types/tests/handlers/messages-sync.spec.d.ts +2 -0
  443. package/dist/types/tests/handlers/messages-sync.spec.d.ts.map +1 -0
  444. package/dist/types/tests/handlers/protocols-configure.spec.d.ts.map +1 -1
  445. package/dist/types/tests/handlers/protocols-query.spec.d.ts.map +1 -1
  446. package/dist/types/tests/handlers/records-count.spec.d.ts +2 -0
  447. package/dist/types/tests/handlers/records-count.spec.d.ts.map +1 -0
  448. package/dist/types/tests/handlers/records-delete.spec.d.ts.map +1 -1
  449. package/dist/types/tests/handlers/records-query.spec.d.ts.map +1 -1
  450. package/dist/types/tests/handlers/records-read.spec.d.ts.map +1 -1
  451. package/dist/types/tests/handlers/records-subscribe.spec.d.ts.map +1 -1
  452. package/dist/types/tests/handlers/records-write.spec.d.ts.map +1 -1
  453. package/dist/types/tests/protocols/permission-grant.spec.d.ts +2 -0
  454. package/dist/types/tests/protocols/permission-grant.spec.d.ts.map +1 -0
  455. package/dist/types/tests/scenarios/deleted-record.spec.d.ts.map +1 -1
  456. package/dist/types/tests/scenarios/end-to-end-tests.spec.d.ts.map +1 -1
  457. package/dist/types/tests/scenarios/nested-roles.spec.d.ts.map +1 -1
  458. package/dist/types/tests/smt/smt-store-level.spec.d.ts +2 -0
  459. package/dist/types/tests/smt/smt-store-level.spec.d.ts.map +1 -0
  460. package/dist/types/tests/smt/sparse-merkle-tree.spec.d.ts +2 -0
  461. package/dist/types/tests/smt/sparse-merkle-tree.spec.d.ts.map +1 -0
  462. package/dist/types/tests/state-index/state-index-level.spec.d.ts +2 -0
  463. package/dist/types/tests/state-index/state-index-level.spec.d.ts.map +1 -0
  464. package/dist/types/tests/store/blockstore-level.spec.d.ts +2 -0
  465. package/dist/types/tests/store/blockstore-level.spec.d.ts.map +1 -0
  466. package/dist/types/tests/store/message-store.spec.d.ts.map +1 -1
  467. package/dist/types/tests/test-stores.d.ts +4 -4
  468. package/dist/types/tests/test-stores.d.ts.map +1 -1
  469. package/dist/types/tests/test-suite.d.ts +2 -2
  470. package/dist/types/tests/test-suite.d.ts.map +1 -1
  471. package/dist/types/tests/utils/encryption-callbacks.spec.d.ts +2 -0
  472. package/dist/types/tests/utils/encryption-callbacks.spec.d.ts.map +1 -0
  473. package/dist/types/tests/utils/test-data-generator.d.ts +31 -28
  474. package/dist/types/tests/utils/test-data-generator.d.ts.map +1 -1
  475. package/dist/types/tests/validation/json-schemas/records/records-read.spec.d.ts +2 -0
  476. package/dist/types/tests/validation/json-schemas/records/records-read.spec.d.ts.map +1 -0
  477. package/package.json +27 -46
  478. package/src/core/dwn-error.ts +27 -3
  479. package/src/core/message-reply.ts +1 -1
  480. package/src/core/message.ts +5 -5
  481. package/src/core/messages-grant-authorization.ts +22 -8
  482. package/src/core/protocol-authorization.ts +345 -68
  483. package/src/core/records-grant-authorization.ts +2 -2
  484. package/src/core/resumable-task-manager.ts +4 -5
  485. package/src/dwn.ts +25 -20
  486. package/src/enums/dwn-interface-method.ts +5 -3
  487. package/src/handlers/messages-subscribe.ts +1 -1
  488. package/src/handlers/messages-sync.ts +129 -0
  489. package/src/handlers/protocols-configure.ts +195 -17
  490. package/src/handlers/protocols-query.ts +7 -5
  491. package/src/handlers/records-count.ts +184 -0
  492. package/src/handlers/records-query.ts +4 -0
  493. package/src/handlers/records-read.ts +4 -8
  494. package/src/handlers/records-write.ts +20 -21
  495. package/src/index.ts +74 -37
  496. package/src/interfaces/messages-read.ts +6 -5
  497. package/src/interfaces/messages-subscribe.ts +7 -6
  498. package/src/interfaces/messages-sync.ts +59 -0
  499. package/src/interfaces/protocols-configure.ts +211 -33
  500. package/src/interfaces/protocols-query.ts +7 -6
  501. package/src/interfaces/records-count.ts +106 -0
  502. package/src/interfaces/records-delete.ts +2 -2
  503. package/src/interfaces/records-query.ts +2 -2
  504. package/src/interfaces/records-read.ts +26 -3
  505. package/src/interfaces/records-subscribe.ts +2 -2
  506. package/src/interfaces/records-write.ts +115 -46
  507. package/src/jose/algorithms/signing/ed25519.ts +13 -12
  508. package/src/jose/algorithms/signing/signature-algorithms.ts +6 -1
  509. package/src/jose/jws/general/builder.ts +3 -3
  510. package/src/jose/jws/general/verifier.ts +3 -3
  511. package/src/protocols/permission-grant.ts +51 -0
  512. package/src/protocols/permission-request.ts +37 -0
  513. package/src/protocols/permissions.ts +5 -5
  514. package/src/schema-validator.ts +11 -3
  515. package/src/smt/smt-store-level.ts +143 -0
  516. package/src/smt/smt-store-memory.ts +53 -0
  517. package/src/smt/smt-utils.ts +149 -0
  518. package/src/smt/sparse-merkle-tree.ts +698 -0
  519. package/src/state-index/state-index-level.ts +241 -0
  520. package/src/store/data-store-level.ts +8 -7
  521. package/src/store/index-level.ts +415 -19
  522. package/src/store/level-wrapper.ts +1 -1
  523. package/src/store/message-store-level.ts +62 -0
  524. package/src/store/storage-controller.ts +21 -19
  525. package/src/types/data-store.ts +2 -4
  526. package/src/types/encryption-types.ts +52 -0
  527. package/src/types/jose-types.ts +10 -42
  528. package/src/types/message-store.ts +11 -0
  529. package/src/types/message-types.ts +21 -0
  530. package/src/types/messages-types.ts +21 -15
  531. package/src/types/method-handler.ts +1 -2
  532. package/src/types/permission-types.ts +2 -2
  533. package/src/types/protocols-types.ts +55 -6
  534. package/src/types/records-types.ts +26 -7
  535. package/src/types/signer.ts +1 -1
  536. package/src/types/smt-types.ts +95 -0
  537. package/src/types/state-index.ts +100 -0
  538. package/src/utils/cid.ts +3 -4
  539. package/src/utils/data-stream.ts +75 -38
  540. package/src/utils/encryption.ts +24 -39
  541. package/src/utils/hd-key.ts +6 -6
  542. package/src/utils/jws.ts +9 -9
  543. package/src/utils/private-key-signer.ts +9 -8
  544. package/src/utils/protocols.ts +132 -6
  545. package/src/utils/records.ts +118 -29
  546. package/src/utils/secp256k1.ts +23 -21
  547. package/src/utils/secp256r1.ts +17 -15
  548. package/src/utils/time.ts +1 -1
  549. package/src/utils/url.ts +1 -1
  550. package/dist/cjs/index.js +0 -36749
  551. package/dist/cjs/package.json +0 -1
  552. package/dist/esm/src/event-log/event-emitter-stream.js.map +0 -1
  553. package/dist/esm/src/event-log/event-log-level.js +0 -63
  554. package/dist/esm/src/event-log/event-log-level.js.map +0 -1
  555. package/dist/esm/src/handlers/messages-query.js +0 -71
  556. package/dist/esm/src/handlers/messages-query.js.map +0 -1
  557. package/dist/esm/src/interfaces/messages-query.js.map +0 -1
  558. package/dist/esm/src/types/event-log.js +0 -2
  559. package/dist/esm/src/types/event-log.js.map +0 -1
  560. package/dist/esm/tests/event-log/event-emitter-stream.spec.js.map +0 -1
  561. package/dist/esm/tests/event-log/event-log-level.spec.js +0 -44
  562. package/dist/esm/tests/event-log/event-log-level.spec.js.map +0 -1
  563. package/dist/esm/tests/event-log/event-log.spec.js +0 -236
  564. package/dist/esm/tests/event-log/event-log.spec.js.map +0 -1
  565. package/dist/esm/tests/event-log/event-stream.spec.js.map +0 -1
  566. package/dist/esm/tests/handlers/messages-query.spec.js +0 -349
  567. package/dist/esm/tests/handlers/messages-query.spec.js.map +0 -1
  568. package/dist/esm/tests/interfaces/messagess-query.spec.js +0 -127
  569. package/dist/esm/tests/interfaces/messagess-query.spec.js.map +0 -1
  570. package/dist/esm/tests/scenarios/messages-query.spec.js +0 -395
  571. package/dist/esm/tests/scenarios/messages-query.spec.js.map +0 -1
  572. package/dist/types/src/event-log/event-emitter-stream.d.ts.map +0 -1
  573. package/dist/types/src/event-log/event-log-level.d.ts +0 -35
  574. package/dist/types/src/event-log/event-log-level.d.ts.map +0 -1
  575. package/dist/types/src/handlers/messages-query.d.ts +0 -17
  576. package/dist/types/src/handlers/messages-query.d.ts.map +0 -1
  577. package/dist/types/src/interfaces/messages-query.d.ts +0 -16
  578. package/dist/types/src/interfaces/messages-query.d.ts.map +0 -1
  579. package/dist/types/src/types/event-log.d.ts +0 -52
  580. package/dist/types/src/types/event-log.d.ts.map +0 -1
  581. package/dist/types/tests/event-log/event-emitter-stream.spec.d.ts.map +0 -1
  582. package/dist/types/tests/event-log/event-log-level.spec.d.ts +0 -2
  583. package/dist/types/tests/event-log/event-log-level.spec.d.ts.map +0 -1
  584. package/dist/types/tests/event-log/event-log.spec.d.ts +0 -2
  585. package/dist/types/tests/event-log/event-log.spec.d.ts.map +0 -1
  586. package/dist/types/tests/event-log/event-stream.spec.d.ts.map +0 -1
  587. package/dist/types/tests/handlers/messages-query.spec.d.ts +0 -2
  588. package/dist/types/tests/handlers/messages-query.spec.d.ts.map +0 -1
  589. package/dist/types/tests/interfaces/messagess-query.spec.d.ts +0 -2
  590. package/dist/types/tests/interfaces/messagess-query.spec.d.ts.map +0 -1
  591. package/dist/types/tests/scenarios/messages-query.spec.d.ts +0 -2
  592. package/dist/types/tests/scenarios/messages-query.spec.d.ts.map +0 -1
  593. package/src/event-log/event-log-level.ts +0 -72
  594. package/src/handlers/messages-query.ts +0 -67
  595. package/src/interfaces/messages-query.ts +0 -60
  596. package/src/types/event-log.ts +0 -52
  597. /package/dist/esm/src/{event-log → event-stream}/event-emitter-stream.js +0 -0
  598. /package/dist/types/src/{event-log → event-stream}/event-emitter-stream.d.ts +0 -0
  599. /package/dist/types/tests/{event-log → event-stream}/event-emitter-stream.spec.d.ts +0 -0
  600. /package/dist/types/tests/{event-log → event-stream}/event-stream.spec.d.ts +0 -0
  601. /package/src/{event-log → event-stream}/event-emitter-stream.ts +0 -0
@@ -0,0 +1,1645 @@
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
+ import sinon from 'sinon';
11
+ import { DataStream } from '../../src/utils/data-stream.js';
12
+ import { Dwn } from '../../src/dwn.js';
13
+ import { Encoder } from '../../src/utils/encoder.js';
14
+ import { Jws } from '../../src/utils/jws.js';
15
+ import { Protocols } from '../../src/utils/protocols.js';
16
+ import { Records } from '../../src/utils/records.js';
17
+ import { Secp256k1 } from '../../src/utils/secp256k1.js';
18
+ import { TestDataGenerator } from '../utils/test-data-generator.js';
19
+ import { TestEventStream } from '../test-event-stream.js';
20
+ import { TestStores } from '../test-stores.js';
21
+ import { TestStubGenerator } from '../utils/test-stub-generator.js';
22
+ import { afterAll, beforeAll, beforeEach, describe, expect, it } from 'bun:test';
23
+ import { DidKey, UniversalResolver } from '@enbox/dids';
24
+ import { DwnErrorCode, Message, ProtocolsConfigure, RecordsDelete, RecordsQuery, RecordsRead, Time } from '../../src/index.js';
25
+ import { HdKey, KeyDerivationScheme } from '../../src/utils/hd-key.js';
26
+ /**
27
+ * Tests for protocol composition using `uses` + `$ref`.
28
+ */
29
+ export function testProtocolComposition() {
30
+ describe('Protocol composition', () => {
31
+ let didResolver;
32
+ let messageStore;
33
+ let dataStore;
34
+ let resumableTaskStore;
35
+ let stateIndex;
36
+ let eventStream;
37
+ let dwn;
38
+ beforeAll(() => __awaiter(this, void 0, void 0, function* () {
39
+ didResolver = new UniversalResolver({ didResolvers: [DidKey] });
40
+ const stores = TestStores.get();
41
+ messageStore = stores.messageStore;
42
+ dataStore = stores.dataStore;
43
+ resumableTaskStore = stores.resumableTaskStore;
44
+ stateIndex = stores.stateIndex;
45
+ eventStream = TestEventStream.get();
46
+ dwn = yield Dwn.create({ didResolver, messageStore, dataStore, stateIndex, eventStream, resumableTaskStore });
47
+ }));
48
+ beforeEach(() => __awaiter(this, void 0, void 0, function* () {
49
+ sinon.restore();
50
+ yield messageStore.clear();
51
+ yield dataStore.clear();
52
+ yield resumableTaskStore.clear();
53
+ yield stateIndex.clear();
54
+ }));
55
+ afterAll(() => __awaiter(this, void 0, void 0, function* () {
56
+ yield dwn.close();
57
+ }));
58
+ // =========================================================================
59
+ // Protocol definitions for tests
60
+ // =========================================================================
61
+ const threadsProtocol = {
62
+ protocol: 'https://threads.example.com',
63
+ published: true,
64
+ types: {
65
+ thread: { schema: 'https://threads.example.com/schemas/thread', dataFormats: ['application/json'] },
66
+ participant: { schema: 'https://threads.example.com/schemas/participant', dataFormats: ['application/json'] },
67
+ message: { schema: 'https://threads.example.com/schemas/message', dataFormats: ['application/json'] },
68
+ },
69
+ structure: {
70
+ thread: {
71
+ $actions: [
72
+ { who: 'anyone', can: ['create', 'read'] }
73
+ ],
74
+ participant: {
75
+ $role: true,
76
+ $actions: [
77
+ { who: 'anyone', can: ['read'] },
78
+ { who: 'author', of: 'thread', can: ['create'] },
79
+ ],
80
+ },
81
+ message: {
82
+ $actions: [
83
+ { role: 'thread/participant', can: ['create', 'read'] },
84
+ ],
85
+ },
86
+ },
87
+ },
88
+ };
89
+ const commentsProtocol = {
90
+ protocol: 'https://comments.example.com',
91
+ published: true,
92
+ uses: {
93
+ threads: 'https://threads.example.com',
94
+ },
95
+ types: {
96
+ comment: { schema: 'https://comments.example.com/schemas/comment', dataFormats: ['application/json'] },
97
+ reaction: { schema: 'https://comments.example.com/schemas/reaction', dataFormats: ['application/json'] },
98
+ },
99
+ structure: {
100
+ thread: {
101
+ $ref: 'threads:thread',
102
+ comment: {
103
+ $actions: [
104
+ { who: 'anyone', can: ['create', 'read'] },
105
+ { role: 'threads:thread/participant', can: ['read', 'co-delete'] },
106
+ ],
107
+ reaction: {
108
+ $actions: [
109
+ { who: 'anyone', can: ['create', 'read'] },
110
+ ],
111
+ },
112
+ },
113
+ },
114
+ },
115
+ };
116
+ // =========================================================================
117
+ // Validation tests (ProtocolsConfigure)
118
+ // =========================================================================
119
+ describe('ProtocolsConfigure validation', () => {
120
+ it('should accept a valid protocol definition with `uses` and `$ref`', () => __awaiter(this, void 0, void 0, function* () {
121
+ const alice = yield TestDataGenerator.generateDidKeyPersona();
122
+ const protocolsConfigure = yield ProtocolsConfigure.create({
123
+ definition: commentsProtocol,
124
+ signer: Jws.createSigner(alice),
125
+ });
126
+ expect(protocolsConfigure.message.descriptor.definition.uses).toEqual({
127
+ threads: 'https://threads.example.com',
128
+ });
129
+ }));
130
+ it('should reject `uses` alias that does not match naming pattern (bypassing JSON schema)', () => __awaiter(this, void 0, void 0, function* () {
131
+ const alice = yield TestDataGenerator.generateDidKeyPersona();
132
+ // The JSON schema enforces alias naming via `patternProperties` + `additionalProperties: false`,
133
+ // which means invalid aliases like '123invalid' are caught by JSON schema before the code-level
134
+ // `validateUses()` check. To exercise the code-level `ProtocolsConfigureInvalidUsesAlias` error,
135
+ // we must stub `Message.validateJsonSchema` to bypass JSON schema validation.
136
+ sinon.stub(Message, 'validateJsonSchema');
137
+ const badDefinition = {
138
+ protocol: 'https://bad.example.com',
139
+ published: true,
140
+ uses: { '123invalid': 'https://foo.example.com' },
141
+ types: {},
142
+ structure: {},
143
+ };
144
+ try {
145
+ yield ProtocolsConfigure.create({
146
+ definition: badDefinition,
147
+ signer: Jws.createSigner(alice),
148
+ });
149
+ throw new Error('Expected an error to be thrown');
150
+ }
151
+ catch (error) {
152
+ expect(error.message).toContain(DwnErrorCode.ProtocolsConfigureInvalidUsesAlias);
153
+ }
154
+ }));
155
+ it('should reject `$ref` with alias not in `uses`', () => __awaiter(this, void 0, void 0, function* () {
156
+ const alice = yield TestDataGenerator.generateDidKeyPersona();
157
+ const badDefinition = {
158
+ protocol: 'https://bad.example.com',
159
+ published: true,
160
+ uses: {
161
+ foo: 'https://foo.example.com',
162
+ },
163
+ types: { comment: {} },
164
+ structure: {
165
+ thread: {
166
+ $ref: 'nonexistent:thread', // alias 'nonexistent' not in uses
167
+ comment: {
168
+ $actions: [{ who: 'anyone', can: ['create'] }],
169
+ },
170
+ },
171
+ },
172
+ };
173
+ try {
174
+ yield ProtocolsConfigure.create({
175
+ definition: badDefinition,
176
+ signer: Jws.createSigner(alice),
177
+ });
178
+ throw new Error('Expected an error to be thrown');
179
+ }
180
+ catch (error) {
181
+ expect(error.message).toContain(DwnErrorCode.ProtocolsConfigureInvalidRefAlias);
182
+ }
183
+ }));
184
+ it('should reject `$ref` node with `$actions`', () => __awaiter(this, void 0, void 0, function* () {
185
+ const alice = yield TestDataGenerator.generateDidKeyPersona();
186
+ const badDefinition = {
187
+ protocol: 'https://bad.example.com',
188
+ published: true,
189
+ uses: { threads: 'https://threads.example.com' },
190
+ types: { comment: {} },
191
+ structure: {
192
+ thread: {
193
+ $ref: 'threads:thread',
194
+ $actions: [{ who: 'anyone', can: ['read'] }], // not allowed on $ref node
195
+ comment: {
196
+ $actions: [{ who: 'anyone', can: ['create'] }],
197
+ },
198
+ },
199
+ },
200
+ };
201
+ try {
202
+ yield ProtocolsConfigure.create({
203
+ definition: badDefinition,
204
+ signer: Jws.createSigner(alice),
205
+ });
206
+ throw new Error('Expected an error to be thrown');
207
+ }
208
+ catch (error) {
209
+ expect(error.message).toContain(DwnErrorCode.ProtocolsConfigureInvalidRefNodeHasDirectives);
210
+ }
211
+ }));
212
+ it('should reject `$ref` node with `$role`', () => __awaiter(this, void 0, void 0, function* () {
213
+ const alice = yield TestDataGenerator.generateDidKeyPersona();
214
+ const badDefinition = {
215
+ protocol: 'https://bad.example.com',
216
+ published: true,
217
+ uses: { threads: 'https://threads.example.com' },
218
+ types: {},
219
+ structure: {
220
+ thread: {
221
+ $ref: 'threads:thread',
222
+ $role: true, // not allowed on $ref node
223
+ },
224
+ },
225
+ };
226
+ try {
227
+ yield ProtocolsConfigure.create({
228
+ definition: badDefinition,
229
+ signer: Jws.createSigner(alice),
230
+ });
231
+ throw new Error('Expected an error to be thrown');
232
+ }
233
+ catch (error) {
234
+ expect(error.message).toContain(DwnErrorCode.ProtocolsConfigureInvalidRefNodeHasDirectives);
235
+ }
236
+ }));
237
+ it('should reject cross-protocol `role` with alias not in `uses`', () => __awaiter(this, void 0, void 0, function* () {
238
+ const alice = yield TestDataGenerator.generateDidKeyPersona();
239
+ const badDefinition = {
240
+ protocol: 'https://bad.example.com',
241
+ published: true,
242
+ uses: { threads: 'https://threads.example.com' },
243
+ types: { comment: {} },
244
+ structure: {
245
+ thread: {
246
+ $ref: 'threads:thread',
247
+ comment: {
248
+ $actions: [
249
+ { role: 'nonexistent:thread/participant', can: ['read'] }, // alias 'nonexistent' not in uses
250
+ ],
251
+ },
252
+ },
253
+ },
254
+ };
255
+ try {
256
+ yield ProtocolsConfigure.create({
257
+ definition: badDefinition,
258
+ signer: Jws.createSigner(alice),
259
+ });
260
+ throw new Error('Expected an error to be thrown');
261
+ }
262
+ catch (error) {
263
+ expect(error.message).toContain(DwnErrorCode.ProtocolsConfigureInvalidCrossProtocolRole);
264
+ }
265
+ }));
266
+ it('should reject `uses` with invalid protocol URL', () => __awaiter(this, void 0, void 0, function* () {
267
+ const alice = yield TestDataGenerator.generateDidKeyPersona();
268
+ const badDefinition = {
269
+ protocol: 'https://bad.example.com',
270
+ published: true,
271
+ uses: { threads: '::invalid::' },
272
+ types: {},
273
+ structure: {},
274
+ };
275
+ try {
276
+ yield ProtocolsConfigure.create({
277
+ definition: badDefinition,
278
+ signer: Jws.createSigner(alice),
279
+ });
280
+ throw new Error('Expected an error to be thrown');
281
+ }
282
+ catch (error) {
283
+ expect(error.message).toContain(DwnErrorCode.ProtocolsConfigureInvalidUsesProtocolUrl);
284
+ }
285
+ }));
286
+ it('should accept `$ref` referencing a multi-segment path in the referenced protocol', () => __awaiter(this, void 0, void 0, function* () {
287
+ const alice = yield TestDataGenerator.generateDidKeyPersona();
288
+ // The root-only constraint means `$ref` must appear at the root of the composing structure,
289
+ // but the REFERENCED path can be multi-segment (e.g., 'thread/participant').
290
+ const definition = {
291
+ protocol: 'https://deep-ref.example.com',
292
+ published: true,
293
+ uses: { threads: 'https://threads.example.com' },
294
+ types: {
295
+ note: { schema: 'https://deep-ref.example.com/schemas/note', dataFormats: ['application/json'] },
296
+ },
297
+ structure: {
298
+ participant: {
299
+ $ref: 'threads:thread/participant', // multi-segment referenced path
300
+ note: {
301
+ $actions: [{ who: 'anyone', can: ['create', 'read'] }],
302
+ },
303
+ },
304
+ },
305
+ };
306
+ // ProtocolsConfigure.create() should succeed — the $ref is at root level
307
+ const protocolsConfigure = yield ProtocolsConfigure.create({
308
+ definition,
309
+ signer: Jws.createSigner(alice),
310
+ });
311
+ expect(protocolsConfigure.message.descriptor.definition.uses).toBeDefined();
312
+ // Install-time validation should also succeed when the referenced protocol is installed
313
+ const threadsConfigure = yield ProtocolsConfigure.create({
314
+ definition: threadsProtocol,
315
+ signer: Jws.createSigner(alice),
316
+ });
317
+ yield dwn.processMessage(alice.did, threadsConfigure.message);
318
+ const installReply = yield dwn.processMessage(alice.did, protocolsConfigure.message);
319
+ expect(installReply.status.code).toBe(202);
320
+ }));
321
+ it('should not require `$ref` types in local types map', () => __awaiter(this, void 0, void 0, function* () {
322
+ const alice = yield TestDataGenerator.generateDidKeyPersona();
323
+ // 'thread' is NOT in the local `types` — it comes from the $ref
324
+ const definition = {
325
+ protocol: 'https://comments.example.com',
326
+ published: true,
327
+ uses: { threads: 'https://threads.example.com' },
328
+ types: { comment: {} }, // no 'thread' type
329
+ structure: {
330
+ thread: {
331
+ $ref: 'threads:thread',
332
+ comment: {
333
+ $actions: [{ who: 'anyone', can: ['create'] }],
334
+ },
335
+ },
336
+ },
337
+ };
338
+ // should succeed without error
339
+ const protocolsConfigure = yield ProtocolsConfigure.create({
340
+ definition,
341
+ signer: Jws.createSigner(alice),
342
+ });
343
+ expect(protocolsConfigure.message.descriptor.definition.uses).toBeDefined();
344
+ }));
345
+ });
346
+ // =========================================================================
347
+ // Install-time validation tests (handler)
348
+ // =========================================================================
349
+ describe('install-time composition dependency validation', () => {
350
+ it('should reject composing protocol if `uses` protocol is not installed', () => __awaiter(this, void 0, void 0, function* () {
351
+ const alice = yield TestDataGenerator.generateDidKeyPersona();
352
+ // Try to install comments protocol WITHOUT installing threads first
353
+ const commentsConfigure = yield ProtocolsConfigure.create({
354
+ definition: commentsProtocol,
355
+ signer: Jws.createSigner(alice),
356
+ });
357
+ const reply = yield dwn.processMessage(alice.did, commentsConfigure.message);
358
+ expect(reply.status.code).toBe(400);
359
+ expect(reply.status.detail).toContain(DwnErrorCode.ProtocolsConfigureComposedProtocolNotInstalled);
360
+ }));
361
+ it('should accept composing protocol when `uses` protocol is already installed', () => __awaiter(this, void 0, void 0, function* () {
362
+ const alice = yield TestDataGenerator.generateDidKeyPersona();
363
+ // Install threads protocol first
364
+ const threadsConfigure = yield ProtocolsConfigure.create({
365
+ definition: threadsProtocol,
366
+ signer: Jws.createSigner(alice),
367
+ });
368
+ const threadsReply = yield dwn.processMessage(alice.did, threadsConfigure.message);
369
+ expect(threadsReply.status.code).toBe(202);
370
+ // Now install comments protocol — should succeed
371
+ const commentsConfigure = yield ProtocolsConfigure.create({
372
+ definition: commentsProtocol,
373
+ signer: Jws.createSigner(alice),
374
+ });
375
+ const commentsReply = yield dwn.processMessage(alice.did, commentsConfigure.message);
376
+ expect(commentsReply.status.code).toBe(202);
377
+ }));
378
+ it('should reject composing protocol if `$ref` path does not exist in referenced protocol', () => __awaiter(this, void 0, void 0, function* () {
379
+ const alice = yield TestDataGenerator.generateDidKeyPersona();
380
+ // Install threads protocol first
381
+ const threadsConfigure = yield ProtocolsConfigure.create({
382
+ definition: threadsProtocol,
383
+ signer: Jws.createSigner(alice),
384
+ });
385
+ yield dwn.processMessage(alice.did, threadsConfigure.message);
386
+ // Install a composing protocol that references a non-existent type path
387
+ const badDefinition = {
388
+ protocol: 'https://bad.example.com',
389
+ published: true,
390
+ uses: { threads: 'https://threads.example.com' },
391
+ types: { comment: {} },
392
+ structure: {
393
+ nonexistent: {
394
+ $ref: 'threads:nonexistent', // 'nonexistent' doesn't exist in threads protocol
395
+ comment: {
396
+ $actions: [{ who: 'anyone', can: ['create'] }],
397
+ },
398
+ },
399
+ },
400
+ };
401
+ const badConfigure = yield ProtocolsConfigure.create({
402
+ definition: badDefinition,
403
+ signer: Jws.createSigner(alice),
404
+ });
405
+ const reply = yield dwn.processMessage(alice.did, badConfigure.message);
406
+ expect(reply.status.code).toBe(400);
407
+ 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', () => __awaiter(this, void 0, void 0, function* () {
410
+ const alice = yield TestDataGenerator.generateDidKeyPersona();
411
+ // Install threads protocol first
412
+ const threadsConfigure = yield ProtocolsConfigure.create({
413
+ definition: threadsProtocol,
414
+ signer: Jws.createSigner(alice),
415
+ });
416
+ yield dwn.processMessage(alice.did, threadsConfigure.message);
417
+ // Install a composing protocol with an `of` reference to a non-existent path
418
+ const badDefinition = {
419
+ protocol: 'https://bad.example.com',
420
+ published: true,
421
+ uses: { threads: 'https://threads.example.com' },
422
+ types: { action: {} },
423
+ structure: {
424
+ thread: {
425
+ $ref: 'threads:thread',
426
+ action: {
427
+ $actions: [
428
+ { who: 'author', of: 'threads:nonexistent', can: ['create'] },
429
+ ],
430
+ },
431
+ },
432
+ },
433
+ };
434
+ const badConfigure = yield ProtocolsConfigure.create({
435
+ definition: badDefinition,
436
+ signer: Jws.createSigner(alice),
437
+ });
438
+ const reply = yield dwn.processMessage(alice.did, badConfigure.message);
439
+ expect(reply.status.code).toBe(400);
440
+ expect(reply.status.detail).toContain(DwnErrorCode.ProtocolsConfigureInvalidCrossProtocolOf);
441
+ }));
442
+ it('should reject composing protocol if cross-protocol role does not exist in referenced protocol', () => __awaiter(this, void 0, void 0, function* () {
443
+ const alice = yield TestDataGenerator.generateDidKeyPersona();
444
+ // Install threads protocol first
445
+ const threadsConfigure = yield ProtocolsConfigure.create({
446
+ definition: threadsProtocol,
447
+ signer: Jws.createSigner(alice),
448
+ });
449
+ yield dwn.processMessage(alice.did, threadsConfigure.message);
450
+ // Install a composing protocol with invalid cross-protocol role
451
+ const badDefinition = {
452
+ protocol: 'https://bad.example.com',
453
+ published: true,
454
+ uses: { threads: 'https://threads.example.com' },
455
+ types: { comment: {} },
456
+ structure: {
457
+ thread: {
458
+ $ref: 'threads:thread',
459
+ comment: {
460
+ $actions: [
461
+ { who: 'anyone', can: ['create'] },
462
+ { role: 'threads:thread/nonexistent', can: ['read'] }, // path exists but not a role
463
+ ],
464
+ },
465
+ },
466
+ },
467
+ };
468
+ const badConfigure = yield ProtocolsConfigure.create({
469
+ definition: badDefinition,
470
+ signer: Jws.createSigner(alice),
471
+ });
472
+ const reply = yield dwn.processMessage(alice.did, badConfigure.message);
473
+ expect(reply.status.code).toBe(400);
474
+ expect(reply.status.detail).toContain(DwnErrorCode.ProtocolsConfigureInvalidCrossProtocolRole);
475
+ }));
476
+ });
477
+ // =========================================================================
478
+ // Runtime cross-protocol record creation tests
479
+ // =========================================================================
480
+ describe('cross-protocol record creation', () => {
481
+ it('should allow creating a child record in a composing protocol under a parent from a different protocol', () => __awaiter(this, void 0, void 0, function* () {
482
+ const alice = yield TestDataGenerator.generateDidKeyPersona();
483
+ // Install both protocols
484
+ const threadsConfigure = yield ProtocolsConfigure.create({
485
+ definition: threadsProtocol,
486
+ signer: Jws.createSigner(alice),
487
+ });
488
+ yield dwn.processMessage(alice.did, threadsConfigure.message);
489
+ const commentsConfigure = yield ProtocolsConfigure.create({
490
+ definition: commentsProtocol,
491
+ signer: Jws.createSigner(alice),
492
+ });
493
+ yield dwn.processMessage(alice.did, commentsConfigure.message);
494
+ // Create a thread record in the threads protocol
495
+ const threadWrite = yield TestDataGenerator.generateRecordsWrite({
496
+ author: alice,
497
+ protocol: threadsProtocol.protocol,
498
+ protocolPath: 'thread',
499
+ schema: 'https://threads.example.com/schemas/thread',
500
+ dataFormat: 'application/json',
501
+ });
502
+ const threadReply = yield dwn.processMessage(alice.did, threadWrite.message, { dataStream: threadWrite.dataStream });
503
+ expect(threadReply.status.code).toBe(202);
504
+ const threadContextId = threadWrite.message.contextId;
505
+ // Create a comment record in the comments protocol, parented under the thread
506
+ const commentWrite = yield TestDataGenerator.generateRecordsWrite({
507
+ author: alice,
508
+ protocol: commentsProtocol.protocol,
509
+ protocolPath: 'thread/comment',
510
+ schema: 'https://comments.example.com/schemas/comment',
511
+ dataFormat: 'application/json',
512
+ parentContextId: threadContextId,
513
+ });
514
+ const commentReply = yield dwn.processMessage(alice.did, commentWrite.message, { dataStream: commentWrite.dataStream });
515
+ expect(commentReply.status.code).toBe(202);
516
+ // The comment's contextId should chain from the thread's contextId
517
+ 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', () => __awaiter(this, void 0, void 0, function* () {
520
+ const alice = yield TestDataGenerator.generateDidKeyPersona();
521
+ // Install both protocols
522
+ const threadsConfigure = yield ProtocolsConfigure.create({
523
+ definition: threadsProtocol,
524
+ signer: Jws.createSigner(alice),
525
+ });
526
+ yield dwn.processMessage(alice.did, threadsConfigure.message);
527
+ const commentsConfigure = yield ProtocolsConfigure.create({
528
+ definition: commentsProtocol,
529
+ signer: Jws.createSigner(alice),
530
+ });
531
+ yield dwn.processMessage(alice.did, commentsConfigure.message);
532
+ // Create thread -> comment -> reaction chain
533
+ const threadWrite = yield TestDataGenerator.generateRecordsWrite({
534
+ author: alice,
535
+ protocol: threadsProtocol.protocol,
536
+ protocolPath: 'thread',
537
+ schema: 'https://threads.example.com/schemas/thread',
538
+ dataFormat: 'application/json',
539
+ });
540
+ yield dwn.processMessage(alice.did, threadWrite.message, { dataStream: threadWrite.dataStream });
541
+ const threadContextId = threadWrite.message.contextId;
542
+ const commentWrite = yield TestDataGenerator.generateRecordsWrite({
543
+ author: alice,
544
+ protocol: commentsProtocol.protocol,
545
+ protocolPath: 'thread/comment',
546
+ schema: 'https://comments.example.com/schemas/comment',
547
+ dataFormat: 'application/json',
548
+ parentContextId: threadContextId,
549
+ });
550
+ yield dwn.processMessage(alice.did, commentWrite.message, { dataStream: commentWrite.dataStream });
551
+ const commentContextId = commentWrite.message.contextId;
552
+ // Create a reaction under the comment (grandchild, same composing protocol)
553
+ const reactionWrite = yield TestDataGenerator.generateRecordsWrite({
554
+ author: alice,
555
+ protocol: commentsProtocol.protocol,
556
+ protocolPath: 'thread/comment/reaction',
557
+ schema: 'https://comments.example.com/schemas/reaction',
558
+ dataFormat: 'application/json',
559
+ parentContextId: commentContextId,
560
+ });
561
+ const reactionReply = yield dwn.processMessage(alice.did, reactionWrite.message, { dataStream: reactionWrite.dataStream });
562
+ expect(reactionReply.status.code).toBe(202);
563
+ }));
564
+ });
565
+ // =========================================================================
566
+ // Writing at the $ref position
567
+ // =========================================================================
568
+ describe('writing at the $ref position', () => {
569
+ it('should reject a non-tenant writing at the `$ref` position because `$ref` nodes have no `$actions`', () => __awaiter(this, void 0, void 0, function* () {
570
+ const alice = yield TestDataGenerator.generateDidKeyPersona();
571
+ const bob = yield TestDataGenerator.generateDidKeyPersona();
572
+ // Install both protocols on Alice's DWN
573
+ const threadsConfigure = yield ProtocolsConfigure.create({
574
+ definition: threadsProtocol,
575
+ signer: Jws.createSigner(alice),
576
+ });
577
+ yield dwn.processMessage(alice.did, threadsConfigure.message);
578
+ const commentsConfigure = yield ProtocolsConfigure.create({
579
+ definition: commentsProtocol,
580
+ signer: Jws.createSigner(alice),
581
+ });
582
+ yield dwn.processMessage(alice.did, commentsConfigure.message);
583
+ // Bob tries to write a record at the `$ref` position (protocolPath: 'thread')
584
+ // through the composing protocol. The `$ref` node has no `$actions`, so this should
585
+ // be rejected for any non-tenant author.
586
+ const threadWriteAtRef = yield TestDataGenerator.generateRecordsWrite({
587
+ author: bob,
588
+ protocol: commentsProtocol.protocol,
589
+ protocolPath: 'thread',
590
+ schema: 'https://threads.example.com/schemas/thread',
591
+ dataFormat: 'application/json',
592
+ });
593
+ const reply = yield dwn.processMessage(alice.did, threadWriteAtRef.message, { dataStream: threadWriteAtRef.dataStream });
594
+ expect(reply.status.code).toBe(401);
595
+ expect(reply.status.detail).toContain(DwnErrorCode.ProtocolAuthorizationActionRulesNotFound);
596
+ }));
597
+ it('should resolve type from the referenced protocol for records at the `$ref` position', () => __awaiter(this, void 0, void 0, function* () {
598
+ const alice = yield TestDataGenerator.generateDidKeyPersona();
599
+ // Install both protocols on Alice's DWN
600
+ const threadsConfigure = yield ProtocolsConfigure.create({
601
+ definition: threadsProtocol,
602
+ signer: Jws.createSigner(alice),
603
+ });
604
+ yield dwn.processMessage(alice.did, threadsConfigure.message);
605
+ const commentsConfigure = yield ProtocolsConfigure.create({
606
+ definition: commentsProtocol,
607
+ signer: Jws.createSigner(alice),
608
+ });
609
+ yield dwn.processMessage(alice.did, commentsConfigure.message);
610
+ // Alice (tenant) writes at the `$ref` position using the WRONG schema.
611
+ // The type at position 'thread' should resolve from the threads protocol, which expects
612
+ // schema 'https://threads.example.com/schemas/thread'. Using a different schema should fail.
613
+ const wrongSchemaWrite = yield TestDataGenerator.generateRecordsWrite({
614
+ author: alice,
615
+ protocol: commentsProtocol.protocol,
616
+ protocolPath: 'thread',
617
+ schema: 'https://comments.example.com/schemas/comment', // wrong schema
618
+ dataFormat: 'application/json',
619
+ });
620
+ const reply = yield dwn.processMessage(alice.did, wrongSchemaWrite.message, { dataStream: wrongSchemaWrite.dataStream });
621
+ expect(reply.status.code).toBe(400);
622
+ // Type verification should fail because the schema doesn't match the referenced protocol's type
623
+ }));
624
+ });
625
+ // =========================================================================
626
+ // Cross-protocol role invocation tests
627
+ // =========================================================================
628
+ describe('cross-protocol role invocation', () => {
629
+ it('should allow a cross-protocol role holder to perform actions in the composing protocol', () => __awaiter(this, void 0, void 0, function* () {
630
+ const alice = yield TestDataGenerator.generateDidKeyPersona();
631
+ const bob = yield TestDataGenerator.generateDidKeyPersona();
632
+ // Install threads protocol on Alice's DWN
633
+ const threadsConfigure = yield ProtocolsConfigure.create({
634
+ definition: threadsProtocol,
635
+ signer: Jws.createSigner(alice),
636
+ });
637
+ const threadsReply = yield dwn.processMessage(alice.did, threadsConfigure.message);
638
+ expect(threadsReply.status.code).toBe(202);
639
+ // Install comments protocol on Alice's DWN
640
+ const commentsConfigure = yield ProtocolsConfigure.create({
641
+ definition: commentsProtocol,
642
+ signer: Jws.createSigner(alice),
643
+ });
644
+ const commentsReply = yield dwn.processMessage(alice.did, commentsConfigure.message);
645
+ expect(commentsReply.status.code).toBe(202);
646
+ // Alice creates a thread
647
+ const threadWrite = yield TestDataGenerator.generateRecordsWrite({
648
+ author: alice,
649
+ protocol: threadsProtocol.protocol,
650
+ protocolPath: 'thread',
651
+ schema: 'https://threads.example.com/schemas/thread',
652
+ dataFormat: 'application/json',
653
+ });
654
+ const threadReply = yield dwn.processMessage(alice.did, threadWrite.message, { dataStream: threadWrite.dataStream });
655
+ expect(threadReply.status.code).toBe(202);
656
+ const threadContextId = threadWrite.message.contextId;
657
+ // Alice assigns Bob as a participant in the thread (role record in threads protocol)
658
+ const participantWrite = yield TestDataGenerator.generateRecordsWrite({
659
+ author: alice,
660
+ recipient: bob.did,
661
+ protocol: threadsProtocol.protocol,
662
+ protocolPath: 'thread/participant',
663
+ schema: 'https://threads.example.com/schemas/participant',
664
+ dataFormat: 'application/json',
665
+ parentContextId: threadContextId,
666
+ });
667
+ const participantReply = yield dwn.processMessage(alice.did, participantWrite.message, { dataStream: participantWrite.dataStream });
668
+ expect(participantReply.status.code).toBe(202);
669
+ // Bob invokes the cross-protocol role to read comments
670
+ const commentWrite = yield TestDataGenerator.generateRecordsWrite({
671
+ author: alice,
672
+ protocol: commentsProtocol.protocol,
673
+ protocolPath: 'thread/comment',
674
+ schema: 'https://comments.example.com/schemas/comment',
675
+ dataFormat: 'application/json',
676
+ parentContextId: threadContextId,
677
+ });
678
+ const commentReply = yield dwn.processMessage(alice.did, commentWrite.message, { dataStream: commentWrite.dataStream });
679
+ expect(commentReply.status.code).toBe(202);
680
+ // Bob reads the comment using the cross-protocol role
681
+ const bobRead = yield RecordsRead.create({
682
+ signer: Jws.createSigner(bob),
683
+ protocolRole: 'threads:thread/participant',
684
+ filter: {
685
+ protocol: commentsProtocol.protocol,
686
+ protocolPath: 'thread/comment',
687
+ contextId: threadContextId,
688
+ },
689
+ });
690
+ const bobReadReply = yield dwn.processMessage(alice.did, bobRead.message);
691
+ expect(bobReadReply.status.code).toBe(200);
692
+ }));
693
+ it('should allow a cross-protocol role holder to query records in the composing protocol', () => __awaiter(this, void 0, void 0, function* () {
694
+ var _a;
695
+ const alice = yield TestDataGenerator.generateDidKeyPersona();
696
+ const bob = yield TestDataGenerator.generateDidKeyPersona();
697
+ // Install both protocols on Alice's DWN
698
+ const threadsConfigure = yield ProtocolsConfigure.create({
699
+ definition: threadsProtocol,
700
+ signer: Jws.createSigner(alice),
701
+ });
702
+ yield dwn.processMessage(alice.did, threadsConfigure.message);
703
+ const commentsConfigure = yield ProtocolsConfigure.create({
704
+ definition: commentsProtocol,
705
+ signer: Jws.createSigner(alice),
706
+ });
707
+ yield dwn.processMessage(alice.did, commentsConfigure.message);
708
+ // Alice creates a thread
709
+ const threadWrite = yield TestDataGenerator.generateRecordsWrite({
710
+ author: alice,
711
+ protocol: threadsProtocol.protocol,
712
+ protocolPath: 'thread',
713
+ schema: 'https://threads.example.com/schemas/thread',
714
+ dataFormat: 'application/json',
715
+ });
716
+ yield dwn.processMessage(alice.did, threadWrite.message, { dataStream: threadWrite.dataStream });
717
+ const threadContextId = threadWrite.message.contextId;
718
+ // Alice assigns Bob as a participant
719
+ const participantWrite = yield TestDataGenerator.generateRecordsWrite({
720
+ author: alice,
721
+ recipient: bob.did,
722
+ protocol: threadsProtocol.protocol,
723
+ protocolPath: 'thread/participant',
724
+ schema: 'https://threads.example.com/schemas/participant',
725
+ dataFormat: 'application/json',
726
+ parentContextId: threadContextId,
727
+ });
728
+ yield dwn.processMessage(alice.did, participantWrite.message, { dataStream: participantWrite.dataStream });
729
+ // Alice creates two comments
730
+ const comment1 = yield TestDataGenerator.generateRecordsWrite({
731
+ author: alice,
732
+ protocol: commentsProtocol.protocol,
733
+ protocolPath: 'thread/comment',
734
+ schema: 'https://comments.example.com/schemas/comment',
735
+ dataFormat: 'application/json',
736
+ parentContextId: threadContextId,
737
+ });
738
+ yield dwn.processMessage(alice.did, comment1.message, { dataStream: comment1.dataStream });
739
+ const comment2 = yield TestDataGenerator.generateRecordsWrite({
740
+ author: alice,
741
+ protocol: commentsProtocol.protocol,
742
+ protocolPath: 'thread/comment',
743
+ schema: 'https://comments.example.com/schemas/comment',
744
+ dataFormat: 'application/json',
745
+ parentContextId: threadContextId,
746
+ });
747
+ yield dwn.processMessage(alice.did, comment2.message, { dataStream: comment2.dataStream });
748
+ // Bob queries comments using the cross-protocol role
749
+ const bobQuery = yield RecordsQuery.create({
750
+ signer: Jws.createSigner(bob),
751
+ protocolRole: 'threads:thread/participant',
752
+ filter: {
753
+ protocol: commentsProtocol.protocol,
754
+ protocolPath: 'thread/comment',
755
+ contextId: threadContextId,
756
+ },
757
+ });
758
+ const bobQueryReply = yield dwn.processMessage(alice.did, bobQuery.message);
759
+ expect(bobQueryReply.status.code).toBe(200);
760
+ expect((_a = bobQueryReply.entries) === null || _a === void 0 ? void 0 : _a.length).toBe(2);
761
+ }));
762
+ it('should allow a cross-protocol role holder to co-delete records in the composing protocol', () => __awaiter(this, void 0, void 0, function* () {
763
+ const alice = yield TestDataGenerator.generateDidKeyPersona();
764
+ const bob = yield TestDataGenerator.generateDidKeyPersona();
765
+ const carol = yield TestDataGenerator.generateDidKeyPersona();
766
+ // Install both protocols on Alice's DWN
767
+ const threadsConfigure = yield ProtocolsConfigure.create({
768
+ definition: threadsProtocol,
769
+ signer: Jws.createSigner(alice),
770
+ });
771
+ yield dwn.processMessage(alice.did, threadsConfigure.message);
772
+ const commentsConfigure = yield ProtocolsConfigure.create({
773
+ definition: commentsProtocol,
774
+ signer: Jws.createSigner(alice),
775
+ });
776
+ yield dwn.processMessage(alice.did, commentsConfigure.message);
777
+ // Alice creates a thread
778
+ const threadWrite = yield TestDataGenerator.generateRecordsWrite({
779
+ author: alice,
780
+ protocol: threadsProtocol.protocol,
781
+ protocolPath: 'thread',
782
+ schema: 'https://threads.example.com/schemas/thread',
783
+ dataFormat: 'application/json',
784
+ });
785
+ yield dwn.processMessage(alice.did, threadWrite.message, { dataStream: threadWrite.dataStream });
786
+ const threadContextId = threadWrite.message.contextId;
787
+ // Alice assigns Bob as a participant (role record)
788
+ const participantWrite = yield TestDataGenerator.generateRecordsWrite({
789
+ author: alice,
790
+ recipient: bob.did,
791
+ protocol: threadsProtocol.protocol,
792
+ protocolPath: 'thread/participant',
793
+ schema: 'https://threads.example.com/schemas/participant',
794
+ dataFormat: 'application/json',
795
+ parentContextId: threadContextId,
796
+ });
797
+ yield dwn.processMessage(alice.did, participantWrite.message, { dataStream: participantWrite.dataStream });
798
+ // Carol creates a comment (via 'anyone' can 'create')
799
+ const carolComment = yield TestDataGenerator.generateRecordsWrite({
800
+ author: carol,
801
+ protocol: commentsProtocol.protocol,
802
+ protocolPath: 'thread/comment',
803
+ schema: 'https://comments.example.com/schemas/comment',
804
+ dataFormat: 'application/json',
805
+ parentContextId: threadContextId,
806
+ });
807
+ yield dwn.processMessage(alice.did, carolComment.message, { dataStream: carolComment.dataStream });
808
+ // Bob (participant) co-deletes Carol's comment using the cross-protocol role
809
+ // The commentsProtocol grants 'threads:thread/participant' the 'co-delete' action on comments
810
+ const bobDelete = yield RecordsDelete.create({
811
+ protocolRole: 'threads:thread/participant',
812
+ recordId: carolComment.message.recordId,
813
+ signer: Jws.createSigner(bob),
814
+ });
815
+ const deleteReply = yield dwn.processMessage(alice.did, bobDelete.message);
816
+ expect(deleteReply.status.code).toBe(202);
817
+ }));
818
+ it('should deny cross-protocol role access when the role record has been deleted (revocation)', () => __awaiter(this, void 0, void 0, function* () {
819
+ const alice = yield TestDataGenerator.generateDidKeyPersona();
820
+ const bob = yield TestDataGenerator.generateDidKeyPersona();
821
+ // Install both protocols on Alice's DWN
822
+ const threadsConfigure = yield ProtocolsConfigure.create({
823
+ definition: threadsProtocol,
824
+ signer: Jws.createSigner(alice),
825
+ });
826
+ yield dwn.processMessage(alice.did, threadsConfigure.message);
827
+ const commentsConfigure = yield ProtocolsConfigure.create({
828
+ definition: commentsProtocol,
829
+ signer: Jws.createSigner(alice),
830
+ });
831
+ yield dwn.processMessage(alice.did, commentsConfigure.message);
832
+ // Alice creates a thread
833
+ const threadWrite = yield TestDataGenerator.generateRecordsWrite({
834
+ author: alice,
835
+ protocol: threadsProtocol.protocol,
836
+ protocolPath: 'thread',
837
+ schema: 'https://threads.example.com/schemas/thread',
838
+ dataFormat: 'application/json',
839
+ });
840
+ yield dwn.processMessage(alice.did, threadWrite.message, { dataStream: threadWrite.dataStream });
841
+ const threadContextId = threadWrite.message.contextId;
842
+ // Alice assigns Bob as a participant
843
+ const participantWrite = yield TestDataGenerator.generateRecordsWrite({
844
+ author: alice,
845
+ recipient: bob.did,
846
+ protocol: threadsProtocol.protocol,
847
+ protocolPath: 'thread/participant',
848
+ schema: 'https://threads.example.com/schemas/participant',
849
+ dataFormat: 'application/json',
850
+ parentContextId: threadContextId,
851
+ });
852
+ yield dwn.processMessage(alice.did, participantWrite.message, { dataStream: participantWrite.dataStream });
853
+ // Alice creates a comment
854
+ const commentWrite = yield TestDataGenerator.generateRecordsWrite({
855
+ author: alice,
856
+ protocol: commentsProtocol.protocol,
857
+ protocolPath: 'thread/comment',
858
+ schema: 'https://comments.example.com/schemas/comment',
859
+ dataFormat: 'application/json',
860
+ parentContextId: threadContextId,
861
+ });
862
+ yield dwn.processMessage(alice.did, commentWrite.message, { dataStream: commentWrite.dataStream });
863
+ // Verify Bob CAN read the comment (role is active)
864
+ const bobRead = yield RecordsRead.create({
865
+ signer: Jws.createSigner(bob),
866
+ protocolRole: 'threads:thread/participant',
867
+ filter: {
868
+ protocol: commentsProtocol.protocol,
869
+ protocolPath: 'thread/comment',
870
+ contextId: threadContextId,
871
+ },
872
+ });
873
+ const bobReadReply = yield dwn.processMessage(alice.did, bobRead.message);
874
+ expect(bobReadReply.status.code).toBe(200);
875
+ // Alice deletes Bob's participant role record (revocation)
876
+ const deleteRole = yield RecordsDelete.create({
877
+ recordId: participantWrite.message.recordId,
878
+ signer: Jws.createSigner(alice),
879
+ });
880
+ const deleteRoleReply = yield dwn.processMessage(alice.did, deleteRole.message);
881
+ expect(deleteRoleReply.status.code).toBe(202);
882
+ // Bob tries to read the comment again — should be denied because role was deleted
883
+ const bobRead2 = yield RecordsRead.create({
884
+ signer: Jws.createSigner(bob),
885
+ protocolRole: 'threads:thread/participant',
886
+ filter: {
887
+ protocol: commentsProtocol.protocol,
888
+ protocolPath: 'thread/comment',
889
+ contextId: threadContextId,
890
+ },
891
+ });
892
+ const bobRead2Reply = yield dwn.processMessage(alice.did, bobRead2.message);
893
+ expect(bobRead2Reply.status.code).toBe(401);
894
+ expect(bobRead2Reply.status.detail).toContain(DwnErrorCode.ProtocolAuthorizationMatchingRoleRecordNotFound);
895
+ }));
896
+ it('should reject a cross-protocol role invocation if the invoker lacks the role record', () => __awaiter(this, void 0, void 0, function* () {
897
+ const alice = yield TestDataGenerator.generateDidKeyPersona();
898
+ const carol = yield TestDataGenerator.generateDidKeyPersona();
899
+ // Install both protocols
900
+ const threadsConfigure = yield ProtocolsConfigure.create({
901
+ definition: threadsProtocol,
902
+ signer: Jws.createSigner(alice),
903
+ });
904
+ yield dwn.processMessage(alice.did, threadsConfigure.message);
905
+ const commentsConfigure = yield ProtocolsConfigure.create({
906
+ definition: commentsProtocol,
907
+ signer: Jws.createSigner(alice),
908
+ });
909
+ yield dwn.processMessage(alice.did, commentsConfigure.message);
910
+ // Alice creates a thread
911
+ const threadWrite = yield TestDataGenerator.generateRecordsWrite({
912
+ author: alice,
913
+ protocol: threadsProtocol.protocol,
914
+ protocolPath: 'thread',
915
+ schema: 'https://threads.example.com/schemas/thread',
916
+ dataFormat: 'application/json',
917
+ });
918
+ yield dwn.processMessage(alice.did, threadWrite.message, { dataStream: threadWrite.dataStream });
919
+ const threadContextId = threadWrite.message.contextId;
920
+ // Alice creates a comment
921
+ const commentWrite = yield TestDataGenerator.generateRecordsWrite({
922
+ author: alice,
923
+ protocol: commentsProtocol.protocol,
924
+ protocolPath: 'thread/comment',
925
+ schema: 'https://comments.example.com/schemas/comment',
926
+ dataFormat: 'application/json',
927
+ parentContextId: threadContextId,
928
+ });
929
+ yield dwn.processMessage(alice.did, commentWrite.message, { dataStream: commentWrite.dataStream });
930
+ // Carol (who has NO participant role) tries to read the comment using the cross-protocol role
931
+ const carolRead = yield RecordsRead.create({
932
+ signer: Jws.createSigner(carol),
933
+ protocolRole: 'threads:thread/participant',
934
+ filter: {
935
+ protocol: commentsProtocol.protocol,
936
+ protocolPath: 'thread/comment',
937
+ contextId: threadContextId,
938
+ },
939
+ });
940
+ const carolReadReply = yield dwn.processMessage(alice.did, carolRead.message);
941
+ expect(carolReadReply.status.code).toBe(401);
942
+ expect(carolReadReply.status.detail).toContain(DwnErrorCode.ProtocolAuthorizationMatchingRoleRecordNotFound);
943
+ }));
944
+ });
945
+ // =========================================================================
946
+ // Cross-protocol `who`/`of` actor checks
947
+ // =========================================================================
948
+ describe('cross-protocol `who`/`of` actor checks', () => {
949
+ it('should allow `author` of a cross-protocol parent to perform actions in the composing protocol', () => __awaiter(this, void 0, void 0, function* () {
950
+ const alice = yield TestDataGenerator.generateDidKeyPersona();
951
+ const bob = yield TestDataGenerator.generateDidKeyPersona();
952
+ // Install both protocols
953
+ const threadsConfigure = yield ProtocolsConfigure.create({
954
+ definition: threadsProtocol,
955
+ signer: Jws.createSigner(alice),
956
+ });
957
+ yield dwn.processMessage(alice.did, threadsConfigure.message);
958
+ const commentsConfigure = yield ProtocolsConfigure.create({
959
+ definition: commentsProtocol,
960
+ signer: Jws.createSigner(alice),
961
+ });
962
+ yield dwn.processMessage(alice.did, commentsConfigure.message);
963
+ // Bob creates a thread (via the 'anyone' can 'create' rule)
964
+ const threadWrite = yield TestDataGenerator.generateRecordsWrite({
965
+ author: bob,
966
+ protocol: threadsProtocol.protocol,
967
+ protocolPath: 'thread',
968
+ schema: 'https://threads.example.com/schemas/thread',
969
+ dataFormat: 'application/json',
970
+ });
971
+ const threadReply = yield dwn.processMessage(alice.did, threadWrite.message, { dataStream: threadWrite.dataStream });
972
+ expect(threadReply.status.code).toBe(202);
973
+ const threadContextId = threadWrite.message.contextId;
974
+ // Anyone can create a comment under the thread (via 'anyone' can 'create' rule)
975
+ const commentWrite = yield TestDataGenerator.generateRecordsWrite({
976
+ author: alice,
977
+ protocol: commentsProtocol.protocol,
978
+ protocolPath: 'thread/comment',
979
+ schema: 'https://comments.example.com/schemas/comment',
980
+ dataFormat: 'application/json',
981
+ parentContextId: threadContextId,
982
+ });
983
+ const commentReply = yield dwn.processMessage(alice.did, commentWrite.message, { dataStream: commentWrite.dataStream });
984
+ expect(commentReply.status.code).toBe(202);
985
+ // Anyone can also read (via 'anyone' can 'read' rule)
986
+ const readComment = yield RecordsRead.create({
987
+ signer: Jws.createSigner(bob),
988
+ filter: {
989
+ protocol: commentsProtocol.protocol,
990
+ protocolPath: 'thread/comment',
991
+ contextId: threadContextId,
992
+ },
993
+ });
994
+ const readReply = yield dwn.processMessage(alice.did, readComment.message);
995
+ expect(readReply.status.code).toBe(200);
996
+ }));
997
+ });
998
+ // =========================================================================
999
+ // Cross-protocol `of` actor check — happy path
1000
+ // =========================================================================
1001
+ 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', () => __awaiter(this, void 0, void 0, function* () {
1003
+ const alice = yield TestDataGenerator.generateDidKeyPersona();
1004
+ const bob = yield TestDataGenerator.generateDidKeyPersona();
1005
+ const carol = yield TestDataGenerator.generateDidKeyPersona();
1006
+ // A protocol that grants the author of a thread (from the threads protocol) the ability to create moderation actions
1007
+ const moderationProtocol = {
1008
+ protocol: 'https://moderation.example.com',
1009
+ published: true,
1010
+ uses: { threads: 'https://threads.example.com' },
1011
+ types: {
1012
+ action: { schema: 'https://moderation.example.com/schemas/action', dataFormats: ['application/json'] },
1013
+ },
1014
+ structure: {
1015
+ thread: {
1016
+ $ref: 'threads:thread',
1017
+ action: {
1018
+ $actions: [
1019
+ { who: 'author', of: 'threads:thread', can: ['create'] },
1020
+ { who: 'anyone', can: ['read'] },
1021
+ ],
1022
+ },
1023
+ },
1024
+ },
1025
+ };
1026
+ // Install both protocols on Alice's DWN
1027
+ const threadsConfigure = yield ProtocolsConfigure.create({
1028
+ definition: threadsProtocol,
1029
+ signer: Jws.createSigner(alice),
1030
+ });
1031
+ yield dwn.processMessage(alice.did, threadsConfigure.message);
1032
+ const moderationConfigure = yield ProtocolsConfigure.create({
1033
+ definition: moderationProtocol,
1034
+ signer: Jws.createSigner(alice),
1035
+ });
1036
+ const modReply = yield dwn.processMessage(alice.did, moderationConfigure.message);
1037
+ expect(modReply.status.code).toBe(202);
1038
+ // Bob creates a thread (via 'anyone' can 'create' in threadsProtocol)
1039
+ const threadWrite = yield TestDataGenerator.generateRecordsWrite({
1040
+ author: bob,
1041
+ protocol: threadsProtocol.protocol,
1042
+ protocolPath: 'thread',
1043
+ schema: 'https://threads.example.com/schemas/thread',
1044
+ dataFormat: 'application/json',
1045
+ });
1046
+ const threadReply = yield dwn.processMessage(alice.did, threadWrite.message, { dataStream: threadWrite.dataStream });
1047
+ expect(threadReply.status.code).toBe(202);
1048
+ const threadContextId = threadWrite.message.contextId;
1049
+ // Bob (as author of the thread) creates a moderation action — should succeed
1050
+ const actionWrite = yield TestDataGenerator.generateRecordsWrite({
1051
+ author: bob,
1052
+ protocol: moderationProtocol.protocol,
1053
+ protocolPath: 'thread/action',
1054
+ schema: 'https://moderation.example.com/schemas/action',
1055
+ dataFormat: 'application/json',
1056
+ parentContextId: threadContextId,
1057
+ });
1058
+ const actionReply = yield dwn.processMessage(alice.did, actionWrite.message, { dataStream: actionWrite.dataStream });
1059
+ expect(actionReply.status.code).toBe(202);
1060
+ // Carol (NOT author of the thread, NOT the tenant) tries to create a moderation action — should fail
1061
+ const carolActionWrite = yield TestDataGenerator.generateRecordsWrite({
1062
+ author: carol,
1063
+ protocol: moderationProtocol.protocol,
1064
+ protocolPath: 'thread/action',
1065
+ schema: 'https://moderation.example.com/schemas/action',
1066
+ dataFormat: 'application/json',
1067
+ parentContextId: threadContextId,
1068
+ });
1069
+ const carolActionReply = yield dwn.processMessage(alice.did, carolActionWrite.message, { dataStream: carolActionWrite.dataStream });
1070
+ expect(carolActionReply.status.code).toBe(401);
1071
+ expect(carolActionReply.status.detail).toContain(DwnErrorCode.ProtocolAuthorizationActionNotAllowed);
1072
+ }));
1073
+ });
1074
+ // =========================================================================
1075
+ // Edge case and error tests
1076
+ // =========================================================================
1077
+ describe('composition error cases', () => {
1078
+ it('should reject `$ref` with malformed format (no colon separator)', () => __awaiter(this, void 0, void 0, function* () {
1079
+ const alice = yield TestDataGenerator.generateDidKeyPersona();
1080
+ const badDefinition = {
1081
+ protocol: 'https://bad.example.com',
1082
+ published: true,
1083
+ uses: { threads: 'https://threads.example.com' },
1084
+ types: { comment: {} },
1085
+ structure: {
1086
+ thread: {
1087
+ $ref: 'threadsthread', // missing colon — rejected by JSON schema pattern
1088
+ comment: {
1089
+ $actions: [{ who: 'anyone', can: ['create'] }],
1090
+ },
1091
+ },
1092
+ },
1093
+ };
1094
+ try {
1095
+ yield ProtocolsConfigure.create({
1096
+ definition: badDefinition,
1097
+ signer: Jws.createSigner(alice),
1098
+ });
1099
+ throw new Error('Expected an error to be thrown');
1100
+ }
1101
+ catch (error) {
1102
+ // JSON schema enforces `$ref` must match pattern `^[a-zA-Z][a-zA-Z0-9_-]*:.+$`
1103
+ expect(error.message).toContain('must match pattern');
1104
+ }
1105
+ }));
1106
+ it('should reject `$ref` that references own protocol', () => __awaiter(this, void 0, void 0, function* () {
1107
+ const alice = yield TestDataGenerator.generateDidKeyPersona();
1108
+ const badDefinition = {
1109
+ protocol: 'https://self.example.com',
1110
+ published: true,
1111
+ uses: { self: 'https://self.example.com' },
1112
+ types: { comment: {} },
1113
+ structure: {
1114
+ thread: {
1115
+ $ref: 'self:thread',
1116
+ comment: {
1117
+ $actions: [{ who: 'anyone', can: ['create'] }],
1118
+ },
1119
+ },
1120
+ },
1121
+ };
1122
+ try {
1123
+ yield ProtocolsConfigure.create({
1124
+ definition: badDefinition,
1125
+ signer: Jws.createSigner(alice),
1126
+ });
1127
+ throw new Error('Expected an error to be thrown');
1128
+ }
1129
+ catch (error) {
1130
+ expect(error.message).toContain(DwnErrorCode.ProtocolsConfigureInvalidUsesSelfReference);
1131
+ }
1132
+ }));
1133
+ it('should reject `uses` with invalid alias name (starts with a number)', () => __awaiter(this, void 0, void 0, function* () {
1134
+ const alice = yield TestDataGenerator.generateDidKeyPersona();
1135
+ const badDefinition = {
1136
+ protocol: 'https://bad.example.com',
1137
+ published: true,
1138
+ uses: { '123invalid': 'https://foo.example.com' },
1139
+ types: {},
1140
+ structure: {},
1141
+ };
1142
+ try {
1143
+ yield ProtocolsConfigure.create({
1144
+ definition: badDefinition,
1145
+ signer: Jws.createSigner(alice),
1146
+ });
1147
+ throw new Error('Expected an error to be thrown');
1148
+ }
1149
+ catch (error) {
1150
+ // JSON schema enforces alias pattern `^[a-zA-Z][a-zA-Z0-9_-]*$` via additionalProperties: false
1151
+ expect(error.message).toContain('must NOT have additional properties');
1152
+ }
1153
+ }));
1154
+ it('should reject cross-protocol `of` with alias not in `uses`', () => __awaiter(this, void 0, void 0, function* () {
1155
+ const alice = yield TestDataGenerator.generateDidKeyPersona();
1156
+ const badDefinition = {
1157
+ protocol: 'https://bad.example.com',
1158
+ published: true,
1159
+ uses: { threads: 'https://threads.example.com' },
1160
+ types: { action: {} },
1161
+ structure: {
1162
+ thread: {
1163
+ $ref: 'threads:thread',
1164
+ action: {
1165
+ $actions: [
1166
+ { who: 'author', of: 'nonexistent:thread', can: ['create'] }, // alias 'nonexistent' not in uses
1167
+ ],
1168
+ },
1169
+ },
1170
+ },
1171
+ };
1172
+ try {
1173
+ yield ProtocolsConfigure.create({
1174
+ definition: badDefinition,
1175
+ signer: Jws.createSigner(alice),
1176
+ });
1177
+ throw new Error('Expected an error to be thrown');
1178
+ }
1179
+ catch (error) {
1180
+ expect(error.message).toContain(DwnErrorCode.ProtocolsConfigureInvalidCrossProtocolOf);
1181
+ }
1182
+ }));
1183
+ it('should return 400 when cross-protocol parent record does not exist at runtime', () => __awaiter(this, void 0, void 0, function* () {
1184
+ const alice = yield TestDataGenerator.generateDidKeyPersona();
1185
+ // Install both protocols
1186
+ const threadsConfigure = yield ProtocolsConfigure.create({
1187
+ definition: threadsProtocol,
1188
+ signer: Jws.createSigner(alice),
1189
+ });
1190
+ yield dwn.processMessage(alice.did, threadsConfigure.message);
1191
+ const commentsConfigure = yield ProtocolsConfigure.create({
1192
+ definition: commentsProtocol,
1193
+ signer: Jws.createSigner(alice),
1194
+ });
1195
+ yield dwn.processMessage(alice.did, commentsConfigure.message);
1196
+ // Try to create a comment under a non-existent thread (fabricate a contextId)
1197
+ const fakeThreadContextId = 'bafybeifake1234567890';
1198
+ const commentWrite = yield TestDataGenerator.generateRecordsWrite({
1199
+ author: alice,
1200
+ protocol: commentsProtocol.protocol,
1201
+ protocolPath: 'thread/comment',
1202
+ schema: 'https://comments.example.com/schemas/comment',
1203
+ dataFormat: 'application/json',
1204
+ parentContextId: fakeThreadContextId,
1205
+ });
1206
+ const reply = yield dwn.processMessage(alice.did, commentWrite.message, { dataStream: commentWrite.dataStream });
1207
+ expect(reply.status.code).toBe(400);
1208
+ expect(reply.status.detail).toContain(DwnErrorCode.ProtocolAuthorizationCrossProtocolParentNotFound);
1209
+ }));
1210
+ it('should reject `$ref` at non-root protocol path', () => __awaiter(this, void 0, void 0, function* () {
1211
+ const alice = yield TestDataGenerator.generateDidKeyPersona();
1212
+ const badDefinition = {
1213
+ protocol: 'https://bad.example.com',
1214
+ published: true,
1215
+ uses: { threads: 'https://threads.example.com' },
1216
+ types: { wrapper: {}, nested: {} },
1217
+ structure: {
1218
+ wrapper: {
1219
+ nested: {
1220
+ $ref: 'threads:thread', // $ref at depth > 1 — not allowed
1221
+ },
1222
+ },
1223
+ },
1224
+ };
1225
+ try {
1226
+ yield ProtocolsConfigure.create({
1227
+ definition: badDefinition,
1228
+ signer: Jws.createSigner(alice),
1229
+ });
1230
+ throw new Error('Expected an error to be thrown');
1231
+ }
1232
+ catch (error) {
1233
+ expect(error.message).toContain(DwnErrorCode.ProtocolsConfigureInvalidRefNotAtRoot);
1234
+ }
1235
+ }));
1236
+ it('should reject `uses` with empty alias map', () => __awaiter(this, void 0, void 0, function* () {
1237
+ const alice = yield TestDataGenerator.generateDidKeyPersona();
1238
+ const badDefinition = {
1239
+ protocol: 'https://bad.example.com',
1240
+ published: true,
1241
+ uses: {},
1242
+ types: {},
1243
+ structure: {},
1244
+ };
1245
+ try {
1246
+ yield ProtocolsConfigure.create({
1247
+ definition: badDefinition,
1248
+ signer: Jws.createSigner(alice),
1249
+ });
1250
+ throw new Error('Expected an error to be thrown');
1251
+ }
1252
+ catch (error) {
1253
+ // JSON schema enforces minProperties: 1 on `uses`
1254
+ expect(error.message).toContain('must NOT have fewer than 1 properties');
1255
+ }
1256
+ }));
1257
+ });
1258
+ // =========================================================================
1259
+ // Temporal correctness — governing timestamp for cross-protocol lookups
1260
+ // =========================================================================
1261
+ describe('temporal correctness — governing timestamp', () => {
1262
+ it('should use the governing timestamp when fetching the referenced protocol for role verification', () => __awaiter(this, void 0, void 0, function* () {
1263
+ // Scenario:
1264
+ // 1. Install threads V1 (participant has $role: true)
1265
+ // 2. Install comments protocol (uses threads, references threads:thread/participant role)
1266
+ // 3. Create thread, assign Bob as participant, create comment — all under V1
1267
+ // 4. Update threads to V2 where participant is no longer a role ($role removed)
1268
+ // 5. Bob reads the comment using the cross-protocol role
1269
+ // → should SUCCEED because the comment's governing timestamp pins to threads V1
1270
+ const alice = yield TestDataGenerator.generateDidKeyPersona();
1271
+ const bob = yield TestDataGenerator.generateDidKeyPersona();
1272
+ // threads V1: participant is a role
1273
+ const threadsV1 = {
1274
+ protocol: 'https://threads-versioned.example.com',
1275
+ published: true,
1276
+ types: {
1277
+ thread: { schema: 'https://threads-versioned.example.com/schemas/thread', dataFormats: ['application/json'] },
1278
+ participant: { schema: 'https://threads-versioned.example.com/schemas/participant', dataFormats: ['application/json'] },
1279
+ },
1280
+ structure: {
1281
+ thread: {
1282
+ $actions: [{ who: 'anyone', can: ['create', 'read'] }],
1283
+ participant: {
1284
+ $role: true,
1285
+ $actions: [
1286
+ { who: 'anyone', can: ['read'] },
1287
+ { who: 'author', of: 'thread', can: ['create'] },
1288
+ ],
1289
+ },
1290
+ },
1291
+ },
1292
+ };
1293
+ // comments protocol: references threads-versioned
1294
+ const commentsVersioned = {
1295
+ protocol: 'https://comments-versioned.example.com',
1296
+ published: true,
1297
+ uses: { threads: 'https://threads-versioned.example.com' },
1298
+ types: {
1299
+ comment: { schema: 'https://comments-versioned.example.com/schemas/comment', dataFormats: ['application/json'] },
1300
+ },
1301
+ structure: {
1302
+ thread: {
1303
+ $ref: 'threads:thread',
1304
+ comment: {
1305
+ $actions: [
1306
+ { who: 'anyone', can: ['create'] },
1307
+ { role: 'threads:thread/participant', can: ['read'] },
1308
+ ],
1309
+ },
1310
+ },
1311
+ },
1312
+ };
1313
+ // Install threads V1
1314
+ const threadsV1Configure = yield ProtocolsConfigure.create({
1315
+ definition: threadsV1,
1316
+ signer: Jws.createSigner(alice),
1317
+ });
1318
+ const threadsV1Reply = yield dwn.processMessage(alice.did, threadsV1Configure.message);
1319
+ expect(threadsV1Reply.status.code).toBe(202);
1320
+ // Install comments protocol (depends on threads)
1321
+ const commentsConfigure = yield ProtocolsConfigure.create({
1322
+ definition: commentsVersioned,
1323
+ signer: Jws.createSigner(alice),
1324
+ });
1325
+ const commentsReply = yield dwn.processMessage(alice.did, commentsConfigure.message);
1326
+ expect(commentsReply.status.code).toBe(202);
1327
+ // Alice creates a thread
1328
+ const threadWrite = yield TestDataGenerator.generateRecordsWrite({
1329
+ author: alice,
1330
+ protocol: threadsV1.protocol,
1331
+ protocolPath: 'thread',
1332
+ schema: 'https://threads-versioned.example.com/schemas/thread',
1333
+ dataFormat: 'application/json',
1334
+ });
1335
+ yield dwn.processMessage(alice.did, threadWrite.message, { dataStream: threadWrite.dataStream });
1336
+ const threadContextId = threadWrite.message.contextId;
1337
+ // Alice assigns Bob as participant (role record in threads V1)
1338
+ const participantWrite = yield TestDataGenerator.generateRecordsWrite({
1339
+ author: alice,
1340
+ recipient: bob.did,
1341
+ protocol: threadsV1.protocol,
1342
+ protocolPath: 'thread/participant',
1343
+ schema: 'https://threads-versioned.example.com/schemas/participant',
1344
+ dataFormat: 'application/json',
1345
+ parentContextId: threadContextId,
1346
+ });
1347
+ yield dwn.processMessage(alice.did, participantWrite.message, { dataStream: participantWrite.dataStream });
1348
+ // Alice creates a comment (under V1)
1349
+ const commentWrite = yield TestDataGenerator.generateRecordsWrite({
1350
+ author: alice,
1351
+ protocol: commentsVersioned.protocol,
1352
+ protocolPath: 'thread/comment',
1353
+ schema: 'https://comments-versioned.example.com/schemas/comment',
1354
+ dataFormat: 'application/json',
1355
+ parentContextId: threadContextId,
1356
+ });
1357
+ yield dwn.processMessage(alice.did, commentWrite.message, { dataStream: commentWrite.dataStream });
1358
+ // Wait to ensure V2 gets a later timestamp
1359
+ yield Time.minimalSleep();
1360
+ // threads V2: participant is NO LONGER a role ($role removed)
1361
+ const threadsV2 = {
1362
+ protocol: 'https://threads-versioned.example.com',
1363
+ published: true,
1364
+ types: {
1365
+ thread: { schema: 'https://threads-versioned.example.com/schemas/thread', dataFormats: ['application/json'] },
1366
+ participant: { schema: 'https://threads-versioned.example.com/schemas/participant', dataFormats: ['application/json'] },
1367
+ },
1368
+ structure: {
1369
+ thread: {
1370
+ $actions: [{ who: 'anyone', can: ['create', 'read'] }],
1371
+ participant: {
1372
+ // $role is removed — participant is no longer a role in V2
1373
+ $actions: [
1374
+ { who: 'anyone', can: ['read'] },
1375
+ { who: 'author', of: 'thread', can: ['create'] },
1376
+ ],
1377
+ },
1378
+ },
1379
+ },
1380
+ };
1381
+ const threadsV2Configure = yield ProtocolsConfigure.create({
1382
+ definition: threadsV2,
1383
+ signer: Jws.createSigner(alice),
1384
+ });
1385
+ const threadsV2Reply = yield dwn.processMessage(alice.did, threadsV2Configure.message);
1386
+ expect(threadsV2Reply.status.code).toBe(202);
1387
+ // Bob reads the comment using the cross-protocol role.
1388
+ // The comment was created under V1. The governing timestamp pins the referenced protocol
1389
+ // lookup to V1 where participant IS a valid role. This should SUCCEED.
1390
+ const bobRead = yield RecordsRead.create({
1391
+ signer: Jws.createSigner(bob),
1392
+ protocolRole: 'threads:thread/participant',
1393
+ filter: {
1394
+ protocol: commentsVersioned.protocol,
1395
+ protocolPath: 'thread/comment',
1396
+ contextId: threadContextId,
1397
+ },
1398
+ });
1399
+ const bobReadReply = yield dwn.processMessage(alice.did, bobRead.message);
1400
+ expect(bobReadReply.status.code).toBe(200);
1401
+ }));
1402
+ });
1403
+ // =========================================================================
1404
+ // Encryption + composition tests
1405
+ // =========================================================================
1406
+ describe('encryption with protocol composition', () => {
1407
+ let encryptionPrivateJwk;
1408
+ let encryptionRootKeyId;
1409
+ beforeAll(() => __awaiter(this, void 0, void 0, function* () {
1410
+ const { privateJwk } = yield Secp256k1.generateKeyPair();
1411
+ encryptionPrivateJwk = privateJwk;
1412
+ encryptionRootKeyId = 'did:example:alice#enc';
1413
+ }));
1414
+ it('should skip `$encryption` injection on `$ref` nodes but inject on their children (raw-key path)', () => __awaiter(this, void 0, void 0, function* () {
1415
+ const composingProtocol = {
1416
+ protocol: 'https://comments.example.com',
1417
+ published: true,
1418
+ uses: { threads: 'https://threads.example.com' },
1419
+ types: {
1420
+ comment: { schema: 'https://comments.example.com/schemas/comment', dataFormats: ['application/json'] },
1421
+ reaction: { schema: 'https://comments.example.com/schemas/reaction', dataFormats: ['application/json'] },
1422
+ },
1423
+ structure: {
1424
+ thread: {
1425
+ $ref: 'threads:thread',
1426
+ comment: {
1427
+ $actions: [{ who: 'anyone', can: ['create', 'read'] }],
1428
+ reaction: {
1429
+ $actions: [{ who: 'anyone', can: ['create', 'read'] }],
1430
+ },
1431
+ },
1432
+ },
1433
+ },
1434
+ };
1435
+ const result = yield Protocols.deriveAndInjectPublicEncryptionKeys(composingProtocol, encryptionRootKeyId, encryptionPrivateJwk);
1436
+ // $ref node must NOT have $encryption
1437
+ expect(result.structure.thread.$encryption).toBeUndefined();
1438
+ // Children of $ref node MUST have $encryption
1439
+ expect(result.structure.thread.comment.$encryption).toBeDefined();
1440
+ expect(result.structure.thread.comment.$encryption.rootKeyId).toBe(encryptionRootKeyId);
1441
+ expect(result.structure.thread.comment.$encryption.publicKeyJwk).toBeDefined();
1442
+ // Grandchild of $ref node
1443
+ expect(result.structure.thread.comment.reaction.$encryption).toBeDefined();
1444
+ expect(result.structure.thread.comment.reaction.$encryption.rootKeyId).toBe(encryptionRootKeyId);
1445
+ }));
1446
+ it('should skip `$encryption` injection on `$ref` nodes but inject on their children (callback path)', () => __awaiter(this, void 0, void 0, function* () {
1447
+ const composingProtocol = {
1448
+ protocol: 'https://comments.example.com',
1449
+ published: true,
1450
+ uses: { threads: 'https://threads.example.com' },
1451
+ types: {
1452
+ comment: { schema: 'https://comments.example.com/schemas/comment', dataFormats: ['application/json'] },
1453
+ reaction: { schema: 'https://comments.example.com/schemas/reaction', dataFormats: ['application/json'] },
1454
+ },
1455
+ structure: {
1456
+ thread: {
1457
+ $ref: 'threads:thread',
1458
+ comment: {
1459
+ $actions: [{ who: 'anyone', can: ['create', 'read'] }],
1460
+ reaction: {
1461
+ $actions: [{ who: 'anyone', can: ['create', 'read'] }],
1462
+ },
1463
+ },
1464
+ },
1465
+ },
1466
+ };
1467
+ const calledPaths = [];
1468
+ const keyDeriver = {
1469
+ rootKeyId: encryptionRootKeyId,
1470
+ derivationScheme: KeyDerivationScheme.ProtocolPath,
1471
+ derivePublicKey: (fullDerivationPath) => __awaiter(this, void 0, void 0, function* () {
1472
+ calledPaths.push([...fullDerivationPath]);
1473
+ const privateKeyBytes = Secp256k1.privateJwkToBytes(encryptionPrivateJwk);
1474
+ const derivedPrivateKeyBytes = yield HdKey.derivePrivateKeyBytes(privateKeyBytes, fullDerivationPath);
1475
+ const derivedPublicKeyBytes = yield Secp256k1.getPublicKey(derivedPrivateKeyBytes);
1476
+ return Secp256k1.publicKeyToJwk(derivedPublicKeyBytes);
1477
+ }),
1478
+ };
1479
+ const result = yield Protocols.deriveAndInjectPublicEncryptionKeys(composingProtocol, keyDeriver);
1480
+ // $ref node must NOT have $encryption
1481
+ expect(result.structure.thread.$encryption).toBeUndefined();
1482
+ // Children of $ref node MUST have $encryption
1483
+ expect(result.structure.thread.comment.$encryption).toBeDefined();
1484
+ expect(result.structure.thread.comment.reaction.$encryption).toBeDefined();
1485
+ // derivePublicKey should NOT have been called for the $ref node itself
1486
+ const threadPath = [KeyDerivationScheme.ProtocolPath, 'https://comments.example.com', 'thread'];
1487
+ expect(calledPaths).not.toContainEqual(threadPath);
1488
+ // derivePublicKey SHOULD have been called for children
1489
+ const commentPath = [KeyDerivationScheme.ProtocolPath, 'https://comments.example.com', 'thread', 'comment'];
1490
+ const reactionPath = [KeyDerivationScheme.ProtocolPath, 'https://comments.example.com', 'thread', 'comment', 'reaction'];
1491
+ expect(calledPaths).toContainEqual(commentPath);
1492
+ expect(calledPaths).toContainEqual(reactionPath);
1493
+ }));
1494
+ it('should produce identical $encryption for children across both overloads', () => __awaiter(this, void 0, void 0, function* () {
1495
+ const composingProtocol = {
1496
+ protocol: 'https://comments.example.com',
1497
+ published: true,
1498
+ uses: { threads: 'https://threads.example.com' },
1499
+ types: {
1500
+ comment: { schema: 'https://comments.example.com/schemas/comment', dataFormats: ['application/json'] },
1501
+ },
1502
+ structure: {
1503
+ thread: {
1504
+ $ref: 'threads:thread',
1505
+ comment: {},
1506
+ },
1507
+ },
1508
+ };
1509
+ // Raw-key path
1510
+ const resultA = yield Protocols.deriveAndInjectPublicEncryptionKeys(composingProtocol, encryptionRootKeyId, encryptionPrivateJwk);
1511
+ // Callback path
1512
+ const keyDeriver = {
1513
+ rootKeyId: encryptionRootKeyId,
1514
+ derivationScheme: KeyDerivationScheme.ProtocolPath,
1515
+ derivePublicKey: (fullDerivationPath) => __awaiter(this, void 0, void 0, function* () {
1516
+ const privateKeyBytes = Secp256k1.privateJwkToBytes(encryptionPrivateJwk);
1517
+ const derivedPrivateKeyBytes = yield HdKey.derivePrivateKeyBytes(privateKeyBytes, fullDerivationPath);
1518
+ const derivedPublicKeyBytes = yield Secp256k1.getPublicKey(derivedPrivateKeyBytes);
1519
+ return Secp256k1.publicKeyToJwk(derivedPublicKeyBytes);
1520
+ }),
1521
+ };
1522
+ const resultB = yield Protocols.deriveAndInjectPublicEncryptionKeys(composingProtocol, keyDeriver);
1523
+ // Both paths must skip $ref
1524
+ expect(resultA.structure.thread.$encryption).toBeUndefined();
1525
+ expect(resultB.structure.thread.$encryption).toBeUndefined();
1526
+ // Both paths must produce identical $encryption on children
1527
+ expect(resultA.structure.thread.comment.$encryption.publicKeyJwk).toEqual(resultB.structure.thread.comment.$encryption.publicKeyJwk);
1528
+ expect(resultA.structure.thread.comment.$encryption.rootKeyId).toBe(resultB.structure.thread.comment.$encryption.rootKeyId);
1529
+ }));
1530
+ it('should successfully install a composing protocol with encryption after $ref skip', () => __awaiter(this, void 0, void 0, function* () {
1531
+ // This test verifies the full pipeline: inject encryption keys → ProtocolsConfigure.create()
1532
+ // → validateRefNode() passes because $ref node has no $encryption.
1533
+ const alice = yield TestDataGenerator.generatePersona();
1534
+ TestStubGenerator.stubDidResolver(didResolver, [alice]);
1535
+ // Install the threads protocol first (required dependency)
1536
+ const threadsConfigure = yield ProtocolsConfigure.create({
1537
+ definition: threadsProtocol,
1538
+ signer: Jws.createSigner(alice),
1539
+ });
1540
+ const threadsInstallReply = yield dwn.processMessage(alice.did, threadsConfigure.message);
1541
+ expect(threadsInstallReply.status.code).toBe(202);
1542
+ // Inject encryption keys into the composing protocol
1543
+ const encryptedComments = yield Protocols.deriveAndInjectPublicEncryptionKeys(commentsProtocol, alice.keyId, alice.keyPair.privateJwk);
1544
+ // $ref node should not have $encryption
1545
+ expect(encryptedComments.structure.thread.$encryption).toBeUndefined();
1546
+ // Children should have $encryption
1547
+ expect(encryptedComments.structure.thread.comment.$encryption).toBeDefined();
1548
+ // ProtocolsConfigure.create() should NOT throw — validateRefNode() will pass
1549
+ // because $ref node has no forbidden directives
1550
+ const commentsConfigure = yield ProtocolsConfigure.create({
1551
+ definition: encryptedComments,
1552
+ signer: Jws.createSigner(alice),
1553
+ });
1554
+ // Install should succeed
1555
+ const commentsInstallReply = yield dwn.processMessage(alice.did, commentsConfigure.message);
1556
+ expect(commentsInstallReply.status.code).toBe(202);
1557
+ }));
1558
+ it('should encrypt and decrypt a child record written under a `$ref` parent', () => __awaiter(this, void 0, void 0, function* () {
1559
+ // Full round-trip: install both protocols → write parent → write encrypted child → read and decrypt
1560
+ const alice = yield TestDataGenerator.generatePersona();
1561
+ TestStubGenerator.stubDidResolver(didResolver, [alice]);
1562
+ // 1. Install the threads protocol (parent)
1563
+ const threadsConfigure = yield ProtocolsConfigure.create({
1564
+ definition: threadsProtocol,
1565
+ signer: Jws.createSigner(alice),
1566
+ });
1567
+ const threadsReply = yield dwn.processMessage(alice.did, threadsConfigure.message);
1568
+ expect(threadsReply.status.code).toBe(202);
1569
+ // 2. Install the comments protocol with encryption keys
1570
+ const encryptedComments = yield Protocols.deriveAndInjectPublicEncryptionKeys(commentsProtocol, alice.keyId, alice.keyPair.privateJwk);
1571
+ const commentsConfigure = yield ProtocolsConfigure.create({
1572
+ definition: encryptedComments,
1573
+ signer: Jws.createSigner(alice),
1574
+ });
1575
+ const commentsReply = yield dwn.processMessage(alice.did, commentsConfigure.message);
1576
+ expect(commentsReply.status.code).toBe(202);
1577
+ // 3. Write a thread record (in the threads protocol — the $ref parent)
1578
+ const threadWrite = yield TestDataGenerator.generateRecordsWrite({
1579
+ author: alice,
1580
+ protocol: threadsProtocol.protocol,
1581
+ protocolPath: 'thread',
1582
+ schema: 'https://threads.example.com/schemas/thread',
1583
+ dataFormat: 'application/json',
1584
+ });
1585
+ const threadWriteReply = yield dwn.processMessage(alice.did, threadWrite.message, { dataStream: threadWrite.dataStream });
1586
+ expect(threadWriteReply.status.code).toBe(202);
1587
+ const threadContextId = threadWrite.message.contextId;
1588
+ // 4. Write an encrypted comment (child of $ref parent, in the comments protocol)
1589
+ const plaintext = 'This is a secret comment';
1590
+ const plaintextBytes = Encoder.stringToBytes(plaintext);
1591
+ const encryptedComment = yield TestDataGenerator.generateProtocolEncryptedRecordsWrite({
1592
+ plaintextBytes,
1593
+ author: alice,
1594
+ protocolDefinition: encryptedComments,
1595
+ protocolPath: 'thread/comment',
1596
+ protocolParentContextId: threadContextId,
1597
+ encryptSymmetricKeyWithProtocolPathDerivedKey: true,
1598
+ encryptSymmetricKeyWithProtocolContextDerivedKey: false,
1599
+ });
1600
+ const commentWriteReply = yield dwn.processMessage(alice.did, encryptedComment.message, { dataStream: DataStream.fromBytes(encryptedComment.encryptedDataBytes) });
1601
+ expect(commentWriteReply.status.code).toBe(202);
1602
+ // 5. Read the encrypted comment back
1603
+ const readMessage = yield RecordsRead.create({
1604
+ signer: Jws.createSigner(alice),
1605
+ filter: { recordId: encryptedComment.message.recordId },
1606
+ });
1607
+ const readReply = yield dwn.processMessage(alice.did, readMessage.message);
1608
+ expect(readReply.status.code).toBe(200);
1609
+ // 6. Decrypt using the composing protocol's key hierarchy.
1610
+ // The key derivation path is [protocolPath, commentsProtocol.protocol, 'thread', 'comment']
1611
+ // — note this uses the COMPOSING protocol's URI, not the threads protocol's URI,
1612
+ // because the comment record's descriptor.protocol is the comments protocol.
1613
+ const rootKey = {
1614
+ rootKeyId: alice.keyId,
1615
+ derivationScheme: KeyDerivationScheme.ProtocolPath,
1616
+ derivedPrivateKey: alice.keyPair.privateJwk,
1617
+ };
1618
+ const decryptedStream = yield Records.decrypt(readReply.entry.recordsWrite, rootKey, readReply.entry.data);
1619
+ const decryptedBytes = yield DataStream.toBytes(decryptedStream);
1620
+ expect(Encoder.bytesToString(decryptedBytes)).toBe(plaintext);
1621
+ }));
1622
+ it('should not inject `$encryption` on the original protocol definition (immutability)', () => __awaiter(this, void 0, void 0, function* () {
1623
+ const composingProtocol = {
1624
+ protocol: 'https://comments.example.com',
1625
+ published: true,
1626
+ uses: { threads: 'https://threads.example.com' },
1627
+ types: {
1628
+ comment: { schema: 'https://comments.example.com/schemas/comment', dataFormats: ['application/json'] },
1629
+ },
1630
+ structure: {
1631
+ thread: {
1632
+ $ref: 'threads:thread',
1633
+ comment: {},
1634
+ },
1635
+ },
1636
+ };
1637
+ yield Protocols.deriveAndInjectPublicEncryptionKeys(composingProtocol, encryptionRootKeyId, encryptionPrivateJwk);
1638
+ // Original must be unmodified
1639
+ expect(composingProtocol.structure.thread.$encryption).toBeUndefined();
1640
+ expect(composingProtocol.structure.thread.comment.$encryption).toBeUndefined();
1641
+ }));
1642
+ });
1643
+ });
1644
+ }
1645
+ //# sourceMappingURL=protocol-composition.spec.js.map