@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
@@ -0,0 +1,245 @@
1
+ import type { Dialect } from '@enbox/dwn-sql-store';
2
+ import type { AdminWebhook, AdminWebhookInput } from './types.js';
3
+
4
+ import { createHmac } from 'crypto';
5
+ import { Kysely } from 'kysely';
6
+ import log from 'loglevel';
7
+ import { v4 as uuidv4 } from 'uuid';
8
+
9
+ /**
10
+ * Payload delivered to webhook endpoints.
11
+ */
12
+ export type WebhookPayload = {
13
+ /** Unique delivery ID. */
14
+ id : string;
15
+ /** ISO-8601 timestamp. */
16
+ timestamp : string;
17
+ /** Event type (e.g. `tenant.suspend`, `quota.warning`). */
18
+ event : string;
19
+ /** Optional target (e.g. tenant DID). */
20
+ target? : string;
21
+ /** Additional event data. */
22
+ data? : Record<string, unknown>;
23
+ };
24
+
25
+ /**
26
+ * Manages webhook registrations and event delivery.
27
+ *
28
+ * Webhooks are stored in a SQL table and fire asynchronously when matching
29
+ * events occur. Delivery includes retry logic and optional HMAC-SHA256 signatures.
30
+ *
31
+ * @see https://github.com/enboxorg/enbox/issues/395
32
+ */
33
+ export class WebhookManager {
34
+ static readonly #tableName = 'adminWebhooks';
35
+ static readonly #maxRetries = 3;
36
+ static readonly #retryDelaysMs = [1000, 5000, 15000];
37
+
38
+ #db: Kysely<WebhookDatabase>;
39
+
40
+ private constructor(dialect: Dialect) {
41
+ this.#db = new Kysely<WebhookDatabase>({ dialect });
42
+ }
43
+
44
+ /**
45
+ * Creates and initializes a `WebhookManager` instance.
46
+ */
47
+ public static async create(dialect: Dialect): Promise<WebhookManager> {
48
+ const manager = new WebhookManager(dialect);
49
+ await manager.#initialize();
50
+ return manager;
51
+ }
52
+
53
+ async #initialize(): Promise<void> {
54
+ await this.#db.schema
55
+ .createTable(WebhookManager.#tableName)
56
+ .ifNotExists()
57
+ .addColumn('id', 'text', (col) => col.primaryKey())
58
+ .addColumn('url', 'text', (col) => col.notNull())
59
+ .addColumn('events', 'text', (col) => col.notNull()) // JSON array
60
+ .addColumn('secret', 'text')
61
+ .addColumn('createdAt', 'text', (col) => col.notNull())
62
+ .execute();
63
+ }
64
+
65
+ // ---------------------------------------------------------------------------
66
+ // CRUD
67
+ // ---------------------------------------------------------------------------
68
+
69
+ /**
70
+ * Registers a new webhook. Returns the created webhook (without secret echoed).
71
+ */
72
+ public async register(input: AdminWebhookInput): Promise<AdminWebhook> {
73
+ const webhook: AdminWebhook = {
74
+ id : uuidv4(),
75
+ url : input.url,
76
+ events : input.events,
77
+ createdAt : new Date().toISOString(),
78
+ };
79
+
80
+ await this.#db
81
+ .insertInto(WebhookManager.#tableName)
82
+ .values({
83
+ id : webhook.id,
84
+ url : webhook.url,
85
+ events : JSON.stringify(webhook.events),
86
+ secret : input.secret ?? null,
87
+ createdAt : webhook.createdAt,
88
+ })
89
+ .execute();
90
+
91
+ return webhook;
92
+ }
93
+
94
+ /**
95
+ * Lists all registered webhooks. Secrets are redacted.
96
+ */
97
+ public async list(): Promise<AdminWebhook[]> {
98
+ const rows = await this.#db
99
+ .selectFrom(WebhookManager.#tableName)
100
+ .select(['id', 'url', 'events', 'secret', 'createdAt'])
101
+ .orderBy('createdAt', 'asc')
102
+ .execute();
103
+
104
+ return rows.map((row): AdminWebhook => ({
105
+ id : row.id,
106
+ url : row.url,
107
+ events : JSON.parse(row.events),
108
+ secret : row.secret ? '***' : undefined,
109
+ createdAt : row.createdAt,
110
+ }));
111
+ }
112
+
113
+ /**
114
+ * Deletes a webhook by ID. Returns `true` if found and deleted.
115
+ */
116
+ public async delete(id: string): Promise<boolean> {
117
+ const result = await this.#db
118
+ .deleteFrom(WebhookManager.#tableName)
119
+ .where('id', '=', id)
120
+ .executeTakeFirstOrThrow();
121
+
122
+ return Number(result.numDeletedRows) > 0;
123
+ }
124
+
125
+ // ---------------------------------------------------------------------------
126
+ // Event dispatch
127
+ // ---------------------------------------------------------------------------
128
+
129
+ /**
130
+ * Fires matching webhooks for a given event. Non-blocking — delivery happens
131
+ * asynchronously and errors are logged but never propagated.
132
+ */
133
+ public fire(event: string, target?: string, data?: Record<string, unknown>): void {
134
+ // Fire-and-forget.
135
+ this.#dispatchAll(event, target, data).catch((err): void => {
136
+ log.error('Webhook dispatch error:', err);
137
+ });
138
+ }
139
+
140
+ async #dispatchAll(event: string, target?: string, data?: Record<string, unknown>): Promise<void> {
141
+ const rows = await this.#db
142
+ .selectFrom(WebhookManager.#tableName)
143
+ .select(['id', 'url', 'events', 'secret'])
144
+ .execute();
145
+
146
+ const payload: WebhookPayload = {
147
+ id : uuidv4(),
148
+ timestamp : new Date().toISOString(),
149
+ event,
150
+ target,
151
+ data,
152
+ };
153
+
154
+ const body = JSON.stringify(payload);
155
+
156
+ for (const row of rows) {
157
+ const patterns: string[] = JSON.parse(row.events);
158
+ if (this.#matchesEvent(event, patterns)) {
159
+ this.#deliver(row.url, body, row.secret).catch((err): void => {
160
+ log.warn(`Webhook delivery failed for ${row.url}:`, err);
161
+ });
162
+ }
163
+ }
164
+ }
165
+
166
+ /**
167
+ * Checks if an event matches any of the subscription patterns.
168
+ * Supports wildcard patterns like `tenant.*`.
169
+ */
170
+ #matchesEvent(event: string, patterns: string[]): boolean {
171
+ for (const pattern of patterns) {
172
+ if (pattern === '*') {
173
+ return true;
174
+ }
175
+ if (pattern === event) {
176
+ return true;
177
+ }
178
+ if (pattern.endsWith('.*')) {
179
+ const prefix = pattern.slice(0, -2);
180
+ if (event.startsWith(prefix + '.') || event === prefix) {
181
+ return true;
182
+ }
183
+ }
184
+ }
185
+ return false;
186
+ }
187
+
188
+ /**
189
+ * Delivers a webhook payload with retry logic and optional HMAC signature.
190
+ */
191
+ async #deliver(url: string, body: string, secret: string | null): Promise<void> {
192
+ const headers: Record<string, string> = {
193
+ 'content-type': 'application/json',
194
+ };
195
+
196
+ if (secret) {
197
+ const signature = createHmac('sha256', secret).update(body).digest('hex');
198
+ headers['x-webhook-signature'] = `sha256=${signature}`;
199
+ }
200
+
201
+ for (let attempt = 0; attempt <= WebhookManager.#maxRetries; attempt++) {
202
+ try {
203
+ const response = await fetch(url, { method: 'POST', headers, body, signal: AbortSignal.timeout(10_000) });
204
+ if (response.ok) {
205
+ return;
206
+ }
207
+ log.warn(`Webhook ${url} returned ${response.status} (attempt ${attempt + 1})`);
208
+ } catch (err) {
209
+ log.warn(`Webhook ${url} fetch error (attempt ${attempt + 1}):`, err);
210
+ }
211
+
212
+ // Wait before retry (skip wait on last attempt).
213
+ if (attempt < WebhookManager.#maxRetries) {
214
+ await new Promise((resolve): ReturnType<typeof setTimeout> =>
215
+ setTimeout(resolve, WebhookManager.#retryDelaysMs[attempt]),
216
+ );
217
+ }
218
+ }
219
+
220
+ log.error(`Webhook delivery to ${url} failed after ${WebhookManager.#maxRetries + 1} attempts`);
221
+ }
222
+
223
+ /**
224
+ * Closes the underlying database connection.
225
+ */
226
+ public async close(): Promise<void> {
227
+ await this.#db.destroy();
228
+ }
229
+ }
230
+
231
+ // ---------------------------------------------------------------------------
232
+ // Kysely type definitions
233
+ // ---------------------------------------------------------------------------
234
+
235
+ interface WebhookRow {
236
+ id : string;
237
+ url : string;
238
+ events : string; // JSON-serialized string[]
239
+ secret : string | null;
240
+ createdAt : string;
241
+ }
242
+
243
+ interface WebhookDatabase {
244
+ adminWebhooks : WebhookRow;
245
+ }
package/src/config.ts CHANGED
@@ -1,3 +1,5 @@
1
+ import { readFileSync } from 'fs';
2
+
1
3
  import bytes from 'bytes';
2
4
 
3
5
  export type DwnServerConfig = typeof config;
@@ -5,11 +7,8 @@ export type DwnServerConfig = typeof config;
5
7
  export const config = {
6
8
  /**
7
9
  * Used to populate the `server` property returned by the `/info` endpoint.
8
- *
9
- * If running using `npm` the `process.env.npm_package_name` variable exists and we use that,
10
- * otherwise we fall back on the use defined `DWN_SERVER_PACKAGE_NAME` or `@enbox/dwn-server`.
11
10
  */
12
- serverName: process.env.npm_package_name || process.env.DWN_SERVER_PACKAGE_NAME || '@enbox/dwn-server',
11
+ serverName: process.env.DWN_SERVER_PACKAGE_NAME || '@enbox/dwn-server',
13
12
 
14
13
  /**
15
14
  * The base external URL of this DWN.
@@ -26,7 +25,7 @@ export const config = {
26
25
  /**
27
26
  * The URL of the TTL cache used by the DWN.
28
27
  * NOTE: Used for session/state keeping, thus requires the cache to be commonly addressable by nodes in a cloud cluster environment.
29
- *
28
+ *
30
29
  * Currently only supports SQL databases, e.g.
31
30
  * Postgres: 'postgres://root:dwn@localhost:5432/dwn'
32
31
  * MySQL: 'mysql://root:dwn@localhost:3306/dwn'
@@ -37,35 +36,204 @@ export const config = {
37
36
  * Used to populate the `version` and `sdkVersion` properties returned by the `/info` endpoint.
38
37
  *
39
38
  * The `version` and `sdkVersion` are pulled from `package.json` at runtime.
40
- * If running using `npm` the `process.env.npm_package_json` variable exists as the filepath, so we use that.
41
- * Otherwise we check to see if a specific `DWN_SERVER_PACKAGE_JSON` exists, if it does we use that.
42
- * Finally if both of those options don't exist we resort to the path within the docker server image, located at `/dwn-server/package.json`
39
+ * If `DWN_SERVER_PACKAGE_JSON` is set, we use that path.
40
+ * Otherwise we resort to the path within the docker server image, located at `/dwn-server/package.json`.
41
+ */
42
+ packageJsonPath : process.env.DWN_SERVER_PACKAGE_JSON || '/dwn-server/package.json',
43
+ /**
44
+ * Maximum size of data that can be provided with a RecordsWrite.
45
+ * Request bodies up to this size are buffered fully into memory, so the
46
+ * default should be conservative enough to prevent memory exhaustion from
47
+ * concurrent large uploads. Operators can raise the limit via the
48
+ * `MAX_RECORD_DATA_SIZE` env var (e.g. `'1gb'`).
43
49
  */
44
- packageJsonPath: process.env.npm_package_json || process.env.DWN_SERVER_PACKAGE_JSON || '/dwn-server/package.json',
45
- // max size of data that can be provided with a RecordsWrite
46
- maxRecordDataSize: bytes(process.env.MAX_RECORD_DATA_SIZE || '1gb'),
50
+ maxRecordDataSize : bytes(process.env.MAX_RECORD_DATA_SIZE || '100mb'),
51
+
52
+ /**
53
+ * Maximum number of unacknowledged subscription events the server will send
54
+ * per subscription before pausing delivery. Clients must send `rpc.ack` to
55
+ * advance the window. Configurable via `DWN_MAX_IN_FLIGHT` env var.
56
+ */
57
+ maxInFlight: parseInt(process.env.DWN_MAX_IN_FLIGHT || '32'),
47
58
 
48
59
  // whether to enable 'ws:'
49
60
  webSocketSupport: { on: true, off: false }[process.env.DS_WEBSOCKET_SERVER] ?? true,
50
61
 
51
62
  /**
52
- * Path to DWN Event Stream plugin to use. Default single-node implementation will be used if left empty.
63
+ * Path to DWN EventLog plugin to use. Default in-memory implementation will be used if left empty.
64
+ * Also accepts the legacy `DWN_EVENT_STREAM_PLUGIN_PATH` env var for backward compatibility.
53
65
  */
54
- eventStreamPluginPath: process.env.DWN_EVENT_STREAM_PLUGIN_PATH,
66
+ eventLogPluginPath: process.env.DWN_EVENT_LOG_PLUGIN_PATH || process.env.DWN_EVENT_STREAM_PLUGIN_PATH,
55
67
 
56
68
  // where to store persistent data
57
- messageStore: process.env.DWN_STORAGE_MESSAGES || process.env.DWN_STORAGE || 'level://data',
58
- dataStore: process.env.DWN_STORAGE_DATA || process.env.DWN_STORAGE || 'level://data',
59
- eventLog: process.env.DWN_STORAGE_EVENTS || process.env.DWN_STORAGE || 'level://data',
60
- resumableTaskStore: process.env.DWN_STORAGE_RESUMABLE_TASKS || process.env.DWN_STORAGE || 'level://data',
69
+ messageStore : process.env.DWN_STORAGE_MESSAGES || process.env.DWN_STORAGE || 'level://data',
70
+ dataStore : process.env.DWN_STORAGE_DATA || process.env.DWN_STORAGE || 'level://data',
71
+ stateIndex : process.env.DWN_STORAGE_STATE_INDEX || process.env.DWN_STORAGE || 'level://data',
72
+ resumableTaskStore : process.env.DWN_STORAGE_RESUMABLE_TASKS || process.env.DWN_STORAGE || 'level://data',
73
+
74
+ /**
75
+ * PostgreSQL connection pool tuning. When multiple DWN stores share the same
76
+ * Postgres connection URL, a single shared pool is used instead of one pool
77
+ * per store (which would be 4 pools x 10 default connections = 40 connections).
78
+ */
79
+ pgPoolMin : parseInt(process.env.DWN_PG_POOL_MIN || '5'),
80
+ pgPoolMax : parseInt(process.env.DWN_PG_POOL_MAX || '30'),
81
+ pgPoolIdleTimeout : parseInt(process.env.DWN_PG_POOL_IDLE_TIMEOUT || '30000'),
61
82
 
62
83
  // tenant registration feature configuration
63
- registrationStoreUrl: process.env.DWN_REGISTRATION_STORE_URL || process.env.DWN_STORAGE,
64
- registrationProofOfWorkSeed: process.env.DWN_REGISTRATION_PROOF_OF_WORK_SEED,
65
- registrationProofOfWorkEnabled: process.env.DWN_REGISTRATION_PROOF_OF_WORK_ENABLED === 'true',
66
- registrationProofOfWorkInitialMaxHash: process.env.DWN_REGISTRATION_PROOF_OF_WORK_INITIAL_MAX_HASH,
67
- termsOfServiceFilePath: process.env.DWN_TERMS_OF_SERVICE_FILE_PATH,
84
+ registrationStoreUrl : process.env.DWN_REGISTRATION_STORE_URL || process.env.DWN_STORAGE,
85
+ registrationProofOfWorkSeed : process.env.DWN_REGISTRATION_PROOF_OF_WORK_SEED,
86
+ registrationProofOfWorkEnabled : process.env.DWN_REGISTRATION_PROOF_OF_WORK_ENABLED === 'true',
87
+ registrationProofOfWorkInitialMaxHash : process.env.DWN_REGISTRATION_PROOF_OF_WORK_INITIAL_MAX_HASH,
88
+ termsOfServiceFilePath : process.env.DWN_TERMS_OF_SERVICE_FILE_PATH,
89
+
90
+ // Provider auth configuration for paid DWN registration
91
+ providerAuthEnabled : process.env.DWN_PROVIDER_AUTH_ENABLED === 'true',
92
+ providerAuthAuthorizeUrl : process.env.DWN_PROVIDER_AUTH_AUTHORIZE_URL,
93
+ providerAuthTokenUrl : process.env.DWN_PROVIDER_AUTH_TOKEN_URL,
94
+ providerAuthRefreshUrl : process.env.DWN_PROVIDER_AUTH_REFRESH_URL,
95
+ providerAuthManagementUrl : process.env.DWN_PROVIDER_AUTH_MANAGEMENT_URL,
96
+ providerAuthPluginPath : process.env.DWN_PROVIDER_AUTH_PLUGIN_PATH,
97
+ providerAuthJwtSecret : process.env.DWN_PROVIDER_AUTH_JWT_SECRET,
98
+ providerAuthJwtJwksUrl : process.env.DWN_PROVIDER_AUTH_JWT_JWKS_URL,
68
99
 
69
100
  // log level - trace/debug/info/warn/error
70
101
  logLevel: process.env.DWN_SERVER_LOG_LEVEL || 'INFO',
102
+
103
+ /**
104
+ * Bearer token for the admin API. If unset (or empty), the admin API is disabled entirely.
105
+ * Can also be read from a file path via `DWN_ADMIN_TOKEN_FILE` (useful for Docker secrets).
106
+ */
107
+ adminToken: process.env.DWN_ADMIN_TOKEN || (
108
+ process.env.DWN_ADMIN_TOKEN_FILE
109
+ ? readAdminTokenFromFile(process.env.DWN_ADMIN_TOKEN_FILE)
110
+ : undefined
111
+ ),
112
+
113
+ /**
114
+ * Maximum number of recent DWN activity events retained in the in-memory
115
+ * ring buffer for the admin `/events` endpoint. Defaults to 10,000.
116
+ */
117
+ adminActivityLogCapacity: parseInt(process.env.DWN_ADMIN_ACTIVITY_LOG_CAPACITY || '10000'),
118
+
119
+ /**
120
+ * Interval (in seconds) at which Prometheus gauge metrics are updated from
121
+ * the admin store. Defaults to 30 seconds.
122
+ */
123
+ adminMetricsUpdateIntervalSeconds: parseInt(process.env.DWN_ADMIN_METRICS_UPDATE_INTERVAL || '30'),
124
+
125
+ // ---------------------------------------------------------------------------
126
+ // Per-tenant storage quotas
127
+ // ---------------------------------------------------------------------------
128
+
129
+ /**
130
+ * Default maximum number of messages a tenant may store. 0 = unlimited (default).
131
+ * Per-tenant overrides are managed via the admin API.
132
+ */
133
+ quotaMaxMessages: parseInt(process.env.DWN_QUOTA_MAX_MESSAGES || '0'),
134
+
135
+ /**
136
+ * Default maximum data storage in bytes a tenant may use. 0 = unlimited (default).
137
+ * Per-tenant overrides are managed via the admin API.
138
+ */
139
+ quotaMaxStorageBytes: parseInt(process.env.DWN_QUOTA_MAX_STORAGE_BYTES || '0'),
140
+
141
+ // ---------------------------------------------------------------------------
142
+ // Audit log retention
143
+ // ---------------------------------------------------------------------------
144
+
145
+ /**
146
+ * Maximum age of audit log entries in days. Entries older than this are purged.
147
+ * 0 = no age limit (default: 90 days).
148
+ *
149
+ * @see https://github.com/enboxorg/enbox/issues/394
150
+ */
151
+ auditLogMaxAgeDays: parseInt(process.env.DWN_AUDIT_LOG_MAX_AGE_DAYS || '90'),
152
+
153
+ /**
154
+ * Maximum number of audit log rows to retain. Oldest entries are purged when exceeded.
155
+ * 0 = no row limit (default: 100000).
156
+ *
157
+ * @see https://github.com/enboxorg/enbox/issues/394
158
+ */
159
+ auditLogMaxRows: parseInt(process.env.DWN_AUDIT_LOG_MAX_ROWS || '100000'),
160
+
161
+ // ---------------------------------------------------------------------------
162
+ // Rate limiting
163
+ // ---------------------------------------------------------------------------
164
+
165
+ /**
166
+ * Maximum HTTP requests per second per IP address. Set to 0 to disable.
167
+ * Defaults to 30 req/s which is generous for normal usage while limiting abuse.
168
+ * Can be reconfigured at runtime via the admin `PATCH /config` endpoint.
169
+ */
170
+ rateLimitRequestsPerSecond: parseInt(process.env.DWN_RATE_LIMIT_REQUESTS_PER_SECOND || '30'),
171
+
172
+ /**
173
+ * Maximum burst size for per-IP rate limiting. Allows short spikes above the
174
+ * sustained rate without triggering 429s. Defaults to 50.
175
+ */
176
+ rateLimitBurst: parseInt(process.env.DWN_RATE_LIMIT_BURST || '50'),
177
+
178
+ /**
179
+ * Maximum DWN requests per second per tenant DID. Set to 0 to disable.
180
+ * Defaults to 20 req/s. Applies to both HTTP and WebSocket transports.
181
+ * Can be reconfigured at runtime via the admin `PATCH /config` endpoint.
182
+ */
183
+ rateLimitTenantRequestsPerSecond: parseInt(process.env.DWN_RATE_LIMIT_TENANT_REQUESTS_PER_SECOND || '20'),
184
+
185
+ /**
186
+ * Maximum burst size for per-tenant rate limiting. Defaults to 50.
187
+ */
188
+ rateLimitTenantBurst: parseInt(process.env.DWN_RATE_LIMIT_TENANT_BURST || '50'),
189
+
190
+ // ---------------------------------------------------------------------------
191
+ // Record delivery & endpoint forwarding
192
+ // ---------------------------------------------------------------------------
193
+
194
+ /**
195
+ * Enable endpoint forwarding: when a RecordsWrite/RecordsDelete is processed,
196
+ * forward the original signed message to the tenant's other DWN service
197
+ * endpoints (discovered via DID resolution). Disabled by default.
198
+ */
199
+ forwardingEnabled: process.env.DWN_FORWARDING_ENABLED === 'true',
200
+
201
+ /**
202
+ * Enable protocol-aware record delivery: when a RecordsWrite/RecordsDelete is
203
+ * processed at a protocol path with `$delivery`, proactively deliver to
204
+ * participants' DWN endpoints. Disabled by default.
205
+ */
206
+ deliveryEnabled: process.env.DWN_DELIVERY_ENABLED === 'true',
207
+
208
+ /**
209
+ * Maximum number of concurrent outbound delivery/forwarding requests.
210
+ * Prevents unbounded parallelism when delivering to many providers.
211
+ * Defaults to 10.
212
+ */
213
+ deliveryMaxConcurrency: parseInt(process.env.DWN_DELIVERY_MAX_CONCURRENCY || '10'),
214
+
215
+ /**
216
+ * TTL in seconds for caching DID document service endpoint resolution results.
217
+ * Avoids resolving the same DID document on every delivery. Defaults to 300 (5 min).
218
+ */
219
+ deliveryEndpointCacheTtlSeconds: parseInt(process.env.DWN_DELIVERY_ENDPOINT_CACHE_TTL || '300'),
220
+
221
+ /**
222
+ * TTL in seconds for the recently-forwarded messageCid deduplication cache.
223
+ * Messages with CIDs in this cache are not forwarded again, reducing redundant
224
+ * outbound requests between peer endpoints. Defaults to 60.
225
+ */
226
+ forwardingDeduplicationTtlSeconds: parseInt(process.env.DWN_FORWARDING_DEDUP_TTL || '60'),
71
227
  };
228
+
229
+ /**
230
+ * Reads the admin token from a file path, trimming whitespace.
231
+ * Returns `undefined` if the file cannot be read.
232
+ */
233
+ function readAdminTokenFromFile(filePath: string): string | undefined {
234
+ try {
235
+ return readFileSync(filePath).toString().trim() || undefined;
236
+ } catch {
237
+ return undefined;
238
+ }
239
+ }
@@ -1,39 +1,89 @@
1
- import type { Dwn } from "@enbox/dwn-sdk-js";
1
+ import type { Dwn } from '@enbox/dwn-sdk-js';
2
+ import type { ServerWebSocket } from 'bun';
2
3
 
3
- import type { IncomingMessage } from "http";
4
- import type { WebSocket } from 'ws';
4
+ import type { ActivityLog } from '../admin/activity-log.js';
5
+ import type { AdminConnectionSnapshot } from '../admin/types.js';
6
+ import type { AdminStore } from '../admin/admin-store.js';
7
+ import type { DwnServerConfig } from '../config.js';
8
+ import type { MessageProcessedHook } from '../message-processed-hook.js';
9
+ import type { RateLimiter } from '../rate-limiter.js';
10
+ import type { RegistrationStore } from '../registration/registration-store.js';
11
+ import type { WsData } from '../http-api.js';
5
12
 
6
- import { SocketConnection } from "./socket-connection.js";
13
+ import { SocketConnection } from './socket-connection.js';
7
14
 
8
15
  /**
9
16
  * Interface for managing `WebSocket` connections as they arrive.
10
17
  */
11
18
  export interface ConnectionManager {
12
- /** connect handler used for the `WebSockets` `'connection'` event. */
13
- connect(socket: WebSocket, request?: IncomingMessage): Promise<void>;
19
+ /** connect handler invoked when a new WebSocket connection is established. */
20
+ connect(socket: ServerWebSocket<WsData>): Promise<void>;
14
21
  /** closes all of the connections */
15
- closeAll(): Promise<void>
22
+ closeAll(): Promise<void>;
23
+ /** Returns the number of active connections. */
24
+ getConnectionCount(): number;
25
+ /** Returns the total number of active subscriptions across all connections. */
26
+ getSubscriptionCount(): number;
27
+ /** Returns serializable snapshots of all active connections. */
28
+ getConnectionSnapshots(): AdminConnectionSnapshot[];
16
29
  }
17
30
 
18
31
  /**
19
32
  * A Simple In Memory ConnectionManager implementation.
20
- * It uses a `Map<WebSocket, SocketConnection>` to manage connections.
33
+ * It uses a `Map<ServerWebSocket, SocketConnection>` to manage connections.
21
34
  */
22
35
  export class InMemoryConnectionManager implements ConnectionManager {
23
- constructor(private dwn: Dwn, private connections: Map<WebSocket, SocketConnection> = new Map()) {}
36
+ constructor(
37
+ private dwn: Dwn,
38
+ private connections: Map<ServerWebSocket<WsData>, SocketConnection> = new Map(),
39
+ private maxInFlight?: number,
40
+ private activityLog?: ActivityLog,
41
+ private adminStore?: AdminStore,
42
+ private registrationStore?: RegistrationStore,
43
+ private serverConfig?: DwnServerConfig,
44
+ private tenantRateLimiter?: RateLimiter,
45
+ private messageProcessedHooks?: MessageProcessedHook[],
46
+ ) {}
24
47
 
25
- async connect(socket: WebSocket): Promise<void> {
26
- const connection = new SocketConnection(socket, this.dwn, () => {
27
- // this is the onClose handler to clean up any closed connections.
28
- this.connections.delete(socket);
29
- });
48
+ async connect(socket: ServerWebSocket<WsData>): Promise<void> {
49
+ const connection = new SocketConnection(
50
+ socket, this.dwn, () => {
51
+ // this is the onClose handler to clean up any closed connections.
52
+ this.connections.delete(socket);
53
+ },
54
+ this.maxInFlight, this.activityLog,
55
+ this.adminStore, this.registrationStore, this.serverConfig, this.tenantRateLimiter, this.messageProcessedHooks,
56
+ );
57
+
58
+ // Attach the connection to the ws.data so Bun's websocket handlers can delegate to it.
59
+ socket.data.connection = connection;
30
60
 
31
61
  this.connections.set(socket, connection);
32
62
  }
33
63
 
34
64
  async closeAll(): Promise<void> {
35
- const closePromises = [];
36
- this.connections.forEach(connection => closePromises.push(connection.close()));
65
+ const closePromises: Promise<void>[] = [];
66
+ this.connections.forEach((connection) => closePromises.push(connection.close()));
37
67
  await Promise.all(closePromises);
38
68
  }
39
- }
69
+
70
+ getConnectionCount(): number {
71
+ return this.connections.size;
72
+ }
73
+
74
+ getSubscriptionCount(): number {
75
+ let count = 0;
76
+ this.connections.forEach((conn) => {
77
+ count += conn.subscriptionCount;
78
+ });
79
+ return count;
80
+ }
81
+
82
+ getConnectionSnapshots(): AdminConnectionSnapshot[] {
83
+ const snapshots: AdminConnectionSnapshot[] = [];
84
+ this.connections.forEach((conn) => {
85
+ snapshots.push(conn.toSnapshot());
86
+ });
87
+ return snapshots;
88
+ }
89
+ }