@enbox/dwn-sdk-js 0.4.0 → 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 (513) hide show
  1. package/README.md +4 -4
  2. package/dist/browser.mjs +8 -8
  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 +30 -68
  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 +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 +55 -1
  113. package/dist/esm/tests/core/replication-apply.spec.js.map +1 -1
  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 +504 -35
  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 +82 -36
  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 -2
  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 +12 -4
  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 -14
  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 +36 -0
  244. package/dist/types/src/core/replication-apply.d.ts.map +1 -1
  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 +33 -20
  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 +14 -16
  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 -37
  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-replay-property.spec.d.ts +2 -0
  330. package/dist/types/tests/core/replication-replay-property.spec.d.ts.map +1 -0
  331. package/dist/types/tests/core/validation-read-closure.spec.d.ts +2 -0
  332. package/dist/types/tests/core/validation-read-closure.spec.d.ts.map +1 -0
  333. package/dist/types/tests/core/validation-state-reader.spec.d.ts +2 -0
  334. package/dist/types/tests/core/validation-state-reader.spec.d.ts.map +1 -0
  335. package/dist/types/tests/durable-event-log.spec.d.ts +2 -0
  336. package/dist/types/tests/durable-event-log.spec.d.ts.map +1 -0
  337. package/dist/types/tests/dwn.spec.d.ts.map +1 -1
  338. package/dist/types/tests/features/author-delegated-grant.spec.d.ts.map +1 -1
  339. package/dist/types/tests/features/owner-delegated-grant.spec.d.ts.map +1 -1
  340. package/dist/types/tests/features/owner-signature.spec.d.ts.map +1 -1
  341. package/dist/types/tests/features/permissions.spec.d.ts.map +1 -1
  342. package/dist/types/tests/features/protocol-composition.spec.d.ts.map +1 -1
  343. package/dist/types/tests/features/protocol-create-action.spec.d.ts.map +1 -1
  344. package/dist/types/tests/features/protocol-delete-action.spec.d.ts.map +1 -1
  345. package/dist/types/tests/features/protocol-update-action.spec.d.ts.map +1 -1
  346. package/dist/types/tests/features/records-delivery.spec.d.ts.map +1 -1
  347. package/dist/types/tests/features/records-immutable.spec.d.ts.map +1 -1
  348. package/dist/types/tests/features/records-nested-query-scope.spec.d.ts +2 -0
  349. package/dist/types/tests/features/records-nested-query-scope.spec.d.ts.map +1 -0
  350. package/dist/types/tests/features/records-prune-cross-protocol.spec.d.ts.map +1 -1
  351. package/dist/types/tests/features/records-prune.spec.d.ts.map +1 -1
  352. package/dist/types/tests/features/records-record-limit.spec.d.ts.map +1 -1
  353. package/dist/types/tests/features/records-squash.spec.d.ts.map +1 -1
  354. package/dist/types/tests/features/records-tags.spec.d.ts.map +1 -1
  355. package/dist/types/tests/features/resumable-tasks.spec.d.ts.map +1 -1
  356. package/dist/types/tests/handlers/messages-query.spec.d.ts +2 -0
  357. package/dist/types/tests/handlers/messages-query.spec.d.ts.map +1 -0
  358. package/dist/types/tests/handlers/messages-read.spec.d.ts.map +1 -1
  359. package/dist/types/tests/handlers/messages-subscribe.spec.d.ts.map +1 -1
  360. package/dist/types/tests/handlers/protocols-configure.spec.d.ts.map +1 -1
  361. package/dist/types/tests/handlers/protocols-query.spec.d.ts.map +1 -1
  362. package/dist/types/tests/handlers/records-count.spec.d.ts.map +1 -1
  363. package/dist/types/tests/handlers/records-delete.spec.d.ts.map +1 -1
  364. package/dist/types/tests/handlers/records-query.spec.d.ts.map +1 -1
  365. package/dist/types/tests/handlers/records-read.spec.d.ts.map +1 -1
  366. package/dist/types/tests/handlers/records-subscribe.spec.d.ts.map +1 -1
  367. package/dist/types/tests/handlers/records-write.spec.d.ts.map +1 -1
  368. package/dist/types/tests/scenarios/deleted-record.spec.d.ts.map +1 -1
  369. package/dist/types/tests/scenarios/end-to-end-tests.spec.d.ts.map +1 -1
  370. package/dist/types/tests/scenarios/nested-roles.spec.d.ts.map +1 -1
  371. package/dist/types/tests/scenarios/subscriptions.spec.d.ts.map +1 -1
  372. package/dist/types/tests/store/message-store.spec.d.ts.map +1 -1
  373. package/dist/types/tests/test-event-stream.d.ts +1 -1
  374. package/dist/types/tests/test-event-stream.d.ts.map +1 -1
  375. package/dist/types/tests/test-stores.d.ts +5 -4
  376. package/dist/types/tests/test-stores.d.ts.map +1 -1
  377. package/dist/types/tests/test-suite.d.ts +1 -2
  378. package/dist/types/tests/test-suite.d.ts.map +1 -1
  379. package/dist/types/tests/utils/test-data-generator.d.ts +20 -1
  380. package/dist/types/tests/utils/test-data-generator.d.ts.map +1 -1
  381. package/dist/types/tests/utils/test-validation-state-reader.d.ts +15 -0
  382. package/dist/types/tests/utils/test-validation-state-reader.d.ts.map +1 -0
  383. package/package.json +2 -2
  384. package/src/core/core-protocol.ts +3 -3
  385. package/src/core/dwn-constant.ts +7 -1
  386. package/src/core/dwn-error.ts +12 -4
  387. package/src/core/grant-authorization.ts +11 -20
  388. package/src/core/message-reply.ts +6 -5
  389. package/src/core/messages-grant-authorization.ts +37 -70
  390. package/src/core/protocol-authorization-action.ts +29 -38
  391. package/src/core/protocol-authorization-validation.ts +39 -96
  392. package/src/core/protocol-authorization.ts +56 -202
  393. package/src/core/protocols-grant-authorization.ts +9 -9
  394. package/src/core/recording-validation-state-reader.ts +130 -0
  395. package/src/core/records-grant-authorization.ts +16 -16
  396. package/src/core/replication-apply.ts +172 -32
  397. package/src/core/resumable-task-manager.ts +10 -8
  398. package/src/core/validation-state-reader.ts +350 -0
  399. package/src/dwn.ts +285 -192
  400. package/src/enums/dwn-interface-method.ts +0 -1
  401. package/src/event-stream/durable-event-log.ts +509 -0
  402. package/src/event-stream/event-emitter-wake-publisher.ts +34 -0
  403. package/src/handlers/messages-query.ts +203 -0
  404. package/src/handlers/messages-read.ts +9 -10
  405. package/src/handlers/messages-subscribe.ts +12 -13
  406. package/src/handlers/protocols-configure.ts +37 -58
  407. package/src/handlers/protocols-query.ts +1 -1
  408. package/src/handlers/records-count.ts +24 -17
  409. package/src/handlers/records-delete.ts +29 -27
  410. package/src/handlers/records-query.ts +38 -17
  411. package/src/handlers/records-read.ts +63 -50
  412. package/src/handlers/records-subscribe.ts +132 -19
  413. package/src/handlers/records-write.ts +77 -168
  414. package/src/index.ts +14 -17
  415. package/src/interfaces/messages-query.ts +70 -0
  416. package/src/interfaces/protocols-configure.ts +12 -4
  417. package/src/interfaces/protocols-query.ts +4 -5
  418. package/src/interfaces/records-count.ts +9 -4
  419. package/src/interfaces/records-delete.ts +25 -5
  420. package/src/interfaces/records-query.ts +9 -4
  421. package/src/interfaces/records-read.ts +4 -4
  422. package/src/interfaces/records-subscribe.ts +9 -4
  423. package/src/interfaces/records-write.ts +41 -13
  424. package/src/protocols/permissions.ts +32 -52
  425. package/src/store/index-level.ts +30 -9
  426. package/src/store/level-wrapper.ts +9 -1
  427. package/src/store/message-store-level.ts +757 -47
  428. package/src/store/storage-controller.ts +74 -63
  429. package/src/types/message-store.ts +45 -2
  430. package/src/types/message-types.ts +3 -1
  431. package/src/types/messages-types.ts +26 -45
  432. package/src/types/method-handler.ts +3 -3
  433. package/src/types/permission-types.ts +1 -1
  434. package/src/types/subscriptions.ts +53 -42
  435. package/src/types/validation-state-reader.ts +127 -0
  436. package/src/utils/messages.ts +25 -1
  437. package/src/utils/record-limit-occupancy.ts +377 -0
  438. package/src/utils/records.ts +69 -13
  439. package/src/utils/replication.ts +122 -0
  440. package/dist/esm/src/core/record-chain.js +0 -64
  441. package/dist/esm/src/core/record-chain.js.map +0 -1
  442. package/dist/esm/src/event-stream/event-emitter-event-log.js +0 -334
  443. package/dist/esm/src/event-stream/event-emitter-event-log.js.map +0 -1
  444. package/dist/esm/src/handlers/messages-sync.js +0 -278
  445. package/dist/esm/src/handlers/messages-sync.js.map +0 -1
  446. package/dist/esm/src/interfaces/messages-sync.js.map +0 -1
  447. package/dist/esm/src/smt/smt-store-level.js +0 -103
  448. package/dist/esm/src/smt/smt-store-level.js.map +0 -1
  449. package/dist/esm/src/smt/smt-store-memory.js +0 -41
  450. package/dist/esm/src/smt/smt-store-memory.js.map +0 -1
  451. package/dist/esm/src/smt/smt-utils.js +0 -129
  452. package/dist/esm/src/smt/smt-utils.js.map +0 -1
  453. package/dist/esm/src/smt/sparse-merkle-tree.js +0 -577
  454. package/dist/esm/src/smt/sparse-merkle-tree.js.map +0 -1
  455. package/dist/esm/src/state-index/state-index-level.js +0 -191
  456. package/dist/esm/src/state-index/state-index-level.js.map +0 -1
  457. package/dist/esm/src/types/smt-types.js +0 -5
  458. package/dist/esm/src/types/smt-types.js.map +0 -1
  459. package/dist/esm/src/types/state-index.js +0 -2
  460. package/dist/esm/src/types/state-index.js.map +0 -1
  461. package/dist/esm/tests/event-emitter-event-log.spec.js +0 -499
  462. package/dist/esm/tests/event-emitter-event-log.spec.js.map +0 -1
  463. package/dist/esm/tests/handlers/messages-sync.spec.js +0 -1088
  464. package/dist/esm/tests/handlers/messages-sync.spec.js.map +0 -1
  465. package/dist/esm/tests/smt/smt-store-level.spec.js +0 -132
  466. package/dist/esm/tests/smt/smt-store-level.spec.js.map +0 -1
  467. package/dist/esm/tests/smt/sparse-merkle-tree.spec.js +0 -732
  468. package/dist/esm/tests/smt/sparse-merkle-tree.spec.js.map +0 -1
  469. package/dist/esm/tests/state-index/state-index-level.spec.js +0 -245
  470. package/dist/esm/tests/state-index/state-index-level.spec.js.map +0 -1
  471. package/dist/types/src/core/record-chain.d.ts +0 -24
  472. package/dist/types/src/core/record-chain.d.ts.map +0 -1
  473. package/dist/types/src/event-stream/event-emitter-event-log.d.ts +0 -80
  474. package/dist/types/src/event-stream/event-emitter-event-log.d.ts.map +0 -1
  475. package/dist/types/src/handlers/messages-sync.d.ts +0 -39
  476. package/dist/types/src/handlers/messages-sync.d.ts.map +0 -1
  477. package/dist/types/src/interfaces/messages-sync.d.ts +0 -20
  478. package/dist/types/src/interfaces/messages-sync.d.ts.map +0 -1
  479. package/dist/types/src/smt/smt-store-level.d.ts +0 -32
  480. package/dist/types/src/smt/smt-store-level.d.ts.map +0 -1
  481. package/dist/types/src/smt/smt-store-memory.d.ts +0 -22
  482. package/dist/types/src/smt/smt-store-memory.d.ts.map +0 -1
  483. package/dist/types/src/smt/smt-utils.d.ts +0 -58
  484. package/dist/types/src/smt/smt-utils.d.ts.map +0 -1
  485. package/dist/types/src/smt/sparse-merkle-tree.d.ts +0 -124
  486. package/dist/types/src/smt/sparse-merkle-tree.d.ts.map +0 -1
  487. package/dist/types/src/state-index/state-index-level.d.ts +0 -83
  488. package/dist/types/src/state-index/state-index-level.d.ts.map +0 -1
  489. package/dist/types/src/types/smt-types.d.ts +0 -81
  490. package/dist/types/src/types/smt-types.d.ts.map +0 -1
  491. package/dist/types/src/types/state-index.d.ts +0 -90
  492. package/dist/types/src/types/state-index.d.ts.map +0 -1
  493. package/dist/types/tests/event-emitter-event-log.spec.d.ts +0 -2
  494. package/dist/types/tests/event-emitter-event-log.spec.d.ts.map +0 -1
  495. package/dist/types/tests/handlers/messages-sync.spec.d.ts +0 -2
  496. package/dist/types/tests/handlers/messages-sync.spec.d.ts.map +0 -1
  497. package/dist/types/tests/smt/smt-store-level.spec.d.ts +0 -2
  498. package/dist/types/tests/smt/smt-store-level.spec.d.ts.map +0 -1
  499. package/dist/types/tests/smt/sparse-merkle-tree.spec.d.ts +0 -2
  500. package/dist/types/tests/smt/sparse-merkle-tree.spec.d.ts.map +0 -1
  501. package/dist/types/tests/state-index/state-index-level.spec.d.ts +0 -2
  502. package/dist/types/tests/state-index/state-index-level.spec.d.ts.map +0 -1
  503. package/src/core/record-chain.ts +0 -99
  504. package/src/event-stream/event-emitter-event-log.ts +0 -430
  505. package/src/handlers/messages-sync.ts +0 -403
  506. package/src/interfaces/messages-sync.ts +0 -69
  507. package/src/smt/smt-store-level.ts +0 -143
  508. package/src/smt/smt-store-memory.ts +0 -53
  509. package/src/smt/smt-utils.ts +0 -149
  510. package/src/smt/sparse-merkle-tree.ts +0 -698
  511. package/src/state-index/state-index-level.ts +0 -239
  512. package/src/types/smt-types.ts +0 -95
  513. package/src/types/state-index.ts +0 -100
@@ -1,14 +1,16 @@
1
1
  import * as block from 'multiformats/block';
2
2
  import * as cbor from '@ipld/dag-cbor';
3
- import { BlockstoreLevel } from './blockstore-level.js';
4
3
  import { Cid } from '../utils/cid.js';
5
4
  import { CID } from 'multiformats/cid';
6
- import { createLevelDatabase } from './level-wrapper.js';
7
5
  import { executeUnlessAborted } from '../utils/abort.js';
6
+ import { FilterUtility } from '../utils/filter.js';
8
7
  import { IndexLevel } from './index-level.js';
9
8
  import { Message } from '../core/message.js';
9
+ import { Replication } from '../utils/replication.js';
10
10
  import { sha256 } from 'multiformats/hashes/sha2';
11
11
  import { SortDirection } from '../types/query-types.js';
12
+ import { createLevelDatabase, LevelWrapper } from './level-wrapper.js';
13
+ import { DwnError, DwnErrorCode } from '../core/dwn-error.js';
12
14
  /**
13
15
  * Default compound indexes that cover the most common DWN query patterns.
14
16
  * These are automatically registered unless overridden via config.
@@ -54,52 +56,150 @@ const DEFAULT_COMPOUND_INDEXES = [
54
56
  },
55
57
  ];
56
58
  /**
57
- * A simple implementation of {@link MessageStore} that works in both the browser and server-side.
58
- * Leverages LevelDB under the hood.
59
+ * Sublevel names under the single Level root. Every mutation touching multiple partitions is
60
+ * committed as one atomic batch on the root, so store↔log divergence is impossible by construction.
61
+ */
62
+ const BLOCKS_PARTITION = 'blocks';
63
+ const INDEX_PARTITION = 'idx';
64
+ const LOG_PARTITION = 'log';
65
+ const CID_TO_SEQ_PARTITION = 'cid';
66
+ const FINGERPRINT_PARTITION = 'fp';
67
+ const HEADS_PARTITION = 'heads';
68
+ const META_PARTITION = 'meta';
69
+ const EPOCH_KEY = 'epoch';
70
+ const CURRENT_PARTITIONS = new Set([
71
+ BLOCKS_PARTITION,
72
+ INDEX_PARTITION,
73
+ LOG_PARTITION,
74
+ CID_TO_SEQ_PARTITION,
75
+ FINGERPRINT_PARTITION,
76
+ HEADS_PARTITION,
77
+ META_PARTITION,
78
+ ]);
79
+ /**
80
+ * A {@link MessageStore} and {@link ReplicationFeedReader} implementation that works in both the
81
+ * browser and server-side, backed by a SINGLE LevelDB root: message blocks, query indexes, the
82
+ * per-tenant replication log, the cid→seq index, fingerprint domains, the tenant counters, and
83
+ * the store epoch are all sublevels of one Level instance, so every mutation commits as one fully
84
+ * atomic batch.
85
+ *
86
+ * A per-tenant async write mutex spans seq assignment through batch write, so commit order equals
87
+ * seq order by construction. Seq assignment is gap-free (a failed batch never persists the head),
88
+ * while the readable log stays sparse after compaction and under filters — readers never assume
89
+ * contiguity.
59
90
  */
60
91
  export class MessageStoreLevel {
61
92
  config;
62
- blockstore;
63
- index;
93
+ wakePublisher;
94
+ partitionsPromise;
95
+ epochPromise;
96
+ writeLocks = new Map();
64
97
  /**
65
98
  * @param {MessageStoreLevelConfig} config
66
- * @param {string} config.blockstoreLocation - must be a directory path (relative or absolute) where
99
+ * @param {string} config.location - must be a directory path (relative or absolute) where
67
100
  * LevelDB will store its files, or in browsers, the name of the
68
101
  * {@link https://developer.mozilla.org/en-US/docs/Web/API/IDBDatabase IDBDatabase} to be opened.
69
- * @param {string} config.indexLocation - same as config.blockstoreLocation
70
102
  * @param {CompoundIndexDefinition[]} config.compoundIndexes - compound indexes to register.
71
103
  * Defaults to DEFAULT_COMPOUND_INDEXES which cover the most common DWN query patterns.
104
+ * @param {WakePublisher} config.wakePublisher - optional bus for store-owned wake publication.
72
105
  */
73
106
  constructor(config = {}) {
74
107
  this.config = {
75
- blockstoreLocation: 'MESSAGESTORE',
76
- indexLocation: 'INDEX',
108
+ location: 'MESSAGESTORE',
77
109
  createLevelDatabase,
78
110
  ...config
79
111
  };
80
- this.blockstore = new BlockstoreLevel({
81
- location: this.config.blockstoreLocation,
82
- createLevelDatabase: this.config.createLevelDatabase,
83
- });
84
- this.index = new IndexLevel({
85
- location: this.config.indexLocation,
86
- createLevelDatabase: this.config.createLevelDatabase,
87
- compoundIndexes: this.config.compoundIndexes ?? DEFAULT_COMPOUND_INDEXES,
88
- });
112
+ this.wakePublisher = this.config.wakePublisher;
89
113
  }
90
114
  async open() {
91
- await this.blockstore.open();
92
- await this.index.open();
115
+ const partitions = await this.partitions();
116
+ await partitions.root.open();
117
+ await this.assertNoPreSubstrateLayout(partitions);
118
+ await this.getEpoch();
93
119
  }
94
120
  async close() {
95
- await this.blockstore.close();
96
- await this.index.close();
121
+ if (this.partitionsPromise === undefined) {
122
+ return;
123
+ }
124
+ const partitions = await this.partitionsPromise;
125
+ await partitions.root.close();
126
+ }
127
+ /**
128
+ * Lazily creates the single Level root and its partition handles.
129
+ */
130
+ async partitions() {
131
+ this.partitionsPromise ??= this.createPartitions().catch((error) => {
132
+ this.partitionsPromise = undefined;
133
+ throw error;
134
+ });
135
+ return this.partitionsPromise;
136
+ }
137
+ async createPartitions() {
138
+ const location = this.config.location;
139
+ const db = await this.config.createLevelDatabase(location, { keyEncoding: 'utf8', valueEncoding: 'utf8' });
140
+ const root = new LevelWrapper({ location, createLevelDatabase: this.config.createLevelDatabase, keyEncoding: 'utf8', valueEncoding: 'utf8' }, db);
141
+ // A binary-valued view over the same underlying Level instance — its partitions carry their
142
+ // own value encoding, so block bytes and JSON strings coexist in one atomic batch domain.
143
+ const binaryView = new LevelWrapper({ location, createLevelDatabase: this.config.createLevelDatabase, keyEncoding: 'utf8', valueEncoding: 'binary' }, db);
144
+ return {
145
+ root,
146
+ blocks: await binaryView.partition(BLOCKS_PARTITION),
147
+ log: await root.partition(LOG_PARTITION),
148
+ cidToSeq: await root.partition(CID_TO_SEQ_PARTITION),
149
+ fingerprints: await root.partition(FINGERPRINT_PARTITION),
150
+ heads: await root.partition(HEADS_PARTITION),
151
+ meta: await root.partition(META_PARTITION),
152
+ };
153
+ }
154
+ /**
155
+ * The query index over the shared root. Constructed lazily alongside the partitions.
156
+ */
157
+ indexPromise;
158
+ async index() {
159
+ this.indexPromise ??= (async () => {
160
+ const partitions = await this.partitions();
161
+ const indexRoot = await partitions.root.partition(INDEX_PARTITION);
162
+ return new IndexLevel({
163
+ location: this.config.location,
164
+ createLevelDatabase: this.config.createLevelDatabase,
165
+ compoundIndexes: this.config.compoundIndexes ?? DEFAULT_COMPOUND_INDEXES,
166
+ }, indexRoot);
167
+ })().catch((error) => {
168
+ this.indexPromise = undefined;
169
+ throw error;
170
+ });
171
+ return this.indexPromise;
172
+ }
173
+ /**
174
+ * Returns the persisted store epoch, generating and persisting a fresh `crypto.randomUUID()`
175
+ * at first open. Replaced only by `clear()` — a full local reset with no surviving cursors.
176
+ */
177
+ async epoch() {
178
+ return this.getEpoch();
179
+ }
180
+ async getEpoch() {
181
+ this.epochPromise ??= this.initializeEpoch().catch((error) => {
182
+ this.epochPromise = undefined;
183
+ throw error;
184
+ });
185
+ return this.epochPromise;
186
+ }
187
+ async initializeEpoch() {
188
+ const partitions = await this.partitions();
189
+ const existing = await partitions.meta.get(EPOCH_KEY);
190
+ if (existing !== undefined) {
191
+ return existing;
192
+ }
193
+ const freshEpoch = crypto.randomUUID();
194
+ await partitions.meta.put(EPOCH_KEY, freshEpoch);
195
+ return freshEpoch;
97
196
  }
98
197
  async get(tenant, cidString, options) {
99
198
  options?.signal?.throwIfAborted();
100
- const partition = await executeUnlessAborted(this.blockstore.partition(tenant), options?.signal);
199
+ const partitions = await executeUnlessAborted(this.partitions(), options?.signal);
200
+ const tenantBlocks = await executeUnlessAborted(partitions.blocks.partition(tenant), options?.signal);
101
201
  const cid = CID.parse(cidString);
102
- const bytes = await partition.get(cid, options);
202
+ const bytes = await tenantBlocks.get(cid.toString(), options);
103
203
  if (!bytes) {
104
204
  return undefined;
105
205
  }
@@ -112,7 +212,8 @@ export class MessageStoreLevel {
112
212
  // creates the query options including sorting and pagination.
113
213
  // this adds 1 to the limit if provided, that way we can check to see if there are additional results and provide a return cursor.
114
214
  const queryOptions = MessageStoreLevel.buildQueryOptions(messageSort, pagination);
115
- const results = await this.index.query(tenant, filters, queryOptions, options);
215
+ const index = await this.index();
216
+ const results = await index.query(tenant, filters, queryOptions, options);
116
217
  let cursor;
117
218
  // checks to see if the returned results are greater than the limit, which would indicate additional results.
118
219
  if (pagination?.limit !== undefined && pagination.limit < results.length) {
@@ -134,7 +235,8 @@ export class MessageStoreLevel {
134
235
  async count(tenant, filters, messageSort, options) {
135
236
  options?.signal?.throwIfAborted();
136
237
  const queryOptions = MessageStoreLevel.buildQueryOptions(messageSort);
137
- return this.index.count(tenant, filters, queryOptions, options);
238
+ const index = await this.index();
239
+ return index.count(tenant, filters, queryOptions, options);
138
240
  }
139
241
  /**
140
242
  * Builds the IndexLevel QueryOptions object given MessageStore sort and pagination parameters.
@@ -164,29 +266,421 @@ export class MessageStoreLevel {
164
266
  }
165
267
  return { sortDirection, sortProperty, limit, cursor };
166
268
  }
167
- async delete(tenant, cidString, options) {
168
- options?.signal?.throwIfAborted();
169
- const partition = await executeUnlessAborted(this.blockstore.partition(tenant), options?.signal);
170
- const cid = CID.parse(cidString);
171
- await partition.delete(cid, options);
172
- await this.index.delete(tenant, cidString, options);
173
- }
174
269
  async put(tenant, message, indexes, options) {
175
270
  options?.signal?.throwIfAborted();
176
- const partition = await executeUnlessAborted(this.blockstore.partition(tenant), options?.signal);
271
+ const partitions = await executeUnlessAborted(this.partitions(), options?.signal);
272
+ const index = await executeUnlessAborted(this.index(), options?.signal);
177
273
  const encodedMessageBlock = await executeUnlessAborted(block.encode({ value: message, codec: cbor, hasher: sha256 }), options?.signal);
178
274
  // MessageStore data may contain `encodedData` which is not taken into account when calculating the blockCID as it is optional data.
179
- const messageCid = Cid.parseCid(await Message.getCid(message));
180
- await partition.put(messageCid, encodedMessageBlock.bytes, options);
181
- const messageCidString = messageCid.toString();
182
- await this.index.put(tenant, messageCidString, indexes, options);
275
+ const messageCid = Cid.parseCid(await Message.getCid(message)).toString();
276
+ return this.withTenantWriteLock(tenant, async () => {
277
+ options?.signal?.throwIfAborted();
278
+ const tenantCidToSeq = await partitions.cidToSeq.partition(tenant);
279
+ const existingSeq = await tenantCidToSeq.get(messageCid, options);
280
+ if (existingSeq !== undefined) {
281
+ // Duplicates mutate nothing and publish no wake.
282
+ return { status: 'duplicate' };
283
+ }
284
+ const head = await this.getHead(partitions, tenant);
285
+ const seq = head + 1n;
286
+ const fingerprintScopes = Replication.computeFingerprintScopes(message, indexes);
287
+ const logEntry = {
288
+ seq: seq.toString(),
289
+ messageCid,
290
+ indexes,
291
+ fingerprintScopes,
292
+ };
293
+ const tenantBlocks = await partitions.blocks.partition(tenant);
294
+ const blockOperation = tenantBlocks.createOperation({ type: 'put', key: messageCid, value: encodedMessageBlock.bytes });
295
+ const tenantLog = await partitions.log.partition(tenant);
296
+ const operations = [
297
+ blockOperation,
298
+ ...await index.createPutOperations(tenant, messageCid, indexes),
299
+ tenantLog.createOperation({ type: 'put', key: Replication.encodePositionKey(seq), value: JSON.stringify(logEntry) }),
300
+ tenantCidToSeq.createOperation({ type: 'put', key: messageCid, value: seq.toString() }),
301
+ partitions.heads.createOperation({ type: 'put', key: tenant, value: seq.toString() }),
302
+ ...await this.createFingerprintFoldOperations(partitions, tenant, messageCid, fingerprintScopes),
303
+ ];
304
+ await partitions.root.batch(operations);
305
+ // Store-owned wake, post-commit, for `inserted` only.
306
+ this.publishWake(tenant, seq);
307
+ return {
308
+ status: 'inserted',
309
+ position: await this.buildToken(tenant, seq, messageCid),
310
+ };
311
+ });
312
+ }
313
+ async updateIndexes(tenant, messageCid, indexes, options) {
314
+ options?.signal?.throwIfAborted();
315
+ const partitions = await executeUnlessAborted(this.partitions(), options?.signal);
316
+ const index = await executeUnlessAborted(this.index(), options?.signal);
317
+ await this.withTenantWriteLock(tenant, async () => {
318
+ const { entry, positionKey, tenantLog } = await this.getLogEntryForMutation(partitions, tenant, messageCid, DwnErrorCode.MessageStoreUpdateIndexesMessageNotFound);
319
+ const storedMessage = await this.readStoredMessage(partitions, tenant, messageCid, DwnErrorCode.MessageStoreUpdateIndexesMessageNotFound, options);
320
+ Replication.assertFingerprintScopesUntouched(entry.fingerprintScopes, storedMessage, messageCid, indexes);
321
+ // Same row, same seq: indexes replaced; fingerprint scopes carried forward verbatim;
322
+ // fingerprints and head stay untouched.
323
+ const updatedEntry = { ...entry, indexes };
324
+ const operations = [
325
+ ...await index.createDeleteOperations(tenant, messageCid),
326
+ ...await index.createPutOperations(tenant, messageCid, indexes),
327
+ tenantLog.createOperation({ type: 'put', key: positionKey, value: JSON.stringify(updatedEntry) }),
328
+ ];
329
+ await partitions.root.batch(operations);
330
+ });
331
+ }
332
+ async updateMessageAndIndexes(tenant, messageCid, message, indexes, options) {
333
+ options?.signal?.throwIfAborted();
334
+ const computedMessageCid = Cid.parseCid(await Message.getCid(message)).toString();
335
+ const normalizedMessageCid = Cid.parseCid(messageCid).toString();
336
+ if (computedMessageCid !== normalizedMessageCid) {
337
+ throw new DwnError(DwnErrorCode.MessageStoreUpdateMessageAndIndexesCidMismatch, `replacement message CID ${computedMessageCid} does not match target CID ${normalizedMessageCid}`);
338
+ }
339
+ const partitions = await executeUnlessAborted(this.partitions(), options?.signal);
340
+ const index = await executeUnlessAborted(this.index(), options?.signal);
341
+ const encodedMessageBlock = await executeUnlessAborted(block.encode({ value: message, codec: cbor, hasher: sha256 }), options?.signal);
342
+ await this.withTenantWriteLock(tenant, async () => {
343
+ const { entry, positionKey, tenantLog } = await this.getLogEntryForMutation(partitions, tenant, normalizedMessageCid, DwnErrorCode.MessageStoreUpdateMessageAndIndexesMessageNotFound);
344
+ Replication.assertFingerprintScopesUntouched(entry.fingerprintScopes, message, normalizedMessageCid, indexes);
345
+ const updatedEntry = { ...entry, indexes };
346
+ const tenantBlocks = await partitions.blocks.partition(tenant);
347
+ const blockOperation = tenantBlocks.createOperation({ type: 'put', key: normalizedMessageCid, value: encodedMessageBlock.bytes });
348
+ const operations = [
349
+ blockOperation,
350
+ ...await index.createDeleteOperations(tenant, normalizedMessageCid),
351
+ ...await index.createPutOperations(tenant, normalizedMessageCid, indexes),
352
+ tenantLog.createOperation({ type: 'put', key: positionKey, value: JSON.stringify(updatedEntry) }),
353
+ ];
354
+ await partitions.root.batch(operations);
355
+ });
356
+ }
357
+ async delete(tenant, cidString, options) {
358
+ options?.signal?.throwIfAborted();
359
+ const partitions = await executeUnlessAborted(this.partitions(), options?.signal);
360
+ const index = await executeUnlessAborted(this.index(), options?.signal);
361
+ const messageCid = CID.parse(cidString).toString();
362
+ await this.withTenantWriteLock(tenant, async () => {
363
+ const tenantCidToSeq = await partitions.cidToSeq.partition(tenant);
364
+ const seqString = await tenantCidToSeq.get(messageCid, options);
365
+ if (seqString === undefined) {
366
+ // Idempotent no-op — the row does not exist.
367
+ return;
368
+ }
369
+ const seq = BigInt(seqString);
370
+ const positionKey = Replication.encodePositionKey(seq);
371
+ const tenantLog = await partitions.log.partition(tenant);
372
+ const serializedEntry = await tenantLog.get(positionKey);
373
+ if (serializedEntry === undefined) {
374
+ throw new DwnError(DwnErrorCode.MessageStoreDeleteLogEntryMissing, `cid index for tenant ${tenant} points to missing log entry at seq ${seqString} (CID ${messageCid})`);
375
+ }
376
+ const entry = JSON.parse(serializedEntry);
377
+ const tenantBlocks = await partitions.blocks.partition(tenant);
378
+ const blockOperation = tenantBlocks.createOperation({ type: 'del', key: messageCid });
379
+ // XOR is self-inverse: folding the persisted scopes again removes the row's contribution.
380
+ const operations = [
381
+ blockOperation,
382
+ ...await index.createDeleteOperations(tenant, messageCid),
383
+ tenantLog.createOperation({ type: 'del', key: positionKey }),
384
+ tenantCidToSeq.createOperation({ type: 'del', key: messageCid }),
385
+ ...await this.createFingerprintFoldOperations(partitions, tenant, messageCid, entry.fingerprintScopes),
386
+ ];
387
+ await partitions.root.batch(operations);
388
+ });
183
389
  }
184
390
  /**
185
- * deletes everything in the underlying blockstore and indices.
391
+ * deletes everything in the underlying store, then persists a fresh epoch — a full local
392
+ * reset after which no previously issued cursor is valid.
186
393
  */
187
394
  async clear() {
188
- await this.blockstore.clear();
189
- await this.index.clear();
395
+ const partitions = await this.partitions();
396
+ await partitions.root.clear();
397
+ this.epochPromise = undefined;
398
+ await this.getEpoch();
399
+ }
400
+ // ---------------------------------------------------------------------------
401
+ // ReplicationFeedReader
402
+ // ---------------------------------------------------------------------------
403
+ async logRead(tenant, options = {}) {
404
+ const partitions = await this.partitions();
405
+ const { cursor, limit, filters } = options;
406
+ // Head-captured-first: the per-tenant write mutex serializes commits in position order, so an
407
+ // observed head H is a visibility barrier — every position <= H is already committed when the
408
+ // range scans below run, and anything committed afterward has a position > H and waits for
409
+ // the next page.
410
+ const head = await this.getHead(partitions, tenant);
411
+ if (cursor !== undefined) {
412
+ await this.validateCursor(partitions, tenant, cursor, head);
413
+ }
414
+ const startPosition = cursor === undefined ? 0n : BigInt(cursor.position);
415
+ if (head === 0n) {
416
+ // Nothing to scan — caught up at the input position.
417
+ return { events: [], cursor, drained: true };
418
+ }
419
+ const maxResults = limit ?? Number.MAX_SAFE_INTEGER;
420
+ if (maxResults <= 0) {
421
+ return { events: [], cursor, drained: startPosition >= head };
422
+ }
423
+ if (startPosition >= head) {
424
+ // Nothing to scan — caught up at the input position.
425
+ return { events: [], cursor, drained: true };
426
+ }
427
+ const tenantLog = await partitions.log.partition(tenant);
428
+ const tenantBlocks = await partitions.blocks.partition(tenant);
429
+ const iteratorRange = { gt: Replication.encodePositionKey(startPosition), lte: Replication.encodePositionKey(head) };
430
+ const logIterator = tenantLog.iterator(iteratorRange);
431
+ const events = [];
432
+ let drained = true;
433
+ let lastScannedPosition = startPosition;
434
+ let lastDeliveredPosition;
435
+ let lastDeliveredMessageCid;
436
+ for await (const [positionKey, serializedEntry] of logIterator) {
437
+ const position = BigInt(positionKey);
438
+ lastScannedPosition = position;
439
+ const entry = JSON.parse(serializedEntry);
440
+ const event = await this.readEventFromLogEntry(tenantBlocks, position, entry, filters);
441
+ if (event === undefined) {
442
+ continue;
443
+ }
444
+ events.push(event);
445
+ lastDeliveredPosition = position;
446
+ lastDeliveredMessageCid = event.messageCid;
447
+ if (events.length >= maxResults) {
448
+ drained = position >= head;
449
+ break;
450
+ }
451
+ }
452
+ // High-water cursor: the highest position scanned — head when the scan completed, the stop
453
+ // position otherwise. `messageCid` is set only when the cursor position is a delivered row.
454
+ const cursorPosition = drained ? head : lastScannedPosition;
455
+ const cursorMessageCid = lastDeliveredPosition === cursorPosition ? lastDeliveredMessageCid : undefined;
456
+ const resultCursor = await this.buildToken(tenant, cursorPosition, cursorMessageCid);
457
+ return { events, cursor: resultCursor, drained };
458
+ }
459
+ async readEventFromLogEntry(tenantBlocks, position, entry, filters) {
460
+ if (filters !== undefined && filters.length > 0 && !FilterUtility.matchAnyFilter(entry.indexes, filters)) {
461
+ return undefined;
462
+ }
463
+ const bytes = await tenantBlocks.get(entry.messageCid);
464
+ if (bytes === undefined) {
465
+ // The row was deleted after the head capture; skip — its positions are gone with it.
466
+ return undefined;
467
+ }
468
+ const decodedBlock = await block.decode({ bytes, codec: cbor, hasher: sha256 });
469
+ const message = decodedBlock.value;
470
+ return {
471
+ seq: entry.seq,
472
+ position: position.toString(),
473
+ event: { message },
474
+ indexes: entry.indexes,
475
+ messageCid: entry.messageCid,
476
+ };
477
+ }
478
+ async logBounds(tenant) {
479
+ const partitions = await this.partitions();
480
+ const head = await this.getHead(partitions, tenant);
481
+ if (head === 0n) {
482
+ return undefined;
483
+ }
484
+ // The log is the live set — replay from zero is always available, so the oldest resumable
485
+ // position is always 0 regardless of compaction.
486
+ const oldest = await this.buildToken(tenant, 0n);
487
+ // Resolve the head position for the latest token's messageCid.
488
+ const headKey = Replication.encodePositionKey(head);
489
+ const tenantLog = await partitions.log.partition(tenant);
490
+ let headMessageCid;
491
+ const headLogEntry = await tenantLog.get(headKey);
492
+ if (headLogEntry !== undefined) {
493
+ headMessageCid = JSON.parse(headLogEntry).messageCid;
494
+ }
495
+ const latest = await this.buildToken(tenant, head, headMessageCid);
496
+ return { oldest, latest };
497
+ }
498
+ async fingerprint(tenant, scopes) {
499
+ const partitions = await this.partitions();
500
+ const tenantFingerprints = await partitions.fingerprints.partition(tenant);
501
+ let composed = Replication.emptyFingerprint();
502
+ for (const scope of scopes) {
503
+ const storedHex = await tenantFingerprints.get(MessageStoreLevel.fingerprintKey(scope));
504
+ if (storedHex !== undefined) {
505
+ composed = Replication.xorFingerprint(composed, Replication.hexToFingerprint(storedHex));
506
+ }
507
+ }
508
+ return Replication.fingerprintToHex(composed);
509
+ }
510
+ // ---------------------------------------------------------------------------
511
+ // Internals
512
+ // ---------------------------------------------------------------------------
513
+ /**
514
+ * Serializes all log-mutating operations for a tenant: the lock spans seq assignment through
515
+ * batch write, so commit order equals seq order (synchronous assignment alone is insufficient —
516
+ * a later batch could land first).
517
+ */
518
+ async withTenantWriteLock(tenant, task) {
519
+ const previous = this.writeLocks.get(tenant) ?? Promise.resolve();
520
+ let release;
521
+ const current = new Promise((resolve) => { release = resolve; });
522
+ this.writeLocks.set(tenant, current);
523
+ await previous;
524
+ try {
525
+ return await task();
526
+ }
527
+ finally {
528
+ release();
529
+ if (this.writeLocks.get(tenant) === current) {
530
+ this.writeLocks.delete(tenant);
531
+ }
532
+ }
533
+ }
534
+ /**
535
+ * Reads the tenant's counter high-water (the highest position ever issued), `0n` when unused.
536
+ */
537
+ async getHead(partitions, tenant) {
538
+ const headString = await partitions.heads.get(tenant);
539
+ return headString === undefined ? 0n : BigInt(headString);
540
+ }
541
+ async assertNoPreSubstrateLayout(partitions) {
542
+ if (await partitions.meta.get(EPOCH_KEY) !== undefined) {
543
+ return;
544
+ }
545
+ for await (const key of partitions.root.keys()) {
546
+ const partition = MessageStoreLevel.parseSublevelPartition(key);
547
+ if (partition === undefined || CURRENT_PARTITIONS.has(partition)) {
548
+ continue;
549
+ }
550
+ throw new DwnError(DwnErrorCode.MessageStorePreSubstrateLayout, `message store location ${this.config.location} contains pre-substrate Level data; reset the store before opening it`);
551
+ }
552
+ }
553
+ static parseSublevelPartition(key) {
554
+ if (!key.startsWith('!')) {
555
+ return undefined;
556
+ }
557
+ const end = key.indexOf('!', 1);
558
+ return end === -1 ? undefined : key.slice(1, end);
559
+ }
560
+ async readStoredMessage(partitions, tenant, messageCid, notFoundErrorCode, options) {
561
+ const tenantBlocks = await partitions.blocks.partition(tenant);
562
+ const bytes = await tenantBlocks.get(messageCid, options);
563
+ if (bytes === undefined) {
564
+ throw new DwnError(notFoundErrorCode, `no message block found for tenant ${tenant} with CID ${messageCid}`);
565
+ }
566
+ const decodedBlock = await block.decode({ bytes, codec: cbor, hasher: sha256 });
567
+ return decodedBlock.value;
568
+ }
569
+ /**
570
+ * Validates a replication cursor against the tenant stream and persisted store epoch.
571
+ * Throws `EventLogProgressGap` with bounds metadata when the cursor cannot be replayed.
572
+ */
573
+ async validateCursor(partitions, tenant, cursor, head) {
574
+ const expectedStreamId = await Replication.deriveStreamId(tenant);
575
+ const reason = await this.validateCursorPosition(partitions, tenant, cursor, head, expectedStreamId);
576
+ if (reason === undefined) {
577
+ return;
578
+ }
579
+ const bounds = await this.logBounds(tenant);
580
+ const gapInfo = {
581
+ requested: cursor,
582
+ oldestAvailable: bounds?.oldest ?? cursor,
583
+ latestAvailable: bounds?.latest ?? cursor,
584
+ reason,
585
+ };
586
+ const error = new DwnError(DwnErrorCode.EventLogProgressGap, `progress token gap: ${reason}`);
587
+ error.gapInfo = gapInfo;
588
+ throw error;
589
+ }
590
+ async validateCursorPosition(partitions, tenant, cursor, head, expectedStreamId) {
591
+ if (cursor.streamId !== expectedStreamId) {
592
+ return 'stream_mismatch';
593
+ }
594
+ if (cursor.epoch !== await this.getEpoch()) {
595
+ return 'epoch_mismatch';
596
+ }
597
+ const cursorPosition = BigInt(cursor.position);
598
+ if (cursorPosition > head) {
599
+ return 'token_too_new';
600
+ }
601
+ if (cursor.messageCid === undefined) {
602
+ return undefined;
603
+ }
604
+ const positionMessageCid = await this.getMessageCidAtPosition(partitions, tenant, cursorPosition);
605
+ if (positionMessageCid !== undefined && positionMessageCid !== cursor.messageCid) {
606
+ return 'message_mismatch';
607
+ }
608
+ return undefined;
609
+ }
610
+ async getMessageCidAtPosition(partitions, tenant, position) {
611
+ if (position <= 0n) {
612
+ return undefined;
613
+ }
614
+ const positionKey = Replication.encodePositionKey(position);
615
+ const tenantLog = await partitions.log.partition(tenant);
616
+ const serializedEntry = await tenantLog.get(positionKey);
617
+ if (serializedEntry !== undefined) {
618
+ return JSON.parse(serializedEntry).messageCid;
619
+ }
620
+ return undefined;
621
+ }
622
+ /**
623
+ * Resolves the log row for a same-CID mutation, throwing the given code when no row exists.
624
+ */
625
+ async getLogEntryForMutation(partitions, tenant, messageCid, notFoundErrorCode) {
626
+ const tenantCidToSeq = await partitions.cidToSeq.partition(tenant);
627
+ const seqString = await tenantCidToSeq.get(messageCid);
628
+ if (seqString === undefined) {
629
+ throw new DwnError(notFoundErrorCode, `no message found for tenant ${tenant} with CID ${messageCid}`);
630
+ }
631
+ const positionKey = Replication.encodePositionKey(BigInt(seqString));
632
+ const tenantLog = await partitions.log.partition(tenant);
633
+ const serializedEntry = await tenantLog.get(positionKey);
634
+ if (serializedEntry === undefined) {
635
+ throw new DwnError(notFoundErrorCode, `no log entry found for tenant ${tenant} at seq ${seqString} (CID ${messageCid})`);
636
+ }
637
+ return { entry: JSON.parse(serializedEntry), positionKey, tenantLog };
638
+ }
639
+ /**
640
+ * Creates the batch operations that fold a message CID's contribution into the given
641
+ * fingerprint domains. XOR is self-inverse, so the same operations serve insert and delete.
642
+ */
643
+ async createFingerprintFoldOperations(partitions, tenant, messageCid, scopes) {
644
+ const contribution = await Replication.hashMessageCid(messageCid);
645
+ const tenantFingerprints = await partitions.fingerprints.partition(tenant);
646
+ const operations = [];
647
+ for (const scope of scopes) {
648
+ const key = MessageStoreLevel.fingerprintKey(scope);
649
+ const storedHex = await tenantFingerprints.get(key);
650
+ const current = storedHex === undefined ? Replication.emptyFingerprint() : Replication.hexToFingerprint(storedHex);
651
+ const folded = Replication.xorFingerprint(current, contribution);
652
+ operations.push(tenantFingerprints.createOperation({ type: 'put', key, value: Replication.fingerprintToHex(folded) }));
653
+ }
654
+ return operations;
655
+ }
656
+ /**
657
+ * Encodes a fingerprint domain name as a Level key. The uniform prefix keeps the global
658
+ * domain (the empty string) a valid key.
659
+ */
660
+ static fingerprintKey(scope) {
661
+ return `d${scope}`;
662
+ }
663
+ async buildToken(tenant, position, messageCid) {
664
+ const token = {
665
+ streamId: await Replication.deriveStreamId(tenant),
666
+ epoch: await this.getEpoch(),
667
+ position: position.toString(),
668
+ };
669
+ if (messageCid !== undefined) {
670
+ token.messageCid = messageCid;
671
+ }
672
+ return token;
673
+ }
674
+ /**
675
+ * Publishes a wake post-commit. Best-effort by contract — never throws into the write path.
676
+ */
677
+ publishWake(tenant, position) {
678
+ try {
679
+ this.wakePublisher?.publish({ tenant, seq: position.toString() });
680
+ }
681
+ catch {
682
+ // A lost wake only delays delivery; consumers' idle re-drain bounds the latency.
683
+ }
190
684
  }
191
685
  }
192
686
  //# sourceMappingURL=message-store-level.js.map