@enbox/dwn-sdk-js 0.3.9 → 0.4.1

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