@enbox/dwn-server 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 (309) hide show
  1. package/LICENSE +3 -2
  2. package/README.md +115 -215
  3. package/dist/esm/src/admin/activity-log.d.ts +44 -0
  4. package/dist/esm/src/admin/activity-log.d.ts.map +1 -0
  5. package/dist/esm/src/admin/activity-log.js +85 -0
  6. package/dist/esm/src/admin/activity-log.js.map +1 -0
  7. package/dist/esm/src/admin/admin-api.d.ts +61 -0
  8. package/dist/esm/src/admin/admin-api.d.ts.map +1 -0
  9. package/dist/esm/src/admin/admin-api.js +1047 -0
  10. package/dist/esm/src/admin/admin-api.js.map +1 -0
  11. package/dist/esm/src/admin/admin-auth.d.ts +9 -0
  12. package/dist/esm/src/admin/admin-auth.d.ts.map +1 -0
  13. package/dist/esm/src/admin/admin-auth.js +45 -0
  14. package/dist/esm/src/admin/admin-auth.js.map +1 -0
  15. package/dist/esm/src/admin/admin-store.d.ts +111 -0
  16. package/dist/esm/src/admin/admin-store.d.ts.map +1 -0
  17. package/dist/esm/src/admin/admin-store.js +376 -0
  18. package/dist/esm/src/admin/admin-store.js.map +1 -0
  19. package/dist/esm/src/admin/audit-log.d.ts +94 -0
  20. package/dist/esm/src/admin/audit-log.d.ts.map +1 -0
  21. package/dist/esm/src/admin/audit-log.js +220 -0
  22. package/dist/esm/src/admin/audit-log.js.map +1 -0
  23. package/dist/esm/src/admin/index.d.ts +10 -0
  24. package/dist/esm/src/admin/index.d.ts.map +1 -0
  25. package/dist/esm/src/admin/index.js +7 -0
  26. package/dist/esm/src/admin/index.js.map +1 -0
  27. package/dist/esm/src/admin/types.d.ts +306 -0
  28. package/dist/esm/src/admin/types.d.ts.map +1 -0
  29. package/dist/esm/src/admin/types.js +2 -0
  30. package/dist/esm/src/admin/types.js.map +1 -0
  31. package/dist/esm/src/admin/webhook-manager.d.ts +55 -0
  32. package/dist/esm/src/admin/webhook-manager.d.ts.map +1 -0
  33. package/dist/esm/src/admin/webhook-manager.js +184 -0
  34. package/dist/esm/src/admin/webhook-manager.js.map +1 -0
  35. package/dist/esm/src/config.d.ts +124 -9
  36. package/dist/esm/src/config.d.ts.map +1 -1
  37. package/dist/esm/src/config.js +155 -13
  38. package/dist/esm/src/config.js.map +1 -1
  39. package/dist/esm/src/connection/connection-manager.d.ts +32 -9
  40. package/dist/esm/src/connection/connection-manager.d.ts.map +1 -1
  41. package/dist/esm/src/connection/connection-manager.js +38 -5
  42. package/dist/esm/src/connection/connection-manager.js.map +1 -1
  43. package/dist/esm/src/connection/flow-controller.d.ts +53 -0
  44. package/dist/esm/src/connection/flow-controller.d.ts.map +1 -0
  45. package/dist/esm/src/connection/flow-controller.js +101 -0
  46. package/dist/esm/src/connection/flow-controller.js.map +1 -0
  47. package/dist/esm/src/connection/socket-connection.d.ts +54 -18
  48. package/dist/esm/src/connection/socket-connection.d.ts.map +1 -1
  49. package/dist/esm/src/connection/socket-connection.js +102 -40
  50. package/dist/esm/src/connection/socket-connection.js.map +1 -1
  51. package/dist/esm/src/delivery-service.d.ts +43 -0
  52. package/dist/esm/src/delivery-service.d.ts.map +1 -0
  53. package/dist/esm/src/delivery-service.js +574 -0
  54. package/dist/esm/src/delivery-service.js.map +1 -0
  55. package/dist/esm/src/dwn-error.d.ts +10 -1
  56. package/dist/esm/src/dwn-error.d.ts.map +1 -1
  57. package/dist/esm/src/dwn-error.js +9 -0
  58. package/dist/esm/src/dwn-error.js.map +1 -1
  59. package/dist/esm/src/dwn-server.d.ts +13 -6
  60. package/dist/esm/src/dwn-server.d.ts.map +1 -1
  61. package/dist/esm/src/dwn-server.js +199 -24
  62. package/dist/esm/src/dwn-server.js.map +1 -1
  63. package/dist/esm/src/http-api.d.ts +28 -13
  64. package/dist/esm/src/http-api.d.ts.map +1 -1
  65. package/dist/esm/src/http-api.js +649 -374
  66. package/dist/esm/src/http-api.js.map +1 -1
  67. package/dist/esm/src/index.d.ts +6 -2
  68. package/dist/esm/src/index.d.ts.map +1 -1
  69. package/dist/esm/src/index.js +4 -1
  70. package/dist/esm/src/index.js.map +1 -1
  71. package/dist/esm/src/json-rpc-api.js +2 -1
  72. package/dist/esm/src/json-rpc-api.js.map +1 -1
  73. package/dist/esm/src/json-rpc-handlers/dwn/process-message.d.ts.map +1 -1
  74. package/dist/esm/src/json-rpc-handlers/dwn/process-message.js +109 -7
  75. package/dist/esm/src/json-rpc-handlers/dwn/process-message.js.map +1 -1
  76. package/dist/esm/src/json-rpc-handlers/subscription/ack.d.ts +20 -0
  77. package/dist/esm/src/json-rpc-handlers/subscription/ack.d.ts.map +1 -0
  78. package/dist/esm/src/json-rpc-handlers/subscription/ack.js +41 -0
  79. package/dist/esm/src/json-rpc-handlers/subscription/ack.js.map +1 -0
  80. package/dist/esm/src/json-rpc-handlers/subscription/close.d.ts.map +1 -1
  81. package/dist/esm/src/json-rpc-handlers/subscription/close.js +1 -1
  82. package/dist/esm/src/json-rpc-handlers/subscription/close.js.map +1 -1
  83. package/dist/esm/src/json-rpc-handlers/subscription/index.d.ts +1 -0
  84. package/dist/esm/src/json-rpc-handlers/subscription/index.d.ts.map +1 -1
  85. package/dist/esm/src/json-rpc-handlers/subscription/index.js +1 -0
  86. package/dist/esm/src/json-rpc-handlers/subscription/index.js.map +1 -1
  87. package/dist/esm/src/lib/json-rpc-router.d.ts +25 -8
  88. package/dist/esm/src/lib/json-rpc-router.d.ts.map +1 -1
  89. package/dist/esm/src/lib/json-rpc-router.js.map +1 -1
  90. package/dist/esm/src/lib/sql-utils.d.ts +6 -0
  91. package/dist/esm/src/lib/sql-utils.d.ts.map +1 -0
  92. package/dist/esm/src/lib/sql-utils.js +8 -0
  93. package/dist/esm/src/lib/sql-utils.js.map +1 -0
  94. package/dist/esm/src/main.js +0 -6
  95. package/dist/esm/src/main.js.map +1 -1
  96. package/dist/esm/src/message-processed-hook.d.ts +35 -0
  97. package/dist/esm/src/message-processed-hook.d.ts.map +1 -0
  98. package/dist/esm/src/message-processed-hook.js +2 -0
  99. package/dist/esm/src/message-processed-hook.js.map +1 -0
  100. package/dist/esm/src/metrics.d.ts +14 -2
  101. package/dist/esm/src/metrics.d.ts.map +1 -1
  102. package/dist/esm/src/metrics.js +41 -1
  103. package/dist/esm/src/metrics.js.map +1 -1
  104. package/dist/esm/src/plugins/event-log-nats.d.ts +25 -0
  105. package/dist/esm/src/plugins/event-log-nats.d.ts.map +1 -0
  106. package/dist/esm/src/plugins/event-log-nats.js +379 -0
  107. package/dist/esm/src/plugins/event-log-nats.js.map +1 -0
  108. package/dist/esm/src/rate-limiter.d.ts +60 -0
  109. package/dist/esm/src/rate-limiter.d.ts.map +1 -0
  110. package/dist/esm/src/rate-limiter.js +116 -0
  111. package/dist/esm/src/rate-limiter.js.map +1 -0
  112. package/dist/esm/src/registration/jwt-provider-auth-plugin.d.ts +53 -0
  113. package/dist/esm/src/registration/jwt-provider-auth-plugin.d.ts.map +1 -0
  114. package/dist/esm/src/registration/jwt-provider-auth-plugin.js +90 -0
  115. package/dist/esm/src/registration/jwt-provider-auth-plugin.js.map +1 -0
  116. package/dist/esm/src/registration/open-auth-handler.d.ts +37 -0
  117. package/dist/esm/src/registration/open-auth-handler.d.ts.map +1 -0
  118. package/dist/esm/src/registration/open-auth-handler.js +214 -0
  119. package/dist/esm/src/registration/open-auth-handler.js.map +1 -0
  120. package/dist/esm/src/registration/proof-of-work-manager.d.ts +1 -1
  121. package/dist/esm/src/registration/proof-of-work-manager.d.ts.map +1 -1
  122. package/dist/esm/src/registration/proof-of-work-manager.js +3 -3
  123. package/dist/esm/src/registration/proof-of-work-manager.js.map +1 -1
  124. package/dist/esm/src/registration/provider-auth-plugin.d.ts +46 -0
  125. package/dist/esm/src/registration/provider-auth-plugin.d.ts.map +1 -0
  126. package/dist/esm/src/registration/provider-auth-plugin.js +29 -0
  127. package/dist/esm/src/registration/provider-auth-plugin.js.map +1 -0
  128. package/dist/esm/src/registration/registration-manager.d.ts +28 -5
  129. package/dist/esm/src/registration/registration-manager.d.ts.map +1 -1
  130. package/dist/esm/src/registration/registration-manager.js +83 -12
  131. package/dist/esm/src/registration/registration-manager.js.map +1 -1
  132. package/dist/esm/src/registration/registration-store.d.ts +83 -3
  133. package/dist/esm/src/registration/registration-store.d.ts.map +1 -1
  134. package/dist/esm/src/registration/registration-store.js +248 -11
  135. package/dist/esm/src/registration/registration-store.js.map +1 -1
  136. package/dist/esm/src/storage.d.ts +5 -5
  137. package/dist/esm/src/storage.d.ts.map +1 -1
  138. package/dist/esm/src/storage.js +105 -24
  139. package/dist/esm/src/storage.js.map +1 -1
  140. package/dist/esm/src/web5-connect/sql-ttl-cache.d.ts.map +1 -1
  141. package/dist/esm/src/web5-connect/sql-ttl-cache.js +11 -3
  142. package/dist/esm/src/web5-connect/sql-ttl-cache.js.map +1 -1
  143. package/dist/esm/src/web5-connect/web5-connect-server.d.ts.map +1 -1
  144. package/dist/esm/src/web5-connect/web5-connect-server.js +2 -2
  145. package/dist/esm/src/web5-connect/web5-connect-server.js.map +1 -1
  146. package/dist/esm/src/ws-api.d.ts +18 -4
  147. package/dist/esm/src/ws-api.d.ts.map +1 -1
  148. package/dist/esm/src/ws-api.js +12 -16
  149. package/dist/esm/src/ws-api.js.map +1 -1
  150. package/package.json +34 -53
  151. package/src/admin/activity-log.ts +100 -0
  152. package/src/admin/admin-api.ts +1308 -0
  153. package/src/admin/admin-auth.ts +56 -0
  154. package/src/admin/admin-store.ts +515 -0
  155. package/src/admin/audit-log.ts +327 -0
  156. package/src/admin/index.ts +34 -0
  157. package/src/admin/types.ts +352 -0
  158. package/src/admin/webhook-manager.ts +245 -0
  159. package/src/config.ts +190 -22
  160. package/src/connection/connection-manager.ts +67 -17
  161. package/src/connection/flow-controller.ts +117 -0
  162. package/src/connection/socket-connection.ts +144 -67
  163. package/src/delivery-service.ts +740 -0
  164. package/src/dwn-error.ts +11 -2
  165. package/src/dwn-server.ts +254 -39
  166. package/src/http-api.ts +736 -392
  167. package/src/index.ts +13 -2
  168. package/src/json-rpc-api.ts +2 -1
  169. package/src/json-rpc-handlers/dwn/process-message.ts +149 -15
  170. package/src/json-rpc-handlers/subscription/ack.ts +63 -0
  171. package/src/json-rpc-handlers/subscription/close.ts +5 -9
  172. package/src/json-rpc-handlers/subscription/index.ts +1 -0
  173. package/src/lib/json-rpc-router.ts +26 -11
  174. package/src/lib/sql-utils.ts +7 -0
  175. package/src/main.ts +0 -8
  176. package/src/message-processed-hook.ts +33 -0
  177. package/src/metrics.ts +57 -8
  178. package/src/plugins/event-log-nats.ts +466 -0
  179. package/src/process-handlers.ts +5 -5
  180. package/src/rate-limiter.ts +143 -0
  181. package/src/registration/jwt-provider-auth-plugin.ts +119 -0
  182. package/src/registration/open-auth-handler.ts +263 -0
  183. package/src/registration/proof-of-work-manager.ts +11 -10
  184. package/src/registration/provider-auth-plugin.ts +84 -0
  185. package/src/registration/registration-manager.ts +129 -31
  186. package/src/registration/registration-store.ts +332 -22
  187. package/src/storage.ts +136 -40
  188. package/src/web5-connect/sql-ttl-cache.ts +12 -5
  189. package/src/web5-connect/web5-connect-server.ts +9 -8
  190. package/src/ws-api.ts +39 -26
  191. package/dist/cjs/index.js +0 -6811
  192. package/dist/cjs/package.json +0 -1
  193. package/dist/esm/src/json-rpc-socket.d.ts +0 -39
  194. package/dist/esm/src/json-rpc-socket.d.ts.map +0 -1
  195. package/dist/esm/src/json-rpc-socket.js +0 -125
  196. package/dist/esm/src/json-rpc-socket.js.map +0 -1
  197. package/dist/esm/src/lib/http-server-shutdown-handler.d.ts +0 -10
  198. package/dist/esm/src/lib/http-server-shutdown-handler.d.ts.map +0 -1
  199. package/dist/esm/src/lib/http-server-shutdown-handler.js +0 -65
  200. package/dist/esm/src/lib/http-server-shutdown-handler.js.map +0 -1
  201. package/dist/esm/src/lib/json-rpc.d.ts +0 -54
  202. package/dist/esm/src/lib/json-rpc.d.ts.map +0 -1
  203. package/dist/esm/src/lib/json-rpc.js +0 -60
  204. package/dist/esm/src/lib/json-rpc.js.map +0 -1
  205. package/dist/esm/src/registration/proof-of-work-types.d.ts +0 -8
  206. package/dist/esm/src/registration/proof-of-work-types.d.ts.map +0 -1
  207. package/dist/esm/src/registration/proof-of-work-types.js +0 -2
  208. package/dist/esm/src/registration/proof-of-work-types.js.map +0 -1
  209. package/dist/esm/src/registration/registration-types.d.ts +0 -18
  210. package/dist/esm/src/registration/registration-types.d.ts.map +0 -1
  211. package/dist/esm/src/registration/registration-types.js +0 -2
  212. package/dist/esm/src/registration/registration-types.js.map +0 -1
  213. package/dist/esm/tests/common-scenario-validator.d.ts +0 -11
  214. package/dist/esm/tests/common-scenario-validator.d.ts.map +0 -1
  215. package/dist/esm/tests/common-scenario-validator.js +0 -114
  216. package/dist/esm/tests/common-scenario-validator.js.map +0 -1
  217. package/dist/esm/tests/connection/connection-manager.spec.d.ts +0 -2
  218. package/dist/esm/tests/connection/connection-manager.spec.d.ts.map +0 -1
  219. package/dist/esm/tests/connection/connection-manager.spec.js +0 -47
  220. package/dist/esm/tests/connection/connection-manager.spec.js.map +0 -1
  221. package/dist/esm/tests/connection/socket-connection.spec.d.ts +0 -2
  222. package/dist/esm/tests/connection/socket-connection.spec.d.ts.map +0 -1
  223. package/dist/esm/tests/connection/socket-connection.spec.js +0 -125
  224. package/dist/esm/tests/connection/socket-connection.spec.js.map +0 -1
  225. package/dist/esm/tests/cors/http-api.browser.d.ts +0 -2
  226. package/dist/esm/tests/cors/http-api.browser.d.ts.map +0 -1
  227. package/dist/esm/tests/cors/http-api.browser.js +0 -60
  228. package/dist/esm/tests/cors/http-api.browser.js.map +0 -1
  229. package/dist/esm/tests/cors/ping.browser.d.ts +0 -2
  230. package/dist/esm/tests/cors/ping.browser.d.ts.map +0 -1
  231. package/dist/esm/tests/cors/ping.browser.js +0 -7
  232. package/dist/esm/tests/cors/ping.browser.js.map +0 -1
  233. package/dist/esm/tests/dwn-process-message.spec.d.ts +0 -2
  234. package/dist/esm/tests/dwn-process-message.spec.d.ts.map +0 -1
  235. package/dist/esm/tests/dwn-process-message.spec.js +0 -172
  236. package/dist/esm/tests/dwn-process-message.spec.js.map +0 -1
  237. package/dist/esm/tests/dwn-server.spec.d.ts +0 -2
  238. package/dist/esm/tests/dwn-server.spec.d.ts.map +0 -1
  239. package/dist/esm/tests/dwn-server.spec.js +0 -49
  240. package/dist/esm/tests/dwn-server.spec.js.map +0 -1
  241. package/dist/esm/tests/http-api.spec.d.ts +0 -2
  242. package/dist/esm/tests/http-api.spec.d.ts.map +0 -1
  243. package/dist/esm/tests/http-api.spec.js +0 -775
  244. package/dist/esm/tests/http-api.spec.js.map +0 -1
  245. package/dist/esm/tests/json-rpc-socket.spec.d.ts +0 -2
  246. package/dist/esm/tests/json-rpc-socket.spec.d.ts.map +0 -1
  247. package/dist/esm/tests/json-rpc-socket.spec.js +0 -225
  248. package/dist/esm/tests/json-rpc-socket.spec.js.map +0 -1
  249. package/dist/esm/tests/plugins/data-store-sqlite.d.ts +0 -17
  250. package/dist/esm/tests/plugins/data-store-sqlite.d.ts.map +0 -1
  251. package/dist/esm/tests/plugins/data-store-sqlite.js +0 -23
  252. package/dist/esm/tests/plugins/data-store-sqlite.js.map +0 -1
  253. package/dist/esm/tests/plugins/event-log-sqlite.d.ts +0 -17
  254. package/dist/esm/tests/plugins/event-log-sqlite.d.ts.map +0 -1
  255. package/dist/esm/tests/plugins/event-log-sqlite.js +0 -23
  256. package/dist/esm/tests/plugins/event-log-sqlite.js.map +0 -1
  257. package/dist/esm/tests/plugins/event-stream-in-memory.d.ts +0 -17
  258. package/dist/esm/tests/plugins/event-stream-in-memory.d.ts.map +0 -1
  259. package/dist/esm/tests/plugins/event-stream-in-memory.js +0 -21
  260. package/dist/esm/tests/plugins/event-stream-in-memory.js.map +0 -1
  261. package/dist/esm/tests/plugins/message-store-sqlite.d.ts +0 -17
  262. package/dist/esm/tests/plugins/message-store-sqlite.d.ts.map +0 -1
  263. package/dist/esm/tests/plugins/message-store-sqlite.js +0 -23
  264. package/dist/esm/tests/plugins/message-store-sqlite.js.map +0 -1
  265. package/dist/esm/tests/plugins/resumable-task-store-sqlite.d.ts +0 -17
  266. package/dist/esm/tests/plugins/resumable-task-store-sqlite.d.ts.map +0 -1
  267. package/dist/esm/tests/plugins/resumable-task-store-sqlite.js +0 -23
  268. package/dist/esm/tests/plugins/resumable-task-store-sqlite.js.map +0 -1
  269. package/dist/esm/tests/process-handler.spec.d.ts +0 -2
  270. package/dist/esm/tests/process-handler.spec.d.ts.map +0 -1
  271. package/dist/esm/tests/process-handler.spec.js +0 -60
  272. package/dist/esm/tests/process-handler.spec.js.map +0 -1
  273. package/dist/esm/tests/registration/proof-of-work-manager.spec.d.ts +0 -2
  274. package/dist/esm/tests/registration/proof-of-work-manager.spec.d.ts.map +0 -1
  275. package/dist/esm/tests/registration/proof-of-work-manager.spec.js +0 -157
  276. package/dist/esm/tests/registration/proof-of-work-manager.spec.js.map +0 -1
  277. package/dist/esm/tests/rpc-subscribe-close.spec.d.ts +0 -2
  278. package/dist/esm/tests/rpc-subscribe-close.spec.d.ts.map +0 -1
  279. package/dist/esm/tests/rpc-subscribe-close.spec.js +0 -81
  280. package/dist/esm/tests/rpc-subscribe-close.spec.js.map +0 -1
  281. package/dist/esm/tests/scenarios/dynamic-plugin-loading.spec.d.ts +0 -2
  282. package/dist/esm/tests/scenarios/dynamic-plugin-loading.spec.d.ts.map +0 -1
  283. package/dist/esm/tests/scenarios/dynamic-plugin-loading.spec.js +0 -73
  284. package/dist/esm/tests/scenarios/dynamic-plugin-loading.spec.js.map +0 -1
  285. package/dist/esm/tests/scenarios/registration.spec.d.ts +0 -2
  286. package/dist/esm/tests/scenarios/registration.spec.d.ts.map +0 -1
  287. package/dist/esm/tests/scenarios/registration.spec.js +0 -507
  288. package/dist/esm/tests/scenarios/registration.spec.js.map +0 -1
  289. package/dist/esm/tests/scenarios/web5-connect.spec.d.ts +0 -2
  290. package/dist/esm/tests/scenarios/web5-connect.spec.d.ts.map +0 -1
  291. package/dist/esm/tests/scenarios/web5-connect.spec.js +0 -137
  292. package/dist/esm/tests/scenarios/web5-connect.spec.js.map +0 -1
  293. package/dist/esm/tests/test-dwn.d.ts +0 -7
  294. package/dist/esm/tests/test-dwn.d.ts.map +0 -1
  295. package/dist/esm/tests/test-dwn.js +0 -34
  296. package/dist/esm/tests/test-dwn.js.map +0 -1
  297. package/dist/esm/tests/utils.d.ts +0 -46
  298. package/dist/esm/tests/utils.d.ts.map +0 -1
  299. package/dist/esm/tests/utils.js +0 -116
  300. package/dist/esm/tests/utils.js.map +0 -1
  301. package/dist/esm/tests/ws-api.spec.d.ts +0 -2
  302. package/dist/esm/tests/ws-api.spec.d.ts.map +0 -1
  303. package/dist/esm/tests/ws-api.spec.js +0 -327
  304. package/dist/esm/tests/ws-api.spec.js.map +0 -1
  305. package/src/json-rpc-socket.ts +0 -155
  306. package/src/lib/http-server-shutdown-handler.ts +0 -79
  307. package/src/lib/json-rpc.ts +0 -126
  308. package/src/registration/proof-of-work-types.ts +0 -7
  309. package/src/registration/registration-types.ts +0 -18
package/src/index.ts CHANGED
@@ -1,6 +1,17 @@
1
- export { DwnServerConfig } from './config.js';
1
+ export { ActivityLog, AdminApi, AdminStore } from './admin/index.js';
2
+ export type {
3
+ AdminActivityEvent,
4
+ AdminConnectionSnapshot,
5
+ AdminHealthCheck,
6
+ AdminServerStats,
7
+ AdminTenantDetail,
8
+ AdminTenantSummary,
9
+ } from './admin/index.js';
10
+ export { config as defaultDwnServerConfig, DwnServerConfig } from './config.js';
11
+ export { DeliveryService } from './delivery-service.js';
2
12
  export { DwnServer, DwnServerOptions } from './dwn-server.js';
3
13
  export { HttpApi } from './http-api.js';
4
14
  export { jsonRpcRouter } from './json-rpc-api.js';
5
- export { StoreType, BackendTypes, DwnStore } from './storage.js';
15
+ export type { MessageProcessedContext, MessageProcessedHook } from './message-processed-hook.js';
16
+ export { getDwnConfig, StoreType, BackendTypes, DwnStore } from './storage.js';
6
17
  export { WsApi } from './ws-api.js';
@@ -1,11 +1,12 @@
1
1
  import { JsonRpcRouter } from './lib/json-rpc-router.js';
2
2
 
3
3
  import { handleDwnProcessMessage } from './json-rpc-handlers/dwn/index.js';
4
- import { handleSubscriptionsClose } from './json-rpc-handlers/subscription/index.js';
4
+ import { handleSubscriptionAck, handleSubscriptionsClose } from './json-rpc-handlers/subscription/index.js';
5
5
 
6
6
  export const jsonRpcRouter = new JsonRpcRouter();
7
7
 
8
8
  jsonRpcRouter.on('dwn.processMessage', handleDwnProcessMessage);
9
9
  jsonRpcRouter.on('rpc.subscribe.dwn.processMessage', handleDwnProcessMessage);
10
10
 
11
+ jsonRpcRouter.on('rpc.ack', handleSubscriptionAck);
11
12
  jsonRpcRouter.on('rpc.subscribe.close', handleSubscriptionsClose);
@@ -1,21 +1,22 @@
1
1
  import type { GenericMessage } from '@enbox/dwn-sdk-js';
2
2
  import { DwnInterfaceName, DwnMethodName } from '@enbox/dwn-sdk-js';
3
3
 
4
- import type { Readable as IsomorphicReadable } from 'readable-stream';
5
4
  import log from 'loglevel';
6
5
  import { v4 as uuidv4 } from 'uuid';
7
6
 
8
- import type { JsonRpcSubscription } from '../../lib/json-rpc.js';
7
+ import type { JsonRpcSubscription } from '@enbox/dwn-clients';
9
8
  import type {
10
9
  HandlerResponse,
11
10
  JsonRpcHandler,
12
11
  } from '../../lib/json-rpc-router.js';
13
12
 
13
+ import { DwnServerErrorCode } from '../../dwn-error.js';
14
+ import { requestDataBytesTotal } from '../../metrics.js';
14
15
  import {
15
16
  createJsonRpcErrorResponse,
16
17
  createJsonRpcSuccessResponse,
17
18
  JsonRpcErrorCodes,
18
- } from '../../lib/json-rpc.js';
19
+ } from '@enbox/dwn-clients';
19
20
 
20
21
 
21
22
  export const handleDwnProcessMessage: JsonRpcHandler = async (
@@ -28,7 +29,7 @@ export const handleDwnProcessMessage: JsonRpcHandler = async (
28
29
 
29
30
  try {
30
31
  // RecordsWrite is only supported on 'http' to support data stream for large data
31
- // TODO: https://github.com/TBD54566975/dwn-server/issues/108
32
+ // TODO: https://github.com/enboxorg/enbox/issues/108
32
33
  if (
33
34
  transport !== 'http' &&
34
35
  message.descriptor.interface === DwnInterfaceName.Records &&
@@ -38,11 +39,11 @@ export const handleDwnProcessMessage: JsonRpcHandler = async (
38
39
  requestId,
39
40
  JsonRpcErrorCodes.InvalidParams,
40
41
  `RecordsWrite is not supported via ${context.transport}`
41
- )
42
+ );
42
43
  return { jsonRpcResponse };
43
44
  }
44
45
 
45
- // subscribe methods must come with a subscriptionRequest context
46
+ // subscribe methods must come with a subscriptionRequest context
46
47
  if (message.descriptor.method === DwnMethodName.Subscribe && subscriptionRequest === undefined) {
47
48
  const jsonRpcResponse = createJsonRpcErrorResponse(
48
49
  requestId,
@@ -58,7 +59,7 @@ export const handleDwnProcessMessage: JsonRpcHandler = async (
58
59
  requestId,
59
60
  JsonRpcErrorCodes.InvalidParams,
60
61
  `subscriptions are not supported via ${context.transport}`
61
- )
62
+ );
62
63
  return { jsonRpcResponse };
63
64
  }
64
65
 
@@ -70,12 +71,40 @@ export const handleDwnProcessMessage: JsonRpcHandler = async (
70
71
  requestId,
71
72
  JsonRpcErrorCodes.InvalidParams,
72
73
  `the subscribe id: ${subscriptionRequest.id} is in use by an active subscription`
73
- )
74
+ );
74
75
  return { jsonRpcResponse };
75
76
  }
76
77
 
78
+ // --- Per-tenant rate limiting (before any DWN processing) ---
79
+ if (context.tenantRateLimiter) {
80
+ const result = context.tenantRateLimiter.consume(target);
81
+ if (result.allowed === false) {
82
+ const retryAfterSec = Math.ceil(result.retryAfterMs / 1000);
83
+ const jsonRpcResponse = createJsonRpcErrorResponse(
84
+ requestId,
85
+ JsonRpcErrorCodes.TooManyRequests,
86
+ `${DwnServerErrorCode.RateLimitExceeded}: tenant rate limit exceeded, retry after ${retryAfterSec}s`,
87
+ { retryAfterSec },
88
+ );
89
+ return { jsonRpcResponse };
90
+ }
91
+ }
92
+
93
+ // --- Per-tenant storage quota enforcement (RecordsWrite only) ---
94
+ if (
95
+ context.config &&
96
+ context.adminStore &&
97
+ message.descriptor.interface === DwnInterfaceName.Records &&
98
+ message.descriptor.method === DwnMethodName.Write
99
+ ) {
100
+ const quotaResult = await enforceQuota(target, message, context);
101
+ if (quotaResult !== undefined) {
102
+ return quotaResult;
103
+ }
104
+ }
105
+
77
106
  const reply = await dwn.processMessage(target, message, {
78
- dataStream: dataStream as IsomorphicReadable,
107
+ dataStream,
79
108
  subscriptionHandler: subscriptionRequest?.subscriptionHandler,
80
109
  });
81
110
 
@@ -83,7 +112,7 @@ export const handleDwnProcessMessage: JsonRpcHandler = async (
83
112
  const { entry } = reply;
84
113
  // RecordsRead or MessagesRead messages optionally return data as a stream to accommodate large amounts of data
85
114
  // we remove the data stream from the reply that will be serialized and return it as a separate property in the response payload.
86
- let recordDataStream: IsomorphicReadable;
115
+ let recordDataStream: ReadableStream<Uint8Array>;
87
116
  if (entry !== undefined && entry.data !== undefined) {
88
117
  recordDataStream = entry.data;
89
118
  delete reply.entry.data; // not serializable via JSON
@@ -97,9 +126,9 @@ export const handleDwnProcessMessage: JsonRpcHandler = async (
97
126
  const subscriptionReply: JsonRpcSubscription = {
98
127
  id: subscriptionRequest.id,
99
128
  close,
100
- }
129
+ };
101
130
  await socketConnection.addSubscription(subscriptionReply);
102
- delete reply.subscription.close // delete the close method from the reply as it's not JSON serializable and has a held reference.
131
+ delete reply.subscription.close; // delete the close method from the reply as it's not JSON serializable and has a held reference.
103
132
  }
104
133
 
105
134
  const jsonRpcResponse = createJsonRpcSuccessResponse(requestId, { reply });
@@ -108,16 +137,121 @@ export const handleDwnProcessMessage: JsonRpcHandler = async (
108
137
  responsePayload.dataStream = recordDataStream;
109
138
  }
110
139
 
140
+ // --- Fire-and-forget: post-processing hooks ---
141
+ const statusCode = reply.status?.code ?? 0;
142
+ if (context.messageProcessedHooks) {
143
+ const hookContext = { tenant: target, message, status: reply.status, transport };
144
+ for (const hook of context.messageProcessedHooks) {
145
+ try {
146
+ const result = hook.onMessageProcessed(hookContext);
147
+ if (result instanceof Promise) {
148
+ result.catch((err: unknown): void => {
149
+ log.error('MessageProcessedHook error', err);
150
+ });
151
+ }
152
+ } catch (err) {
153
+ log.error('MessageProcessedHook error', err);
154
+ }
155
+ }
156
+ }
157
+
158
+ // Capture activity event and per-request metrics.
159
+ const dwnInterface = message.descriptor.interface as string;
160
+ const dwnMethod = message.descriptor.method as string;
161
+ const dataSizeBytes = (message.descriptor as { dataSize?: number }).dataSize;
162
+
163
+ if (dataSizeBytes !== undefined && dataSizeBytes > 0) {
164
+ requestDataBytesTotal.inc({ interface: dwnInterface, method: dwnMethod }, dataSizeBytes);
165
+ }
166
+
167
+ if (context.activityLog) {
168
+ context.activityLog.record({
169
+ tenant : target,
170
+ interface : dwnInterface,
171
+ method : dwnMethod,
172
+ statusCode,
173
+ transport,
174
+ dataSizeBytes,
175
+ });
176
+ }
177
+
111
178
  return responsePayload;
112
179
  } catch (error) {
180
+ // Log the full error internally but return a generic message to the client
181
+ // to avoid leaking implementation details (SQL errors, file paths, etc.).
182
+ log.error('handleDwnProcessMessage error', error);
183
+
113
184
  const jsonRpcResponse = createJsonRpcErrorResponse(
114
185
  requestId,
115
186
  JsonRpcErrorCodes.InternalError,
116
- error.message,
187
+ 'an unexpected error occurred while processing the message',
117
188
  );
118
189
 
119
- // log the unhandled error response
120
- log.error('handleDwnProcessMessage error', jsonRpcResponse, dwnRequest, error);
121
190
  return { jsonRpcResponse } as HandlerResponse;
122
191
  }
123
192
  };
193
+
194
+ // ---------------------------------------------------------------------------
195
+ // Quota enforcement helper
196
+ // ---------------------------------------------------------------------------
197
+
198
+ /**
199
+ * Checks whether the tenant has exceeded their message count or storage quota.
200
+ * Returns a JSON-RPC error response if the quota is exceeded, or `undefined` to proceed.
201
+ */
202
+ async function enforceQuota(
203
+ target: string,
204
+ message: GenericMessage,
205
+ context: Parameters<JsonRpcHandler>[1],
206
+ ): Promise<HandlerResponse | undefined> {
207
+ const { config, adminStore, registrationStore } = context;
208
+ const requestId = (message as any).recordId ?? uuidv4();
209
+
210
+ // Resolve effective quota: per-tenant override > global config > unlimited.
211
+ let maxMessages = config!.quotaMaxMessages ?? 0;
212
+ let maxStorageBytes = config!.quotaMaxStorageBytes ?? 0;
213
+
214
+ if (registrationStore) {
215
+ const tenantQuota = await registrationStore.getQuota(target);
216
+ if (tenantQuota !== undefined) {
217
+ maxMessages = tenantQuota.maxMessages ?? maxMessages;
218
+ maxStorageBytes = tenantQuota.maxStorageBytes ?? maxStorageBytes;
219
+ }
220
+ }
221
+
222
+ // 0 means unlimited — skip enforcement.
223
+ if (maxMessages === 0 && maxStorageBytes === 0) {
224
+ return undefined;
225
+ }
226
+
227
+ // Check message count quota.
228
+ if (maxMessages > 0) {
229
+ const currentMessages = await adminStore!.getTenantMessageCount(target);
230
+ if (currentMessages >= maxMessages) {
231
+ return {
232
+ jsonRpcResponse: createJsonRpcErrorResponse(
233
+ requestId,
234
+ JsonRpcErrorCodes.InvalidRequest,
235
+ `${DwnServerErrorCode.TenantMessageQuotaExceeded}: tenant has reached the message limit of ${maxMessages}`,
236
+ ),
237
+ };
238
+ }
239
+ }
240
+
241
+ // Check storage size quota.
242
+ if (maxStorageBytes > 0) {
243
+ const dataSize = (message.descriptor as { dataSize?: number }).dataSize ?? 0;
244
+ const currentStorage = await adminStore!.getTenantStorageSize(target);
245
+ if (currentStorage + dataSize > maxStorageBytes) {
246
+ return {
247
+ jsonRpcResponse: createJsonRpcErrorResponse(
248
+ requestId,
249
+ JsonRpcErrorCodes.InvalidRequest,
250
+ `${DwnServerErrorCode.TenantStorageQuotaExceeded}: tenant would exceed storage limit of ${maxStorageBytes} bytes`,
251
+ ),
252
+ };
253
+ }
254
+ }
255
+
256
+ return undefined;
257
+ }
@@ -0,0 +1,63 @@
1
+ import { v4 as uuidv4 } from 'uuid';
2
+
3
+ import type { JsonRpcId } from '@enbox/dwn-clients';
4
+ import type {
5
+ HandlerResponse,
6
+ JsonRpcHandler,
7
+ } from '../../lib/json-rpc-router.js';
8
+
9
+ import { createJsonRpcErrorResponse, createJsonRpcSuccessResponse, JsonRpcErrorCodes } from '@enbox/dwn-clients';
10
+
11
+ /**
12
+ * Handles `rpc.ack` — acknowledges receipt of subscription events up to the
13
+ * given cursor, advancing the per-subscription flow-control window.
14
+ *
15
+ * Request shape:
16
+ * ```json
17
+ * {
18
+ * "jsonrpc": "2.0",
19
+ * "method": "rpc.ack",
20
+ * "params": { "cursor": "<opaque-cursor>" },
21
+ * "subscription": { "id": "<subscription-id>" }
22
+ * }
23
+ * ```
24
+ *
25
+ * This is a notification (no `id` required), but the server sends a response
26
+ * if validation fails (missing params, unknown subscription, etc.).
27
+ */
28
+ export const handleSubscriptionAck: JsonRpcHandler = async (
29
+ jsonRpcRequest,
30
+ context,
31
+ ) => {
32
+ const requestId = jsonRpcRequest.id ?? uuidv4();
33
+
34
+ if (context.socketConnection === undefined) {
35
+ const jsonRpcResponse = createJsonRpcErrorResponse(
36
+ requestId, JsonRpcErrorCodes.InvalidRequest, 'socket connection does not exist'
37
+ );
38
+ return { jsonRpcResponse };
39
+ }
40
+
41
+ if (jsonRpcRequest.subscription === undefined) {
42
+ const jsonRpcResponse = createJsonRpcErrorResponse(
43
+ requestId, JsonRpcErrorCodes.InvalidParams, 'subscription options are required'
44
+ );
45
+ return { jsonRpcResponse };
46
+ }
47
+
48
+ const { id: subscriptionId } = jsonRpcRequest.subscription as { id: JsonRpcId };
49
+ const { cursor } = (jsonRpcRequest.params ?? {}) as { cursor?: string };
50
+
51
+ if (cursor === undefined || typeof cursor !== 'string' || cursor === '') {
52
+ const jsonRpcResponse = createJsonRpcErrorResponse(
53
+ requestId, JsonRpcErrorCodes.InvalidParams, 'params.cursor is required and must be a non-empty string'
54
+ );
55
+ return { jsonRpcResponse };
56
+ }
57
+
58
+ const { socketConnection } = context;
59
+ socketConnection.ackSubscription(subscriptionId, cursor);
60
+
61
+ const jsonRpcResponse = createJsonRpcSuccessResponse(requestId, { reply: { status: 200, detail: 'OK' } });
62
+ return { jsonRpcResponse } as HandlerResponse;
63
+ };
@@ -1,17 +1,13 @@
1
1
  import { v4 as uuidv4 } from 'uuid';
2
2
 
3
- import { DwnServerErrorCode } from '../../dwn-error.js';
4
3
  import type {
5
4
  HandlerResponse,
6
5
  JsonRpcHandler,
7
6
  } from '../../lib/json-rpc-router.js';
7
+ import type { JsonRpcId, JsonRpcResponse } from '@enbox/dwn-clients';
8
8
 
9
- import type { JsonRpcId, JsonRpcResponse } from '../../lib/json-rpc.js';
10
- import {
11
- createJsonRpcErrorResponse,
12
- createJsonRpcSuccessResponse,
13
- JsonRpcErrorCodes,
14
- } from '../../lib/json-rpc.js';
9
+ import { DwnServerErrorCode } from '../../dwn-error.js';
10
+ import { createJsonRpcErrorResponse, createJsonRpcSuccessResponse, JsonRpcErrorCodes } from '@enbox/dwn-clients';
15
11
 
16
12
  /**
17
13
  * Closes a subscription tied to a specific `SocketConnection`.
@@ -43,7 +39,7 @@ export const handleSubscriptionsClose: JsonRpcHandler = async (
43
39
  // closing the subscription and cleaning up the reference within the given connection.
44
40
  await socketConnection.closeSubscription(id);
45
41
  jsonRpcResponse = createJsonRpcSuccessResponse(requestId, { reply: { status: 200, detail: 'Accepted' } });
46
- } catch(error) {
42
+ } catch (error) {
47
43
  if (error.code === DwnServerErrorCode.ConnectionSubscriptionJsonRpcIdNotFound) {
48
44
  jsonRpcResponse = createJsonRpcErrorResponse(requestId, JsonRpcErrorCodes.InvalidParams, `subscription ${id} does not exist.`);
49
45
  } else {
@@ -56,4 +52,4 @@ export const handleSubscriptionsClose: JsonRpcHandler = async (
56
52
  }
57
53
 
58
54
  return { jsonRpcResponse } as HandlerResponse;
59
- }
55
+ };
@@ -1 +1,2 @@
1
+ export * from './ack.js';
1
2
  export * from './close.js';
@@ -1,9 +1,12 @@
1
- import type { Dwn, MessageSubscriptionHandler } from '@enbox/dwn-sdk-js';
2
-
3
- import type { Readable } from 'node:stream';
4
-
5
- import type { JsonRpcId, JsonRpcRequest, JsonRpcResponse } from './json-rpc.js';
1
+ import type { ActivityLog } from '../admin/activity-log.js';
2
+ import type { AdminStore } from '../admin/admin-store.js';
3
+ import type { DwnServerConfig } from '../config.js';
4
+ import type { MessageProcessedHook } from '../message-processed-hook.js';
5
+ import type { RateLimiter } from '../rate-limiter.js';
6
+ import type { RegistrationStore } from '../registration/registration-store.js';
6
7
  import type { SocketConnection } from '../connection/socket-connection.js';
8
+ import type { Dwn, SubscriptionListener } from '@enbox/dwn-sdk-js';
9
+ import type { JsonRpcId, JsonRpcRequest, JsonRpcResponse } from '@enbox/dwn-clients';
7
10
 
8
11
  export type RequestContext = {
9
12
  transport: 'http' | 'ws';
@@ -12,17 +15,29 @@ export type RequestContext = {
12
15
  socketConnection?: SocketConnection;
13
16
  subscriptionRequest?: {
14
17
  /** The JsonRpcId of the subscription handler */
15
- id: JsonRpcId;
16
- /** The `MessageEvent` handler associated with a subscription request, only used in `ws` requests */
17
- subscriptionHandler: MessageSubscriptionHandler;
18
+ id: JsonRpcId;
19
+ /** The `SubscriptionMessage` handler associated with a subscription request, only used in `ws` requests */
20
+ subscriptionHandler: SubscriptionListener;
18
21
  }
19
- /** The `Readable` stream associated with a `RecordsWrite` request only used in `http` requests */
20
- dataStream?: Readable;
22
+ /** The `ReadableStream` associated with a `RecordsWrite` request only used in `http` requests */
23
+ dataStream?: ReadableStream<Uint8Array>;
24
+ /** The admin activity log for capturing DWN request events (optional). */
25
+ activityLog?: ActivityLog;
26
+ /** The admin store for quota usage queries (optional). */
27
+ adminStore?: AdminStore;
28
+ /** The registration store for per-tenant quota lookups (optional). */
29
+ registrationStore?: RegistrationStore;
30
+ /** Server configuration for global quota/rate-limit defaults (optional). */
31
+ config?: DwnServerConfig;
32
+ /** Per-tenant rate limiter (optional). */
33
+ tenantRateLimiter?: RateLimiter;
34
+ /** Hooks invoked after every `dwn.processMessage()` call (fire-and-forget). */
35
+ messageProcessedHooks?: MessageProcessedHook[];
21
36
  };
22
37
 
23
38
  export type HandlerResponse = {
24
39
  jsonRpcResponse: JsonRpcResponse;
25
- dataStream?: Readable;
40
+ dataStream?: ReadableStream<Uint8Array>;
26
41
  };
27
42
 
28
43
  export type JsonRpcHandler = (
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Escapes SQL LIKE wildcard characters (`%`, `_`, `\`) in user-supplied input
3
+ * so that they are treated as literal characters rather than pattern operators.
4
+ */
5
+ export function escapeLikeWildcards(input: string): string {
6
+ return input.replace(/[%_\\]/g, '\\$&');
7
+ }
package/src/main.ts CHANGED
@@ -1,14 +1,6 @@
1
1
  #!/usr/bin/env node
2
- // node.js 18 and earlier, needs globalThis.crypto polyfill. needed for dwn-sdk-js
3
- import { webcrypto } from 'node:crypto';
4
-
5
2
  import { DwnServer } from './dwn-server.js';
6
3
 
7
- if (!globalThis.crypto) {
8
- // @ts-ignore
9
- globalThis.crypto = webcrypto;
10
- }
11
-
12
4
  const dwnServer = new DwnServer();
13
5
 
14
6
  await dwnServer.start();
@@ -0,0 +1,33 @@
1
+ import type { GenericMessage } from '@enbox/dwn-sdk-js';
2
+
3
+ /**
4
+ * Context provided to hooks after `dwn.processMessage()` returns.
5
+ */
6
+ export type MessageProcessedContext = {
7
+ /** The tenant DID that owns the DWN. */
8
+ tenant: string;
9
+ /** The original signed DWN message. */
10
+ message: GenericMessage;
11
+ /** The reply status from the DWN engine. */
12
+ status: { code: number; detail: string };
13
+ /** The transport over which the message was received. */
14
+ transport: 'http' | 'ws';
15
+ };
16
+
17
+ /**
18
+ * Hook that is invoked after every successful `dwn.processMessage()` call.
19
+ *
20
+ * Hooks are fire-and-forget: they run asynchronously and their return values
21
+ * are ignored. Errors thrown by hooks are logged but never propagate to the
22
+ * request handler or the client.
23
+ *
24
+ * Implementations should return quickly and schedule any expensive work
25
+ * (network I/O, retries) internally.
26
+ */
27
+ export interface MessageProcessedHook {
28
+ /**
29
+ * Called after a DWN message has been processed.
30
+ * May return void or a Promise — both are handled gracefully.
31
+ */
32
+ onMessageProcessed(context: MessageProcessedContext): void | Promise<void>;
33
+ }
package/src/metrics.ts CHANGED
@@ -1,14 +1,63 @@
1
- import { Counter, Histogram } from 'prom-client';
1
+ import { Counter, Gauge, Histogram } from 'prom-client';
2
+
3
+ // ---------------------------------------------------------------------------
4
+ // Existing metrics
5
+ // ---------------------------------------------------------------------------
2
6
 
3
7
  export const requestCounter = new Counter({
4
- name: 'dwn_requests_total',
5
- help: 'all dwn requests processed',
6
- labelNames: ['method', 'status', 'error'],
8
+ name : 'dwn_requests_total',
9
+ help : 'all dwn requests processed',
10
+ labelNames : ['method', 'status', 'error'],
7
11
  });
8
12
 
9
13
  export const responseHistogram = new Histogram({
10
- name: 'http_response',
11
- help: 'response histogram',
12
- buckets: [0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5, 10],
13
- labelNames: ['route', 'code'],
14
+ name : 'http_response',
15
+ help : 'response histogram',
16
+ buckets : [0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5, 10],
17
+ labelNames : ['route', 'code'],
18
+ });
19
+
20
+ // ---------------------------------------------------------------------------
21
+ // Enhanced gauges — updated periodically by AdminApi.startMetricsUpdater()
22
+ // ---------------------------------------------------------------------------
23
+
24
+ /** Number of active (registered) tenants. */
25
+ export const activeTenants = new Gauge({
26
+ name : 'dwn_active_tenants',
27
+ help : 'number of active registered tenants',
28
+ });
29
+
30
+ /** Total messages stored across all tenants. */
31
+ export const totalMessages = new Gauge({
32
+ name : 'dwn_total_messages',
33
+ help : 'total messages stored across all tenants',
34
+ });
35
+
36
+ /** Total data storage in bytes across all tenants. */
37
+ export const totalDataBytes = new Gauge({
38
+ name : 'dwn_total_data_bytes',
39
+ help : 'total data storage bytes across all tenants',
40
+ });
41
+
42
+ /** Number of active WebSocket connections. */
43
+ export const websocketConnections = new Gauge({
44
+ name : 'dwn_websocket_connections',
45
+ help : 'number of active websocket connections',
46
+ });
47
+
48
+ /** Number of active WebSocket subscriptions. */
49
+ export const websocketSubscriptions = new Gauge({
50
+ name : 'dwn_websocket_subscriptions',
51
+ help : 'number of active websocket subscriptions',
52
+ });
53
+
54
+ // ---------------------------------------------------------------------------
55
+ // Enhanced counters — incremented per-request
56
+ // ---------------------------------------------------------------------------
57
+
58
+ /** Total data bytes written via RecordsWrite, labeled by interface + method. */
59
+ export const requestDataBytesTotal = new Counter({
60
+ name : 'dwn_request_data_bytes_total',
61
+ help : 'total data bytes processed in DWN requests',
62
+ labelNames : ['interface', 'method'],
14
63
  });