@enbox/dwn-sdk-js 0.0.2 → 0.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (601) hide show
  1. package/README.md +52 -301
  2. package/dist/bundles/dwn.js +19 -21
  3. package/dist/esm/generated/precompiled-validators.js +2764 -1773
  4. package/dist/esm/generated/precompiled-validators.js.map +1 -1
  5. package/dist/esm/src/core/dwn-error.js +27 -3
  6. package/dist/esm/src/core/dwn-error.js.map +1 -1
  7. package/dist/esm/src/core/message.js.map +1 -1
  8. package/dist/esm/src/core/messages-grant-authorization.js +17 -6
  9. package/dist/esm/src/core/messages-grant-authorization.js.map +1 -1
  10. package/dist/esm/src/core/protocol-authorization.js +245 -69
  11. package/dist/esm/src/core/protocol-authorization.js.map +1 -1
  12. package/dist/esm/src/core/resumable-task-manager.js +4 -4
  13. package/dist/esm/src/core/resumable-task-manager.js.map +1 -1
  14. package/dist/esm/src/dwn.js +10 -8
  15. package/dist/esm/src/dwn.js.map +1 -1
  16. package/dist/esm/src/enums/dwn-interface-method.js +4 -2
  17. package/dist/esm/src/enums/dwn-interface-method.js.map +1 -1
  18. package/dist/esm/src/event-stream/event-emitter-stream.js.map +1 -0
  19. package/dist/esm/src/handlers/messages-subscribe.js +1 -1
  20. package/dist/esm/src/handlers/messages-subscribe.js.map +1 -1
  21. package/dist/esm/src/handlers/messages-sync.js +116 -0
  22. package/dist/esm/src/handlers/messages-sync.js.map +1 -0
  23. package/dist/esm/src/handlers/protocols-configure.js +149 -16
  24. package/dist/esm/src/handlers/protocols-configure.js.map +1 -1
  25. package/dist/esm/src/handlers/protocols-query.js +2 -2
  26. package/dist/esm/src/handlers/protocols-query.js.map +1 -1
  27. package/dist/esm/src/handlers/records-count.js +143 -0
  28. package/dist/esm/src/handlers/records-count.js.map +1 -0
  29. package/dist/esm/src/handlers/records-query.js +4 -0
  30. package/dist/esm/src/handlers/records-query.js.map +1 -1
  31. package/dist/esm/src/handlers/records-read.js +4 -6
  32. package/dist/esm/src/handlers/records-read.js.map +1 -1
  33. package/dist/esm/src/handlers/records-write.js +17 -18
  34. package/dist/esm/src/handlers/records-write.js.map +1 -1
  35. package/dist/esm/src/index.js +9 -5
  36. package/dist/esm/src/index.js.map +1 -1
  37. package/dist/esm/src/interfaces/messages-read.js +2 -7
  38. package/dist/esm/src/interfaces/messages-read.js.map +1 -1
  39. package/dist/esm/src/interfaces/messages-subscribe.js +1 -0
  40. package/dist/esm/src/interfaces/messages-subscribe.js.map +1 -1
  41. package/dist/esm/src/interfaces/{messages-query.js → messages-sync.js} +11 -12
  42. package/dist/esm/src/interfaces/messages-sync.js.map +1 -0
  43. package/dist/esm/src/interfaces/protocols-configure.js +153 -30
  44. package/dist/esm/src/interfaces/protocols-configure.js.map +1 -1
  45. package/dist/esm/src/interfaces/protocols-query.js +1 -0
  46. package/dist/esm/src/interfaces/protocols-query.js.map +1 -1
  47. package/dist/esm/src/interfaces/records-count.js +91 -0
  48. package/dist/esm/src/interfaces/records-count.js.map +1 -0
  49. package/dist/esm/src/interfaces/records-read.js +15 -1
  50. package/dist/esm/src/interfaces/records-read.js.map +1 -1
  51. package/dist/esm/src/interfaces/records-write.js +64 -15
  52. package/dist/esm/src/interfaces/records-write.js.map +1 -1
  53. package/dist/esm/src/jose/algorithms/signing/ed25519.js.map +1 -1
  54. package/dist/esm/src/jose/algorithms/signing/signature-algorithms.js.map +1 -1
  55. package/dist/esm/src/jose/jws/general/builder.js.map +1 -1
  56. package/dist/esm/src/jose/jws/general/verifier.js.map +1 -1
  57. package/dist/esm/src/protocols/permission-grant.js +30 -0
  58. package/dist/esm/src/protocols/permission-grant.js.map +1 -1
  59. package/dist/esm/src/protocols/permission-request.js +24 -0
  60. package/dist/esm/src/protocols/permission-request.js.map +1 -1
  61. package/dist/esm/src/protocols/permissions.js +1 -1
  62. package/dist/esm/src/protocols/permissions.js.map +1 -1
  63. package/dist/esm/src/schema-validator.js +0 -1
  64. package/dist/esm/src/schema-validator.js.map +1 -1
  65. package/dist/esm/src/smt/smt-store-level.js +125 -0
  66. package/dist/esm/src/smt/smt-store-level.js.map +1 -0
  67. package/dist/esm/src/smt/smt-store-memory.js +67 -0
  68. package/dist/esm/src/smt/smt-store-memory.js.map +1 -0
  69. package/dist/esm/src/smt/smt-utils.js +146 -0
  70. package/dist/esm/src/smt/smt-utils.js.map +1 -0
  71. package/dist/esm/src/smt/sparse-merkle-tree.js +622 -0
  72. package/dist/esm/src/smt/sparse-merkle-tree.js.map +1 -0
  73. package/dist/esm/src/state-index/state-index-level.js +228 -0
  74. package/dist/esm/src/state-index/state-index-level.js.map +1 -0
  75. package/dist/esm/src/store/data-store-level.js +6 -6
  76. package/dist/esm/src/store/data-store-level.js.map +1 -1
  77. package/dist/esm/src/store/index-level.js +375 -17
  78. package/dist/esm/src/store/index-level.js.map +1 -1
  79. package/dist/esm/src/store/message-store-level.js +56 -0
  80. package/dist/esm/src/store/message-store-level.js.map +1 -1
  81. package/dist/esm/src/store/storage-controller.js +19 -16
  82. package/dist/esm/src/store/storage-controller.js.map +1 -1
  83. package/dist/esm/src/types/encryption-types.js +2 -0
  84. package/dist/esm/src/types/encryption-types.js.map +1 -0
  85. package/dist/esm/src/types/message-types.js.map +1 -1
  86. package/dist/esm/src/types/protocols-types.js +0 -2
  87. package/dist/esm/src/types/protocols-types.js.map +1 -1
  88. package/dist/esm/src/types/records-types.js +2 -0
  89. package/dist/esm/src/types/records-types.js.map +1 -1
  90. package/dist/esm/src/types/smt-types.js +5 -0
  91. package/dist/esm/src/types/smt-types.js.map +1 -0
  92. package/dist/esm/src/types/state-index.js +2 -0
  93. package/dist/esm/src/types/state-index.js.map +1 -0
  94. package/dist/esm/src/utils/cid.js +2 -1
  95. package/dist/esm/src/utils/cid.js.map +1 -1
  96. package/dist/esm/src/utils/data-stream.js +84 -29
  97. package/dist/esm/src/utils/data-stream.js.map +1 -1
  98. package/dist/esm/src/utils/encryption.js +22 -31
  99. package/dist/esm/src/utils/encryption.js.map +1 -1
  100. package/dist/esm/src/utils/hd-key.js +3 -3
  101. package/dist/esm/src/utils/hd-key.js.map +1 -1
  102. package/dist/esm/src/utils/jws.js +4 -4
  103. package/dist/esm/src/utils/jws.js.map +1 -1
  104. package/dist/esm/src/utils/private-key-signer.js +4 -3
  105. package/dist/esm/src/utils/private-key-signer.js.map +1 -1
  106. package/dist/esm/src/utils/protocols.js +82 -9
  107. package/dist/esm/src/utils/protocols.js.map +1 -1
  108. package/dist/esm/src/utils/records.js +82 -26
  109. package/dist/esm/src/utils/records.js.map +1 -1
  110. package/dist/esm/src/utils/secp256k1.js +4 -3
  111. package/dist/esm/src/utils/secp256k1.js.map +1 -1
  112. package/dist/esm/src/utils/secp256r1.js +3 -2
  113. package/dist/esm/src/utils/secp256r1.js.map +1 -1
  114. package/dist/esm/src/utils/time.js +1 -1
  115. package/dist/esm/src/utils/url.js +1 -1
  116. package/dist/esm/src/utils/url.js.map +1 -1
  117. package/dist/esm/tests/core/auth.spec.js +2 -2
  118. package/dist/esm/tests/core/auth.spec.js.map +1 -1
  119. package/dist/esm/tests/core/message-reply.spec.js +3 -3
  120. package/dist/esm/tests/core/message-reply.spec.js.map +1 -1
  121. package/dist/esm/tests/core/message.spec.js +13 -13
  122. package/dist/esm/tests/core/message.spec.js.map +1 -1
  123. package/dist/esm/tests/core/protocol-authorization.spec.js +3 -3
  124. package/dist/esm/tests/core/protocol-authorization.spec.js.map +1 -1
  125. package/dist/esm/tests/dwn.spec.js +27 -37
  126. package/dist/esm/tests/dwn.spec.js.map +1 -1
  127. package/dist/esm/tests/{event-log → event-stream}/event-emitter-stream.spec.js +14 -15
  128. package/dist/esm/tests/event-stream/event-emitter-stream.spec.js.map +1 -0
  129. package/dist/esm/tests/{event-log → event-stream}/event-stream.spec.js +13 -15
  130. package/dist/esm/tests/event-stream/event-stream.spec.js.map +1 -0
  131. package/dist/esm/tests/features/author-delegated-grant.spec.js +281 -135
  132. package/dist/esm/tests/features/author-delegated-grant.spec.js.map +1 -1
  133. package/dist/esm/tests/features/owner-delegated-grant.spec.js +57 -59
  134. package/dist/esm/tests/features/owner-delegated-grant.spec.js.map +1 -1
  135. package/dist/esm/tests/features/owner-signature.spec.js +32 -34
  136. package/dist/esm/tests/features/owner-signature.spec.js.map +1 -1
  137. package/dist/esm/tests/features/permissions.spec.js +73 -95
  138. package/dist/esm/tests/features/permissions.spec.js.map +1 -1
  139. package/dist/esm/tests/features/protocol-composition.spec.js +1645 -0
  140. package/dist/esm/tests/features/protocol-composition.spec.js.map +1 -0
  141. package/dist/esm/tests/features/protocol-create-action.spec.js +25 -27
  142. package/dist/esm/tests/features/protocol-create-action.spec.js.map +1 -1
  143. package/dist/esm/tests/features/protocol-delete-action.spec.js +42 -44
  144. package/dist/esm/tests/features/protocol-delete-action.spec.js.map +1 -1
  145. package/dist/esm/tests/features/protocol-update-action.spec.js +53 -55
  146. package/dist/esm/tests/features/protocol-update-action.spec.js.map +1 -1
  147. package/dist/esm/tests/features/records-prune.spec.js +126 -100
  148. package/dist/esm/tests/features/records-prune.spec.js.map +1 -1
  149. package/dist/esm/tests/features/records-tags.spec.js +272 -272
  150. package/dist/esm/tests/features/records-tags.spec.js.map +1 -1
  151. package/dist/esm/tests/features/resumable-tasks.spec.js +35 -37
  152. package/dist/esm/tests/features/resumable-tasks.spec.js.map +1 -1
  153. package/dist/esm/tests/handlers/messages-read.spec.js +112 -112
  154. package/dist/esm/tests/handlers/messages-read.spec.js.map +1 -1
  155. package/dist/esm/tests/handlers/messages-subscribe.spec.js +78 -76
  156. package/dist/esm/tests/handlers/messages-subscribe.spec.js.map +1 -1
  157. package/dist/esm/tests/handlers/messages-sync.spec.js +528 -0
  158. package/dist/esm/tests/handlers/messages-sync.spec.js.map +1 -0
  159. package/dist/esm/tests/handlers/protocols-configure.spec.js +545 -152
  160. package/dist/esm/tests/handlers/protocols-configure.spec.js.map +1 -1
  161. package/dist/esm/tests/handlers/protocols-query.spec.js +70 -72
  162. package/dist/esm/tests/handlers/protocols-query.spec.js.map +1 -1
  163. package/dist/esm/tests/handlers/records-count.spec.js +313 -0
  164. package/dist/esm/tests/handlers/records-count.spec.js.map +1 -0
  165. package/dist/esm/tests/handlers/records-delete.spec.js +106 -109
  166. package/dist/esm/tests/handlers/records-delete.spec.js.map +1 -1
  167. package/dist/esm/tests/handlers/records-query.spec.js +863 -463
  168. package/dist/esm/tests/handlers/records-query.spec.js.map +1 -1
  169. package/dist/esm/tests/handlers/records-read.spec.js +439 -209
  170. package/dist/esm/tests/handlers/records-read.spec.js.map +1 -1
  171. package/dist/esm/tests/handlers/records-subscribe.spec.js +292 -97
  172. package/dist/esm/tests/handlers/records-subscribe.spec.js.map +1 -1
  173. package/dist/esm/tests/handlers/records-write.spec.js +481 -483
  174. package/dist/esm/tests/handlers/records-write.spec.js.map +1 -1
  175. package/dist/esm/tests/interfaces/messages-get.spec.js +31 -11
  176. package/dist/esm/tests/interfaces/messages-get.spec.js.map +1 -1
  177. package/dist/esm/tests/interfaces/messages-subscribe.spec.js +5 -5
  178. package/dist/esm/tests/interfaces/messages-subscribe.spec.js.map +1 -1
  179. package/dist/esm/tests/interfaces/protocols-configure.spec.js +64 -134
  180. package/dist/esm/tests/interfaces/protocols-configure.spec.js.map +1 -1
  181. package/dist/esm/tests/interfaces/protocols-query.spec.js +4 -6
  182. package/dist/esm/tests/interfaces/protocols-query.spec.js.map +1 -1
  183. package/dist/esm/tests/interfaces/records-delete.spec.js +3 -5
  184. package/dist/esm/tests/interfaces/records-delete.spec.js.map +1 -1
  185. package/dist/esm/tests/interfaces/records-query.spec.js +9 -11
  186. package/dist/esm/tests/interfaces/records-query.spec.js.map +1 -1
  187. package/dist/esm/tests/interfaces/records-read.spec.js +76 -7
  188. package/dist/esm/tests/interfaces/records-read.spec.js.map +1 -1
  189. package/dist/esm/tests/interfaces/records-subscribe.spec.js +7 -9
  190. package/dist/esm/tests/interfaces/records-subscribe.spec.js.map +1 -1
  191. package/dist/esm/tests/interfaces/records-write.spec.js +244 -48
  192. package/dist/esm/tests/interfaces/records-write.spec.js.map +1 -1
  193. package/dist/esm/tests/jose/jws/general.spec.js +15 -18
  194. package/dist/esm/tests/jose/jws/general.spec.js.map +1 -1
  195. package/dist/esm/tests/protocols/permission-grant.spec.js +114 -0
  196. package/dist/esm/tests/protocols/permission-grant.spec.js.map +1 -0
  197. package/dist/esm/tests/protocols/permission-request.spec.js +43 -7
  198. package/dist/esm/tests/protocols/permission-request.spec.js.map +1 -1
  199. package/dist/esm/tests/protocols/permissions.spec.js +9 -11
  200. package/dist/esm/tests/protocols/permissions.spec.js.map +1 -1
  201. package/dist/esm/tests/scenarios/aggregator.spec.js +90 -92
  202. package/dist/esm/tests/scenarios/aggregator.spec.js.map +1 -1
  203. package/dist/esm/tests/scenarios/deleted-record.spec.js +17 -19
  204. package/dist/esm/tests/scenarios/deleted-record.spec.js.map +1 -1
  205. package/dist/esm/tests/scenarios/end-to-end-tests.spec.js +27 -29
  206. package/dist/esm/tests/scenarios/end-to-end-tests.spec.js.map +1 -1
  207. package/dist/esm/tests/scenarios/nested-roles.spec.js +37 -39
  208. package/dist/esm/tests/scenarios/nested-roles.spec.js.map +1 -1
  209. package/dist/esm/tests/scenarios/subscriptions.spec.js +163 -163
  210. package/dist/esm/tests/scenarios/subscriptions.spec.js.map +1 -1
  211. package/dist/esm/tests/smt/smt-store-level.spec.js +143 -0
  212. package/dist/esm/tests/smt/smt-store-level.spec.js.map +1 -0
  213. package/dist/esm/tests/smt/sparse-merkle-tree.spec.js +741 -0
  214. package/dist/esm/tests/smt/sparse-merkle-tree.spec.js.map +1 -0
  215. package/dist/esm/tests/state-index/state-index-level.spec.js +254 -0
  216. package/dist/esm/tests/state-index/state-index-level.spec.js.map +1 -0
  217. package/dist/esm/tests/store/blockstore-level.spec.js +136 -0
  218. package/dist/esm/tests/store/blockstore-level.spec.js.map +1 -0
  219. package/dist/esm/tests/store/blockstore-mock.spec.js +29 -28
  220. package/dist/esm/tests/store/blockstore-mock.spec.js.map +1 -1
  221. package/dist/esm/tests/store/data-store-level.spec.js +23 -25
  222. package/dist/esm/tests/store/data-store-level.spec.js.map +1 -1
  223. package/dist/esm/tests/store/index-level.spec.js +544 -194
  224. package/dist/esm/tests/store/index-level.spec.js.map +1 -1
  225. package/dist/esm/tests/store/message-store-level.spec.js +4 -4
  226. package/dist/esm/tests/store/message-store-level.spec.js.map +1 -1
  227. package/dist/esm/tests/store/message-store.spec.js +147 -73
  228. package/dist/esm/tests/store/message-store.spec.js.map +1 -1
  229. package/dist/esm/tests/store-dependent-tests.spec.js +1 -0
  230. package/dist/esm/tests/store-dependent-tests.spec.js.map +1 -1
  231. package/dist/esm/tests/test-stores.js +5 -5
  232. package/dist/esm/tests/test-stores.js.map +1 -1
  233. package/dist/esm/tests/test-suite.js +9 -8
  234. package/dist/esm/tests/test-suite.js.map +1 -1
  235. package/dist/esm/tests/utils/cid.spec.js +8 -11
  236. package/dist/esm/tests/utils/cid.spec.js.map +1 -1
  237. package/dist/esm/tests/utils/data-stream.spec.js +167 -13
  238. package/dist/esm/tests/utils/data-stream.spec.js.map +1 -1
  239. package/dist/esm/tests/utils/encryption-callbacks.spec.js +233 -0
  240. package/dist/esm/tests/utils/encryption-callbacks.spec.js.map +1 -0
  241. package/dist/esm/tests/utils/encryption.spec.js +34 -85
  242. package/dist/esm/tests/utils/encryption.spec.js.map +1 -1
  243. package/dist/esm/tests/utils/filters.spec.js +67 -69
  244. package/dist/esm/tests/utils/filters.spec.js.map +1 -1
  245. package/dist/esm/tests/utils/hd-key.spec.js +3 -3
  246. package/dist/esm/tests/utils/hd-key.spec.js.map +1 -1
  247. package/dist/esm/tests/utils/jws.spec.js +54 -3
  248. package/dist/esm/tests/utils/jws.spec.js.map +1 -1
  249. package/dist/esm/tests/utils/memory-cache.spec.js +6 -9
  250. package/dist/esm/tests/utils/memory-cache.spec.js.map +1 -1
  251. package/dist/esm/tests/utils/messages.spec.js +63 -29
  252. package/dist/esm/tests/utils/messages.spec.js.map +1 -1
  253. package/dist/esm/tests/utils/object.spec.js +3 -3
  254. package/dist/esm/tests/utils/object.spec.js.map +1 -1
  255. package/dist/esm/tests/utils/poller.js +1 -1
  256. package/dist/esm/tests/utils/poller.js.map +1 -1
  257. package/dist/esm/tests/utils/private-key-signer.spec.js +6 -6
  258. package/dist/esm/tests/utils/private-key-signer.spec.js.map +1 -1
  259. package/dist/esm/tests/utils/records.spec.js +37 -5
  260. package/dist/esm/tests/utils/records.spec.js.map +1 -1
  261. package/dist/esm/tests/utils/secp256k1.spec.js +7 -7
  262. package/dist/esm/tests/utils/secp256k1.spec.js.map +1 -1
  263. package/dist/esm/tests/utils/secp256r1.spec.js +7 -7
  264. package/dist/esm/tests/utils/secp256r1.spec.js.map +1 -1
  265. package/dist/esm/tests/utils/test-data-generator.js +47 -28
  266. package/dist/esm/tests/utils/test-data-generator.js.map +1 -1
  267. package/dist/esm/tests/utils/time.spec.js +7 -7
  268. package/dist/esm/tests/utils/time.spec.js.map +1 -1
  269. package/dist/esm/tests/utils/url.spec.js +25 -27
  270. package/dist/esm/tests/utils/url.spec.js.map +1 -1
  271. package/dist/esm/tests/validation/json-schemas/definitions.spec.js +4 -4
  272. package/dist/esm/tests/validation/json-schemas/definitions.spec.js.map +1 -1
  273. package/dist/esm/tests/validation/json-schemas/jwk/general-jwk.spec.js +15 -3
  274. package/dist/esm/tests/validation/json-schemas/jwk/general-jwk.spec.js.map +1 -1
  275. package/dist/esm/tests/validation/json-schemas/jwk/public-jwk.spec.js +8 -8
  276. package/dist/esm/tests/validation/json-schemas/jwk/public-jwk.spec.js.map +1 -1
  277. package/dist/esm/tests/validation/json-schemas/jwk-verification-method.spec.js +8 -18
  278. package/dist/esm/tests/validation/json-schemas/jwk-verification-method.spec.js.map +1 -1
  279. package/dist/esm/tests/validation/json-schemas/protocols/protocols-configure.spec.js +3 -3
  280. package/dist/esm/tests/validation/json-schemas/protocols/protocols-configure.spec.js.map +1 -1
  281. package/dist/esm/tests/validation/json-schemas/records/records-query.spec.js +9 -9
  282. package/dist/esm/tests/validation/json-schemas/records/records-query.spec.js.map +1 -1
  283. package/dist/esm/tests/validation/json-schemas/records/records-read.spec.js +106 -0
  284. package/dist/esm/tests/validation/json-schemas/records/records-read.spec.js.map +1 -0
  285. package/dist/esm/tests/validation/json-schemas/records/records-write.spec.js +18 -18
  286. package/dist/esm/tests/validation/json-schemas/records/records-write.spec.js.map +1 -1
  287. package/dist/esm/tests/vectors/protocol-definitions/email.json +1 -1
  288. package/dist/esm/tests/vectors/protocol-definitions/friend-role.json +2 -4
  289. package/dist/esm/tests/vectors/protocol-definitions/slack.json +2 -6
  290. package/dist/esm/tests/vectors/protocol-definitions/thread-role.json +2 -6
  291. package/dist/types/generated/precompiled-validators.d.ts +82 -64
  292. package/dist/types/generated/precompiled-validators.d.ts.map +1 -1
  293. package/dist/types/src/core/dwn-error.d.ts +27 -3
  294. package/dist/types/src/core/dwn-error.d.ts.map +1 -1
  295. package/dist/types/src/core/message-reply.d.ts +1 -1
  296. package/dist/types/src/core/message.d.ts +3 -3
  297. package/dist/types/src/core/message.d.ts.map +1 -1
  298. package/dist/types/src/core/messages-grant-authorization.d.ts +4 -4
  299. package/dist/types/src/core/messages-grant-authorization.d.ts.map +1 -1
  300. package/dist/types/src/core/protocol-authorization.d.ts +43 -2
  301. package/dist/types/src/core/protocol-authorization.d.ts.map +1 -1
  302. package/dist/types/src/core/records-grant-authorization.d.ts +2 -2
  303. package/dist/types/src/core/records-grant-authorization.d.ts.map +1 -1
  304. package/dist/types/src/core/resumable-task-manager.d.ts +1 -0
  305. package/dist/types/src/core/resumable-task-manager.d.ts.map +1 -1
  306. package/dist/types/src/dwn.d.ts +8 -8
  307. package/dist/types/src/dwn.d.ts.map +1 -1
  308. package/dist/types/src/enums/dwn-interface-method.d.ts +5 -3
  309. package/dist/types/src/enums/dwn-interface-method.d.ts.map +1 -1
  310. package/dist/types/src/event-stream/event-emitter-stream.d.ts.map +1 -0
  311. package/dist/types/src/handlers/messages-sync.d.ts +21 -0
  312. package/dist/types/src/handlers/messages-sync.d.ts.map +1 -0
  313. package/dist/types/src/handlers/protocols-configure.d.ts +24 -4
  314. package/dist/types/src/handlers/protocols-configure.d.ts.map +1 -1
  315. package/dist/types/src/handlers/protocols-query.d.ts.map +1 -1
  316. package/dist/types/src/handlers/records-count.d.ts +43 -0
  317. package/dist/types/src/handlers/records-count.d.ts.map +1 -0
  318. package/dist/types/src/handlers/records-query.d.ts.map +1 -1
  319. package/dist/types/src/handlers/records-read.d.ts.map +1 -1
  320. package/dist/types/src/handlers/records-write.d.ts +5 -5
  321. package/dist/types/src/handlers/records-write.d.ts.map +1 -1
  322. package/dist/types/src/index.d.ts +72 -37
  323. package/dist/types/src/index.d.ts.map +1 -1
  324. package/dist/types/src/interfaces/messages-read.d.ts +2 -2
  325. package/dist/types/src/interfaces/messages-read.d.ts.map +1 -1
  326. package/dist/types/src/interfaces/messages-subscribe.d.ts +2 -2
  327. package/dist/types/src/interfaces/messages-subscribe.d.ts.map +1 -1
  328. package/dist/types/src/interfaces/messages-sync.d.ts +16 -0
  329. package/dist/types/src/interfaces/messages-sync.d.ts.map +1 -0
  330. package/dist/types/src/interfaces/protocols-configure.d.ts +22 -2
  331. package/dist/types/src/interfaces/protocols-configure.d.ts.map +1 -1
  332. package/dist/types/src/interfaces/protocols-query.d.ts +2 -2
  333. package/dist/types/src/interfaces/protocols-query.d.ts.map +1 -1
  334. package/dist/types/src/interfaces/records-count.d.ts +27 -0
  335. package/dist/types/src/interfaces/records-count.d.ts.map +1 -0
  336. package/dist/types/src/interfaces/records-delete.d.ts +2 -2
  337. package/dist/types/src/interfaces/records-delete.d.ts.map +1 -1
  338. package/dist/types/src/interfaces/records-query.d.ts +2 -2
  339. package/dist/types/src/interfaces/records-query.d.ts.map +1 -1
  340. package/dist/types/src/interfaces/records-read.d.ts +4 -2
  341. package/dist/types/src/interfaces/records-read.d.ts.map +1 -1
  342. package/dist/types/src/interfaces/records-subscribe.d.ts +2 -2
  343. package/dist/types/src/interfaces/records-subscribe.d.ts.map +1 -1
  344. package/dist/types/src/interfaces/records-write.d.ts +37 -15
  345. package/dist/types/src/interfaces/records-write.d.ts.map +1 -1
  346. package/dist/types/src/jose/algorithms/signing/ed25519.d.ts.map +1 -1
  347. package/dist/types/src/jose/algorithms/signing/signature-algorithms.d.ts +5 -1
  348. package/dist/types/src/jose/algorithms/signing/signature-algorithms.d.ts.map +1 -1
  349. package/dist/types/src/jose/jws/general/builder.d.ts +3 -3
  350. package/dist/types/src/jose/jws/general/builder.d.ts.map +1 -1
  351. package/dist/types/src/protocols/permission-grant.d.ts +11 -0
  352. package/dist/types/src/protocols/permission-grant.d.ts.map +1 -1
  353. package/dist/types/src/protocols/permission-request.d.ts +11 -0
  354. package/dist/types/src/protocols/permission-request.d.ts.map +1 -1
  355. package/dist/types/src/protocols/permissions.d.ts +4 -4
  356. package/dist/types/src/protocols/permissions.d.ts.map +1 -1
  357. package/dist/types/src/schema-validator.d.ts +1 -1
  358. package/dist/types/src/schema-validator.d.ts.map +1 -1
  359. package/dist/types/src/smt/smt-store-level.d.ts +32 -0
  360. package/dist/types/src/smt/smt-store-level.d.ts.map +1 -0
  361. package/dist/types/src/smt/smt-store-memory.d.ts +22 -0
  362. package/dist/types/src/smt/smt-store-memory.d.ts.map +1 -0
  363. package/dist/types/src/smt/smt-utils.d.ts +58 -0
  364. package/dist/types/src/smt/smt-utils.d.ts.map +1 -0
  365. package/dist/types/src/smt/sparse-merkle-tree.d.ts +124 -0
  366. package/dist/types/src/smt/sparse-merkle-tree.d.ts.map +1 -0
  367. package/dist/types/src/state-index/state-index-level.d.ts +83 -0
  368. package/dist/types/src/state-index/state-index-level.d.ts.map +1 -0
  369. package/dist/types/src/store/data-store-level.d.ts +1 -2
  370. package/dist/types/src/store/data-store-level.d.ts.map +1 -1
  371. package/dist/types/src/store/index-level.d.ts +98 -2
  372. package/dist/types/src/store/index-level.d.ts.map +1 -1
  373. package/dist/types/src/store/level-wrapper.d.ts.map +1 -1
  374. package/dist/types/src/store/message-store-level.d.ts +5 -0
  375. package/dist/types/src/store/message-store-level.d.ts.map +1 -1
  376. package/dist/types/src/store/storage-controller.d.ts +7 -7
  377. package/dist/types/src/store/storage-controller.d.ts.map +1 -1
  378. package/dist/types/src/types/data-store.d.ts +2 -3
  379. package/dist/types/src/types/data-store.d.ts.map +1 -1
  380. package/dist/types/src/types/encryption-types.d.ts +48 -0
  381. package/dist/types/src/types/encryption-types.d.ts.map +1 -0
  382. package/dist/types/src/types/jose-types.d.ts +9 -40
  383. package/dist/types/src/types/jose-types.d.ts.map +1 -1
  384. package/dist/types/src/types/message-store.d.ts +5 -0
  385. package/dist/types/src/types/message-store.d.ts.map +1 -1
  386. package/dist/types/src/types/message-types.d.ts +19 -0
  387. package/dist/types/src/types/message-types.d.ts.map +1 -1
  388. package/dist/types/src/types/messages-types.d.ts +16 -11
  389. package/dist/types/src/types/messages-types.d.ts.map +1 -1
  390. package/dist/types/src/types/method-handler.d.ts +1 -2
  391. package/dist/types/src/types/method-handler.d.ts.map +1 -1
  392. package/dist/types/src/types/permission-types.d.ts +2 -2
  393. package/dist/types/src/types/permission-types.d.ts.map +1 -1
  394. package/dist/types/src/types/protocols-types.d.ts +49 -5
  395. package/dist/types/src/types/protocols-types.d.ts.map +1 -1
  396. package/dist/types/src/types/records-types.d.ts +23 -7
  397. package/dist/types/src/types/records-types.d.ts.map +1 -1
  398. package/dist/types/src/types/signer.d.ts +1 -1
  399. package/dist/types/src/types/signer.d.ts.map +1 -1
  400. package/dist/types/src/types/smt-types.d.ts +81 -0
  401. package/dist/types/src/types/smt-types.d.ts.map +1 -0
  402. package/dist/types/src/types/state-index.d.ts +90 -0
  403. package/dist/types/src/types/state-index.d.ts.map +1 -0
  404. package/dist/types/src/utils/cid.d.ts +1 -2
  405. package/dist/types/src/utils/cid.d.ts.map +1 -1
  406. package/dist/types/src/utils/data-stream.d.ts +14 -7
  407. package/dist/types/src/utils/data-stream.d.ts.map +1 -1
  408. package/dist/types/src/utils/encryption.d.ts +2 -3
  409. package/dist/types/src/utils/encryption.d.ts.map +1 -1
  410. package/dist/types/src/utils/hd-key.d.ts +4 -4
  411. package/dist/types/src/utils/hd-key.d.ts.map +1 -1
  412. package/dist/types/src/utils/jws.d.ts +7 -7
  413. package/dist/types/src/utils/jws.d.ts.map +1 -1
  414. package/dist/types/src/utils/private-key-signer.d.ts +4 -4
  415. package/dist/types/src/utils/private-key-signer.d.ts.map +1 -1
  416. package/dist/types/src/utils/protocols.d.ts +46 -3
  417. package/dist/types/src/utils/protocols.d.ts.map +1 -1
  418. package/dist/types/src/utils/records.d.ts +33 -6
  419. package/dist/types/src/utils/records.d.ts.map +1 -1
  420. package/dist/types/src/utils/secp256k1.d.ts +11 -11
  421. package/dist/types/src/utils/secp256k1.d.ts.map +1 -1
  422. package/dist/types/src/utils/secp256r1.d.ts +8 -8
  423. package/dist/types/src/utils/secp256r1.d.ts.map +1 -1
  424. package/dist/types/src/utils/time.d.ts +1 -1
  425. package/dist/types/tests/dwn.spec.d.ts.map +1 -1
  426. package/dist/types/tests/event-stream/event-emitter-stream.spec.d.ts.map +1 -0
  427. package/dist/types/tests/event-stream/event-stream.spec.d.ts.map +1 -0
  428. package/dist/types/tests/features/author-delegated-grant.spec.d.ts.map +1 -1
  429. package/dist/types/tests/features/owner-delegated-grant.spec.d.ts.map +1 -1
  430. package/dist/types/tests/features/owner-signature.spec.d.ts.map +1 -1
  431. package/dist/types/tests/features/permissions.spec.d.ts.map +1 -1
  432. package/dist/types/tests/features/protocol-composition.spec.d.ts +5 -0
  433. package/dist/types/tests/features/protocol-composition.spec.d.ts.map +1 -0
  434. package/dist/types/tests/features/protocol-create-action.spec.d.ts.map +1 -1
  435. package/dist/types/tests/features/protocol-delete-action.spec.d.ts.map +1 -1
  436. package/dist/types/tests/features/protocol-update-action.spec.d.ts.map +1 -1
  437. package/dist/types/tests/features/records-prune.spec.d.ts.map +1 -1
  438. package/dist/types/tests/features/records-tags.spec.d.ts.map +1 -1
  439. package/dist/types/tests/features/resumable-tasks.spec.d.ts.map +1 -1
  440. package/dist/types/tests/handlers/messages-read.spec.d.ts.map +1 -1
  441. package/dist/types/tests/handlers/messages-subscribe.spec.d.ts.map +1 -1
  442. package/dist/types/tests/handlers/messages-sync.spec.d.ts +2 -0
  443. package/dist/types/tests/handlers/messages-sync.spec.d.ts.map +1 -0
  444. package/dist/types/tests/handlers/protocols-configure.spec.d.ts.map +1 -1
  445. package/dist/types/tests/handlers/protocols-query.spec.d.ts.map +1 -1
  446. package/dist/types/tests/handlers/records-count.spec.d.ts +2 -0
  447. package/dist/types/tests/handlers/records-count.spec.d.ts.map +1 -0
  448. package/dist/types/tests/handlers/records-delete.spec.d.ts.map +1 -1
  449. package/dist/types/tests/handlers/records-query.spec.d.ts.map +1 -1
  450. package/dist/types/tests/handlers/records-read.spec.d.ts.map +1 -1
  451. package/dist/types/tests/handlers/records-subscribe.spec.d.ts.map +1 -1
  452. package/dist/types/tests/handlers/records-write.spec.d.ts.map +1 -1
  453. package/dist/types/tests/protocols/permission-grant.spec.d.ts +2 -0
  454. package/dist/types/tests/protocols/permission-grant.spec.d.ts.map +1 -0
  455. package/dist/types/tests/scenarios/deleted-record.spec.d.ts.map +1 -1
  456. package/dist/types/tests/scenarios/end-to-end-tests.spec.d.ts.map +1 -1
  457. package/dist/types/tests/scenarios/nested-roles.spec.d.ts.map +1 -1
  458. package/dist/types/tests/smt/smt-store-level.spec.d.ts +2 -0
  459. package/dist/types/tests/smt/smt-store-level.spec.d.ts.map +1 -0
  460. package/dist/types/tests/smt/sparse-merkle-tree.spec.d.ts +2 -0
  461. package/dist/types/tests/smt/sparse-merkle-tree.spec.d.ts.map +1 -0
  462. package/dist/types/tests/state-index/state-index-level.spec.d.ts +2 -0
  463. package/dist/types/tests/state-index/state-index-level.spec.d.ts.map +1 -0
  464. package/dist/types/tests/store/blockstore-level.spec.d.ts +2 -0
  465. package/dist/types/tests/store/blockstore-level.spec.d.ts.map +1 -0
  466. package/dist/types/tests/store/message-store.spec.d.ts.map +1 -1
  467. package/dist/types/tests/test-stores.d.ts +4 -4
  468. package/dist/types/tests/test-stores.d.ts.map +1 -1
  469. package/dist/types/tests/test-suite.d.ts +2 -2
  470. package/dist/types/tests/test-suite.d.ts.map +1 -1
  471. package/dist/types/tests/utils/encryption-callbacks.spec.d.ts +2 -0
  472. package/dist/types/tests/utils/encryption-callbacks.spec.d.ts.map +1 -0
  473. package/dist/types/tests/utils/test-data-generator.d.ts +31 -28
  474. package/dist/types/tests/utils/test-data-generator.d.ts.map +1 -1
  475. package/dist/types/tests/validation/json-schemas/records/records-read.spec.d.ts +2 -0
  476. package/dist/types/tests/validation/json-schemas/records/records-read.spec.d.ts.map +1 -0
  477. package/package.json +27 -46
  478. package/src/core/dwn-error.ts +27 -3
  479. package/src/core/message-reply.ts +1 -1
  480. package/src/core/message.ts +5 -5
  481. package/src/core/messages-grant-authorization.ts +22 -8
  482. package/src/core/protocol-authorization.ts +345 -68
  483. package/src/core/records-grant-authorization.ts +2 -2
  484. package/src/core/resumable-task-manager.ts +4 -5
  485. package/src/dwn.ts +25 -20
  486. package/src/enums/dwn-interface-method.ts +5 -3
  487. package/src/handlers/messages-subscribe.ts +1 -1
  488. package/src/handlers/messages-sync.ts +129 -0
  489. package/src/handlers/protocols-configure.ts +195 -17
  490. package/src/handlers/protocols-query.ts +7 -5
  491. package/src/handlers/records-count.ts +184 -0
  492. package/src/handlers/records-query.ts +4 -0
  493. package/src/handlers/records-read.ts +4 -8
  494. package/src/handlers/records-write.ts +20 -21
  495. package/src/index.ts +74 -37
  496. package/src/interfaces/messages-read.ts +6 -5
  497. package/src/interfaces/messages-subscribe.ts +7 -6
  498. package/src/interfaces/messages-sync.ts +59 -0
  499. package/src/interfaces/protocols-configure.ts +211 -33
  500. package/src/interfaces/protocols-query.ts +7 -6
  501. package/src/interfaces/records-count.ts +106 -0
  502. package/src/interfaces/records-delete.ts +2 -2
  503. package/src/interfaces/records-query.ts +2 -2
  504. package/src/interfaces/records-read.ts +26 -3
  505. package/src/interfaces/records-subscribe.ts +2 -2
  506. package/src/interfaces/records-write.ts +115 -46
  507. package/src/jose/algorithms/signing/ed25519.ts +13 -12
  508. package/src/jose/algorithms/signing/signature-algorithms.ts +6 -1
  509. package/src/jose/jws/general/builder.ts +3 -3
  510. package/src/jose/jws/general/verifier.ts +3 -3
  511. package/src/protocols/permission-grant.ts +51 -0
  512. package/src/protocols/permission-request.ts +37 -0
  513. package/src/protocols/permissions.ts +5 -5
  514. package/src/schema-validator.ts +11 -3
  515. package/src/smt/smt-store-level.ts +143 -0
  516. package/src/smt/smt-store-memory.ts +53 -0
  517. package/src/smt/smt-utils.ts +149 -0
  518. package/src/smt/sparse-merkle-tree.ts +698 -0
  519. package/src/state-index/state-index-level.ts +241 -0
  520. package/src/store/data-store-level.ts +8 -7
  521. package/src/store/index-level.ts +415 -19
  522. package/src/store/level-wrapper.ts +1 -1
  523. package/src/store/message-store-level.ts +62 -0
  524. package/src/store/storage-controller.ts +21 -19
  525. package/src/types/data-store.ts +2 -4
  526. package/src/types/encryption-types.ts +52 -0
  527. package/src/types/jose-types.ts +10 -42
  528. package/src/types/message-store.ts +11 -0
  529. package/src/types/message-types.ts +21 -0
  530. package/src/types/messages-types.ts +21 -15
  531. package/src/types/method-handler.ts +1 -2
  532. package/src/types/permission-types.ts +2 -2
  533. package/src/types/protocols-types.ts +55 -6
  534. package/src/types/records-types.ts +26 -7
  535. package/src/types/signer.ts +1 -1
  536. package/src/types/smt-types.ts +95 -0
  537. package/src/types/state-index.ts +100 -0
  538. package/src/utils/cid.ts +3 -4
  539. package/src/utils/data-stream.ts +75 -38
  540. package/src/utils/encryption.ts +24 -39
  541. package/src/utils/hd-key.ts +6 -6
  542. package/src/utils/jws.ts +9 -9
  543. package/src/utils/private-key-signer.ts +9 -8
  544. package/src/utils/protocols.ts +132 -6
  545. package/src/utils/records.ts +118 -29
  546. package/src/utils/secp256k1.ts +23 -21
  547. package/src/utils/secp256r1.ts +17 -15
  548. package/src/utils/time.ts +1 -1
  549. package/src/utils/url.ts +1 -1
  550. package/dist/cjs/index.js +0 -36749
  551. package/dist/cjs/package.json +0 -1
  552. package/dist/esm/src/event-log/event-emitter-stream.js.map +0 -1
  553. package/dist/esm/src/event-log/event-log-level.js +0 -63
  554. package/dist/esm/src/event-log/event-log-level.js.map +0 -1
  555. package/dist/esm/src/handlers/messages-query.js +0 -71
  556. package/dist/esm/src/handlers/messages-query.js.map +0 -1
  557. package/dist/esm/src/interfaces/messages-query.js.map +0 -1
  558. package/dist/esm/src/types/event-log.js +0 -2
  559. package/dist/esm/src/types/event-log.js.map +0 -1
  560. package/dist/esm/tests/event-log/event-emitter-stream.spec.js.map +0 -1
  561. package/dist/esm/tests/event-log/event-log-level.spec.js +0 -44
  562. package/dist/esm/tests/event-log/event-log-level.spec.js.map +0 -1
  563. package/dist/esm/tests/event-log/event-log.spec.js +0 -236
  564. package/dist/esm/tests/event-log/event-log.spec.js.map +0 -1
  565. package/dist/esm/tests/event-log/event-stream.spec.js.map +0 -1
  566. package/dist/esm/tests/handlers/messages-query.spec.js +0 -349
  567. package/dist/esm/tests/handlers/messages-query.spec.js.map +0 -1
  568. package/dist/esm/tests/interfaces/messagess-query.spec.js +0 -127
  569. package/dist/esm/tests/interfaces/messagess-query.spec.js.map +0 -1
  570. package/dist/esm/tests/scenarios/messages-query.spec.js +0 -395
  571. package/dist/esm/tests/scenarios/messages-query.spec.js.map +0 -1
  572. package/dist/types/src/event-log/event-emitter-stream.d.ts.map +0 -1
  573. package/dist/types/src/event-log/event-log-level.d.ts +0 -35
  574. package/dist/types/src/event-log/event-log-level.d.ts.map +0 -1
  575. package/dist/types/src/handlers/messages-query.d.ts +0 -17
  576. package/dist/types/src/handlers/messages-query.d.ts.map +0 -1
  577. package/dist/types/src/interfaces/messages-query.d.ts +0 -16
  578. package/dist/types/src/interfaces/messages-query.d.ts.map +0 -1
  579. package/dist/types/src/types/event-log.d.ts +0 -52
  580. package/dist/types/src/types/event-log.d.ts.map +0 -1
  581. package/dist/types/tests/event-log/event-emitter-stream.spec.d.ts.map +0 -1
  582. package/dist/types/tests/event-log/event-log-level.spec.d.ts +0 -2
  583. package/dist/types/tests/event-log/event-log-level.spec.d.ts.map +0 -1
  584. package/dist/types/tests/event-log/event-log.spec.d.ts +0 -2
  585. package/dist/types/tests/event-log/event-log.spec.d.ts.map +0 -1
  586. package/dist/types/tests/event-log/event-stream.spec.d.ts.map +0 -1
  587. package/dist/types/tests/handlers/messages-query.spec.d.ts +0 -2
  588. package/dist/types/tests/handlers/messages-query.spec.d.ts.map +0 -1
  589. package/dist/types/tests/interfaces/messagess-query.spec.d.ts +0 -2
  590. package/dist/types/tests/interfaces/messagess-query.spec.d.ts.map +0 -1
  591. package/dist/types/tests/scenarios/messages-query.spec.d.ts +0 -2
  592. package/dist/types/tests/scenarios/messages-query.spec.d.ts.map +0 -1
  593. package/src/event-log/event-log-level.ts +0 -72
  594. package/src/handlers/messages-query.ts +0 -67
  595. package/src/interfaces/messages-query.ts +0 -60
  596. package/src/types/event-log.ts +0 -52
  597. /package/dist/esm/src/{event-log → event-stream}/event-emitter-stream.js +0 -0
  598. /package/dist/types/src/{event-log → event-stream}/event-emitter-stream.d.ts +0 -0
  599. /package/dist/types/tests/{event-log → event-stream}/event-emitter-stream.spec.d.ts +0 -0
  600. /package/dist/types/tests/{event-log → event-stream}/event-stream.spec.d.ts +0 -0
  601. /package/src/{event-log → event-stream}/event-emitter-stream.ts +0 -0
@@ -10,13 +10,35 @@ import { FilterSelector, FilterUtility } from '../utils/filter.js';
10
10
 
11
11
  export type IndexLevelConfig = {
12
12
  location: string,
13
- createLevelDatabase?: typeof createLevelDatabase
13
+ createLevelDatabase?: typeof createLevelDatabase,
14
+ compoundIndexes?: CompoundIndexDefinition[],
14
15
  };
15
16
 
16
17
  export type IndexedItem = { messageCid: string, indexes: KeyValues };
17
18
 
19
+ /**
20
+ * Defines a compound index that covers multiple filter properties and a sort property.
21
+ *
22
+ * When a query's equality filter properties match all `properties` AND its sort property
23
+ * matches `sortProperty`, this compound index can serve the entire query from a single
24
+ * LevelDB range scan — no in-memory filtering or sorting required.
25
+ *
26
+ * Key encoding: `<prop1>\x01<prop2>\x01...\x01<sortValue>\x00<messageCid>`
27
+ */
28
+ export type CompoundIndexDefinition = {
29
+ /** Unique name for this compound index (used as the sublevel partition name). */
30
+ name: string;
31
+ /** Filter properties that must all be present as equality filters in the query. */
32
+ properties: string[];
33
+ /** The sort property embedded at the end of the compound key. */
34
+ sortProperty: string;
35
+ };
36
+
18
37
  const INDEX_SUBLEVEL_NAME = 'index';
19
38
 
39
+ /** Separator between compound key segments (higher than \x00 so prefix scans work correctly). */
40
+ const COMPOUND_SEGMENT_SEPARATOR = '\x01';
41
+
20
42
  export interface IndexLevelOptions {
21
43
  signal?: AbortSignal;
22
44
  }
@@ -27,6 +49,7 @@ export interface IndexLevelOptions {
27
49
  export class IndexLevel {
28
50
  db: LevelWrapper<string>;
29
51
  config: IndexLevelConfig;
52
+ private _compoundIndexes: CompoundIndexDefinition[];
30
53
 
31
54
  constructor(config: IndexLevelConfig) {
32
55
  this.config = {
@@ -34,6 +57,8 @@ export class IndexLevel {
34
57
  ...config,
35
58
  };
36
59
 
60
+ this._compoundIndexes = config.compoundIndexes ?? [];
61
+
37
62
  this.db = new LevelWrapper<string>({
38
63
  location : this.config.location,
39
64
  createLevelDatabase : this.config.createLevelDatabase,
@@ -94,6 +119,14 @@ export class IndexLevel {
94
119
  }
95
120
  }
96
121
 
122
+ // create compound index entries for any registered compound indexes whose properties are all present in the indexes.
123
+ for (const compoundIndex of this._compoundIndexes) {
124
+ const compoundOp = this.createCompoundIndexPutOperation(tenant, item, compoundIndex);
125
+ if (compoundOp !== undefined) {
126
+ opCreationPromises.push(compoundOp);
127
+ }
128
+ }
129
+
97
130
  // create a reverse lookup for the sortedIndex values. This is used during deletion and cursor starting point lookup.
98
131
  const partitionOperationPromise = this.createOperationForIndexesLookupPartition(
99
132
  tenant,
@@ -136,6 +169,14 @@ export class IndexLevel {
136
169
  }
137
170
  }
138
171
 
172
+ // delete compound index entries
173
+ for (const compoundIndex of this._compoundIndexes) {
174
+ const compoundOp = this.createCompoundIndexDeleteOperation(tenant, messageCid, indexes, compoundIndex);
175
+ if (compoundOp !== undefined) {
176
+ opCreationPromises.push(compoundOp);
177
+ }
178
+ }
179
+
139
180
  const indexOps = await Promise.all(opCreationPromises);
140
181
  const tenantPartition = await this.db.partition(tenant);
141
182
  await tenantPartition.batch(indexOps, options);
@@ -233,6 +274,12 @@ export class IndexLevel {
233
274
  /**
234
275
  * Queries the index for items that match the filters. If no filters are provided, all items are returned.
235
276
  *
277
+ * Query strategy selection (in priority order):
278
+ * 1. **Compound index**: If a single filter matches a registered compound index (all equality properties + sort property),
279
+ * the entire query is served from a single LevelDB range scan. Supports cursors natively.
280
+ * 2. **In-memory paging**: For "concise" filters without cursors. Scans the most selective property index, verifies in memory.
281
+ * 3. **Iterator paging**: Default fallback. Scans the sort property index, testing each item against the filter.
282
+ *
236
283
  * @param filters Array of filters that are treated as an OR query.
237
284
  * @param queryOptions query options for sort and pagination, requires at least `sortProperty`. The default sort direction is ascending.
238
285
  * @param options IndexLevelOptions that include an AbortSignal.
@@ -240,13 +287,45 @@ export class IndexLevel {
240
287
  */
241
288
  async query(tenant: string, filters: Filter[], queryOptions: QueryOptions, options?: IndexLevelOptions): Promise<IndexedItem[]> {
242
289
 
243
- // check if we should query using in-memory paging or iterator paging
290
+ // Strategy 1: try compound index for single-filter queries
291
+ if (filters.length === 1 && !isEmptyObject(filters[0])) {
292
+ const compoundResult = this.selectCompoundIndex(filters[0], queryOptions);
293
+ if (compoundResult !== undefined) {
294
+ return this.queryWithCompoundIndex(tenant, filters[0], queryOptions, compoundResult, options);
295
+ }
296
+ }
297
+
298
+ // Strategy 2: in-memory paging for concise filters
244
299
  if (IndexLevel.shouldQueryWithInMemoryPaging(filters, queryOptions)) {
245
300
  return this.queryWithInMemoryPaging(tenant, filters, queryOptions, options);
246
301
  }
302
+
303
+ // Strategy 3: iterator paging (default)
247
304
  return this.queryWithIteratorPaging(tenant, filters, queryOptions, options);
248
305
  }
249
306
 
307
+ /**
308
+ * Counts the number of items that match the given filters without loading full records.
309
+ *
310
+ * When a compound index covers the query, counting is a simple key iteration without value deserialization.
311
+ * Otherwise, falls back to counting via the existing query strategies.
312
+ */
313
+ async count(tenant: string, filters: Filter[], queryOptions: Omit<QueryOptions, 'limit' | 'cursor'>,
314
+ options?: IndexLevelOptions): Promise<number> {
315
+
316
+ // try compound index for single-filter queries
317
+ if (filters.length === 1 && !isEmptyObject(filters[0])) {
318
+ const compoundResult = this.selectCompoundIndex(filters[0], { ...queryOptions });
319
+ if (compoundResult !== undefined) {
320
+ return this.countWithCompoundIndex(tenant, filters[0], compoundResult, options);
321
+ }
322
+ }
323
+
324
+ // fallback: run a full query without limit and count the results
325
+ const results = await this.query(tenant, filters, { ...queryOptions }, options);
326
+ return results.length;
327
+ }
328
+
250
329
  /**
251
330
  * Queries the sort property index for items that match the filters. If no filters are provided, all items are returned.
252
331
  * This query is a linear iterator over the sorted index, checking each item for a match.
@@ -508,6 +587,10 @@ export class IndexLevel {
508
587
 
509
588
  /**
510
589
  * Returns items that match the range filter.
590
+ *
591
+ * For `lte` bounds, the encoded value is extended with a trailing `\xff` so that composite keys
592
+ * of the form `<encodedValue>\x00<messageCid>` are naturally included in the range scan,
593
+ * eliminating the need for a separate exact-match query.
511
594
  */
512
595
  private async filterRangeMatches(
513
596
  tenant: string,
@@ -518,7 +601,15 @@ export class IndexLevel {
518
601
  const iteratorOptions: LevelWrapperIteratorOptions<string> = {};
519
602
  for (const comparator in rangeFilter) {
520
603
  const comparatorName = comparator as keyof RangeFilter;
521
- iteratorOptions[comparatorName] = IndexLevel.encodeValue(rangeFilter[comparatorName]!);
604
+ const encodedValue = IndexLevel.encodeValue(rangeFilter[comparatorName]!);
605
+ if (comparatorName === 'lte') {
606
+ // Extend the lte bound so that composite keys `<encodedValue>\x00<messageCid>` are included.
607
+ // Since \x00 < \xff, any key starting with encodedValue followed by the \x00 delimiter
608
+ // will be lexicographically less than encodedValue + \xff.
609
+ iteratorOptions[comparatorName] = encodedValue + '\xff';
610
+ } else {
611
+ iteratorOptions[comparatorName] = encodedValue;
612
+ }
522
613
  }
523
614
 
524
615
  // if there is no lower bound specified (`gt` or `gte`), we need to iterate from the upper bound,
@@ -531,24 +622,13 @@ export class IndexLevel {
531
622
  const filterPartition = await this.getIndexPartition(tenant, propertyName);
532
623
 
533
624
  for await (const [ key, value ] of filterPartition.iterator(iteratorOptions, options)) {
534
- // if "greater-than" is specified, skip all keys that contains the exact value given in the "greater-than" condition
625
+ // if "greater-than" is specified, skip all keys that contain the exact value given in the "greater-than" condition
535
626
  if ('gt' in rangeFilter && this.extractIndexValueFromKey(key) === IndexLevel.encodeValue(rangeFilter.gt!)) {
536
627
  continue;
537
628
  }
538
629
  matches.push(JSON.parse(value) as IndexedItem);
539
630
  }
540
631
 
541
- if ('lte' in rangeFilter) {
542
- // When `lte` is used, we must also query the exact match explicitly because the exact match will not be included in the iterator above.
543
- // This is due to the extra data appended to the (property + value) key prefix, e.g.
544
- // the key '"2023-05-25T11:22:33.000000Z"\u0000bayfreigu....'
545
- // would be considered greater than `lte` value in { lte: '"2023-05-25T11:22:33.000000Z"' } iterator options,
546
- // thus would not be included in the iterator even though we'd like it to be.
547
- for (const item of await this.filterExactMatches(tenant, propertyName, rangeFilter.lte as EqualFilter, options)) {
548
- matches.push(item);
549
- }
550
- }
551
-
552
632
  return matches;
553
633
  }
554
634
 
@@ -570,12 +650,14 @@ export class IndexLevel {
570
650
  }
571
651
 
572
652
  /**
573
- * Find the starting position for pagination within the IndexedItem array.
574
- * Returns the index of the first item found which is either greater than or less than the given cursor, depending on sort order.
653
+ * Find the starting position for pagination within the IndexedItem array using binary search.
654
+ * Returns the index of the first item after the cursor, or -1 if no such item exists.
655
+ *
656
+ * Since the array is already sorted, binary search provides O(log n) performance instead of O(n).
575
657
  */
576
658
  private findCursorStartingIndex(items: IndexedItem[], sortDirection: SortDirection, sortProperty: string, cursorStartingKey: string): number {
577
659
 
578
- const firstItemAfterCursor = (item: IndexedItem): boolean => {
660
+ const isAfterCursor = (item: IndexedItem): boolean => {
579
661
  const { messageCid, indexes } = item;
580
662
  const sortValue = indexes[sortProperty] as string | number;
581
663
  const itemCompareValue = IndexLevel.keySegmentJoin(IndexLevel.encodeValue(sortValue), messageCid);
@@ -585,7 +667,20 @@ export class IndexLevel {
585
667
  itemCompareValue < cursorStartingKey;
586
668
  };
587
669
 
588
- return items.findIndex(firstItemAfterCursor);
670
+ // binary search for the first item after the cursor
671
+ let low = 0;
672
+ let high = items.length;
673
+
674
+ while (low < high) {
675
+ const mid = (low + high) >>> 1;
676
+ if (isAfterCursor(items[mid])) {
677
+ high = mid;
678
+ } else {
679
+ low = mid + 1;
680
+ }
681
+ }
682
+
683
+ return low < items.length ? low : -1;
589
684
  }
590
685
 
591
686
  /**
@@ -654,6 +749,307 @@ export class IndexLevel {
654
749
  }
655
750
  }
656
751
 
752
+ // =========================================================================
753
+ // Compound index methods
754
+ // =========================================================================
755
+
756
+ /**
757
+ * Gets the compound index partition for a given compound index definition.
758
+ * Compound index sublevels use the naming convention `__compound:<name>__`.
759
+ */
760
+ private async getCompoundIndexPartition(tenant: string, compoundIndex: CompoundIndexDefinition): Promise<LevelWrapper<string>> {
761
+ const partitionName = `__compound:${compoundIndex.name}__`;
762
+ return (await this.db.partition(tenant)).partition(partitionName);
763
+ }
764
+
765
+ /**
766
+ * Builds a compound index key from the given indexes and compound index definition.
767
+ *
768
+ * Key format: `<prop1>\x01<prop2>\x01...\x01<sortValue>\x00<messageCid>`
769
+ *
770
+ * @returns the compound key, or undefined if the indexes don't contain all required properties.
771
+ */
772
+ private static buildCompoundKey(messageCid: string, indexes: KeyValues, compoundIndex: CompoundIndexDefinition): string | undefined {
773
+ const segments: string[] = [];
774
+
775
+ for (const property of compoundIndex.properties) {
776
+ const value = indexes[property];
777
+ if (value === undefined || Array.isArray(value)) {
778
+ return undefined; // compound indexes don't support array values or missing properties
779
+ }
780
+ segments.push(IndexLevel.encodeValue(value));
781
+ }
782
+
783
+ const sortValue = indexes[compoundIndex.sortProperty];
784
+ if (sortValue === undefined || Array.isArray(sortValue)) {
785
+ return undefined;
786
+ }
787
+
788
+ // join prefix segments with \x01, then append sort value and messageCid with the standard delimiters
789
+ const prefixPart = segments.join(COMPOUND_SEGMENT_SEPARATOR);
790
+ const sortPart = IndexLevel.encodeValue(sortValue);
791
+ return prefixPart + COMPOUND_SEGMENT_SEPARATOR + sortPart + IndexLevel.delimiter + messageCid;
792
+ }
793
+
794
+ /**
795
+ * Builds the prefix portion of a compound key from filter values (without the sort/messageCid suffix).
796
+ * Used for range scans: all entries with this prefix match the filter.
797
+ */
798
+ private static buildCompoundPrefix(filter: Filter, compoundIndex: CompoundIndexDefinition): string | undefined {
799
+ const segments: string[] = [];
800
+
801
+ for (const property of compoundIndex.properties) {
802
+ const filterValue = filter[property];
803
+ if (filterValue === undefined || typeof filterValue === 'object') {
804
+ return undefined; // compound prefix only works with equality filters
805
+ }
806
+ segments.push(IndexLevel.encodeValue(filterValue));
807
+ }
808
+
809
+ return segments.join(COMPOUND_SEGMENT_SEPARATOR) + COMPOUND_SEGMENT_SEPARATOR;
810
+ }
811
+
812
+ /**
813
+ * Creates a put operation for a compound index entry.
814
+ * Returns undefined if the indexes don't contain all required compound index properties.
815
+ */
816
+ private createCompoundIndexPutOperation(
817
+ tenant: string,
818
+ item: IndexedItem,
819
+ compoundIndex: CompoundIndexDefinition
820
+ ): Promise<LevelWrapperBatchOperation<string>> | undefined {
821
+ const key = IndexLevel.buildCompoundKey(item.messageCid, item.indexes, compoundIndex);
822
+ if (key === undefined) {
823
+ return undefined;
824
+ }
825
+
826
+ return this.createOperationForPartition(tenant, `__compound:${compoundIndex.name}__`, {
827
+ type : 'put',
828
+ key,
829
+ value : JSON.stringify(item),
830
+ });
831
+ }
832
+
833
+ /**
834
+ * Creates a delete operation for a compound index entry.
835
+ * Returns undefined if the indexes don't contain all required compound index properties.
836
+ */
837
+ private createCompoundIndexDeleteOperation(
838
+ tenant: string,
839
+ messageCid: string,
840
+ indexes: KeyValues,
841
+ compoundIndex: CompoundIndexDefinition
842
+ ): Promise<LevelWrapperBatchOperation<string>> | undefined {
843
+ const key = IndexLevel.buildCompoundKey(messageCid, indexes, compoundIndex);
844
+ if (key === undefined) {
845
+ return undefined;
846
+ }
847
+
848
+ return this.createOperationForPartition(tenant, `__compound:${compoundIndex.name}__`, {
849
+ type: 'del',
850
+ key,
851
+ });
852
+ }
853
+
854
+ /**
855
+ * Generic helper to create a batch operation for any named partition under a tenant.
856
+ */
857
+ private async createOperationForPartition(
858
+ tenant: string,
859
+ partitionName: string,
860
+ operation: LevelWrapperBatchOperation<string>
861
+ ): Promise<LevelWrapperBatchOperation<string>> {
862
+ const tenantPartition = await this.db.partition(tenant);
863
+ return tenantPartition.createPartitionOperation(partitionName, operation);
864
+ }
865
+
866
+ /**
867
+ * Selects the best compound index that covers the given filter and sort requirements.
868
+ *
869
+ * A compound index "covers" a query when:
870
+ * 1. Every property in the compound index definition is present in the filter as an equality filter.
871
+ * 2. The compound index's sort property matches the query's sort property.
872
+ *
873
+ * Among multiple matching compound indexes, the one with the most properties is preferred
874
+ * (more specific = fewer false positives in the prefix scan).
875
+ */
876
+ private selectCompoundIndex(filter: Filter, queryOptions: QueryOptions): CompoundIndexDefinition | undefined {
877
+ let bestMatch: CompoundIndexDefinition | undefined;
878
+ let bestPropertyCount = 0;
879
+
880
+ for (const compoundIndex of this._compoundIndexes) {
881
+ // check that the sort property matches
882
+ if (compoundIndex.sortProperty !== queryOptions.sortProperty) {
883
+ continue;
884
+ }
885
+
886
+ // check that all compound properties are present in the filter as equality filters
887
+ let allPropertiesMatch = true;
888
+ for (const property of compoundIndex.properties) {
889
+ const filterValue = filter[property];
890
+ if (filterValue === undefined || typeof filterValue === 'object') {
891
+ allPropertiesMatch = false;
892
+ break;
893
+ }
894
+ }
895
+
896
+ if (allPropertiesMatch && compoundIndex.properties.length > bestPropertyCount) {
897
+ bestMatch = compoundIndex;
898
+ bestPropertyCount = compoundIndex.properties.length;
899
+ }
900
+ }
901
+
902
+ return bestMatch;
903
+ }
904
+
905
+ /**
906
+ * Queries using a compound index. This is the most efficient query strategy: a single LevelDB
907
+ * range scan that filters, sorts, and paginates all at once.
908
+ *
909
+ * The compound key encodes the filter properties as a prefix and the sort property as a suffix,
910
+ * so iterating over keys with the filter prefix yields results in sort order.
911
+ *
912
+ * Any remaining filter properties not covered by the compound index are verified in memory.
913
+ */
914
+ private async queryWithCompoundIndex(
915
+ tenant: string,
916
+ filter: Filter,
917
+ queryOptions: QueryOptions,
918
+ compoundIndex: CompoundIndexDefinition,
919
+ options?: IndexLevelOptions
920
+ ): Promise<IndexedItem[]> {
921
+ const { sortDirection = SortDirection.Ascending, cursor, limit } = queryOptions;
922
+
923
+ const prefix = IndexLevel.buildCompoundPrefix(filter, compoundIndex);
924
+ if (prefix === undefined) {
925
+ // should not happen since selectCompoundIndex already validated, but guard against it
926
+ return this.queryWithIteratorPaging(tenant, [filter], queryOptions, options);
927
+ }
928
+
929
+ const partition = await this.getCompoundIndexPartition(tenant, compoundIndex);
930
+
931
+ // determine the iterator bounds from the prefix
932
+ const iteratorOptions: LevelWrapperIteratorOptions<string> = {};
933
+
934
+ if (cursor !== undefined) {
935
+ // build the full compound key for the cursor position
936
+ const cursorSortEncoded = IndexLevel.encodeValue(cursor.value);
937
+ const cursorKey = prefix + cursorSortEncoded + IndexLevel.delimiter + cursor.messageCid;
938
+
939
+ if (sortDirection === SortDirection.Ascending) {
940
+ iteratorOptions.gt = cursorKey;
941
+ // upper bound: everything with this prefix (prefix + \xff is past all valid compound keys with this prefix)
942
+ iteratorOptions.lt = prefix + '\xff';
943
+ } else {
944
+ iteratorOptions.lt = cursorKey;
945
+ iteratorOptions.gt = prefix;
946
+ iteratorOptions.reverse = true;
947
+ }
948
+ } else {
949
+ if (sortDirection === SortDirection.Ascending) {
950
+ iteratorOptions.gt = prefix;
951
+ iteratorOptions.lt = prefix + '\xff';
952
+ } else {
953
+ // for descending without cursor, start from the end of the prefix range
954
+ iteratorOptions.gt = prefix;
955
+ iteratorOptions.lt = prefix + '\xff';
956
+ iteratorOptions.reverse = true;
957
+ }
958
+ }
959
+
960
+ // determine which filter properties are NOT covered by the compound index
961
+ // (need in-memory verification for these)
962
+ // NOTE: the compound index equality properties are fully covered by the prefix scan,
963
+ // but the sort property is only covered for ordering — any range filter on the sort
964
+ // property must still be applied as a residual filter.
965
+ const coveredEqualityProperties = new Set(compoundIndex.properties);
966
+ const residualFilter: Filter = {};
967
+ let hasResidualFilter = false;
968
+ for (const property in filter) {
969
+ if (!coveredEqualityProperties.has(property)) {
970
+ residualFilter[property] = filter[property];
971
+ hasResidualFilter = true;
972
+ }
973
+ }
974
+
975
+ const matches: IndexedItem[] = [];
976
+ for await (const [_key, value] of partition.iterator(iteratorOptions, options)) {
977
+ if (limit !== undefined && matches.length === limit) {
978
+ break;
979
+ }
980
+
981
+ const item = JSON.parse(value) as IndexedItem;
982
+
983
+ // verify any residual filter properties in memory
984
+ if (hasResidualFilter && !FilterUtility.matchFilter(item.indexes, residualFilter)) {
985
+ continue;
986
+ }
987
+
988
+ matches.push(item);
989
+ }
990
+
991
+ return matches;
992
+ }
993
+
994
+ /**
995
+ * Counts items matching a compound index prefix without loading full records.
996
+ * Iterates only keys (not values) for maximum efficiency.
997
+ */
998
+ private async countWithCompoundIndex(
999
+ tenant: string,
1000
+ filter: Filter,
1001
+ compoundIndex: CompoundIndexDefinition,
1002
+ options?: IndexLevelOptions
1003
+ ): Promise<number> {
1004
+ const prefix = IndexLevel.buildCompoundPrefix(filter, compoundIndex);
1005
+ if (prefix === undefined) {
1006
+ // fallback
1007
+ const results = await this.query(tenant, [filter], { sortProperty: compoundIndex.sortProperty }, options);
1008
+ return results.length;
1009
+ }
1010
+
1011
+ const partition = await this.getCompoundIndexPartition(tenant, compoundIndex);
1012
+
1013
+ // determine which filter properties are NOT covered by the compound index
1014
+ // (same logic as queryWithCompoundIndex: sort property range filters are residual)
1015
+ const coveredEqualityProperties = new Set(compoundIndex.properties);
1016
+ let hasResidualFilter = false;
1017
+ const residualFilter: Filter = {};
1018
+ for (const property in filter) {
1019
+ if (!coveredEqualityProperties.has(property)) {
1020
+ residualFilter[property] = filter[property];
1021
+ hasResidualFilter = true;
1022
+ }
1023
+ }
1024
+
1025
+ const iteratorOptions: LevelWrapperIteratorOptions<string> = {
1026
+ gt : prefix,
1027
+ lt : prefix + '\xff',
1028
+ };
1029
+
1030
+ let count = 0;
1031
+ if (hasResidualFilter) {
1032
+ // must read values to check residual filter
1033
+ for await (const [_key, value] of partition.iterator(iteratorOptions, options)) {
1034
+ const item = JSON.parse(value) as IndexedItem;
1035
+ if (FilterUtility.matchFilter(item.indexes, residualFilter)) {
1036
+ count++;
1037
+ }
1038
+ }
1039
+ } else {
1040
+ // no residual filter — iterate keys via iterator without parsing values
1041
+ for await (const [_key, _value] of partition.iterator(iteratorOptions, options)) {
1042
+ count++;
1043
+ }
1044
+ }
1045
+
1046
+ return count;
1047
+ }
1048
+
1049
+ // =========================================================================
1050
+ // Query strategy selection
1051
+ // =========================================================================
1052
+
657
1053
  private static shouldQueryWithInMemoryPaging(filters: Filter[], queryOptions: QueryOptions): boolean {
658
1054
  for (const filter of filters) {
659
1055
  if (!IndexLevel.isFilterConcise(filter, queryOptions)) {
@@ -1,4 +1,4 @@
1
- import type { AbstractBatchOperation, AbstractDatabaseOptions, AbstractIteratorOptions, AbstractLevel } from 'abstract-level';
1
+ import type { AbstractBatchOperation, AbstractDatabaseOptions, AbstractIteratorOptions } from 'abstract-level';
2
2
 
3
3
  import { executeUnlessAborted } from '../utils/abort.js';
4
4
  import { Level } from 'level';
@@ -1,4 +1,5 @@
1
1
 
2
+ import type { CompoundIndexDefinition } from './index-level.js';
2
3
  import type { Filter, KeyValues, PaginationCursor, QueryOptions } from '../types/query-types.js';
3
4
  import type { GenericMessage, MessageSort, Pagination } from '../types/message-types.js';
4
5
  import type { MessageStore, MessageStoreOptions } from '../types/message-store.js';
@@ -17,6 +18,51 @@ import { sha256 } from 'multiformats/hashes/sha2';
17
18
  import { SortDirection } from '../types/query-types.js';
18
19
 
19
20
 
21
+ /**
22
+ * Default compound indexes that cover the most common DWN query patterns.
23
+ * These are automatically registered unless overridden via config.
24
+ *
25
+ * Each compound index enables a single-scan query when the filter matches
26
+ * all equality properties and the sort matches the sort property.
27
+ */
28
+ const DEFAULT_COMPOUND_INDEXES: CompoundIndexDefinition[] = [
29
+ {
30
+ // Covers: RecordsQuery with protocol + protocolPath, sorted by messageTimestamp
31
+ // Example: "all chat messages in protocol X, path 'chat/message', sorted by time"
32
+ name : 'protocol-protocolPath-messageTimestamp',
33
+ properties : ['protocol', 'protocolPath'],
34
+ sortProperty : 'messageTimestamp',
35
+ },
36
+ {
37
+ // Covers: RecordsQuery with protocol + protocolPath, sorted by dateCreated
38
+ name : 'protocol-protocolPath-dateCreated',
39
+ properties : ['protocol', 'protocolPath'],
40
+ sortProperty : 'dateCreated',
41
+ },
42
+ {
43
+ // Covers: RecordsQuery with schema, sorted by messageTimestamp
44
+ // Example: "all records with schema X, sorted by time"
45
+ name : 'schema-messageTimestamp',
46
+ properties : ['schema'],
47
+ sortProperty : 'messageTimestamp',
48
+ },
49
+ {
50
+ // Covers: RecordsQuery with schema, sorted by dateCreated
51
+ name : 'schema-dateCreated',
52
+ properties : ['schema'],
53
+ sortProperty : 'dateCreated',
54
+ },
55
+ {
56
+ // Covers: RecordsQuery with protocol + contextId, sorted by messageTimestamp
57
+ // Example: "all messages in a specific protocol context (conversation), sorted by time"
58
+ // NOTE: contextId is often a prefix/range filter. Compound indexes only support equality.
59
+ // This index is useful when contextId is an exact match (e.g., root-level context queries).
60
+ name : 'protocol-contextId-messageTimestamp',
61
+ properties : ['protocol', 'contextId'],
62
+ sortProperty : 'messageTimestamp',
63
+ },
64
+ ];
65
+
20
66
  /**
21
67
  * A simple implementation of {@link MessageStore} that works in both the browser and server-side.
22
68
  * Leverages LevelDB under the hood.
@@ -33,6 +79,8 @@ export class MessageStoreLevel implements MessageStore {
33
79
  * LevelDB will store its files, or in browsers, the name of the
34
80
  * {@link https://developer.mozilla.org/en-US/docs/Web/API/IDBDatabase IDBDatabase} to be opened.
35
81
  * @param {string} config.indexLocation - same as config.blockstoreLocation
82
+ * @param {CompoundIndexDefinition[]} config.compoundIndexes - compound indexes to register.
83
+ * Defaults to DEFAULT_COMPOUND_INDEXES which cover the most common DWN query patterns.
36
84
  */
37
85
  constructor(config: MessageStoreLevelConfig = {}) {
38
86
  this.config = {
@@ -50,6 +98,7 @@ export class MessageStoreLevel implements MessageStore {
50
98
  this.index = new IndexLevel({
51
99
  location : this.config.indexLocation!,
52
100
  createLevelDatabase : this.config.createLevelDatabase,
101
+ compoundIndexes : this.config.compoundIndexes ?? DEFAULT_COMPOUND_INDEXES,
53
102
  });
54
103
  }
55
104
 
@@ -115,6 +164,18 @@ export class MessageStoreLevel implements MessageStore {
115
164
  return { messages, cursor };
116
165
  }
117
166
 
167
+ async count(
168
+ tenant: string,
169
+ filters: Filter[],
170
+ messageSort?: MessageSort,
171
+ options?: MessageStoreOptions
172
+ ): Promise<number> {
173
+ options?.signal?.throwIfAborted();
174
+
175
+ const queryOptions = MessageStoreLevel.buildQueryOptions(messageSort);
176
+ return this.index.count(tenant, filters, queryOptions, options);
177
+ }
178
+
118
179
  /**
119
180
  * Builds the IndexLevel QueryOptions object given MessageStore sort and pagination parameters.
120
181
  */
@@ -192,4 +253,5 @@ export type MessageStoreLevelConfig = {
192
253
  blockstoreLocation?: string,
193
254
  indexLocation?: string,
194
255
  createLevelDatabase?: typeof createLevelDatabase,
256
+ compoundIndexes?: CompoundIndexDefinition[],
195
257
  };