@enbox/dwn-server 0.0.3 → 0.0.5

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 (295) hide show
  1. package/LICENSE +3 -2
  2. package/README.md +112 -212
  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 +122 -3
  36. package/dist/esm/src/config.d.ts.map +1 -1
  37. package/dist/esm/src/config.js +151 -5
  38. package/dist/esm/src/config.js.map +1 -1
  39. package/dist/esm/src/connection/connection-manager.d.ts +24 -1
  40. package/dist/esm/src/connection/connection-manager.d.ts.map +1 -1
  41. package/dist/esm/src/connection/connection-manager.js +33 -2
  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 +39 -4
  48. package/dist/esm/src/connection/socket-connection.d.ts.map +1 -1
  49. package/dist/esm/src/connection/socket-connection.js +80 -9
  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 +8 -0
  60. package/dist/esm/src/dwn-server.d.ts.map +1 -1
  61. package/dist/esm/src/dwn-server.js +198 -12
  62. package/dist/esm/src/dwn-server.js.map +1 -1
  63. package/dist/esm/src/http-api.d.ts +19 -2
  64. package/dist/esm/src/http-api.d.ts.map +1 -1
  65. package/dist/esm/src/http-api.js +219 -19
  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 +106 -4
  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 +22 -4
  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 +13 -1
  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/provider-auth-plugin.d.ts +46 -0
  123. package/dist/esm/src/registration/provider-auth-plugin.d.ts.map +1 -0
  124. package/dist/esm/src/registration/provider-auth-plugin.js +29 -0
  125. package/dist/esm/src/registration/provider-auth-plugin.js.map +1 -0
  126. package/dist/esm/src/registration/registration-manager.d.ts +27 -4
  127. package/dist/esm/src/registration/registration-manager.d.ts.map +1 -1
  128. package/dist/esm/src/registration/registration-manager.js +77 -6
  129. package/dist/esm/src/registration/registration-manager.js.map +1 -1
  130. package/dist/esm/src/registration/registration-store.d.ts +83 -3
  131. package/dist/esm/src/registration/registration-store.d.ts.map +1 -1
  132. package/dist/esm/src/registration/registration-store.js +248 -11
  133. package/dist/esm/src/registration/registration-store.js.map +1 -1
  134. package/dist/esm/src/storage.d.ts +4 -4
  135. package/dist/esm/src/storage.d.ts.map +1 -1
  136. package/dist/esm/src/storage.js +100 -20
  137. package/dist/esm/src/storage.js.map +1 -1
  138. package/dist/esm/src/web5-connect/sql-ttl-cache.d.ts.map +1 -1
  139. package/dist/esm/src/web5-connect/sql-ttl-cache.js +8 -1
  140. package/dist/esm/src/web5-connect/sql-ttl-cache.js.map +1 -1
  141. package/dist/esm/src/ws-api.d.ts +17 -1
  142. package/dist/esm/src/ws-api.d.ts.map +1 -1
  143. package/dist/esm/src/ws-api.js +9 -2
  144. package/dist/esm/src/ws-api.js.map +1 -1
  145. package/package.json +18 -16
  146. package/src/admin/activity-log.ts +100 -0
  147. package/src/admin/admin-api.ts +1308 -0
  148. package/src/admin/admin-auth.ts +56 -0
  149. package/src/admin/admin-store.ts +515 -0
  150. package/src/admin/audit-log.ts +327 -0
  151. package/src/admin/index.ts +34 -0
  152. package/src/admin/types.ts +352 -0
  153. package/src/admin/webhook-manager.ts +245 -0
  154. package/src/config.ts +177 -5
  155. package/src/connection/connection-manager.ts +50 -6
  156. package/src/connection/flow-controller.ts +117 -0
  157. package/src/connection/socket-connection.ts +103 -21
  158. package/src/delivery-service.ts +740 -0
  159. package/src/dwn-error.ts +9 -0
  160. package/src/dwn-server.ts +242 -14
  161. package/src/http-api.ts +271 -30
  162. package/src/index.ts +13 -2
  163. package/src/json-rpc-api.ts +2 -1
  164. package/src/json-rpc-handlers/dwn/process-message.ts +140 -5
  165. package/src/json-rpc-handlers/subscription/ack.ts +63 -0
  166. package/src/json-rpc-handlers/subscription/close.ts +2 -6
  167. package/src/json-rpc-handlers/subscription/index.ts +1 -0
  168. package/src/lib/json-rpc-router.ts +22 -6
  169. package/src/lib/sql-utils.ts +7 -0
  170. package/src/main.ts +0 -8
  171. package/src/message-processed-hook.ts +33 -0
  172. package/src/metrics.ts +50 -1
  173. package/src/plugins/event-log-nats.ts +466 -0
  174. package/src/rate-limiter.ts +143 -0
  175. package/src/registration/jwt-provider-auth-plugin.ts +119 -0
  176. package/src/registration/open-auth-handler.ts +263 -0
  177. package/src/registration/proof-of-work-manager.ts +1 -1
  178. package/src/registration/provider-auth-plugin.ts +84 -0
  179. package/src/registration/registration-manager.ts +108 -12
  180. package/src/registration/registration-store.ts +326 -17
  181. package/src/storage.ts +121 -27
  182. package/src/web5-connect/sql-ttl-cache.ts +7 -1
  183. package/src/ws-api.ts +30 -2
  184. package/dist/esm/src/json-rpc-socket.d.ts +0 -39
  185. package/dist/esm/src/json-rpc-socket.d.ts.map +0 -1
  186. package/dist/esm/src/json-rpc-socket.js +0 -125
  187. package/dist/esm/src/json-rpc-socket.js.map +0 -1
  188. package/dist/esm/src/lib/json-rpc.d.ts +0 -54
  189. package/dist/esm/src/lib/json-rpc.d.ts.map +0 -1
  190. package/dist/esm/src/lib/json-rpc.js +0 -60
  191. package/dist/esm/src/lib/json-rpc.js.map +0 -1
  192. package/dist/esm/src/registration/proof-of-work-types.d.ts +0 -8
  193. package/dist/esm/src/registration/proof-of-work-types.d.ts.map +0 -1
  194. package/dist/esm/src/registration/proof-of-work-types.js +0 -2
  195. package/dist/esm/src/registration/proof-of-work-types.js.map +0 -1
  196. package/dist/esm/src/registration/registration-types.d.ts +0 -18
  197. package/dist/esm/src/registration/registration-types.d.ts.map +0 -1
  198. package/dist/esm/src/registration/registration-types.js +0 -2
  199. package/dist/esm/src/registration/registration-types.js.map +0 -1
  200. package/dist/esm/tests/common-scenario-validator.d.ts +0 -11
  201. package/dist/esm/tests/common-scenario-validator.d.ts.map +0 -1
  202. package/dist/esm/tests/common-scenario-validator.js +0 -113
  203. package/dist/esm/tests/common-scenario-validator.js.map +0 -1
  204. package/dist/esm/tests/connection/connection-manager.spec.d.ts +0 -2
  205. package/dist/esm/tests/connection/connection-manager.spec.d.ts.map +0 -1
  206. package/dist/esm/tests/connection/connection-manager.spec.js +0 -49
  207. package/dist/esm/tests/connection/connection-manager.spec.js.map +0 -1
  208. package/dist/esm/tests/connection/socket-connection.spec.d.ts +0 -2
  209. package/dist/esm/tests/connection/socket-connection.spec.d.ts.map +0 -1
  210. package/dist/esm/tests/connection/socket-connection.spec.js +0 -147
  211. package/dist/esm/tests/connection/socket-connection.spec.js.map +0 -1
  212. package/dist/esm/tests/cors/http-api.browser.d.ts +0 -2
  213. package/dist/esm/tests/cors/http-api.browser.d.ts.map +0 -1
  214. package/dist/esm/tests/cors/http-api.browser.js +0 -60
  215. package/dist/esm/tests/cors/http-api.browser.js.map +0 -1
  216. package/dist/esm/tests/cors/ping.browser.d.ts +0 -2
  217. package/dist/esm/tests/cors/ping.browser.d.ts.map +0 -1
  218. package/dist/esm/tests/cors/ping.browser.js +0 -7
  219. package/dist/esm/tests/cors/ping.browser.js.map +0 -1
  220. package/dist/esm/tests/dwn-process-message.spec.d.ts +0 -2
  221. package/dist/esm/tests/dwn-process-message.spec.d.ts.map +0 -1
  222. package/dist/esm/tests/dwn-process-message.spec.js +0 -172
  223. package/dist/esm/tests/dwn-process-message.spec.js.map +0 -1
  224. package/dist/esm/tests/dwn-server.spec.d.ts +0 -2
  225. package/dist/esm/tests/dwn-server.spec.d.ts.map +0 -1
  226. package/dist/esm/tests/dwn-server.spec.js +0 -48
  227. package/dist/esm/tests/dwn-server.spec.js.map +0 -1
  228. package/dist/esm/tests/http-api.spec.d.ts +0 -2
  229. package/dist/esm/tests/http-api.spec.d.ts.map +0 -1
  230. package/dist/esm/tests/http-api.spec.js +0 -782
  231. package/dist/esm/tests/http-api.spec.js.map +0 -1
  232. package/dist/esm/tests/json-rpc-socket.spec.d.ts +0 -2
  233. package/dist/esm/tests/json-rpc-socket.spec.d.ts.map +0 -1
  234. package/dist/esm/tests/json-rpc-socket.spec.js +0 -227
  235. package/dist/esm/tests/json-rpc-socket.spec.js.map +0 -1
  236. package/dist/esm/tests/plugins/data-store-sqlite.d.ts +0 -17
  237. package/dist/esm/tests/plugins/data-store-sqlite.d.ts.map +0 -1
  238. package/dist/esm/tests/plugins/data-store-sqlite.js +0 -23
  239. package/dist/esm/tests/plugins/data-store-sqlite.js.map +0 -1
  240. package/dist/esm/tests/plugins/event-log-sqlite.d.ts +0 -17
  241. package/dist/esm/tests/plugins/event-log-sqlite.d.ts.map +0 -1
  242. package/dist/esm/tests/plugins/event-log-sqlite.js +0 -23
  243. package/dist/esm/tests/plugins/event-log-sqlite.js.map +0 -1
  244. package/dist/esm/tests/plugins/event-stream-in-memory.d.ts +0 -17
  245. package/dist/esm/tests/plugins/event-stream-in-memory.d.ts.map +0 -1
  246. package/dist/esm/tests/plugins/event-stream-in-memory.js +0 -21
  247. package/dist/esm/tests/plugins/event-stream-in-memory.js.map +0 -1
  248. package/dist/esm/tests/plugins/message-store-sqlite.d.ts +0 -17
  249. package/dist/esm/tests/plugins/message-store-sqlite.d.ts.map +0 -1
  250. package/dist/esm/tests/plugins/message-store-sqlite.js +0 -23
  251. package/dist/esm/tests/plugins/message-store-sqlite.js.map +0 -1
  252. package/dist/esm/tests/plugins/resumable-task-store-sqlite.d.ts +0 -17
  253. package/dist/esm/tests/plugins/resumable-task-store-sqlite.d.ts.map +0 -1
  254. package/dist/esm/tests/plugins/resumable-task-store-sqlite.js +0 -23
  255. package/dist/esm/tests/plugins/resumable-task-store-sqlite.js.map +0 -1
  256. package/dist/esm/tests/process-handler.spec.d.ts +0 -2
  257. package/dist/esm/tests/process-handler.spec.d.ts.map +0 -1
  258. package/dist/esm/tests/process-handler.spec.js +0 -60
  259. package/dist/esm/tests/process-handler.spec.js.map +0 -1
  260. package/dist/esm/tests/registration/proof-of-work-manager.spec.d.ts +0 -2
  261. package/dist/esm/tests/registration/proof-of-work-manager.spec.d.ts.map +0 -1
  262. package/dist/esm/tests/registration/proof-of-work-manager.spec.js +0 -156
  263. package/dist/esm/tests/registration/proof-of-work-manager.spec.js.map +0 -1
  264. package/dist/esm/tests/rpc-subscribe-close.spec.d.ts +0 -2
  265. package/dist/esm/tests/rpc-subscribe-close.spec.d.ts.map +0 -1
  266. package/dist/esm/tests/rpc-subscribe-close.spec.js +0 -81
  267. package/dist/esm/tests/rpc-subscribe-close.spec.js.map +0 -1
  268. package/dist/esm/tests/scenarios/dynamic-plugin-loading.spec.d.ts +0 -2
  269. package/dist/esm/tests/scenarios/dynamic-plugin-loading.spec.d.ts.map +0 -1
  270. package/dist/esm/tests/scenarios/dynamic-plugin-loading.spec.js +0 -74
  271. package/dist/esm/tests/scenarios/dynamic-plugin-loading.spec.js.map +0 -1
  272. package/dist/esm/tests/scenarios/registration.spec.d.ts +0 -2
  273. package/dist/esm/tests/scenarios/registration.spec.d.ts.map +0 -1
  274. package/dist/esm/tests/scenarios/registration.spec.js +0 -511
  275. package/dist/esm/tests/scenarios/registration.spec.js.map +0 -1
  276. package/dist/esm/tests/scenarios/web5-connect.spec.d.ts +0 -2
  277. package/dist/esm/tests/scenarios/web5-connect.spec.d.ts.map +0 -1
  278. package/dist/esm/tests/scenarios/web5-connect.spec.js +0 -141
  279. package/dist/esm/tests/scenarios/web5-connect.spec.js.map +0 -1
  280. package/dist/esm/tests/test-dwn.d.ts +0 -7
  281. package/dist/esm/tests/test-dwn.d.ts.map +0 -1
  282. package/dist/esm/tests/test-dwn.js +0 -28
  283. package/dist/esm/tests/test-dwn.js.map +0 -1
  284. package/dist/esm/tests/utils.d.ts +0 -43
  285. package/dist/esm/tests/utils.d.ts.map +0 -1
  286. package/dist/esm/tests/utils.js +0 -107
  287. package/dist/esm/tests/utils.js.map +0 -1
  288. package/dist/esm/tests/ws-api.spec.d.ts +0 -2
  289. package/dist/esm/tests/ws-api.spec.d.ts.map +0 -1
  290. package/dist/esm/tests/ws-api.spec.js +0 -332
  291. package/dist/esm/tests/ws-api.spec.js.map +0 -1
  292. package/src/json-rpc-socket.ts +0 -156
  293. package/src/lib/json-rpc.ts +0 -126
  294. package/src/registration/proof-of-work-types.ts +0 -7
  295. package/src/registration/registration-types.ts +0 -18
@@ -0,0 +1,379 @@
1
+ import log from 'loglevel';
2
+ import { connect } from '@nats-io/transport-node';
3
+ import { AckPolicy, DeliverPolicy, jetstream, jetstreamManager } from '@nats-io/jetstream';
4
+ function loadConfig() {
5
+ return {
6
+ url: process.env.NATS_URL || 'nats://localhost:4222',
7
+ streamName: process.env.NATS_STREAM_NAME || 'DWN_EVENTS',
8
+ streamMaxAge: parseInt(process.env.NATS_STREAM_MAX_AGE || '604800000000000'), // 7 days in nanos
9
+ replicas: parseInt(process.env.NATS_STREAM_REPLICAS || '1'),
10
+ maxMsgsPerSubject: parseInt(process.env.NATS_MAX_MSGS_PER_SUBJECT || '100000'),
11
+ };
12
+ }
13
+ // ---------------------------------------------------------------------------
14
+ // Minimal filter matching (OR semantics, matching FilterUtility behaviour)
15
+ // ---------------------------------------------------------------------------
16
+ /**
17
+ * Returns `true` if the indexed key-values match at least one of the given
18
+ * filters (OR semantics). An empty or undefined filter array matches all events.
19
+ */
20
+ function matchAnyFilter(keyValues, filters) {
21
+ if (filters === undefined || filters.length === 0) {
22
+ return true;
23
+ }
24
+ for (const filter of filters) {
25
+ if (matchFilter(keyValues, filter)) {
26
+ return true;
27
+ }
28
+ }
29
+ return false;
30
+ }
31
+ /** Returns `true` if every property in the filter matches the indexed values (AND semantics). */
32
+ function matchFilter(indexedValues, filter) {
33
+ for (const key in filter) {
34
+ const filterValue = filter[key];
35
+ const indexValue = indexedValues[key];
36
+ if (indexValue === undefined) {
37
+ return false;
38
+ }
39
+ const values = Array.isArray(indexValue) ? indexValue : [indexValue];
40
+ let anyMatch = false;
41
+ for (const v of values) {
42
+ if (matchSingleValue(filterValue, v)) {
43
+ anyMatch = true;
44
+ break;
45
+ }
46
+ }
47
+ if (!anyMatch) {
48
+ return false;
49
+ }
50
+ }
51
+ return true;
52
+ }
53
+ /** Match a single index value against a filter value (equal, oneOf, or range). */
54
+ function matchSingleValue(filterValue, indexValue) {
55
+ if (typeof filterValue === 'object' && filterValue !== null) {
56
+ if (Array.isArray(filterValue)) {
57
+ // OneOfFilter
58
+ return filterValue.includes(indexValue);
59
+ }
60
+ // RangeFilter
61
+ const range = filterValue;
62
+ if (range.lt !== undefined && indexValue >= range.lt) {
63
+ return false;
64
+ }
65
+ if (range.lte !== undefined && indexValue > range.lte) {
66
+ return false;
67
+ }
68
+ if (range.gt !== undefined && indexValue <= range.gt) {
69
+ return false;
70
+ }
71
+ if (range.gte !== undefined && indexValue < range.gte) {
72
+ return false;
73
+ }
74
+ return true;
75
+ }
76
+ // EqualFilter
77
+ return indexValue === filterValue;
78
+ }
79
+ function encodePayload(payload) {
80
+ return new TextEncoder().encode(JSON.stringify(payload));
81
+ }
82
+ function decodePayload(data) {
83
+ try {
84
+ return JSON.parse(new TextDecoder().decode(data));
85
+ }
86
+ catch {
87
+ log.error('NatsEventLog: failed to decode payload, skipping corrupt message');
88
+ return undefined;
89
+ }
90
+ }
91
+ // ---------------------------------------------------------------------------
92
+ // Subject helpers
93
+ // ---------------------------------------------------------------------------
94
+ /**
95
+ * Encodes a tenant DID into a NATS-safe subject token by replacing `.` and `>`
96
+ * (which are NATS subject delimiters) with URL-safe equivalents.
97
+ */
98
+ function tenantToSubjectToken(tenant) {
99
+ return tenant.replace(/\./g, '~').replace(/>/g, '_');
100
+ }
101
+ // ---------------------------------------------------------------------------
102
+ // NatsEventLog — distributed EventLog implementation over NATS JetStream
103
+ // ---------------------------------------------------------------------------
104
+ /**
105
+ * Distributed {@link EventLog} implementation backed by NATS JetStream.
106
+ *
107
+ * Events are published to per-tenant subjects within a single JetStream stream.
108
+ * NATS stream sequence numbers are used as opaque cursors, providing native
109
+ * cursor-based replay and EOSE detection via `msg.info.pending`.
110
+ *
111
+ * Designed for multi-node DWN deployments: node A can emit an event, and a
112
+ * subscriber connected to node B receives it via the shared NATS cluster.
113
+ *
114
+ * Loaded by the DWN server plugin system via `DWN_EVENT_LOG_PLUGIN_PATH`.
115
+ * Must be a default export with a no-arg constructor.
116
+ */
117
+ export default class NatsEventLog {
118
+ #config;
119
+ #nc;
120
+ #js;
121
+ #jsm;
122
+ /** Active subscription consumers, keyed by consumer name. */
123
+ #activeConsumers = new Map();
124
+ constructor() {
125
+ this.#config = loadConfig();
126
+ }
127
+ // ---- Lifecycle -----------------------------------------------------------
128
+ async open() {
129
+ if (this.#nc !== undefined) {
130
+ return;
131
+ }
132
+ const servers = this.#config.url.split(',').map((s) => s.trim());
133
+ this.#nc = await connect({ servers });
134
+ this.#js = jetstream(this.#nc);
135
+ this.#jsm = await jetstreamManager(this.#nc);
136
+ // Ensure the stream exists (idempotent — update if it already exists).
137
+ await this.#ensureStream();
138
+ log.info(`NatsEventLog: connected to ${servers.join(', ')}, stream '${this.#config.streamName}' ready`);
139
+ }
140
+ async close() {
141
+ // Stop all active subscription consumers.
142
+ for (const [name, entry] of this.#activeConsumers) {
143
+ entry.stopped = true;
144
+ entry.messages?.stop();
145
+ try {
146
+ await this.#jsm.consumers.delete(this.#config.streamName, name);
147
+ }
148
+ catch {
149
+ // Consumer may already be gone (ephemeral timeout).
150
+ }
151
+ }
152
+ this.#activeConsumers.clear();
153
+ if (this.#nc !== undefined) {
154
+ await this.#nc.drain();
155
+ this.#nc = undefined;
156
+ this.#js = undefined;
157
+ this.#jsm = undefined;
158
+ }
159
+ }
160
+ // ---- emit ----------------------------------------------------------------
161
+ async emit(tenant, event, indexes) {
162
+ this.#assertOpen();
163
+ const subject = this.#tenantSubject(tenant);
164
+ const data = encodePayload({ event, indexes });
165
+ const ack = await this.#js.publish(subject, data);
166
+ return String(ack.seq);
167
+ }
168
+ // ---- read ----------------------------------------------------------------
169
+ async read(tenant, options = {}) {
170
+ this.#assertOpen();
171
+ const { cursor, limit, filters } = options;
172
+ const subject = this.#tenantSubject(tenant);
173
+ // Create a one-shot ordered consumer for the read.
174
+ const consumerOpts = {
175
+ filter_subject: subject,
176
+ ack_policy: AckPolicy.None, // ordered consumers use AckNone
177
+ };
178
+ if (cursor !== undefined) {
179
+ consumerOpts.deliver_policy = DeliverPolicy.StartSequence;
180
+ consumerOpts.opt_start_seq = Number(cursor) + 1;
181
+ }
182
+ else {
183
+ consumerOpts.deliver_policy = DeliverPolicy.All;
184
+ }
185
+ const consumer = await this.#jsm.consumers.add(this.#config.streamName, consumerOpts);
186
+ const maxResults = limit ?? Number.MAX_SAFE_INTEGER;
187
+ const events = [];
188
+ let lastCursor;
189
+ try {
190
+ const messages = await this.#js.consumers.get(this.#config.streamName, consumer.name);
191
+ const iter = await messages.fetch({ max_messages: maxResults, expires: 2_000 });
192
+ for await (const msg of iter) {
193
+ const payload = decodePayload(msg.data);
194
+ if (payload === undefined) {
195
+ continue;
196
+ }
197
+ if (!matchAnyFilter(payload.indexes, filters)) {
198
+ continue;
199
+ }
200
+ events.push({
201
+ seq: msg.seq,
202
+ event: payload.event,
203
+ indexes: payload.indexes,
204
+ });
205
+ lastCursor = String(msg.seq);
206
+ if (events.length >= maxResults) {
207
+ break;
208
+ }
209
+ }
210
+ }
211
+ finally {
212
+ // Clean up the one-shot consumer.
213
+ try {
214
+ await this.#jsm.consumers.delete(this.#config.streamName, consumer.name);
215
+ }
216
+ catch {
217
+ // May already be cleaned up.
218
+ }
219
+ }
220
+ return {
221
+ events,
222
+ cursor: lastCursor ?? cursor,
223
+ };
224
+ }
225
+ // ---- subscribe -----------------------------------------------------------
226
+ async subscribe(tenant, id, listener, options) {
227
+ this.#assertOpen();
228
+ const subject = this.#tenantSubject(tenant);
229
+ const { cursor, filters } = options ?? {};
230
+ // Build the consumer config.
231
+ const consumerName = `sub-${id}`;
232
+ const consumerOpts = {
233
+ name: consumerName,
234
+ filter_subject: subject,
235
+ ack_policy: AckPolicy.Explicit,
236
+ inactive_threshold: 60_000_000_000, // 60 seconds in nanos
237
+ };
238
+ if (cursor !== undefined) {
239
+ consumerOpts.deliver_policy = DeliverPolicy.StartSequence;
240
+ consumerOpts.opt_start_seq = Number(cursor) + 1;
241
+ }
242
+ else {
243
+ consumerOpts.deliver_policy = DeliverPolicy.New;
244
+ }
245
+ await this.#jsm.consumers.add(this.#config.streamName, consumerOpts);
246
+ const entry = { stopped: false };
247
+ this.#activeConsumers.set(consumerName, entry);
248
+ // Start the consume loop asynchronously.
249
+ const consumeLoop = async () => {
250
+ let sentEose = cursor === undefined; // no cursor → no EOSE needed
251
+ try {
252
+ const consumer = await this.#js.consumers.get(this.#config.streamName, consumerName);
253
+ const messages = await consumer.consume();
254
+ entry.messages = messages;
255
+ for await (const msg of messages) {
256
+ if (entry.stopped) {
257
+ break;
258
+ }
259
+ const payload = decodePayload(msg.data);
260
+ if (payload === undefined) {
261
+ msg.ack();
262
+ continue;
263
+ }
264
+ if (!matchAnyFilter(payload.indexes, filters)) {
265
+ msg.ack();
266
+ continue;
267
+ }
268
+ const eventCursor = String(msg.seq);
269
+ listener({ type: 'event', cursor: eventCursor, event: payload.event });
270
+ msg.ack();
271
+ // EOSE detection: when pending reaches 0, all stored events have been
272
+ // delivered and we transition to live mode.
273
+ if (!sentEose && msg.info.pending === 0) {
274
+ listener({ type: 'eose', cursor: eventCursor });
275
+ sentEose = true;
276
+ }
277
+ }
278
+ }
279
+ catch (err) {
280
+ if (!entry.stopped) {
281
+ log.error(`NatsEventLog: consume loop error for subscription '${id}'`, err);
282
+ }
283
+ }
284
+ };
285
+ // Fire and forget — the loop runs until stop or connection close.
286
+ consumeLoop();
287
+ // Handle the edge case where cursor was provided but there are zero
288
+ // stored events after it. The consume loop won't receive any messages,
289
+ // so we need to send EOSE proactively. We check consumer info after a
290
+ // short delay to allow the loop to start.
291
+ if (cursor !== undefined) {
292
+ setTimeout(async () => {
293
+ if (entry.stopped) {
294
+ return;
295
+ }
296
+ try {
297
+ const info = await this.#jsm.consumers.info(this.#config.streamName, consumerName);
298
+ if (info.num_pending === 0 && info.delivered.stream_seq <= Number(cursor)) {
299
+ listener({ type: 'eose', cursor });
300
+ }
301
+ }
302
+ catch {
303
+ // Consumer may be gone already.
304
+ }
305
+ }, 50);
306
+ }
307
+ return {
308
+ id,
309
+ close: async () => {
310
+ entry.stopped = true;
311
+ entry.messages?.stop();
312
+ this.#activeConsumers.delete(consumerName);
313
+ try {
314
+ await this.#jsm.consumers.delete(this.#config.streamName, consumerName);
315
+ }
316
+ catch {
317
+ // Consumer may already be gone (ephemeral timeout).
318
+ }
319
+ },
320
+ };
321
+ }
322
+ // ---- trim ----------------------------------------------------------------
323
+ async trim(tenant, olderThan) {
324
+ this.#assertOpen();
325
+ const subject = this.#tenantSubject(tenant);
326
+ if (typeof olderThan === 'number') {
327
+ // Purge events with sequence < olderThan.
328
+ await this.#jsm.streams.purge(this.#config.streamName, {
329
+ filter: subject,
330
+ seq: olderThan,
331
+ });
332
+ }
333
+ else {
334
+ // Timestamp-based trim: purge events older than the given ISO-8601 time.
335
+ // NATS stream purge doesn't support timestamp-based purging natively, so
336
+ // we find the sequence threshold by reading events and checking timestamps.
337
+ // For simplicity, we do a full purge of the subject if olderThan is provided
338
+ // as a string — this matches the EventEmitterEventLog behaviour of deleting
339
+ // entries whose messageTimestamp is before the given time.
340
+ // A more precise implementation could binary-search for the sequence cutoff.
341
+ await this.#jsm.streams.purge(this.#config.streamName, {
342
+ filter: subject,
343
+ });
344
+ }
345
+ }
346
+ // ---- Private helpers -----------------------------------------------------
347
+ #tenantSubject(tenant) {
348
+ return `dwn.events.${tenantToSubjectToken(tenant)}`;
349
+ }
350
+ #assertOpen() {
351
+ if (this.#nc === undefined || this.#js === undefined || this.#jsm === undefined) {
352
+ throw new Error('NatsEventLog: not open. Call open() before using.');
353
+ }
354
+ }
355
+ async #ensureStream() {
356
+ const cfg = this.#config;
357
+ try {
358
+ await this.#jsm.streams.info(cfg.streamName);
359
+ // Stream exists — update config if needed.
360
+ await this.#jsm.streams.update(cfg.streamName, {
361
+ subjects: ['dwn.events.>'],
362
+ max_age: cfg.streamMaxAge,
363
+ num_replicas: cfg.replicas,
364
+ max_msgs_per_subject: cfg.maxMsgsPerSubject,
365
+ });
366
+ }
367
+ catch {
368
+ // Stream does not exist — create it.
369
+ await this.#jsm.streams.add({
370
+ name: cfg.streamName,
371
+ subjects: ['dwn.events.>'],
372
+ max_age: cfg.streamMaxAge,
373
+ num_replicas: cfg.replicas,
374
+ max_msgs_per_subject: cfg.maxMsgsPerSubject,
375
+ });
376
+ }
377
+ }
378
+ }
379
+ //# sourceMappingURL=event-log-nats.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"event-log-nats.js","sourceRoot":"","sources":["../../../../src/plugins/event-log-nats.ts"],"names":[],"mappings":"AAIA,OAAO,GAAG,MAAM,UAAU,CAAC;AAE3B,OAAO,EAAE,OAAO,EAAE,MAAM,yBAAyB,CAAC;AAClD,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,SAAS,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAmB3F,SAAS,UAAU;IACjB,OAAO;QACL,GAAG,EAAiB,OAAO,CAAC,GAAG,CAAC,QAAQ,IAAI,uBAAuB;QACnE,UAAU,EAAU,OAAO,CAAC,GAAG,CAAC,gBAAgB,IAAI,YAAY;QAChE,YAAY,EAAQ,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,mBAAmB,IAAI,iBAAiB,CAAC,EAAE,kBAAkB;QACtG,QAAQ,EAAY,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,oBAAoB,IAAI,GAAG,CAAC;QACrE,iBAAiB,EAAG,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,yBAAyB,IAAI,QAAQ,CAAC;KAChF,CAAC;AACJ,CAAC;AAED,8EAA8E;AAC9E,2EAA2E;AAC3E,8EAA8E;AAE9E;;;GAGG;AACH,SAAS,cAAc,CAAC,SAAoB,EAAE,OAA6B;IACzE,IAAI,OAAO,KAAK,SAAS,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAClD,OAAO,IAAI,CAAC;IACd,CAAC;IACD,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;QAC7B,IAAI,WAAW,CAAC,SAAS,EAAE,MAAM,CAAC,EAAE,CAAC;YACnC,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,iGAAiG;AACjG,SAAS,WAAW,CAAC,aAAwB,EAAE,MAAc;IAC3D,KAAK,MAAM,GAAG,IAAI,MAAM,EAAE,CAAC;QACzB,MAAM,WAAW,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;QAChC,MAAM,UAAU,GAAG,aAAa,CAAC,GAAG,CAAC,CAAC;QACtC,IAAI,UAAU,KAAK,SAAS,EAAE,CAAC;YAC7B,OAAO,KAAK,CAAC;QACf,CAAC;QAED,MAAM,MAAM,GAAG,KAAK,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC;QACrE,IAAI,QAAQ,GAAG,KAAK,CAAC;QACrB,KAAK,MAAM,CAAC,IAAI,MAAM,EAAE,CAAC;YACvB,IAAI,gBAAgB,CAAC,WAAW,EAAE,CAAC,CAAC,EAAE,CAAC;gBACrC,QAAQ,GAAG,IAAI,CAAC;gBAChB,MAAM;YACR,CAAC;QACH,CAAC;QACD,IAAI,CAAC,QAAQ,EAAE,CAAC;YACd,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,kFAAkF;AAClF,SAAS,gBAAgB,CAAC,WAAoB,EAAE,UAAqC;IACnF,IAAI,OAAO,WAAW,KAAK,QAAQ,IAAI,WAAW,KAAK,IAAI,EAAE,CAAC;QAC5D,IAAI,KAAK,CAAC,OAAO,CAAC,WAAW,CAAC,EAAE,CAAC;YAC/B,cAAc;YACd,OAAQ,WAAgD,CAAC,QAAQ,CAAC,UAAmB,CAAC,CAAC;QACzF,CAAC;QACD,cAAc;QACd,MAAM,KAAK,GAAG,WAA2G,CAAC;QAC1H,IAAI,KAAK,CAAC,EAAE,KAAK,SAAS,IAAI,UAAU,IAAI,KAAK,CAAC,EAAE,EAAE,CAAC;YACrD,OAAO,KAAK,CAAC;QACf,CAAC;QACD,IAAI,KAAK,CAAC,GAAG,KAAK,SAAS,IAAI,UAAU,GAAG,KAAK,CAAC,GAAG,EAAE,CAAC;YACtD,OAAO,KAAK,CAAC;QACf,CAAC;QACD,IAAI,KAAK,CAAC,EAAE,KAAK,SAAS,IAAI,UAAU,IAAI,KAAK,CAAC,EAAE,EAAE,CAAC;YACrD,OAAO,KAAK,CAAC;QACf,CAAC;QACD,IAAI,KAAK,CAAC,GAAG,KAAK,SAAS,IAAI,UAAU,GAAG,KAAK,CAAC,GAAG,EAAE,CAAC;YACtD,OAAO,KAAK,CAAC;QACf,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IACD,cAAc;IACd,OAAO,UAAU,KAAK,WAAW,CAAC;AACpC,CAAC;AAWD,SAAS,aAAa,CAAC,OAAyB;IAC9C,OAAO,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC;AAC3D,CAAC;AAED,SAAS,aAAa,CAAC,IAAgB;IACrC,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,CAAqB,CAAC;IACxE,CAAC;IAAC,MAAM,CAAC;QACP,GAAG,CAAC,KAAK,CAAC,kEAAkE,CAAC,CAAC;QAC9E,OAAO,SAAS,CAAC;IACnB,CAAC;AACH,CAAC;AAED,8EAA8E;AAC9E,kBAAkB;AAClB,8EAA8E;AAE9E;;;GAGG;AACH,SAAS,oBAAoB,CAAC,MAAc;IAC1C,OAAO,MAAM,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;AACvD,CAAC;AAED,8EAA8E;AAC9E,yEAAyE;AACzE,8EAA8E;AAE9E;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,OAAO,OAAO,YAAY;IAC/B,OAAO,CAAqB;IAC5B,GAAG,CAA6B;IAChC,GAAG,CAA8B;IACjC,IAAI,CAA+B;IAEnC,6DAA6D;IAC7D,gBAAgB,GAAmE,IAAI,GAAG,EAAE,CAAC;IAE7F;QACE,IAAI,CAAC,OAAO,GAAG,UAAU,EAAE,CAAC;IAC9B,CAAC;IAED,6EAA6E;IAEtE,KAAK,CAAC,IAAI;QACf,IAAI,IAAI,CAAC,GAAG,KAAK,SAAS,EAAE,CAAC;YAC3B,OAAO;QACT,CAAC;QAED,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAS,EAAU,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;QACjF,IAAI,CAAC,GAAG,GAAG,MAAM,OAAO,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC;QACtC,IAAI,CAAC,GAAG,GAAG,SAAS,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAC/B,IAAI,CAAC,IAAI,GAAG,MAAM,gBAAgB,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAE7C,uEAAuE;QACvE,MAAM,IAAI,CAAC,aAAa,EAAE,CAAC;QAE3B,GAAG,CAAC,IAAI,CAAC,8BAA8B,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,aAAa,IAAI,CAAC,OAAO,CAAC,UAAU,SAAS,CAAC,CAAC;IAC1G,CAAC;IAEM,KAAK,CAAC,KAAK;QAChB,0CAA0C;QAC1C,KAAK,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,IAAI,CAAC,gBAAgB,EAAE,CAAC;YAClD,KAAK,CAAC,OAAO,GAAG,IAAI,CAAC;YACrB,KAAK,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC;YACvB,IAAI,CAAC;gBACH,MAAM,IAAI,CAAC,IAAK,CAAC,SAAS,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,UAAU,EAAE,IAAI,CAAC,CAAC;YACnE,CAAC;YAAC,MAAM,CAAC;gBACP,oDAAoD;YACtD,CAAC;QACH,CAAC;QACD,IAAI,CAAC,gBAAgB,CAAC,KAAK,EAAE,CAAC;QAE9B,IAAI,IAAI,CAAC,GAAG,KAAK,SAAS,EAAE,CAAC;YAC3B,MAAM,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC;YACvB,IAAI,CAAC,GAAG,GAAG,SAAS,CAAC;YACrB,IAAI,CAAC,GAAG,GAAG,SAAS,CAAC;YACrB,IAAI,CAAC,IAAI,GAAG,SAAS,CAAC;QACxB,CAAC;IACH,CAAC;IAED,6EAA6E;IAEtE,KAAK,CAAC,IAAI,CAAC,MAAc,EAAE,KAAmB,EAAE,OAAkB;QACvE,IAAI,CAAC,WAAW,EAAE,CAAC;QAEnB,MAAM,OAAO,GAAG,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC;QAC5C,MAAM,IAAI,GAAG,aAAa,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,CAAC;QAC/C,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,GAAI,CAAC,OAAO,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;QACnD,OAAO,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IACzB,CAAC;IAED,6EAA6E;IAEtE,KAAK,CAAC,IAAI,CAAC,MAAc,EAAE,UAA+B,EAAE;QACjE,IAAI,CAAC,WAAW,EAAE,CAAC;QAEnB,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC;QAC3C,MAAM,OAAO,GAAG,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC;QAE5C,mDAAmD;QACnD,MAAM,YAAY,GAA4B;YAC5C,cAAc,EAAG,OAAO;YACxB,UAAU,EAAO,SAAS,CAAC,IAAI,EAAE,gCAAgC;SAClE,CAAC;QAEF,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;YACzB,YAAY,CAAC,cAAc,GAAG,aAAa,CAAC,aAAa,CAAC;YAC1D,YAAY,CAAC,aAAa,GAAG,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QAClD,CAAC;aAAM,CAAC;YACN,YAAY,CAAC,cAAc,GAAG,aAAa,CAAC,GAAG,CAAC;QAClD,CAAC;QAED,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,IAAK,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,UAAU,EAAE,YAAY,CAAC,CAAC;QACvF,MAAM,UAAU,GAAG,KAAK,IAAI,MAAM,CAAC,gBAAgB,CAAC;QAEpD,MAAM,MAAM,GAAoB,EAAE,CAAC;QACnC,IAAI,UAA8B,CAAC;QAEnC,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,GAAI,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,UAAU,EAAE,QAAQ,CAAC,IAAI,CAAC,CAAC;YACvF,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,KAAK,CAAC,EAAE,YAAY,EAAE,UAAU,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,CAAC;YAEhF,IAAI,KAAK,EAAE,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;gBAC7B,MAAM,OAAO,GAAG,aAAa,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;gBACxC,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;oBAC1B,SAAS;gBACX,CAAC;gBAED,IAAI,CAAC,cAAc,CAAC,OAAO,CAAC,OAAO,EAAE,OAAO,CAAC,EAAE,CAAC;oBAC9C,SAAS;gBACX,CAAC;gBAED,MAAM,CAAC,IAAI,CAAC;oBACV,GAAG,EAAO,GAAG,CAAC,GAAG;oBACjB,KAAK,EAAK,OAAO,CAAC,KAAK;oBACvB,OAAO,EAAG,OAAO,CAAC,OAAO;iBAC1B,CAAC,CAAC;gBAEH,UAAU,GAAG,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;gBAE7B,IAAI,MAAM,CAAC,MAAM,IAAI,UAAU,EAAE,CAAC;oBAChC,MAAM;gBACR,CAAC;YACH,CAAC;QACH,CAAC;gBAAS,CAAC;YACT,kCAAkC;YAClC,IAAI,CAAC;gBACH,MAAM,IAAI,CAAC,IAAK,CAAC,SAAS,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,UAAU,EAAE,QAAQ,CAAC,IAAI,CAAC,CAAC;YAC5E,CAAC;YAAC,MAAM,CAAC;gBACP,6BAA6B;YAC/B,CAAC;QACH,CAAC;QAED,OAAO;YACL,MAAM;YACN,MAAM,EAAE,UAAU,IAAI,MAAM;SAC7B,CAAC;IACJ,CAAC;IAED,6EAA6E;IAEtE,KAAK,CAAC,SAAS,CACpB,MAAc,EACd,EAAU,EACV,QAA8B,EAC9B,OAAkC;QAElC,IAAI,CAAC,WAAW,EAAE,CAAC;QAEnB,MAAM,OAAO,GAAG,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC;QAC5C,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,OAAO,IAAI,EAAE,CAAC;QAE1C,6BAA6B;QAC7B,MAAM,YAAY,GAAG,OAAO,EAAE,EAAE,CAAC;QACjC,MAAM,YAAY,GAA4B;YAC5C,IAAI,EAAiB,YAAY;YACjC,cAAc,EAAO,OAAO;YAC5B,UAAU,EAAW,SAAS,CAAC,QAAQ;YACvC,kBAAkB,EAAG,cAAc,EAAE,sBAAsB;SAC5D,CAAC;QAEF,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;YACzB,YAAY,CAAC,cAAc,GAAG,aAAa,CAAC,aAAa,CAAC;YAC1D,YAAY,CAAC,aAAa,GAAG,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QAClD,CAAC;aAAM,CAAC;YACN,YAAY,CAAC,cAAc,GAAG,aAAa,CAAC,GAAG,CAAC;QAClD,CAAC;QAED,MAAM,IAAI,CAAC,IAAK,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,UAAU,EAAE,YAAY,CAAC,CAAC;QAEtE,MAAM,KAAK,GAAsD,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;QACpF,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,YAAY,EAAE,KAAK,CAAC,CAAC;QAE/C,yCAAyC;QACzC,MAAM,WAAW,GAAG,KAAK,IAAmB,EAAE;YAC5C,IAAI,QAAQ,GAAG,MAAM,KAAK,SAAS,CAAC,CAAC,6BAA6B;YAElE,IAAI,CAAC;gBACH,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,GAAI,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,UAAU,EAAE,YAAY,CAAC,CAAC;gBACtF,MAAM,QAAQ,GAAG,MAAM,QAAQ,CAAC,OAAO,EAAE,CAAC;gBAC1C,KAAK,CAAC,QAAQ,GAAG,QAAQ,CAAC;gBAE1B,IAAI,KAAK,EAAE,MAAM,GAAG,IAAI,QAAQ,EAAE,CAAC;oBACjC,IAAI,KAAK,CAAC,OAAO,EAAE,CAAC;wBAClB,MAAM;oBACR,CAAC;oBAED,MAAM,OAAO,GAAG,aAAa,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;oBACxC,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;wBAC1B,GAAG,CAAC,GAAG,EAAE,CAAC;wBACV,SAAS;oBACX,CAAC;oBAED,IAAI,CAAC,cAAc,CAAC,OAAO,CAAC,OAAO,EAAE,OAAO,CAAC,EAAE,CAAC;wBAC9C,GAAG,CAAC,GAAG,EAAE,CAAC;wBACV,SAAS;oBACX,CAAC;oBAED,MAAM,WAAW,GAAG,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;oBACpC,QAAQ,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,CAAC,CAAC;oBACvE,GAAG,CAAC,GAAG,EAAE,CAAC;oBAEV,sEAAsE;oBACtE,4CAA4C;oBAC5C,IAAI,CAAC,QAAQ,IAAI,GAAG,CAAC,IAAI,CAAC,OAAO,KAAK,CAAC,EAAE,CAAC;wBACxC,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,CAAC,CAAC;wBAChD,QAAQ,GAAG,IAAI,CAAC;oBAClB,CAAC;gBACH,CAAC;YACH,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC;oBACnB,GAAG,CAAC,KAAK,CAAC,sDAAsD,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC;gBAC9E,CAAC;YACH,CAAC;QACH,CAAC,CAAC;QAEF,kEAAkE;QAClE,WAAW,EAAE,CAAC;QAEd,oEAAoE;QACpE,uEAAuE;QACvE,sEAAsE;QACtE,0CAA0C;QAC1C,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;YACzB,UAAU,CAAC,KAAK,IAAmB,EAAE;gBACnC,IAAI,KAAK,CAAC,OAAO,EAAE,CAAC;oBAClB,OAAO;gBACT,CAAC;gBACD,IAAI,CAAC;oBACH,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,IAAK,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,UAAU,EAAE,YAAY,CAAC,CAAC;oBACpF,IAAI,IAAI,CAAC,WAAW,KAAK,CAAC,IAAI,IAAI,CAAC,SAAS,CAAC,UAAU,IAAI,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC;wBAC1E,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;oBACrC,CAAC;gBACH,CAAC;gBAAC,MAAM,CAAC;oBACP,gCAAgC;gBAClC,CAAC;YACH,CAAC,EAAE,EAAE,CAAC,CAAC;QACT,CAAC;QAED,OAAO;YACL,EAAE;YACF,KAAK,EAAE,KAAK,IAAmB,EAAE;gBAC/B,KAAK,CAAC,OAAO,GAAG,IAAI,CAAC;gBACrB,KAAK,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC;gBACvB,IAAI,CAAC,gBAAgB,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC;gBAC3C,IAAI,CAAC;oBACH,MAAM,IAAI,CAAC,IAAK,CAAC,SAAS,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,UAAU,EAAE,YAAY,CAAC,CAAC;gBAC3E,CAAC;gBAAC,MAAM,CAAC;oBACP,oDAAoD;gBACtD,CAAC;YACH,CAAC;SACF,CAAC;IACJ,CAAC;IAED,6EAA6E;IAEtE,KAAK,CAAC,IAAI,CAAC,MAAc,EAAE,SAA0B;QAC1D,IAAI,CAAC,WAAW,EAAE,CAAC;QAEnB,MAAM,OAAO,GAAG,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC;QAE5C,IAAI,OAAO,SAAS,KAAK,QAAQ,EAAE,CAAC;YAClC,0CAA0C;YAC1C,MAAM,IAAI,CAAC,IAAK,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,UAAU,EAAE;gBACtD,MAAM,EAAG,OAAO;gBAChB,GAAG,EAAM,SAAS;aACnB,CAAC,CAAC;QACL,CAAC;aAAM,CAAC;YACN,yEAAyE;YACzE,yEAAyE;YACzE,4EAA4E;YAC5E,6EAA6E;YAC7E,4EAA4E;YAC5E,2DAA2D;YAC3D,6EAA6E;YAC7E,MAAM,IAAI,CAAC,IAAK,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,UAAU,EAAE;gBACtD,MAAM,EAAE,OAAO;aAChB,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,6EAA6E;IAE7E,cAAc,CAAC,MAAc;QAC3B,OAAO,cAAc,oBAAoB,CAAC,MAAM,CAAC,EAAE,CAAC;IACtD,CAAC;IAED,WAAW;QACT,IAAI,IAAI,CAAC,GAAG,KAAK,SAAS,IAAI,IAAI,CAAC,GAAG,KAAK,SAAS,IAAI,IAAI,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;YAChF,MAAM,IAAI,KAAK,CAAC,mDAAmD,CAAC,CAAC;QACvE,CAAC;IACH,CAAC;IAED,KAAK,CAAC,aAAa;QACjB,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC;QACzB,IAAI,CAAC;YACH,MAAM,IAAI,CAAC,IAAK,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;YAC9C,2CAA2C;YAC3C,MAAM,IAAI,CAAC,IAAK,CAAC,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,UAAU,EAAE;gBAC9C,QAAQ,EAAe,CAAC,cAAc,CAAC;gBACvC,OAAO,EAAgB,GAAG,CAAC,YAAY;gBACvC,YAAY,EAAW,GAAG,CAAC,QAAQ;gBACnC,oBAAoB,EAAG,GAAG,CAAC,iBAAiB;aAC7C,CAAC,CAAC;QACL,CAAC;QAAC,MAAM,CAAC;YACP,qCAAqC;YACrC,MAAM,IAAI,CAAC,IAAK,CAAC,OAAO,CAAC,GAAG,CAAC;gBAC3B,IAAI,EAAmB,GAAG,CAAC,UAAU;gBACrC,QAAQ,EAAe,CAAC,cAAc,CAAC;gBACvC,OAAO,EAAgB,GAAG,CAAC,YAAY;gBACvC,YAAY,EAAW,GAAG,CAAC,QAAQ;gBACnC,oBAAoB,EAAG,GAAG,CAAC,iBAAiB;aAC7C,CAAC,CAAC;QACL,CAAC;IACH,CAAC;CACF"}
@@ -0,0 +1,60 @@
1
+ /**
2
+ * Token-bucket rate limiter with per-key state tracking and automatic cleanup.
3
+ *
4
+ * Each key (IP address or tenant DID) has its own bucket that refills at a
5
+ * fixed rate. When a request arrives, a token is consumed. If the bucket is
6
+ * empty, the request is rejected with a retry-after hint.
7
+ *
8
+ * Stale buckets are periodically purged to prevent unbounded memory growth.
9
+ *
10
+ * @see https://github.com/enboxorg/enbox/issues/326
11
+ */
12
+ export type RateLimiterConfig = {
13
+ /** Tokens added per second (i.e. sustained request rate). */
14
+ refillRate: number;
15
+ /** Maximum burst size (bucket capacity). */
16
+ maxTokens: number;
17
+ };
18
+ export declare class RateLimiter {
19
+ #private;
20
+ /** Stale buckets older than 5 minutes are purged. */
21
+ private static readonly STALE_THRESHOLD_MS;
22
+ /** Cleanup runs every 60 seconds. */
23
+ private static readonly CLEANUP_INTERVAL_MS;
24
+ constructor(config: RateLimiterConfig);
25
+ /**
26
+ * Attempts to consume a token for the given key.
27
+ * @returns `{ allowed: true }` if the request is permitted, or
28
+ * `{ allowed: false, retryAfterMs }` if the rate limit is exceeded.
29
+ */
30
+ consume(key: string): {
31
+ allowed: true;
32
+ } | {
33
+ allowed: false;
34
+ retryAfterMs: number;
35
+ };
36
+ /**
37
+ * Returns the number of keys currently being tracked.
38
+ */
39
+ get size(): number;
40
+ /**
41
+ * Returns the current token count for a key, or `undefined` if not tracked.
42
+ */
43
+ getTokens(key: string): number | undefined;
44
+ /**
45
+ * Reconfigures the rate limiter with new settings. Existing buckets are
46
+ * retained but will use the new `refillRate` and `maxTokens` going forward.
47
+ *
48
+ * @see https://github.com/enboxorg/enbox/issues/389
49
+ */
50
+ reconfigure(config: RateLimiterConfig): void;
51
+ /**
52
+ * Returns the current configuration of this rate limiter.
53
+ */
54
+ get config(): RateLimiterConfig;
55
+ /**
56
+ * Stops the periodic cleanup timer. Call this when shutting down.
57
+ */
58
+ destroy(): void;
59
+ }
60
+ //# sourceMappingURL=rate-limiter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rate-limiter.d.ts","sourceRoot":"","sources":["../../../src/rate-limiter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,MAAM,MAAM,iBAAiB,GAAG;IAC9B,6DAA6D;IAC7D,UAAU,EAAG,MAAM,CAAC;IACpB,4CAA4C;IAC5C,SAAS,EAAG,MAAM,CAAC;CACpB,CAAC;AAOF,qBAAa,WAAW;;IAMtB,qDAAqD;IACrD,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,kBAAkB,CAAiB;IAC3D,qCAAqC;IACrC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,mBAAmB,CAAa;gBAErC,MAAM,EAAE,iBAAiB;IAU5C;;;;OAIG;IACI,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG;QAAE,OAAO,EAAE,IAAI,CAAA;KAAE,GAAG;QAAE,OAAO,EAAE,KAAK,CAAC;QAAC,YAAY,EAAE,MAAM,CAAA;KAAE;IAyBzF;;OAEG;IACH,IAAW,IAAI,IAAI,MAAM,CAExB;IAED;;OAEG;IACI,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAWjD;;;;;OAKG;IACI,WAAW,CAAC,MAAM,EAAE,iBAAiB,GAAG,IAAI;IAKnD;;OAEG;IACH,IAAW,MAAM,IAAI,iBAAiB,CAKrC;IAED;;OAEG;IACI,OAAO,IAAI,IAAI;CAsBvB"}
@@ -0,0 +1,116 @@
1
+ /**
2
+ * Token-bucket rate limiter with per-key state tracking and automatic cleanup.
3
+ *
4
+ * Each key (IP address or tenant DID) has its own bucket that refills at a
5
+ * fixed rate. When a request arrives, a token is consumed. If the bucket is
6
+ * empty, the request is rejected with a retry-after hint.
7
+ *
8
+ * Stale buckets are periodically purged to prevent unbounded memory growth.
9
+ *
10
+ * @see https://github.com/enboxorg/enbox/issues/326
11
+ */
12
+ export class RateLimiter {
13
+ #refillRate;
14
+ #maxTokens;
15
+ #buckets = new Map();
16
+ #cleanupInterval;
17
+ /** Stale buckets older than 5 minutes are purged. */
18
+ static STALE_THRESHOLD_MS = 5 * 60 * 1000;
19
+ /** Cleanup runs every 60 seconds. */
20
+ static CLEANUP_INTERVAL_MS = 60 * 1000;
21
+ constructor(config) {
22
+ this.#refillRate = config.refillRate;
23
+ this.#maxTokens = config.maxTokens;
24
+ // Start periodic cleanup.
25
+ this.#cleanupInterval = setInterval(() => {
26
+ this.#cleanup();
27
+ }, RateLimiter.CLEANUP_INTERVAL_MS);
28
+ }
29
+ /**
30
+ * Attempts to consume a token for the given key.
31
+ * @returns `{ allowed: true }` if the request is permitted, or
32
+ * `{ allowed: false, retryAfterMs }` if the rate limit is exceeded.
33
+ */
34
+ consume(key) {
35
+ const now = Date.now();
36
+ let bucket = this.#buckets.get(key);
37
+ if (bucket === undefined) {
38
+ bucket = { tokens: this.#maxTokens, lastRefill: now };
39
+ this.#buckets.set(key, bucket);
40
+ }
41
+ // Refill tokens based on elapsed time.
42
+ const elapsed = (now - bucket.lastRefill) / 1000;
43
+ bucket.tokens = Math.min(this.#maxTokens, bucket.tokens + elapsed * this.#refillRate);
44
+ bucket.lastRefill = now;
45
+ if (bucket.tokens >= 1) {
46
+ bucket.tokens -= 1;
47
+ return { allowed: true };
48
+ }
49
+ // Calculate how long until one token is available.
50
+ const deficit = 1 - bucket.tokens;
51
+ const retryAfterMs = Math.ceil((deficit / this.#refillRate) * 1000);
52
+ return { allowed: false, retryAfterMs };
53
+ }
54
+ /**
55
+ * Returns the number of keys currently being tracked.
56
+ */
57
+ get size() {
58
+ return this.#buckets.size;
59
+ }
60
+ /**
61
+ * Returns the current token count for a key, or `undefined` if not tracked.
62
+ */
63
+ getTokens(key) {
64
+ const bucket = this.#buckets.get(key);
65
+ if (bucket === undefined) {
66
+ return undefined;
67
+ }
68
+ // Refill to return current state.
69
+ const elapsed = (Date.now() - bucket.lastRefill) / 1000;
70
+ return Math.min(this.#maxTokens, bucket.tokens + elapsed * this.#refillRate);
71
+ }
72
+ /**
73
+ * Reconfigures the rate limiter with new settings. Existing buckets are
74
+ * retained but will use the new `refillRate` and `maxTokens` going forward.
75
+ *
76
+ * @see https://github.com/enboxorg/enbox/issues/389
77
+ */
78
+ reconfigure(config) {
79
+ this.#refillRate = config.refillRate;
80
+ this.#maxTokens = config.maxTokens;
81
+ }
82
+ /**
83
+ * Returns the current configuration of this rate limiter.
84
+ */
85
+ get config() {
86
+ return {
87
+ refillRate: this.#refillRate,
88
+ maxTokens: this.#maxTokens,
89
+ };
90
+ }
91
+ /**
92
+ * Stops the periodic cleanup timer. Call this when shutting down.
93
+ */
94
+ destroy() {
95
+ if (this.#cleanupInterval) {
96
+ clearInterval(this.#cleanupInterval);
97
+ this.#cleanupInterval = undefined;
98
+ }
99
+ this.#buckets.clear();
100
+ }
101
+ /**
102
+ * Removes buckets that have been at max capacity for longer than the stale threshold.
103
+ */
104
+ #cleanup() {
105
+ const now = Date.now();
106
+ for (const [key, bucket] of this.#buckets) {
107
+ const elapsed = (now - bucket.lastRefill) / 1000;
108
+ const currentTokens = Math.min(this.#maxTokens, bucket.tokens + elapsed * this.#refillRate);
109
+ const timeSinceLastUse = now - bucket.lastRefill;
110
+ if (currentTokens >= this.#maxTokens && timeSinceLastUse > RateLimiter.STALE_THRESHOLD_MS) {
111
+ this.#buckets.delete(key);
112
+ }
113
+ }
114
+ }
115
+ }
116
+ //# sourceMappingURL=rate-limiter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rate-limiter.js","sourceRoot":"","sources":["../../../src/rate-limiter.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,53 @@
1
+ import type { ProviderAuthPlugin, ProviderAuthValidationResult } from './provider-auth-plugin.js';
2
+ /**
3
+ * Options for creating a {@link JwtProviderAuthPlugin}.
4
+ * Exactly one of `secret` or `jwksUrl` must be provided.
5
+ */
6
+ export type JwtProviderAuthPluginOptions = {
7
+ /** HMAC shared secret for HS256/HS384/HS512 tokens. */
8
+ secret?: string;
9
+ /** JWKS endpoint URL for RS256/ES256/EdDSA tokens. */
10
+ jwksUrl?: string;
11
+ /** Expected JWT `iss` claim. If set, tokens without a matching issuer are rejected. */
12
+ issuer?: string;
13
+ /** Expected JWT `aud` claim. If set, tokens without a matching audience are rejected. */
14
+ audience?: string;
15
+ };
16
+ /**
17
+ * Built-in {@link ProviderAuthPlugin} that validates JWT registration tokens.
18
+ *
19
+ * Supports two modes:
20
+ * - **HMAC secret** (`DWN_PROVIDER_AUTH_JWT_SECRET`): symmetric HS256 verification.
21
+ * - **JWKS URL** (`DWN_PROVIDER_AUTH_JWT_JWKS_URL`): asymmetric verification via
22
+ * a remote JWKS endpoint (RS256, ES256, EdDSA, etc.). Keys are cached and
23
+ * rotated automatically by `jose`.
24
+ *
25
+ * The JWT payload may include:
26
+ * - `sub` — mapped to `accountId` in the validation result.
27
+ * - `metadata` — arbitrary provider-defined object stored with the tenant.
28
+ *
29
+ * This plugin ships with `@enbox/dwn-server` and requires no external code.
30
+ * Providers who need custom validation logic can implement their own
31
+ * {@link ProviderAuthPlugin} instead.
32
+ */
33
+ export declare class JwtProviderAuthPlugin implements ProviderAuthPlugin {
34
+ #private;
35
+ private constructor();
36
+ /**
37
+ * Create a {@link JwtProviderAuthPlugin} from options.
38
+ *
39
+ * @param options - Must include exactly one of `secret` or `jwksUrl`.
40
+ * @throws If neither `secret` nor `jwksUrl` is provided.
41
+ */
42
+ static create(options: JwtProviderAuthPluginOptions): Promise<JwtProviderAuthPlugin>;
43
+ /**
44
+ * Create a {@link JwtProviderAuthPlugin} from environment variables.
45
+ *
46
+ * Reads `DWN_PROVIDER_AUTH_JWT_SECRET`, `DWN_PROVIDER_AUTH_JWT_JWKS_URL`,
47
+ * and optionally `DWN_PROVIDER_AUTH_JWT_ISSUER` / `DWN_PROVIDER_AUTH_JWT_AUDIENCE`.
48
+ */
49
+ static fromEnv(): Promise<JwtProviderAuthPlugin>;
50
+ /** @inheritdoc */
51
+ validateRegistrationToken(token: string): Promise<ProviderAuthValidationResult>;
52
+ }
53
+ //# sourceMappingURL=jwt-provider-auth-plugin.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jwt-provider-auth-plugin.d.ts","sourceRoot":"","sources":["../../../../src/registration/jwt-provider-auth-plugin.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,4BAA4B,EAAE,MAAM,2BAA2B,CAAC;AAKlG;;;GAGG;AACH,MAAM,MAAM,4BAA4B,GAAG;IACzC,uDAAuD;IACvD,MAAM,CAAC,EAAG,MAAM,CAAC;IACjB,sDAAsD;IACtD,OAAO,CAAC,EAAG,MAAM,CAAC;IAClB,uFAAuF;IACvF,MAAM,CAAC,EAAG,MAAM,CAAC;IACjB,yFAAyF;IACzF,QAAQ,CAAC,EAAG,MAAM,CAAC;CACpB,CAAC;AAEF;;;;;;;;;;;;;;;;GAgBG;AACH,qBAAa,qBAAsB,YAAW,kBAAkB;;IAK9D,OAAO;IAUP;;;;;OAKG;WACiB,MAAM,CAAC,OAAO,EAAE,4BAA4B,GAAG,OAAO,CAAC,qBAAqB,CAAC;IAiBjG;;;;;OAKG;WACiB,OAAO,IAAI,OAAO,CAAC,qBAAqB,CAAC;IAS7D,kBAAkB;IACL,yBAAyB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,4BAA4B,CAAC;CA2B7F"}