@enbox/dwn-sdk-js 0.4.0 → 0.4.2

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 (522) hide show
  1. package/README.md +4 -4
  2. package/dist/browser.mjs +3 -10
  3. package/dist/browser.mjs.map +4 -4
  4. package/dist/esm/generated/precompiled-validators.js +799 -885
  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 +12 -4
  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 -45
  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 +34 -89
  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 +123 -28
  28. package/dist/esm/src/core/replication-apply.js.map +1 -1
  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 +165 -132
  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 +6 -7
  64. package/dist/esm/src/index.js.map +1 -1
  65. package/dist/esm/src/interfaces/{messages-sync.js → messages-query.js} +21 -15
  66. package/dist/esm/src/interfaces/messages-query.js.map +1 -0
  67. package/dist/esm/src/interfaces/protocols-configure.js +12 -9
  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/protocol-tags.js +262 -0
  99. package/dist/esm/src/utils/protocol-tags.js.map +1 -0
  100. package/dist/esm/src/utils/record-limit-occupancy.js +244 -0
  101. package/dist/esm/src/utils/record-limit-occupancy.js.map +1 -0
  102. package/dist/esm/src/utils/records.js +50 -14
  103. package/dist/esm/src/utils/records.js.map +1 -1
  104. package/dist/esm/src/utils/replication.js +85 -0
  105. package/dist/esm/src/utils/replication.js.map +1 -0
  106. package/dist/esm/tests/core/grant-authorization.spec.js +4 -4
  107. package/dist/esm/tests/core/grant-authorization.spec.js.map +1 -1
  108. package/dist/esm/tests/core/process-message-parity.spec.js +222 -0
  109. package/dist/esm/tests/core/process-message-parity.spec.js.map +1 -0
  110. package/dist/esm/tests/core/protocol-authorization.spec.js +5 -2
  111. package/dist/esm/tests/core/protocol-authorization.spec.js.map +1 -1
  112. package/dist/esm/tests/core/records-grant-authorization.spec.js +5 -5
  113. package/dist/esm/tests/core/records-grant-authorization.spec.js.map +1 -1
  114. package/dist/esm/tests/core/replication-apply.spec.js +55 -1
  115. package/dist/esm/tests/core/replication-apply.spec.js.map +1 -1
  116. package/dist/esm/tests/core/replication-replay-property.spec.js +350 -0
  117. package/dist/esm/tests/core/replication-replay-property.spec.js.map +1 -0
  118. package/dist/esm/tests/core/validation-read-closure.spec.js +469 -0
  119. package/dist/esm/tests/core/validation-read-closure.spec.js.map +1 -0
  120. package/dist/esm/tests/core/validation-state-reader.spec.js +716 -0
  121. package/dist/esm/tests/core/validation-state-reader.spec.js.map +1 -0
  122. package/dist/esm/tests/durable-event-log.spec.js +373 -0
  123. package/dist/esm/tests/durable-event-log.spec.js.map +1 -0
  124. package/dist/esm/tests/dwn.spec.js +504 -35
  125. package/dist/esm/tests/dwn.spec.js.map +1 -1
  126. package/dist/esm/tests/features/author-delegated-grant.spec.js +9 -6
  127. package/dist/esm/tests/features/author-delegated-grant.spec.js.map +1 -1
  128. package/dist/esm/tests/features/owner-delegated-grant.spec.js +1 -4
  129. package/dist/esm/tests/features/owner-delegated-grant.spec.js.map +1 -1
  130. package/dist/esm/tests/features/owner-signature.spec.js +1 -4
  131. package/dist/esm/tests/features/owner-signature.spec.js.map +1 -1
  132. package/dist/esm/tests/features/permissions.spec.js +165 -4
  133. package/dist/esm/tests/features/permissions.spec.js.map +1 -1
  134. package/dist/esm/tests/features/protocol-composition.spec.js +8 -11
  135. package/dist/esm/tests/features/protocol-composition.spec.js.map +1 -1
  136. package/dist/esm/tests/features/protocol-create-action.spec.js +1 -4
  137. package/dist/esm/tests/features/protocol-create-action.spec.js.map +1 -1
  138. package/dist/esm/tests/features/protocol-delete-action.spec.js +3 -5
  139. package/dist/esm/tests/features/protocol-delete-action.spec.js.map +1 -1
  140. package/dist/esm/tests/features/protocol-update-action.spec.js +3 -6
  141. package/dist/esm/tests/features/protocol-update-action.spec.js.map +1 -1
  142. package/dist/esm/tests/features/records-delivery.spec.js +1 -4
  143. package/dist/esm/tests/features/records-delivery.spec.js.map +1 -1
  144. package/dist/esm/tests/features/records-immutable.spec.js +1 -4
  145. package/dist/esm/tests/features/records-immutable.spec.js.map +1 -1
  146. package/dist/esm/tests/features/records-nested-query-scope.spec.js +281 -0
  147. package/dist/esm/tests/features/records-nested-query-scope.spec.js.map +1 -0
  148. package/dist/esm/tests/features/records-prune-cross-protocol.spec.js +3 -7
  149. package/dist/esm/tests/features/records-prune-cross-protocol.spec.js.map +1 -1
  150. package/dist/esm/tests/features/records-prune.spec.js +11 -22
  151. package/dist/esm/tests/features/records-prune.spec.js.map +1 -1
  152. package/dist/esm/tests/features/records-record-limit.spec.js +441 -231
  153. package/dist/esm/tests/features/records-record-limit.spec.js.map +1 -1
  154. package/dist/esm/tests/features/records-squash.spec.js +6 -4
  155. package/dist/esm/tests/features/records-squash.spec.js.map +1 -1
  156. package/dist/esm/tests/features/records-tags.spec.js +1 -4
  157. package/dist/esm/tests/features/records-tags.spec.js.map +1 -1
  158. package/dist/esm/tests/features/resumable-tasks.spec.js +3 -5
  159. package/dist/esm/tests/features/resumable-tasks.spec.js.map +1 -1
  160. package/dist/esm/tests/fuzz/message-store.fuzz.spec.js +1 -2
  161. package/dist/esm/tests/fuzz/message-store.fuzz.spec.js.map +1 -1
  162. package/dist/esm/tests/fuzz/process-message.fuzz.spec.js +2 -4
  163. package/dist/esm/tests/fuzz/process-message.fuzz.spec.js.map +1 -1
  164. package/dist/esm/tests/fuzz/schema-validation.fuzz.spec.js +1 -1
  165. package/dist/esm/tests/fuzz/schema-validation.fuzz.spec.js.map +1 -1
  166. package/dist/esm/tests/handlers/messages-query.spec.js +246 -0
  167. package/dist/esm/tests/handlers/messages-query.spec.js.map +1 -0
  168. package/dist/esm/tests/handlers/messages-read.spec.js +2 -5
  169. package/dist/esm/tests/handlers/messages-read.spec.js.map +1 -1
  170. package/dist/esm/tests/handlers/messages-subscribe.spec.js +3 -14
  171. package/dist/esm/tests/handlers/messages-subscribe.spec.js.map +1 -1
  172. package/dist/esm/tests/handlers/protocols-configure.spec.js +27 -26
  173. package/dist/esm/tests/handlers/protocols-configure.spec.js.map +1 -1
  174. package/dist/esm/tests/handlers/protocols-query.spec.js +1 -4
  175. package/dist/esm/tests/handlers/protocols-query.spec.js.map +1 -1
  176. package/dist/esm/tests/handlers/records-count.spec.js +1 -4
  177. package/dist/esm/tests/handlers/records-count.spec.js.map +1 -1
  178. package/dist/esm/tests/handlers/records-delete.spec.js +312 -30
  179. package/dist/esm/tests/handlers/records-delete.spec.js.map +1 -1
  180. package/dist/esm/tests/handlers/records-query.spec.js +32 -9
  181. package/dist/esm/tests/handlers/records-query.spec.js.map +1 -1
  182. package/dist/esm/tests/handlers/records-read.spec.js +4 -4
  183. package/dist/esm/tests/handlers/records-read.spec.js.map +1 -1
  184. package/dist/esm/tests/handlers/records-subscribe.spec.js +33 -14
  185. package/dist/esm/tests/handlers/records-subscribe.spec.js.map +1 -1
  186. package/dist/esm/tests/handlers/records-write.spec.js +82 -36
  187. package/dist/esm/tests/handlers/records-write.spec.js.map +1 -1
  188. package/dist/esm/tests/interfaces/records-delete.spec.js +69 -2
  189. package/dist/esm/tests/interfaces/records-delete.spec.js.map +1 -1
  190. package/dist/esm/tests/interfaces/records-write.spec.js +4 -3
  191. package/dist/esm/tests/interfaces/records-write.spec.js.map +1 -1
  192. package/dist/esm/tests/protocols/permissions.spec.js +55 -6
  193. package/dist/esm/tests/protocols/permissions.spec.js.map +1 -1
  194. package/dist/esm/tests/scenarios/aggregator.spec.js +1 -4
  195. package/dist/esm/tests/scenarios/aggregator.spec.js.map +1 -1
  196. package/dist/esm/tests/scenarios/deleted-record.spec.js +1 -4
  197. package/dist/esm/tests/scenarios/deleted-record.spec.js.map +1 -1
  198. package/dist/esm/tests/scenarios/end-to-end-tests.spec.js +1 -4
  199. package/dist/esm/tests/scenarios/end-to-end-tests.spec.js.map +1 -1
  200. package/dist/esm/tests/scenarios/nested-roles.spec.js +1 -4
  201. package/dist/esm/tests/scenarios/nested-roles.spec.js.map +1 -1
  202. package/dist/esm/tests/scenarios/subscriptions.spec.js +1 -4
  203. package/dist/esm/tests/scenarios/subscriptions.spec.js.map +1 -1
  204. package/dist/esm/tests/store/message-store-level.spec.js +361 -5
  205. package/dist/esm/tests/store/message-store-level.spec.js.map +1 -1
  206. package/dist/esm/tests/store/message-store.spec.js +60 -0
  207. package/dist/esm/tests/store/message-store.spec.js.map +1 -1
  208. package/dist/esm/tests/test-event-stream.js +7 -3
  209. package/dist/esm/tests/test-event-stream.js.map +1 -1
  210. package/dist/esm/tests/test-stores.js +19 -9
  211. package/dist/esm/tests/test-stores.js.map +1 -1
  212. package/dist/esm/tests/test-suite.js +4 -2
  213. package/dist/esm/tests/test-suite.js.map +1 -1
  214. package/dist/esm/tests/utils/protocol-tags.spec.js +96 -0
  215. package/dist/esm/tests/utils/protocol-tags.spec.js.map +1 -0
  216. package/dist/esm/tests/utils/test-data-generator.js +25 -0
  217. package/dist/esm/tests/utils/test-data-generator.js.map +1 -1
  218. package/dist/esm/tests/utils/test-stub-generator.js.map +1 -1
  219. package/dist/esm/tests/utils/test-validation-state-reader.js +16 -0
  220. package/dist/esm/tests/utils/test-validation-state-reader.js.map +1 -0
  221. package/dist/types/generated/precompiled-validators.d.ts +6 -6
  222. package/dist/types/generated/precompiled-validators.d.ts.map +1 -1
  223. package/dist/types/src/core/core-protocol.d.ts +3 -3
  224. package/dist/types/src/core/core-protocol.d.ts.map +1 -1
  225. package/dist/types/src/core/dwn-constant.d.ts +5 -0
  226. package/dist/types/src/core/dwn-constant.d.ts.map +1 -1
  227. package/dist/types/src/core/dwn-error.d.ts +12 -4
  228. package/dist/types/src/core/dwn-error.d.ts.map +1 -1
  229. package/dist/types/src/core/grant-authorization.d.ts +5 -5
  230. package/dist/types/src/core/grant-authorization.d.ts.map +1 -1
  231. package/dist/types/src/core/message-reply.d.ts +5 -4
  232. package/dist/types/src/core/message-reply.d.ts.map +1 -1
  233. package/dist/types/src/core/messages-grant-authorization.d.ts +12 -14
  234. package/dist/types/src/core/messages-grant-authorization.d.ts.map +1 -1
  235. package/dist/types/src/core/protocol-authorization-action.d.ts +4 -5
  236. package/dist/types/src/core/protocol-authorization-action.d.ts.map +1 -1
  237. package/dist/types/src/core/protocol-authorization-validation.d.ts +14 -17
  238. package/dist/types/src/core/protocol-authorization-validation.d.ts.map +1 -1
  239. package/dist/types/src/core/protocol-authorization.d.ts +8 -33
  240. package/dist/types/src/core/protocol-authorization.d.ts.map +1 -1
  241. package/dist/types/src/core/protocols-grant-authorization.d.ts +4 -4
  242. package/dist/types/src/core/protocols-grant-authorization.d.ts.map +1 -1
  243. package/dist/types/src/core/recording-validation-state-reader.d.ts +75 -0
  244. package/dist/types/src/core/recording-validation-state-reader.d.ts.map +1 -0
  245. package/dist/types/src/core/records-grant-authorization.d.ts +8 -8
  246. package/dist/types/src/core/records-grant-authorization.d.ts.map +1 -1
  247. package/dist/types/src/core/replication-apply.d.ts +36 -0
  248. package/dist/types/src/core/replication-apply.d.ts.map +1 -1
  249. package/dist/types/src/core/resumable-task-manager.d.ts +1 -1
  250. package/dist/types/src/core/resumable-task-manager.d.ts.map +1 -1
  251. package/dist/types/src/core/validation-state-reader.d.ts +79 -0
  252. package/dist/types/src/core/validation-state-reader.d.ts.map +1 -0
  253. package/dist/types/src/dwn.d.ts +33 -20
  254. package/dist/types/src/dwn.d.ts.map +1 -1
  255. package/dist/types/src/enums/dwn-interface-method.d.ts +0 -1
  256. package/dist/types/src/enums/dwn-interface-method.d.ts.map +1 -1
  257. package/dist/types/src/event-stream/durable-event-log.d.ts +69 -0
  258. package/dist/types/src/event-stream/durable-event-log.d.ts.map +1 -0
  259. package/dist/types/src/event-stream/event-emitter-wake-publisher.d.ts +13 -0
  260. package/dist/types/src/event-stream/event-emitter-wake-publisher.d.ts.map +1 -0
  261. package/dist/types/src/handlers/messages-query.d.ts +20 -0
  262. package/dist/types/src/handlers/messages-query.d.ts.map +1 -0
  263. package/dist/types/src/handlers/messages-read.d.ts +1 -1
  264. package/dist/types/src/handlers/messages-read.d.ts.map +1 -1
  265. package/dist/types/src/handlers/messages-subscribe.d.ts.map +1 -1
  266. package/dist/types/src/handlers/protocols-configure.d.ts +0 -5
  267. package/dist/types/src/handlers/protocols-configure.d.ts.map +1 -1
  268. package/dist/types/src/handlers/records-count.d.ts +2 -1
  269. package/dist/types/src/handlers/records-count.d.ts.map +1 -1
  270. package/dist/types/src/handlers/records-delete.d.ts +2 -2
  271. package/dist/types/src/handlers/records-delete.d.ts.map +1 -1
  272. package/dist/types/src/handlers/records-query.d.ts +1 -1
  273. package/dist/types/src/handlers/records-query.d.ts.map +1 -1
  274. package/dist/types/src/handlers/records-read.d.ts +2 -1
  275. package/dist/types/src/handlers/records-read.d.ts.map +1 -1
  276. package/dist/types/src/handlers/records-subscribe.d.ts +4 -5
  277. package/dist/types/src/handlers/records-subscribe.d.ts.map +1 -1
  278. package/dist/types/src/handlers/records-write.d.ts +3 -11
  279. package/dist/types/src/handlers/records-write.d.ts.map +1 -1
  280. package/dist/types/src/index.d.ts +14 -16
  281. package/dist/types/src/index.d.ts.map +1 -1
  282. package/dist/types/src/interfaces/messages-query.d.ts +23 -0
  283. package/dist/types/src/interfaces/messages-query.d.ts.map +1 -0
  284. package/dist/types/src/interfaces/protocols-configure.d.ts +3 -3
  285. package/dist/types/src/interfaces/protocols-configure.d.ts.map +1 -1
  286. package/dist/types/src/interfaces/protocols-query.d.ts +2 -2
  287. package/dist/types/src/interfaces/protocols-query.d.ts.map +1 -1
  288. package/dist/types/src/interfaces/records-count.d.ts +3 -3
  289. package/dist/types/src/interfaces/records-count.d.ts.map +1 -1
  290. package/dist/types/src/interfaces/records-delete.d.ts +11 -3
  291. package/dist/types/src/interfaces/records-delete.d.ts.map +1 -1
  292. package/dist/types/src/interfaces/records-query.d.ts +3 -3
  293. package/dist/types/src/interfaces/records-query.d.ts.map +1 -1
  294. package/dist/types/src/interfaces/records-read.d.ts +3 -3
  295. package/dist/types/src/interfaces/records-read.d.ts.map +1 -1
  296. package/dist/types/src/interfaces/records-subscribe.d.ts +3 -3
  297. package/dist/types/src/interfaces/records-subscribe.d.ts.map +1 -1
  298. package/dist/types/src/interfaces/records-write.d.ts +15 -7
  299. package/dist/types/src/interfaces/records-write.d.ts.map +1 -1
  300. package/dist/types/src/protocols/permissions.d.ts +9 -12
  301. package/dist/types/src/protocols/permissions.d.ts.map +1 -1
  302. package/dist/types/src/store/index-level.d.ts +10 -1
  303. package/dist/types/src/store/index-level.d.ts.map +1 -1
  304. package/dist/types/src/store/level-wrapper.d.ts +5 -0
  305. package/dist/types/src/store/level-wrapper.d.ts.map +1 -1
  306. package/dist/types/src/store/message-store-level.d.ts +94 -14
  307. package/dist/types/src/store/message-store-level.d.ts.map +1 -1
  308. package/dist/types/src/store/storage-controller.d.ts +17 -14
  309. package/dist/types/src/store/storage-controller.d.ts.map +1 -1
  310. package/dist/types/src/types/message-store.d.ts +29 -1
  311. package/dist/types/src/types/message-store.d.ts.map +1 -1
  312. package/dist/types/src/types/message-types.d.ts +2 -0
  313. package/dist/types/src/types/message-types.d.ts.map +1 -1
  314. package/dist/types/src/types/messages-types.d.ts +21 -37
  315. package/dist/types/src/types/messages-types.d.ts.map +1 -1
  316. package/dist/types/src/types/method-handler.d.ts +2 -2
  317. package/dist/types/src/types/method-handler.d.ts.map +1 -1
  318. package/dist/types/src/types/permission-types.d.ts +1 -1
  319. package/dist/types/src/types/subscriptions.d.ts +50 -39
  320. package/dist/types/src/types/subscriptions.d.ts.map +1 -1
  321. package/dist/types/src/types/validation-state-reader.d.ts +116 -0
  322. package/dist/types/src/types/validation-state-reader.d.ts.map +1 -0
  323. package/dist/types/src/utils/messages.d.ts +10 -0
  324. package/dist/types/src/utils/messages.d.ts.map +1 -1
  325. package/dist/types/src/utils/protocol-tags.d.ts +15 -0
  326. package/dist/types/src/utils/protocol-tags.d.ts.map +1 -0
  327. package/dist/types/src/utils/record-limit-occupancy.d.ts +40 -0
  328. package/dist/types/src/utils/record-limit-occupancy.d.ts.map +1 -0
  329. package/dist/types/src/utils/records.d.ts +25 -3
  330. package/dist/types/src/utils/records.d.ts.map +1 -1
  331. package/dist/types/src/utils/replication.d.ts +22 -0
  332. package/dist/types/src/utils/replication.d.ts.map +1 -0
  333. package/dist/types/tests/core/process-message-parity.spec.d.ts +2 -0
  334. package/dist/types/tests/core/process-message-parity.spec.d.ts.map +1 -0
  335. package/dist/types/tests/core/replication-replay-property.spec.d.ts +2 -0
  336. package/dist/types/tests/core/replication-replay-property.spec.d.ts.map +1 -0
  337. package/dist/types/tests/core/validation-read-closure.spec.d.ts +2 -0
  338. package/dist/types/tests/core/validation-read-closure.spec.d.ts.map +1 -0
  339. package/dist/types/tests/core/validation-state-reader.spec.d.ts +2 -0
  340. package/dist/types/tests/core/validation-state-reader.spec.d.ts.map +1 -0
  341. package/dist/types/tests/durable-event-log.spec.d.ts +2 -0
  342. package/dist/types/tests/durable-event-log.spec.d.ts.map +1 -0
  343. package/dist/types/tests/dwn.spec.d.ts.map +1 -1
  344. package/dist/types/tests/features/author-delegated-grant.spec.d.ts.map +1 -1
  345. package/dist/types/tests/features/owner-delegated-grant.spec.d.ts.map +1 -1
  346. package/dist/types/tests/features/owner-signature.spec.d.ts.map +1 -1
  347. package/dist/types/tests/features/permissions.spec.d.ts.map +1 -1
  348. package/dist/types/tests/features/protocol-composition.spec.d.ts.map +1 -1
  349. package/dist/types/tests/features/protocol-create-action.spec.d.ts.map +1 -1
  350. package/dist/types/tests/features/protocol-delete-action.spec.d.ts.map +1 -1
  351. package/dist/types/tests/features/protocol-update-action.spec.d.ts.map +1 -1
  352. package/dist/types/tests/features/records-delivery.spec.d.ts.map +1 -1
  353. package/dist/types/tests/features/records-immutable.spec.d.ts.map +1 -1
  354. package/dist/types/tests/features/records-nested-query-scope.spec.d.ts +2 -0
  355. package/dist/types/tests/features/records-nested-query-scope.spec.d.ts.map +1 -0
  356. package/dist/types/tests/features/records-prune-cross-protocol.spec.d.ts.map +1 -1
  357. package/dist/types/tests/features/records-prune.spec.d.ts.map +1 -1
  358. package/dist/types/tests/features/records-record-limit.spec.d.ts.map +1 -1
  359. package/dist/types/tests/features/records-squash.spec.d.ts.map +1 -1
  360. package/dist/types/tests/features/records-tags.spec.d.ts.map +1 -1
  361. package/dist/types/tests/features/resumable-tasks.spec.d.ts.map +1 -1
  362. package/dist/types/tests/handlers/messages-query.spec.d.ts +2 -0
  363. package/dist/types/tests/handlers/messages-query.spec.d.ts.map +1 -0
  364. package/dist/types/tests/handlers/messages-read.spec.d.ts.map +1 -1
  365. package/dist/types/tests/handlers/messages-subscribe.spec.d.ts.map +1 -1
  366. package/dist/types/tests/handlers/protocols-configure.spec.d.ts.map +1 -1
  367. package/dist/types/tests/handlers/protocols-query.spec.d.ts.map +1 -1
  368. package/dist/types/tests/handlers/records-count.spec.d.ts.map +1 -1
  369. package/dist/types/tests/handlers/records-delete.spec.d.ts.map +1 -1
  370. package/dist/types/tests/handlers/records-query.spec.d.ts.map +1 -1
  371. package/dist/types/tests/handlers/records-read.spec.d.ts.map +1 -1
  372. package/dist/types/tests/handlers/records-subscribe.spec.d.ts.map +1 -1
  373. package/dist/types/tests/handlers/records-write.spec.d.ts.map +1 -1
  374. package/dist/types/tests/scenarios/deleted-record.spec.d.ts.map +1 -1
  375. package/dist/types/tests/scenarios/end-to-end-tests.spec.d.ts.map +1 -1
  376. package/dist/types/tests/scenarios/nested-roles.spec.d.ts.map +1 -1
  377. package/dist/types/tests/scenarios/subscriptions.spec.d.ts.map +1 -1
  378. package/dist/types/tests/store/message-store.spec.d.ts.map +1 -1
  379. package/dist/types/tests/test-event-stream.d.ts +1 -1
  380. package/dist/types/tests/test-event-stream.d.ts.map +1 -1
  381. package/dist/types/tests/test-stores.d.ts +5 -4
  382. package/dist/types/tests/test-stores.d.ts.map +1 -1
  383. package/dist/types/tests/test-suite.d.ts +1 -2
  384. package/dist/types/tests/test-suite.d.ts.map +1 -1
  385. package/dist/types/tests/utils/protocol-tags.spec.d.ts +2 -0
  386. package/dist/types/tests/utils/protocol-tags.spec.d.ts.map +1 -0
  387. package/dist/types/tests/utils/test-data-generator.d.ts +20 -1
  388. package/dist/types/tests/utils/test-data-generator.d.ts.map +1 -1
  389. package/dist/types/tests/utils/test-validation-state-reader.d.ts +15 -0
  390. package/dist/types/tests/utils/test-validation-state-reader.d.ts.map +1 -0
  391. package/package.json +2 -2
  392. package/src/core/core-protocol.ts +3 -3
  393. package/src/core/dwn-constant.ts +7 -1
  394. package/src/core/dwn-error.ts +12 -4
  395. package/src/core/grant-authorization.ts +11 -20
  396. package/src/core/message-reply.ts +6 -5
  397. package/src/core/messages-grant-authorization.ts +37 -70
  398. package/src/core/protocol-authorization-action.ts +29 -38
  399. package/src/core/protocol-authorization-validation.ts +47 -121
  400. package/src/core/protocol-authorization.ts +56 -202
  401. package/src/core/protocols-grant-authorization.ts +9 -9
  402. package/src/core/recording-validation-state-reader.ts +130 -0
  403. package/src/core/records-grant-authorization.ts +16 -16
  404. package/src/core/replication-apply.ts +172 -32
  405. package/src/core/resumable-task-manager.ts +10 -8
  406. package/src/core/validation-state-reader.ts +350 -0
  407. package/src/dwn.ts +285 -192
  408. package/src/enums/dwn-interface-method.ts +0 -1
  409. package/src/event-stream/durable-event-log.ts +509 -0
  410. package/src/event-stream/event-emitter-wake-publisher.ts +34 -0
  411. package/src/handlers/messages-query.ts +203 -0
  412. package/src/handlers/messages-read.ts +9 -10
  413. package/src/handlers/messages-subscribe.ts +12 -13
  414. package/src/handlers/protocols-configure.ts +37 -58
  415. package/src/handlers/protocols-query.ts +1 -1
  416. package/src/handlers/records-count.ts +24 -17
  417. package/src/handlers/records-delete.ts +29 -27
  418. package/src/handlers/records-query.ts +38 -17
  419. package/src/handlers/records-read.ts +63 -50
  420. package/src/handlers/records-subscribe.ts +132 -19
  421. package/src/handlers/records-write.ts +77 -168
  422. package/src/index.ts +14 -17
  423. package/src/interfaces/messages-query.ts +70 -0
  424. package/src/interfaces/protocols-configure.ts +20 -10
  425. package/src/interfaces/protocols-query.ts +4 -5
  426. package/src/interfaces/records-count.ts +9 -4
  427. package/src/interfaces/records-delete.ts +25 -5
  428. package/src/interfaces/records-query.ts +9 -4
  429. package/src/interfaces/records-read.ts +4 -4
  430. package/src/interfaces/records-subscribe.ts +9 -4
  431. package/src/interfaces/records-write.ts +41 -13
  432. package/src/protocols/permissions.ts +32 -52
  433. package/src/store/index-level.ts +30 -9
  434. package/src/store/level-wrapper.ts +9 -1
  435. package/src/store/message-store-level.ts +757 -47
  436. package/src/store/storage-controller.ts +74 -63
  437. package/src/types/message-store.ts +45 -2
  438. package/src/types/message-types.ts +3 -1
  439. package/src/types/messages-types.ts +26 -45
  440. package/src/types/method-handler.ts +3 -3
  441. package/src/types/permission-types.ts +1 -1
  442. package/src/types/subscriptions.ts +53 -42
  443. package/src/types/validation-state-reader.ts +127 -0
  444. package/src/utils/messages.ts +25 -1
  445. package/src/utils/protocol-tags.ts +366 -0
  446. package/src/utils/record-limit-occupancy.ts +377 -0
  447. package/src/utils/records.ts +69 -13
  448. package/src/utils/replication.ts +122 -0
  449. package/dist/esm/src/core/record-chain.js +0 -64
  450. package/dist/esm/src/core/record-chain.js.map +0 -1
  451. package/dist/esm/src/event-stream/event-emitter-event-log.js +0 -334
  452. package/dist/esm/src/event-stream/event-emitter-event-log.js.map +0 -1
  453. package/dist/esm/src/handlers/messages-sync.js +0 -278
  454. package/dist/esm/src/handlers/messages-sync.js.map +0 -1
  455. package/dist/esm/src/interfaces/messages-sync.js.map +0 -1
  456. package/dist/esm/src/smt/smt-store-level.js +0 -103
  457. package/dist/esm/src/smt/smt-store-level.js.map +0 -1
  458. package/dist/esm/src/smt/smt-store-memory.js +0 -41
  459. package/dist/esm/src/smt/smt-store-memory.js.map +0 -1
  460. package/dist/esm/src/smt/smt-utils.js +0 -129
  461. package/dist/esm/src/smt/smt-utils.js.map +0 -1
  462. package/dist/esm/src/smt/sparse-merkle-tree.js +0 -577
  463. package/dist/esm/src/smt/sparse-merkle-tree.js.map +0 -1
  464. package/dist/esm/src/state-index/state-index-level.js +0 -191
  465. package/dist/esm/src/state-index/state-index-level.js.map +0 -1
  466. package/dist/esm/src/types/smt-types.js +0 -5
  467. package/dist/esm/src/types/smt-types.js.map +0 -1
  468. package/dist/esm/src/types/state-index.js +0 -2
  469. package/dist/esm/src/types/state-index.js.map +0 -1
  470. package/dist/esm/tests/event-emitter-event-log.spec.js +0 -499
  471. package/dist/esm/tests/event-emitter-event-log.spec.js.map +0 -1
  472. package/dist/esm/tests/handlers/messages-sync.spec.js +0 -1088
  473. package/dist/esm/tests/handlers/messages-sync.spec.js.map +0 -1
  474. package/dist/esm/tests/smt/smt-store-level.spec.js +0 -132
  475. package/dist/esm/tests/smt/smt-store-level.spec.js.map +0 -1
  476. package/dist/esm/tests/smt/sparse-merkle-tree.spec.js +0 -732
  477. package/dist/esm/tests/smt/sparse-merkle-tree.spec.js.map +0 -1
  478. package/dist/esm/tests/state-index/state-index-level.spec.js +0 -245
  479. package/dist/esm/tests/state-index/state-index-level.spec.js.map +0 -1
  480. package/dist/types/src/core/record-chain.d.ts +0 -24
  481. package/dist/types/src/core/record-chain.d.ts.map +0 -1
  482. package/dist/types/src/event-stream/event-emitter-event-log.d.ts +0 -80
  483. package/dist/types/src/event-stream/event-emitter-event-log.d.ts.map +0 -1
  484. package/dist/types/src/handlers/messages-sync.d.ts +0 -39
  485. package/dist/types/src/handlers/messages-sync.d.ts.map +0 -1
  486. package/dist/types/src/interfaces/messages-sync.d.ts +0 -20
  487. package/dist/types/src/interfaces/messages-sync.d.ts.map +0 -1
  488. package/dist/types/src/smt/smt-store-level.d.ts +0 -32
  489. package/dist/types/src/smt/smt-store-level.d.ts.map +0 -1
  490. package/dist/types/src/smt/smt-store-memory.d.ts +0 -22
  491. package/dist/types/src/smt/smt-store-memory.d.ts.map +0 -1
  492. package/dist/types/src/smt/smt-utils.d.ts +0 -58
  493. package/dist/types/src/smt/smt-utils.d.ts.map +0 -1
  494. package/dist/types/src/smt/sparse-merkle-tree.d.ts +0 -124
  495. package/dist/types/src/smt/sparse-merkle-tree.d.ts.map +0 -1
  496. package/dist/types/src/state-index/state-index-level.d.ts +0 -83
  497. package/dist/types/src/state-index/state-index-level.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/src/core/record-chain.ts +0 -99
  513. package/src/event-stream/event-emitter-event-log.ts +0 -430
  514. package/src/handlers/messages-sync.ts +0 -403
  515. package/src/interfaces/messages-sync.ts +0 -69
  516. package/src/smt/smt-store-level.ts +0 -143
  517. package/src/smt/smt-store-memory.ts +0 -53
  518. package/src/smt/smt-utils.ts +0 -149
  519. package/src/smt/sparse-merkle-tree.ts +0 -698
  520. package/src/state-index/state-index-level.ts +0 -239
  521. package/src/types/smt-types.ts +0 -95
  522. package/src/types/state-index.ts +0 -100
@@ -2,21 +2,24 @@ import sinon from 'sinon';
2
2
  import { DataStream } from '../../src/utils/data-stream.js';
3
3
  import { DidKey } from '@enbox/dids';
4
4
  import { Dwn } from '../../src/dwn.js';
5
+ import { DwnConstant } from '../../src/core/dwn-constant.js';
5
6
  import { DwnErrorCode } from '../../src/core/dwn-error.js';
6
7
  import { Jws } from '../../src/utils/jws.js';
7
8
  import { ProtocolsConfigure } from '../../src/interfaces/protocols-configure.js';
9
+ import { RecordsRead } from '../../src/interfaces/records-read.js';
10
+ import { sleep } from '@enbox/common';
8
11
  import { TestDataGenerator } from '../utils/test-data-generator.js';
9
12
  import { TestEventLog } from '../test-event-stream.js';
10
13
  import { TestStores } from '../test-stores.js';
11
14
  import { UniversalResolver } from '@enbox/dids';
12
15
  import { afterAll, beforeAll, beforeEach, describe, expect, it } from 'bun:test';
16
+ import { DataStoreLevel, MessageStoreLevel, ResumableTaskStoreLevel } from '../../src/index.js';
13
17
  export function testRecordsRecordLimit() {
14
18
  describe('Records $recordLimit', () => {
15
19
  let didResolver;
16
20
  let messageStore;
17
21
  let dataStore;
18
22
  let resumableTaskStore;
19
- let stateIndex;
20
23
  let eventLog;
21
24
  let dwn;
22
25
  // important to follow the `before` and `after` pattern to initialize and clean the stores in tests
@@ -27,20 +30,99 @@ export function testRecordsRecordLimit() {
27
30
  messageStore = stores.messageStore;
28
31
  dataStore = stores.dataStore;
29
32
  resumableTaskStore = stores.resumableTaskStore;
30
- stateIndex = stores.stateIndex;
31
33
  eventLog = TestEventLog.get();
32
- dwn = await Dwn.create({ didResolver, messageStore, dataStore, stateIndex, eventLog, resumableTaskStore });
34
+ dwn = await Dwn.create({ didResolver, messageStore, dataStore, eventLog, resumableTaskStore });
33
35
  });
34
36
  beforeEach(async () => {
35
37
  sinon.restore();
36
38
  await messageStore.clear();
37
39
  await dataStore.clear();
38
40
  await resumableTaskStore.clear();
39
- await stateIndex.clear();
40
41
  });
41
42
  afterAll(async () => {
42
43
  await dwn.close();
43
44
  });
45
+ async function installProtocol(input) {
46
+ const protocolConfig = await TestDataGenerator.generateProtocolsConfigure({
47
+ author: input.author,
48
+ protocolDefinition: input.definition,
49
+ messageTimestamp: '2024-01-01T00:00:00.000000Z',
50
+ });
51
+ const reply = await (input.targetDwn ?? dwn).processMessage(input.author.did, protocolConfig.message);
52
+ expect(reply.status.code).toBe(202);
53
+ }
54
+ async function writeProtocolRecord(input) {
55
+ const record = await TestDataGenerator.generateRecordsWrite({
56
+ author: input.author,
57
+ recipient: input.author.did,
58
+ protocol: input.protocol,
59
+ protocolPath: input.protocolPath,
60
+ parentContextId: input.parentContextId,
61
+ dateCreated: input.dateCreated,
62
+ messageTimestamp: input.dateCreated,
63
+ });
64
+ const reply = await (input.targetDwn ?? dwn).processMessage(input.author.did, record.message, { dataStream: record.dataStream });
65
+ expect(reply.status.code).toBe(202);
66
+ return record;
67
+ }
68
+ async function queryProtocolRecordIds(input) {
69
+ const recordsQuery = await TestDataGenerator.generateRecordsQuery({
70
+ author: input.author,
71
+ filter: {
72
+ protocol: input.protocol,
73
+ protocolPath: input.protocolPath,
74
+ ...(input.contextId === undefined ? {} : { contextId: input.contextId }),
75
+ },
76
+ pagination: input.pagination,
77
+ });
78
+ const reply = await (input.targetDwn ?? dwn).processMessage(input.author.did, recordsQuery.message);
79
+ expect(reply.status.code).toBe(200);
80
+ return {
81
+ recordIds: reply.entries?.map((entry) => entry.recordId) ?? [],
82
+ cursor: reply.cursor,
83
+ };
84
+ }
85
+ async function countProtocolRecords(input) {
86
+ const recordsCount = await TestDataGenerator.generateRecordsCount({
87
+ author: input.author,
88
+ filter: {
89
+ protocol: input.protocol,
90
+ protocolPath: input.protocolPath,
91
+ ...(input.contextId === undefined ? {} : { contextId: input.contextId }),
92
+ },
93
+ });
94
+ const reply = await (input.targetDwn ?? dwn).processMessage(input.author.did, recordsCount.message);
95
+ expect(reply.status.code).toBe(200);
96
+ return reply.count;
97
+ }
98
+ async function readRecord(input) {
99
+ const recordsRead = await RecordsRead.create({
100
+ signer: Jws.createSigner(input.author),
101
+ filter: { recordId: input.recordId },
102
+ });
103
+ return await (input.targetDwn ?? dwn).processMessage(input.author.did, recordsRead.message);
104
+ }
105
+ async function subscribeSnapshotRecordIds(input) {
106
+ const recordsSubscribe = await TestDataGenerator.generateRecordsSubscribe({
107
+ author: input.author,
108
+ filter: {
109
+ protocol: input.protocol,
110
+ protocolPath: input.protocolPath,
111
+ },
112
+ });
113
+ const reply = await (input.targetDwn ?? dwn).processMessage(input.author.did, recordsSubscribe.message, { subscriptionHandler: () => { } });
114
+ expect(reply.status.code).toBe(200);
115
+ await reply.subscription?.close();
116
+ return reply.entries?.map((entry) => entry.recordId) ?? [];
117
+ }
118
+ async function applyReplicatedWrite(input) {
119
+ const result = await input.targetDwn.applyReplicatedMessage(input.tenant, input.record.message, {
120
+ dataStream: DataStream.fromBytes(input.record.dataBytes),
121
+ });
122
+ if (result.kind !== 'Applied' && result.kind !== 'Duplicate' && result.kind !== 'Superseded') {
123
+ throw new Error(`unexpected replicated write result ${result.kind}`);
124
+ }
125
+ }
44
126
  describe('ProtocolsConfigure validation', () => {
45
127
  it('should allow $recordLimit with valid max and strategy', async () => {
46
128
  const alice = await TestDataGenerator.generateDidKeyPersona();
@@ -85,6 +167,24 @@ export function testRecordsRecordLimit() {
85
167
  strategy: 'reject',
86
168
  });
87
169
  });
170
+ it('should reject $recordLimit above the supported max', async () => {
171
+ const alice = await TestDataGenerator.generateDidKeyPersona();
172
+ const definition = {
173
+ protocol: 'http://example.com/record-limit',
174
+ published: true,
175
+ types: { message: {} },
176
+ structure: {
177
+ message: {
178
+ $recordLimit: { max: DwnConstant.maxRecordLimit + 1, strategy: 'reject' },
179
+ }
180
+ }
181
+ };
182
+ const promise = ProtocolsConfigure.create({
183
+ signer: Jws.createSigner(alice),
184
+ definition,
185
+ });
186
+ await expect(promise).rejects.toThrow(DwnErrorCode.ProtocolsConfigureInvalidRecordLimit);
187
+ });
88
188
  it('should allow $recordLimit with purgeOldest strategy (schema validation only)', async () => {
89
189
  const alice = await TestDataGenerator.generateDidKeyPersona();
90
190
  const definition = {
@@ -206,42 +306,11 @@ export function testRecordsRecordLimit() {
206
306
  });
207
307
  });
208
308
  });
209
- describe('runtime enforcement — reject strategy', () => {
210
- it('should allow writes up to the record limit', async () => {
309
+ describe('read-time occupancy projection — reject strategy', () => {
310
+ it('should admit candidates and project Query, Read, and Count to top-ranked occupants', async () => {
211
311
  const alice = await TestDataGenerator.generateDidKeyPersona();
212
312
  const protocolDefinition = {
213
- protocol: 'http://record-limit-test.xyz',
214
- published: true,
215
- types: { blob: {} },
216
- structure: {
217
- blob: {
218
- $recordLimit: { max: 3, strategy: 'reject' },
219
- }
220
- }
221
- };
222
- const protocol = protocolDefinition.protocol;
223
- const protocolConfig = await TestDataGenerator.generateProtocolsConfigure({
224
- author: alice,
225
- protocolDefinition,
226
- });
227
- const configReply = await dwn.processMessage(alice.did, protocolConfig.message);
228
- expect(configReply.status.code).toBe(202);
229
- // write 3 records — all should succeed
230
- for (let i = 0; i < 3; i++) {
231
- const record = await TestDataGenerator.generateRecordsWrite({
232
- author: alice,
233
- recipient: alice.did,
234
- protocol,
235
- protocolPath: 'blob',
236
- });
237
- const reply = await dwn.processMessage(alice.did, record.message, { dataStream: record.dataStream });
238
- expect(reply.status.code).toBe(202);
239
- }
240
- });
241
- it('should reject writes that exceed the record limit', async () => {
242
- const alice = await TestDataGenerator.generateDidKeyPersona();
243
- const protocolDefinition = {
244
- protocol: 'http://record-limit-test.xyz',
313
+ protocol: `http://record-limit-${TestDataGenerator.randomString(12)}.xyz`,
245
314
  published: true,
246
315
  types: { blob: {} },
247
316
  structure: {
@@ -251,38 +320,44 @@ export function testRecordsRecordLimit() {
251
320
  }
252
321
  };
253
322
  const protocol = protocolDefinition.protocol;
254
- const protocolConfig = await TestDataGenerator.generateProtocolsConfigure({
323
+ await installProtocol({ author: alice, definition: protocolDefinition });
324
+ const record1 = await writeProtocolRecord({
255
325
  author: alice,
256
- protocolDefinition,
326
+ protocol,
327
+ protocolPath: 'blob',
328
+ dateCreated: '2025-01-01T00:00:00.000000Z',
257
329
  });
258
- const configReply = await dwn.processMessage(alice.did, protocolConfig.message);
259
- expect(configReply.status.code).toBe(202);
260
- // write 2 records — both should succeed
261
- for (let i = 0; i < 2; i++) {
262
- const record = await TestDataGenerator.generateRecordsWrite({
263
- author: alice,
264
- recipient: alice.did,
265
- protocol,
266
- protocolPath: 'blob',
267
- });
268
- const reply = await dwn.processMessage(alice.did, record.message, { dataStream: record.dataStream });
269
- expect(reply.status.code).toBe(202);
270
- }
271
- // 3rd record should be rejected
272
- const record3 = await TestDataGenerator.generateRecordsWrite({
330
+ const duplicateReply = await dwn.processMessage(alice.did, record1.message, {
331
+ dataStream: DataStream.fromBytes(record1.dataBytes),
332
+ });
333
+ expect(duplicateReply.status.code).toBe(409);
334
+ const record2 = await writeProtocolRecord({
273
335
  author: alice,
274
- recipient: alice.did,
275
336
  protocol,
276
337
  protocolPath: 'blob',
338
+ dateCreated: '2025-01-02T00:00:00.000000Z',
277
339
  });
278
- const reply3 = await dwn.processMessage(alice.did, record3.message, { dataStream: record3.dataStream });
279
- expect(reply3.status.code).toBe(400);
280
- expect(reply3.status.detail).toContain(DwnErrorCode.ProtocolAuthorizationRecordLimitExceeded);
340
+ const record3 = await writeProtocolRecord({
341
+ author: alice,
342
+ protocol,
343
+ protocolPath: 'blob',
344
+ dateCreated: '2025-01-03T00:00:00.000000Z',
345
+ });
346
+ expect((await queryProtocolRecordIds({ author: alice, protocol, protocolPath: 'blob' })).recordIds).toEqual([
347
+ record1.message.recordId,
348
+ record2.message.recordId,
349
+ ]);
350
+ expect(await countProtocolRecords({ author: alice, protocol, protocolPath: 'blob' })).toBe(2);
351
+ const visibleRead = await readRecord({ author: alice, recordId: record1.message.recordId });
352
+ expect(visibleRead.status.code).toBe(200);
353
+ expect(visibleRead.entry?.recordsWrite?.recordId).toBe(record1.message.recordId);
354
+ const hiddenRead = await readRecord({ author: alice, recordId: record3.message.recordId });
355
+ expect(hiddenRead.status.code).toBe(404);
281
356
  });
282
- it('should enforce $recordLimit with max of 1 (singleton pattern)', async () => {
357
+ it('should promote the next candidate at read time when the current occupant is deleted', async () => {
283
358
  const alice = await TestDataGenerator.generateDidKeyPersona();
284
359
  const protocolDefinition = {
285
- protocol: 'http://record-limit-test.xyz',
360
+ protocol: `http://record-limit-${TestDataGenerator.randomString(12)}.xyz`,
286
361
  published: true,
287
362
  types: { profile: {} },
288
363
  structure: {
@@ -292,227 +367,377 @@ export function testRecordsRecordLimit() {
292
367
  }
293
368
  };
294
369
  const protocol = protocolDefinition.protocol;
295
- const protocolConfig = await TestDataGenerator.generateProtocolsConfigure({
370
+ await installProtocol({ author: alice, definition: protocolDefinition });
371
+ const record1 = await writeProtocolRecord({
296
372
  author: alice,
297
- protocolDefinition,
298
- });
299
- const configReply = await dwn.processMessage(alice.did, protocolConfig.message);
300
- expect(configReply.status.code).toBe(202);
301
- // first record should succeed
302
- const record1 = await TestDataGenerator.generateRecordsWrite({
303
- author: alice,
304
- recipient: alice.did,
305
373
  protocol,
306
374
  protocolPath: 'profile',
375
+ dateCreated: '2025-01-01T00:00:00.000000Z',
307
376
  });
308
- const reply1 = await dwn.processMessage(alice.did, record1.message, { dataStream: record1.dataStream });
309
- expect(reply1.status.code).toBe(202);
310
- // exact duplicate delivery is idempotent and should not count the
311
- // same recordId against its own singleton limit
312
- const duplicateReply = await dwn.processMessage(alice.did, record1.message, {
313
- dataStream: DataStream.fromBytes(record1.dataBytes),
314
- });
315
- expect(duplicateReply.status.code).toBe(409);
316
- // second record should be rejected
317
- const record2 = await TestDataGenerator.generateRecordsWrite({
377
+ const record2 = await writeProtocolRecord({
318
378
  author: alice,
319
- recipient: alice.did,
320
379
  protocol,
321
380
  protocolPath: 'profile',
381
+ dateCreated: '2025-01-02T00:00:00.000000Z',
322
382
  });
323
- const reply2 = await dwn.processMessage(alice.did, record2.message, { dataStream: record2.dataStream });
324
- expect(reply2.status.code).toBe(400);
325
- expect(reply2.status.detail).toContain(DwnErrorCode.ProtocolAuthorizationRecordLimitExceeded);
383
+ expect((await queryProtocolRecordIds({ author: alice, protocol, protocolPath: 'profile' })).recordIds).toEqual([
384
+ record1.message.recordId,
385
+ ]);
386
+ const deleteRecord = await TestDataGenerator.generateRecordsDelete({
387
+ author: alice,
388
+ recordId: record1.message.recordId,
389
+ });
390
+ const deleteReply = await dwn.processMessage(alice.did, deleteRecord.message);
391
+ expect(deleteReply.status.code).toBe(202);
392
+ expect((await queryProtocolRecordIds({ author: alice, protocol, protocolPath: 'profile' })).recordIds).toEqual([
393
+ record2.message.recordId,
394
+ ]);
395
+ expect((await readRecord({ author: alice, recordId: record2.message.recordId })).status.code).toBe(200);
326
396
  });
327
- it('should allow new writes after records are deleted to free up space', async () => {
397
+ it('should project occupancy independently per exact parent context and reject broad cross-context queries', async () => {
328
398
  const alice = await TestDataGenerator.generateDidKeyPersona();
329
399
  const protocolDefinition = {
330
- protocol: 'http://record-limit-test.xyz',
400
+ protocol: `http://record-limit-${TestDataGenerator.randomString(12)}.xyz`,
331
401
  published: true,
332
- types: { blob: {} },
402
+ types: {
403
+ room: {},
404
+ message: {},
405
+ },
333
406
  structure: {
334
- blob: {
335
- $recordLimit: { max: 1, strategy: 'reject' },
407
+ room: {
408
+ message: {
409
+ $recordLimit: { max: 2, strategy: 'reject' },
410
+ }
336
411
  }
337
412
  }
338
413
  };
339
414
  const protocol = protocolDefinition.protocol;
340
- const protocolConfig = await TestDataGenerator.generateProtocolsConfigure({
415
+ await installProtocol({ author: alice, definition: protocolDefinition });
416
+ const room1 = await writeProtocolRecord({
341
417
  author: alice,
342
- protocolDefinition,
343
- });
344
- const configReply = await dwn.processMessage(alice.did, protocolConfig.message);
345
- expect(configReply.status.code).toBe(202);
346
- // write first record
347
- const record1 = await TestDataGenerator.generateRecordsWrite({
348
- author: alice,
349
- recipient: alice.did,
350
418
  protocol,
351
- protocolPath: 'blob',
419
+ protocolPath: 'room',
420
+ dateCreated: '2025-01-01T00:00:00.000000Z',
352
421
  });
353
- const reply1 = await dwn.processMessage(alice.did, record1.message, { dataStream: record1.dataStream });
354
- expect(reply1.status.code).toBe(202);
355
- // second write should be rejected
356
- const record2 = await TestDataGenerator.generateRecordsWrite({
422
+ const room2 = await writeProtocolRecord({
357
423
  author: alice,
358
- recipient: alice.did,
359
424
  protocol,
360
- protocolPath: 'blob',
425
+ protocolPath: 'room',
426
+ dateCreated: '2025-01-02T00:00:00.000000Z',
361
427
  });
362
- const reply2 = await dwn.processMessage(alice.did, record2.message, { dataStream: record2.dataStream });
363
- expect(reply2.status.code).toBe(400);
364
- // delete the first record
365
- const deleteRecord = await TestDataGenerator.generateRecordsDelete({
428
+ const room1Messages = [
429
+ await writeProtocolRecord({
430
+ author: alice,
431
+ protocol,
432
+ protocolPath: 'room/message',
433
+ parentContextId: room1.message.contextId,
434
+ dateCreated: '2025-02-01T00:00:00.000000Z',
435
+ }),
436
+ await writeProtocolRecord({
437
+ author: alice,
438
+ protocol,
439
+ protocolPath: 'room/message',
440
+ parentContextId: room1.message.contextId,
441
+ dateCreated: '2025-02-02T00:00:00.000000Z',
442
+ }),
443
+ await writeProtocolRecord({
444
+ author: alice,
445
+ protocol,
446
+ protocolPath: 'room/message',
447
+ parentContextId: room1.message.contextId,
448
+ dateCreated: '2025-02-03T00:00:00.000000Z',
449
+ }),
450
+ ];
451
+ const room2Messages = [
452
+ await writeProtocolRecord({
453
+ author: alice,
454
+ protocol,
455
+ protocolPath: 'room/message',
456
+ parentContextId: room2.message.contextId,
457
+ dateCreated: '2025-03-01T00:00:00.000000Z',
458
+ }),
459
+ await writeProtocolRecord({
460
+ author: alice,
461
+ protocol,
462
+ protocolPath: 'room/message',
463
+ parentContextId: room2.message.contextId,
464
+ dateCreated: '2025-03-02T00:00:00.000000Z',
465
+ }),
466
+ await writeProtocolRecord({
467
+ author: alice,
468
+ protocol,
469
+ protocolPath: 'room/message',
470
+ parentContextId: room2.message.contextId,
471
+ dateCreated: '2025-03-03T00:00:00.000000Z',
472
+ }),
473
+ ];
474
+ expect((await queryProtocolRecordIds({
366
475
  author: alice,
367
- recordId: record1.message.recordId,
368
- });
369
- const deleteReply = await dwn.processMessage(alice.did, deleteRecord.message);
370
- expect(deleteReply.status.code).toBe(202);
371
- // now a new write should succeed
372
- const record3 = await TestDataGenerator.generateRecordsWrite({
476
+ protocol,
477
+ protocolPath: 'room/message',
478
+ contextId: room1.message.contextId,
479
+ })).recordIds).toEqual([
480
+ room1Messages[0].message.recordId,
481
+ room1Messages[1].message.recordId,
482
+ ]);
483
+ expect((await queryProtocolRecordIds({
373
484
  author: alice,
374
- recipient: alice.did,
375
485
  protocol,
376
- protocolPath: 'blob',
486
+ protocolPath: 'room/message',
487
+ contextId: room2.message.contextId,
488
+ })).recordIds).toEqual([
489
+ room2Messages[0].message.recordId,
490
+ room2Messages[1].message.recordId,
491
+ ]);
492
+ const broadQuery = await TestDataGenerator.generateRecordsQuery({
493
+ author: alice,
494
+ filter: {
495
+ protocol,
496
+ protocolPath: 'room/message',
497
+ },
377
498
  });
378
- const reply3 = await dwn.processMessage(alice.did, record3.message, { dataStream: record3.dataStream });
379
- expect(reply3.status.code).toBe(202);
499
+ const broadReply = await dwn.processMessage(alice.did, broadQuery.message);
500
+ expect(broadReply.status.code).toBe(400);
501
+ expect(broadReply.status.detail).toContain(DwnErrorCode.RecordsQueryNestedProtocolPathContextIdInvalid);
380
502
  });
381
- it('should not count updates toward the record limit', async () => {
503
+ it('should paginate over the projected occupant set for max:N scopes', async () => {
382
504
  const alice = await TestDataGenerator.generateDidKeyPersona();
383
505
  const protocolDefinition = {
384
- protocol: 'http://record-limit-test.xyz',
506
+ protocol: `http://record-limit-${TestDataGenerator.randomString(12)}.xyz`,
385
507
  published: true,
386
- types: { blob: {} },
508
+ types: { item: {} },
387
509
  structure: {
388
- blob: {
389
- $recordLimit: { max: 1, strategy: 'reject' },
510
+ item: {
511
+ $recordLimit: { max: 3, strategy: 'reject' },
390
512
  }
391
513
  }
392
514
  };
393
515
  const protocol = protocolDefinition.protocol;
394
- const protocolConfig = await TestDataGenerator.generateProtocolsConfigure({
395
- author: alice,
396
- protocolDefinition,
397
- });
398
- const configReply = await dwn.processMessage(alice.did, protocolConfig.message);
399
- expect(configReply.status.code).toBe(202);
400
- // write the record
401
- const record1 = await TestDataGenerator.generateRecordsWrite({
516
+ await installProtocol({ author: alice, definition: protocolDefinition });
517
+ const records = [];
518
+ for (let i = 1; i <= 5; i++) {
519
+ records.push(await writeProtocolRecord({
520
+ author: alice,
521
+ protocol,
522
+ protocolPath: 'item',
523
+ dateCreated: `2025-01-0${i}T00:00:00.000000Z`,
524
+ }));
525
+ }
526
+ const page1 = await queryProtocolRecordIds({
402
527
  author: alice,
403
- recipient: alice.did,
404
528
  protocol,
405
- protocolPath: 'blob',
406
- });
407
- const reply1 = await dwn.processMessage(alice.did, record1.message, { dataStream: record1.dataStream });
408
- expect(reply1.status.code).toBe(202);
409
- // update the same record — should succeed (not counted as new)
410
- const updatedRecord = await TestDataGenerator.generateFromRecordsWrite({
529
+ protocolPath: 'item',
530
+ pagination: { limit: 2 },
531
+ });
532
+ expect(page1.recordIds).toEqual([
533
+ records[0].message.recordId,
534
+ records[1].message.recordId,
535
+ ]);
536
+ expect(page1.cursor).toBeDefined();
537
+ const page2 = await queryProtocolRecordIds({
411
538
  author: alice,
412
- existingWrite: record1.recordsWrite,
539
+ protocol,
540
+ protocolPath: 'item',
541
+ pagination: { limit: 2, cursor: page1.cursor },
413
542
  });
414
- const updateReply = await dwn.processMessage(alice.did, updatedRecord.message, { dataStream: updatedRecord.dataStream });
415
- expect(updateReply.status.code).toBe(202);
543
+ expect(page2.recordIds).toEqual([records[2].message.recordId]);
544
+ expect(page2.cursor).toBeUndefined();
416
545
  });
417
- it('should enforce $recordLimit per parent context for nested records', async () => {
546
+ it('should project RecordsSubscribe snapshots and suppress hidden live writes', async () => {
418
547
  const alice = await TestDataGenerator.generateDidKeyPersona();
419
548
  const protocolDefinition = {
420
- protocol: 'http://record-limit-test.xyz',
549
+ protocol: `http://record-limit-${TestDataGenerator.randomString(12)}.xyz`,
421
550
  published: true,
422
- types: {
423
- room: {},
424
- message: {},
425
- },
551
+ types: { status: {} },
426
552
  structure: {
427
- room: {
428
- message: {
429
- $recordLimit: { max: 2, strategy: 'reject' },
430
- }
553
+ status: {
554
+ $recordLimit: { max: 1, strategy: 'reject' },
431
555
  }
432
556
  }
433
557
  };
434
558
  const protocol = protocolDefinition.protocol;
435
- const protocolConfig = await TestDataGenerator.generateProtocolsConfigure({
559
+ await installProtocol({ author: alice, definition: protocolDefinition });
560
+ const visibleRecord = await writeProtocolRecord({
436
561
  author: alice,
437
- protocolDefinition,
562
+ protocol,
563
+ protocolPath: 'status',
564
+ dateCreated: '2025-01-01T00:00:00.000000Z',
438
565
  });
439
- const configReply = await dwn.processMessage(alice.did, protocolConfig.message);
440
- expect(configReply.status.code).toBe(202);
441
- // create two rooms
442
- const room1 = await TestDataGenerator.generateRecordsWrite({
566
+ const hiddenRecord = await writeProtocolRecord({
443
567
  author: alice,
444
- recipient: alice.did,
445
568
  protocol,
446
- protocolPath: 'room',
569
+ protocolPath: 'status',
570
+ dateCreated: '2025-01-02T00:00:00.000000Z',
571
+ });
572
+ expect(await subscribeSnapshotRecordIds({ author: alice, protocol, protocolPath: 'status' })).toEqual([
573
+ visibleRecord.message.recordId,
574
+ ]);
575
+ const liveRecordIds = [];
576
+ const recordsSubscribe = await TestDataGenerator.generateRecordsSubscribe({
577
+ author: alice,
578
+ filter: {
579
+ protocol,
580
+ protocolPath: 'status',
581
+ },
582
+ });
583
+ const subscribeReply = await dwn.processMessage(alice.did, recordsSubscribe.message, {
584
+ subscriptionHandler: (subscriptionMessage) => {
585
+ if (subscriptionMessage.type === 'event') {
586
+ const eventMessage = subscriptionMessage.event.message;
587
+ if (eventMessage.descriptor.method === 'Write' && 'recordId' in eventMessage && typeof eventMessage.recordId === 'string') {
588
+ liveRecordIds.push(eventMessage.recordId);
589
+ }
590
+ }
591
+ },
447
592
  });
448
- const room1Reply = await dwn.processMessage(alice.did, room1.message, { dataStream: room1.dataStream });
449
- expect(room1Reply.status.code).toBe(202);
450
- const room2 = await TestDataGenerator.generateRecordsWrite({
593
+ expect(subscribeReply.status.code).toBe(200);
594
+ const laterHiddenRecord = await writeProtocolRecord({
451
595
  author: alice,
452
- recipient: alice.did,
453
596
  protocol,
454
- protocolPath: 'room',
597
+ protocolPath: 'status',
598
+ dateCreated: '2025-01-03T00:00:00.000000Z',
455
599
  });
456
- const room2Reply = await dwn.processMessage(alice.did, room2.message, { dataStream: room2.dataStream });
457
- expect(room2Reply.status.code).toBe(202);
458
- // write 2 messages in room1 — both should succeed
459
- let firstRoom1Message;
460
- for (let i = 0; i < 2; i++) {
461
- const msg = await TestDataGenerator.generateRecordsWrite({
600
+ expect(hiddenRecord.message.recordId).not.toBe(laterHiddenRecord.message.recordId);
601
+ await sleep(100);
602
+ expect(liveRecordIds).toEqual([]);
603
+ await subscribeReply.subscription?.close();
604
+ });
605
+ it('should converge projected query results when replicas create max:1 and max:N occupants offline', async () => {
606
+ const alice = await TestDataGenerator.generateDidKeyPersona();
607
+ const testId = TestDataGenerator.randomString(12);
608
+ const replicaAStore = new MessageStoreLevel({ location: `__TESTDATA__/record-limit-${testId}/message-a` });
609
+ const replicaADataStore = new DataStoreLevel({ blockstoreLocation: `__TESTDATA__/record-limit-${testId}/data-a` });
610
+ const replicaATaskStore = new ResumableTaskStoreLevel({ location: `__TESTDATA__/record-limit-${testId}/task-a` });
611
+ const replicaBStore = new MessageStoreLevel({ location: `__TESTDATA__/record-limit-${testId}/message-b` });
612
+ const replicaBDataStore = new DataStoreLevel({ blockstoreLocation: `__TESTDATA__/record-limit-${testId}/data-b` });
613
+ const replicaBTaskStore = new ResumableTaskStoreLevel({ location: `__TESTDATA__/record-limit-${testId}/task-b` });
614
+ const replicaA = await Dwn.create({
615
+ didResolver: didResolver,
616
+ messageStore: replicaAStore,
617
+ dataStore: replicaADataStore,
618
+ resumableTaskStore: replicaATaskStore,
619
+ });
620
+ const replicaB = await Dwn.create({
621
+ didResolver: didResolver,
622
+ messageStore: replicaBStore,
623
+ dataStore: replicaBDataStore,
624
+ resumableTaskStore: replicaBTaskStore,
625
+ });
626
+ try {
627
+ const protocolDefinition = {
628
+ protocol: `http://record-limit-offline-${testId}.xyz`,
629
+ published: true,
630
+ types: {
631
+ note: {},
632
+ profile: {},
633
+ },
634
+ structure: {
635
+ note: {
636
+ $recordLimit: { max: 2, strategy: 'reject' },
637
+ },
638
+ profile: {
639
+ $recordLimit: { max: 1, strategy: 'reject' },
640
+ }
641
+ }
642
+ };
643
+ const protocol = protocolDefinition.protocol;
644
+ await installProtocol({ author: alice, definition: protocolDefinition, targetDwn: replicaA });
645
+ await installProtocol({ author: alice, definition: protocolDefinition, targetDwn: replicaB });
646
+ const profileA = await writeProtocolRecord({
462
647
  author: alice,
463
- recipient: alice.did,
464
648
  protocol,
465
- protocolPath: 'room/message',
466
- parentContextId: room1.message.contextId,
649
+ protocolPath: 'profile',
650
+ dateCreated: '2025-01-01T00:00:00.000000Z',
651
+ targetDwn: replicaA,
467
652
  });
468
- const msgReply = await dwn.processMessage(alice.did, msg.message, { dataStream: msg.dataStream });
469
- expect(msgReply.status.code).toBe(202);
470
- firstRoom1Message ??= msg;
471
- }
472
- // exact duplicate nested delivery is idempotent within the parent context
473
- const duplicateNestedReply = await dwn.processMessage(alice.did, firstRoom1Message.message, {
474
- dataStream: DataStream.fromBytes(firstRoom1Message.dataBytes),
475
- });
476
- expect(duplicateNestedReply.status.code).toBe(409);
477
- // 3rd message in room1 should be rejected
478
- const msg3 = await TestDataGenerator.generateRecordsWrite({
479
- author: alice,
480
- recipient: alice.did,
481
- protocol,
482
- protocolPath: 'room/message',
483
- parentContextId: room1.message.contextId,
484
- });
485
- const msg3Reply = await dwn.processMessage(alice.did, msg3.message, { dataStream: msg3.dataStream });
486
- expect(msg3Reply.status.code).toBe(400);
487
- expect(msg3Reply.status.detail).toContain(DwnErrorCode.ProtocolAuthorizationRecordLimitExceeded);
488
- // but room2 still has capacity — write 2 messages
489
- for (let i = 0; i < 2; i++) {
490
- const msg = await TestDataGenerator.generateRecordsWrite({
653
+ const profileB = await writeProtocolRecord({
491
654
  author: alice,
492
- recipient: alice.did,
493
655
  protocol,
494
- protocolPath: 'room/message',
495
- parentContextId: room2.message.contextId,
656
+ protocolPath: 'profile',
657
+ dateCreated: '2025-01-02T00:00:00.000000Z',
658
+ targetDwn: replicaB,
496
659
  });
497
- const msgReply = await dwn.processMessage(alice.did, msg.message, { dataStream: msg.dataStream });
498
- expect(msgReply.status.code).toBe(202);
660
+ const noteA1 = await writeProtocolRecord({
661
+ author: alice,
662
+ protocol,
663
+ protocolPath: 'note',
664
+ dateCreated: '2025-02-01T00:00:00.000000Z',
665
+ targetDwn: replicaA,
666
+ });
667
+ const noteB = await writeProtocolRecord({
668
+ author: alice,
669
+ protocol,
670
+ protocolPath: 'note',
671
+ dateCreated: '2025-02-02T00:00:00.000000Z',
672
+ targetDwn: replicaB,
673
+ });
674
+ const noteA2 = await writeProtocolRecord({
675
+ author: alice,
676
+ protocol,
677
+ protocolPath: 'note',
678
+ dateCreated: '2025-02-03T00:00:00.000000Z',
679
+ targetDwn: replicaA,
680
+ });
681
+ await applyReplicatedWrite({ targetDwn: replicaA, tenant: alice.did, record: profileB });
682
+ await applyReplicatedWrite({ targetDwn: replicaB, tenant: alice.did, record: profileA });
683
+ await applyReplicatedWrite({ targetDwn: replicaA, tenant: alice.did, record: noteB });
684
+ await applyReplicatedWrite({ targetDwn: replicaB, tenant: alice.did, record: noteA1 });
685
+ await applyReplicatedWrite({ targetDwn: replicaB, tenant: alice.did, record: noteA2 });
686
+ const expectedProfileIds = [profileA.message.recordId];
687
+ const expectedNoteIds = [
688
+ noteA1.message.recordId,
689
+ noteB.message.recordId,
690
+ ];
691
+ expect((await queryProtocolRecordIds({ author: alice, protocol, protocolPath: 'profile', targetDwn: replicaA })).recordIds).toEqual(expectedProfileIds);
692
+ expect((await queryProtocolRecordIds({ author: alice, protocol, protocolPath: 'profile', targetDwn: replicaB })).recordIds).toEqual(expectedProfileIds);
693
+ expect((await queryProtocolRecordIds({ author: alice, protocol, protocolPath: 'note', targetDwn: replicaA })).recordIds).toEqual(expectedNoteIds);
694
+ expect((await queryProtocolRecordIds({ author: alice, protocol, protocolPath: 'note', targetDwn: replicaB })).recordIds).toEqual(expectedNoteIds);
695
+ }
696
+ finally {
697
+ await replicaAStore.clear();
698
+ await replicaADataStore.clear();
699
+ await replicaATaskStore.clear();
700
+ await replicaBStore.clear();
701
+ await replicaBDataStore.clear();
702
+ await replicaBTaskStore.clear();
703
+ await replicaA.close();
704
+ await replicaB.close();
499
705
  }
500
- // 3rd message in room2 should also be rejected
501
- const msg3Room2 = await TestDataGenerator.generateRecordsWrite({
706
+ });
707
+ it('should not count updates toward the record limit', async () => {
708
+ const alice = await TestDataGenerator.generateDidKeyPersona();
709
+ const protocolDefinition = {
710
+ protocol: `http://record-limit-${TestDataGenerator.randomString(12)}.xyz`,
711
+ published: true,
712
+ types: { blob: {} },
713
+ structure: {
714
+ blob: {
715
+ $recordLimit: { max: 1, strategy: 'reject' },
716
+ }
717
+ }
718
+ };
719
+ const protocol = protocolDefinition.protocol;
720
+ await installProtocol({ author: alice, definition: protocolDefinition });
721
+ const record = await writeProtocolRecord({
502
722
  author: alice,
503
- recipient: alice.did,
504
723
  protocol,
505
- protocolPath: 'room/message',
506
- parentContextId: room2.message.contextId,
724
+ protocolPath: 'blob',
725
+ dateCreated: '2025-01-01T00:00:00.000000Z',
726
+ });
727
+ const updatedRecord = await TestDataGenerator.generateFromRecordsWrite({
728
+ author: alice,
729
+ existingWrite: record.recordsWrite,
507
730
  });
508
- const msg3Room2Reply = await dwn.processMessage(alice.did, msg3Room2.message, { dataStream: msg3Room2.dataStream });
509
- expect(msg3Room2Reply.status.code).toBe(400);
510
- expect(msg3Room2Reply.status.detail).toContain(DwnErrorCode.ProtocolAuthorizationRecordLimitExceeded);
731
+ const updateReply = await dwn.processMessage(alice.did, updatedRecord.message, { dataStream: updatedRecord.dataStream });
732
+ expect(updateReply.status.code).toBe(202);
733
+ expect((await queryProtocolRecordIds({ author: alice, protocol, protocolPath: 'blob' })).recordIds).toEqual([
734
+ record.message.recordId,
735
+ ]);
511
736
  });
512
737
  it('should reject purgeOldest strategy at write time with not-implemented error', async () => {
513
738
  const alice = await TestDataGenerator.generateDidKeyPersona();
514
739
  const protocolDefinition = {
515
- protocol: 'http://record-limit-test.xyz',
740
+ protocol: `http://record-limit-${TestDataGenerator.randomString(12)}.xyz`,
516
741
  published: true,
517
742
  types: { blob: {} },
518
743
  structure: {
@@ -522,31 +747,16 @@ export function testRecordsRecordLimit() {
522
747
  }
523
748
  };
524
749
  const protocol = protocolDefinition.protocol;
525
- const protocolConfig = await TestDataGenerator.generateProtocolsConfigure({
526
- author: alice,
527
- protocolDefinition,
528
- });
529
- const configReply = await dwn.processMessage(alice.did, protocolConfig.message);
530
- expect(configReply.status.code).toBe(202);
531
- // first record should succeed
532
- const record1 = await TestDataGenerator.generateRecordsWrite({
533
- author: alice,
534
- recipient: alice.did,
535
- protocol,
536
- protocolPath: 'blob',
537
- });
538
- const reply1 = await dwn.processMessage(alice.did, record1.message, { dataStream: record1.dataStream });
539
- expect(reply1.status.code).toBe(202);
540
- // second record should fail because purgeOldest is not yet implemented
541
- const record2 = await TestDataGenerator.generateRecordsWrite({
750
+ await installProtocol({ author: alice, definition: protocolDefinition });
751
+ const record = await TestDataGenerator.generateRecordsWrite({
542
752
  author: alice,
543
753
  recipient: alice.did,
544
754
  protocol,
545
755
  protocolPath: 'blob',
546
756
  });
547
- const reply2 = await dwn.processMessage(alice.did, record2.message, { dataStream: record2.dataStream });
548
- expect(reply2.status.code).toBe(400);
549
- expect(reply2.status.detail).toContain(DwnErrorCode.ProtocolAuthorizationRecordLimitStrategyNotImplemented);
757
+ const reply = await dwn.processMessage(alice.did, record.message, { dataStream: record.dataStream });
758
+ expect(reply.status.code).toBe(400);
759
+ expect(reply.status.detail).toContain(DwnErrorCode.ProtocolAuthorizationRecordLimitStrategyNotImplemented);
550
760
  });
551
761
  });
552
762
  });