@heavybit/shared-lib 2.1.0 → 2.7.0

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 (296) hide show
  1. package/README.md +163 -2
  2. package/dist/cjs/access/index.js +9 -1
  3. package/dist/cjs/access/index.js.map +1 -1
  4. package/dist/cjs/approvals/index.js +137 -0
  5. package/dist/cjs/approvals/index.js.map +1 -0
  6. package/dist/cjs/audit/index.js +74 -0
  7. package/dist/cjs/audit/index.js.map +1 -0
  8. package/dist/cjs/cache/index.js +90 -16
  9. package/dist/cjs/cache/index.js.map +1 -1
  10. package/dist/cjs/cache/keyspace.js +101 -0
  11. package/dist/cjs/cache/keyspace.js.map +1 -0
  12. package/dist/cjs/common/circuit-breaker.js +74 -0
  13. package/dist/cjs/common/circuit-breaker.js.map +1 -0
  14. package/dist/cjs/common/index.js +4 -0
  15. package/dist/cjs/common/index.js.map +1 -1
  16. package/dist/cjs/common/internal-client.js +177 -0
  17. package/dist/cjs/common/internal-client.js.map +1 -0
  18. package/dist/cjs/common/internal-constants.js +12 -0
  19. package/dist/cjs/common/internal-constants.js.map +1 -0
  20. package/dist/cjs/common/internal-service-auth.js +160 -0
  21. package/dist/cjs/common/internal-service-auth.js.map +1 -0
  22. package/dist/cjs/common/money.js +215 -0
  23. package/dist/cjs/common/money.js.map +1 -0
  24. package/dist/cjs/common/phone.js +41 -22
  25. package/dist/cjs/common/phone.js.map +1 -1
  26. package/dist/cjs/common/time.js +28 -2
  27. package/dist/cjs/common/time.js.map +1 -1
  28. package/dist/cjs/config/index.js +139 -19
  29. package/dist/cjs/config/index.js.map +1 -1
  30. package/dist/cjs/contracts/notification-dispatch.js +1 -1
  31. package/dist/cjs/database/db.js +37 -0
  32. package/dist/cjs/database/db.js.map +1 -0
  33. package/dist/cjs/database/index.js +22 -51
  34. package/dist/cjs/database/index.js.map +1 -1
  35. package/dist/cjs/database/pagination.js +88 -0
  36. package/dist/cjs/database/pagination.js.map +1 -0
  37. package/dist/cjs/database/pool.js +60 -0
  38. package/dist/cjs/database/pool.js.map +1 -0
  39. package/dist/cjs/database/scoped-repository.js +132 -0
  40. package/dist/cjs/database/scoped-repository.js.map +1 -0
  41. package/dist/cjs/database/scopes.js +166 -0
  42. package/dist/cjs/database/scopes.js.map +1 -0
  43. package/dist/cjs/database/table-names.js +14 -0
  44. package/dist/cjs/database/table-names.js.map +1 -0
  45. package/dist/cjs/database/tables.js +71 -0
  46. package/dist/cjs/database/tables.js.map +1 -0
  47. package/dist/cjs/express/auth.js +64 -50
  48. package/dist/cjs/express/auth.js.map +1 -1
  49. package/dist/cjs/express/index.js +2 -0
  50. package/dist/cjs/express/index.js.map +1 -1
  51. package/dist/cjs/express/internal-caller.js +52 -0
  52. package/dist/cjs/express/internal-caller.js.map +1 -0
  53. package/dist/cjs/express/step-up.js +41 -0
  54. package/dist/cjs/express/step-up.js.map +1 -0
  55. package/dist/cjs/http/index.js +3 -71
  56. package/dist/cjs/http/index.js.map +1 -1
  57. package/dist/cjs/identity/index.js +269 -67
  58. package/dist/cjs/identity/index.js.map +1 -1
  59. package/dist/cjs/identity/step-up.js +52 -0
  60. package/dist/cjs/identity/step-up.js.map +1 -0
  61. package/dist/cjs/index.js +6 -0
  62. package/dist/cjs/index.js.map +1 -1
  63. package/dist/cjs/knex/columns.js +36 -0
  64. package/dist/cjs/knex/columns.js.map +1 -0
  65. package/dist/cjs/knex/index.js +26 -43
  66. package/dist/cjs/knex/index.js.map +1 -1
  67. package/dist/cjs/knex/migration-helpers.js +57 -0
  68. package/dist/cjs/knex/migration-helpers.js.map +1 -0
  69. package/dist/cjs/logging/redact.js +6 -11
  70. package/dist/cjs/logging/redact.js.map +1 -1
  71. package/dist/cjs/messaging/base-consumer.js +36 -0
  72. package/dist/cjs/messaging/base-consumer.js.map +1 -0
  73. package/dist/cjs/messaging/consumer.js +142 -73
  74. package/dist/cjs/messaging/consumer.js.map +1 -1
  75. package/dist/cjs/messaging/envelope.js +53 -10
  76. package/dist/cjs/messaging/envelope.js.map +1 -1
  77. package/dist/cjs/messaging/idempotency.js +95 -8
  78. package/dist/cjs/messaging/idempotency.js.map +1 -1
  79. package/dist/cjs/messaging/index.js +2 -1
  80. package/dist/cjs/messaging/index.js.map +1 -1
  81. package/dist/cjs/messaging/outbox.js +97 -55
  82. package/dist/cjs/messaging/outbox.js.map +1 -1
  83. package/dist/cjs/messaging/publisher.js +86 -38
  84. package/dist/cjs/messaging/publisher.js.map +1 -1
  85. package/dist/cjs/messaging/staff-notifier.js +4 -4
  86. package/dist/cjs/messaging/staff-notifier.js.map +1 -1
  87. package/dist/cjs/messaging/topology.js +41 -5
  88. package/dist/cjs/messaging/topology.js.map +1 -1
  89. package/dist/cjs/rate-limit/index.js +57 -6
  90. package/dist/cjs/rate-limit/index.js.map +1 -1
  91. package/dist/cjs/rate-limit/keys.js +47 -0
  92. package/dist/cjs/rate-limit/keys.js.map +1 -0
  93. package/dist/cjs/socket/index.js +127 -16
  94. package/dist/cjs/socket/index.js.map +1 -1
  95. package/dist/cjs/socket/ticket.js +124 -0
  96. package/dist/cjs/socket/ticket.js.map +1 -0
  97. package/dist/cjs/testing/index.js +23 -14
  98. package/dist/cjs/testing/index.js.map +1 -1
  99. package/dist/esm/access/index.js +9 -1
  100. package/dist/esm/access/index.js.map +1 -1
  101. package/dist/esm/approvals/index.js +127 -0
  102. package/dist/esm/approvals/index.js.map +1 -0
  103. package/dist/esm/audit/index.js +70 -0
  104. package/dist/esm/audit/index.js.map +1 -0
  105. package/dist/esm/cache/index.js +70 -13
  106. package/dist/esm/cache/index.js.map +1 -1
  107. package/dist/esm/cache/keyspace.js +95 -0
  108. package/dist/esm/cache/keyspace.js.map +1 -0
  109. package/dist/esm/common/circuit-breaker.js +70 -0
  110. package/dist/esm/common/circuit-breaker.js.map +1 -0
  111. package/dist/esm/common/index.js +4 -0
  112. package/dist/esm/common/index.js.map +1 -1
  113. package/dist/esm/common/internal-client.js +173 -0
  114. package/dist/esm/common/internal-client.js.map +1 -0
  115. package/dist/esm/common/internal-constants.js +9 -0
  116. package/dist/esm/common/internal-constants.js.map +1 -0
  117. package/dist/esm/common/internal-service-auth.js +150 -0
  118. package/dist/esm/common/internal-service-auth.js.map +1 -0
  119. package/dist/esm/common/money.js +207 -0
  120. package/dist/esm/common/money.js.map +1 -0
  121. package/dist/esm/common/phone.js +41 -22
  122. package/dist/esm/common/phone.js.map +1 -1
  123. package/dist/esm/common/time.js +27 -2
  124. package/dist/esm/common/time.js.map +1 -1
  125. package/dist/esm/config/index.js +135 -18
  126. package/dist/esm/config/index.js.map +1 -1
  127. package/dist/esm/contracts/notification-dispatch.js +1 -1
  128. package/dist/esm/database/db.js +32 -0
  129. package/dist/esm/database/db.js.map +1 -0
  130. package/dist/esm/database/index.js +9 -45
  131. package/dist/esm/database/index.js.map +1 -1
  132. package/dist/esm/database/pagination.js +81 -0
  133. package/dist/esm/database/pagination.js.map +1 -0
  134. package/dist/esm/database/pool.js +51 -0
  135. package/dist/esm/database/pool.js.map +1 -0
  136. package/dist/esm/database/scoped-repository.js +128 -0
  137. package/dist/esm/database/scoped-repository.js.map +1 -0
  138. package/dist/esm/database/scopes.js +157 -0
  139. package/dist/esm/database/scopes.js.map +1 -0
  140. package/dist/esm/database/table-names.js +11 -0
  141. package/dist/esm/database/table-names.js.map +1 -0
  142. package/dist/esm/database/tables.js +61 -0
  143. package/dist/esm/database/tables.js.map +1 -0
  144. package/dist/esm/express/auth.js +62 -48
  145. package/dist/esm/express/auth.js.map +1 -1
  146. package/dist/esm/express/index.js +2 -0
  147. package/dist/esm/express/index.js.map +1 -1
  148. package/dist/esm/express/internal-caller.js +49 -0
  149. package/dist/esm/express/internal-caller.js.map +1 -0
  150. package/dist/esm/express/step-up.js +38 -0
  151. package/dist/esm/express/step-up.js.map +1 -0
  152. package/dist/esm/http/index.js +3 -70
  153. package/dist/esm/http/index.js.map +1 -1
  154. package/dist/esm/identity/index.js +246 -64
  155. package/dist/esm/identity/index.js.map +1 -1
  156. package/dist/esm/identity/step-up.js +47 -0
  157. package/dist/esm/identity/step-up.js.map +1 -0
  158. package/dist/esm/index.js +6 -0
  159. package/dist/esm/index.js.map +1 -1
  160. package/dist/esm/knex/columns.js +28 -0
  161. package/dist/esm/knex/columns.js.map +1 -0
  162. package/dist/esm/knex/index.js +8 -34
  163. package/dist/esm/knex/index.js.map +1 -1
  164. package/dist/esm/knex/migration-helpers.js +51 -0
  165. package/dist/esm/knex/migration-helpers.js.map +1 -0
  166. package/dist/esm/logging/redact.js +3 -7
  167. package/dist/esm/logging/redact.js.map +1 -1
  168. package/dist/esm/messaging/base-consumer.js +32 -0
  169. package/dist/esm/messaging/base-consumer.js.map +1 -0
  170. package/dist/esm/messaging/consumer.js +139 -71
  171. package/dist/esm/messaging/consumer.js.map +1 -1
  172. package/dist/esm/messaging/envelope.js +50 -10
  173. package/dist/esm/messaging/envelope.js.map +1 -1
  174. package/dist/esm/messaging/idempotency.js +91 -7
  175. package/dist/esm/messaging/idempotency.js.map +1 -1
  176. package/dist/esm/messaging/index.js +2 -1
  177. package/dist/esm/messaging/index.js.map +1 -1
  178. package/dist/esm/messaging/outbox.js +97 -54
  179. package/dist/esm/messaging/outbox.js.map +1 -1
  180. package/dist/esm/messaging/publisher.js +86 -38
  181. package/dist/esm/messaging/publisher.js.map +1 -1
  182. package/dist/esm/messaging/staff-notifier.js +4 -4
  183. package/dist/esm/messaging/staff-notifier.js.map +1 -1
  184. package/dist/esm/messaging/topology.js +39 -4
  185. package/dist/esm/messaging/topology.js.map +1 -1
  186. package/dist/esm/rate-limit/index.js +41 -6
  187. package/dist/esm/rate-limit/index.js.map +1 -1
  188. package/dist/esm/rate-limit/keys.js +43 -0
  189. package/dist/esm/rate-limit/keys.js.map +1 -0
  190. package/dist/esm/socket/index.js +112 -17
  191. package/dist/esm/socket/index.js.map +1 -1
  192. package/dist/esm/socket/ticket.js +117 -0
  193. package/dist/esm/socket/ticket.js.map +1 -0
  194. package/dist/esm/testing/index.js +24 -15
  195. package/dist/esm/testing/index.js.map +1 -1
  196. package/dist/types/access/index.d.ts +9 -3
  197. package/dist/types/access/index.d.ts.map +1 -1
  198. package/dist/types/approvals/index.d.ts +99 -0
  199. package/dist/types/approvals/index.d.ts.map +1 -0
  200. package/dist/types/audit/index.d.ts +82 -0
  201. package/dist/types/audit/index.d.ts.map +1 -0
  202. package/dist/types/cache/index.d.ts +64 -14
  203. package/dist/types/cache/index.d.ts.map +1 -1
  204. package/dist/types/cache/keyspace.d.ts +84 -0
  205. package/dist/types/cache/keyspace.d.ts.map +1 -0
  206. package/dist/types/common/circuit-breaker.d.ts +51 -0
  207. package/dist/types/common/circuit-breaker.d.ts.map +1 -0
  208. package/dist/types/common/index.d.ts +4 -0
  209. package/dist/types/common/index.d.ts.map +1 -1
  210. package/dist/types/common/internal-client.d.ts +116 -0
  211. package/dist/types/common/internal-client.d.ts.map +1 -0
  212. package/dist/types/common/internal-constants.d.ts +9 -0
  213. package/dist/types/common/internal-constants.d.ts.map +1 -0
  214. package/dist/types/common/internal-service-auth.d.ts +102 -0
  215. package/dist/types/common/internal-service-auth.d.ts.map +1 -0
  216. package/dist/types/common/money.d.ts +104 -0
  217. package/dist/types/common/money.d.ts.map +1 -0
  218. package/dist/types/common/phone.d.ts +25 -15
  219. package/dist/types/common/phone.d.ts.map +1 -1
  220. package/dist/types/common/time.d.ts +20 -2
  221. package/dist/types/common/time.d.ts.map +1 -1
  222. package/dist/types/config/index.d.ts +251 -27
  223. package/dist/types/config/index.d.ts.map +1 -1
  224. package/dist/types/contracts/notification-dispatch.d.ts +13 -13
  225. package/dist/types/database/db.d.ts +46 -0
  226. package/dist/types/database/db.d.ts.map +1 -0
  227. package/dist/types/database/index.d.ts +9 -22
  228. package/dist/types/database/index.d.ts.map +1 -1
  229. package/dist/types/database/pagination.d.ts +68 -0
  230. package/dist/types/database/pagination.d.ts.map +1 -0
  231. package/dist/types/database/pool.d.ts +39 -0
  232. package/dist/types/database/pool.d.ts.map +1 -0
  233. package/dist/types/database/scoped-repository.d.ts +92 -0
  234. package/dist/types/database/scoped-repository.d.ts.map +1 -0
  235. package/dist/types/database/scopes.d.ts +159 -0
  236. package/dist/types/database/scopes.d.ts.map +1 -0
  237. package/dist/types/database/table-names.d.ts +13 -0
  238. package/dist/types/database/table-names.d.ts.map +1 -0
  239. package/dist/types/database/tables.d.ts +748 -0
  240. package/dist/types/database/tables.d.ts.map +1 -0
  241. package/dist/types/express/auth.d.ts +40 -24
  242. package/dist/types/express/auth.d.ts.map +1 -1
  243. package/dist/types/express/index.d.ts +2 -0
  244. package/dist/types/express/index.d.ts.map +1 -1
  245. package/dist/types/express/internal-caller.d.ts +53 -0
  246. package/dist/types/express/internal-caller.d.ts.map +1 -0
  247. package/dist/types/express/step-up.d.ts +38 -0
  248. package/dist/types/express/step-up.d.ts.map +1 -0
  249. package/dist/types/http/index.d.ts +3 -31
  250. package/dist/types/http/index.d.ts.map +1 -1
  251. package/dist/types/identity/index.d.ts +159 -21
  252. package/dist/types/identity/index.d.ts.map +1 -1
  253. package/dist/types/identity/step-up.d.ts +41 -0
  254. package/dist/types/identity/step-up.d.ts.map +1 -0
  255. package/dist/types/index.d.ts +6 -0
  256. package/dist/types/index.d.ts.map +1 -1
  257. package/dist/types/knex/columns.d.ts +15 -0
  258. package/dist/types/knex/columns.d.ts.map +1 -0
  259. package/dist/types/knex/index.d.ts +3 -11
  260. package/dist/types/knex/index.d.ts.map +1 -1
  261. package/dist/types/knex/migration-helpers.d.ts +28 -0
  262. package/dist/types/knex/migration-helpers.d.ts.map +1 -0
  263. package/dist/types/logging/redact.d.ts +2 -2
  264. package/dist/types/logging/redact.d.ts.map +1 -1
  265. package/dist/types/messaging/base-consumer.d.ts +33 -0
  266. package/dist/types/messaging/base-consumer.d.ts.map +1 -0
  267. package/dist/types/messaging/consumer.d.ts +59 -21
  268. package/dist/types/messaging/consumer.d.ts.map +1 -1
  269. package/dist/types/messaging/envelope.d.ts +50 -11
  270. package/dist/types/messaging/envelope.d.ts.map +1 -1
  271. package/dist/types/messaging/idempotency.d.ts +60 -3
  272. package/dist/types/messaging/idempotency.d.ts.map +1 -1
  273. package/dist/types/messaging/index.d.ts +7 -0
  274. package/dist/types/messaging/index.d.ts.map +1 -1
  275. package/dist/types/messaging/outbox.d.ts +62 -22
  276. package/dist/types/messaging/outbox.d.ts.map +1 -1
  277. package/dist/types/messaging/publisher.d.ts +54 -21
  278. package/dist/types/messaging/publisher.d.ts.map +1 -1
  279. package/dist/types/messaging/staff-notifier.d.ts +9 -9
  280. package/dist/types/messaging/staff-notifier.d.ts.map +1 -1
  281. package/dist/types/messaging/staff-recipients.d.ts +1 -1
  282. package/dist/types/messaging/topology.d.ts +40 -8
  283. package/dist/types/messaging/topology.d.ts.map +1 -1
  284. package/dist/types/rate-limit/index.d.ts +40 -4
  285. package/dist/types/rate-limit/index.d.ts.map +1 -1
  286. package/dist/types/rate-limit/keys.d.ts +52 -0
  287. package/dist/types/rate-limit/keys.d.ts.map +1 -0
  288. package/dist/types/socket/index.d.ts +86 -2
  289. package/dist/types/socket/index.d.ts.map +1 -1
  290. package/dist/types/socket/ticket.d.ts +90 -0
  291. package/dist/types/socket/ticket.d.ts.map +1 -0
  292. package/dist/types/testing/index.d.ts +31 -10
  293. package/dist/types/testing/index.d.ts.map +1 -1
  294. package/dist/types/types/index.d.ts +26 -14
  295. package/dist/types/types/index.d.ts.map +1 -1
  296. package/package.json +3 -1
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/knex/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAEjC;;;GAGG;AAEH,2CAA2C;AAC3C,wBAAgB,WAAW,CAAC,KAAK,EAAE,IAAI,CAAC,kBAAkB,EAAE,IAAI,SAAO,GAAG,IAAI,CAE7E;AAED,iGAAiG;AACjG,wBAAgB,UAAU,CAAC,KAAK,EAAE,IAAI,CAAC,kBAAkB,GAAG,IAAI,CAAC,iBAAiB,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC,aAAa,CAEpH;AAED,gFAAgF;AAChF,wBAAgB,UAAU,CAAC,KAAK,EAAE,IAAI,CAAC,kBAAkB,EAAE,IAAI,EAAE,IAAI,GAAG,IAAI,CAG3E;AAED,8EAA8E;AAC9E,wBAAgB,YAAY,CAAC,KAAK,EAAE,IAAI,CAAC,kBAAkB,GAAG,IAAI,CAGjE;AAED,qCAAqC;AACrC,wBAAgB,QAAQ,CAAC,KAAK,EAAE,IAAI,CAAC,kBAAkB,GAAG,IAAI,CAAC,iBAAiB,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC,aAAa,CAElH;AAED;;;GAGG;AACH,wBAAsB,gBAAgB,CAAC,IAAI,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,KAAK,EAAE,IAAI,CAAC,kBAAkB,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAYxI;AAED,wDAAwD;AACxD,wBAAsB,iBAAiB,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,SAAiB,GAAG,OAAO,CAAC,IAAI,CAAC,CAYxF;AAED,0EAA0E;AAC1E,wBAAgB,OAAO,CAAC,KAAK,EAAE,IAAI,CAAC,kBAAkB,GAAG,IAAI,CAI5D"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/knex/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAKjC,cAAc,cAAc,CAAC;AAC7B,cAAc,wBAAwB,CAAC;AAEvC;;;GAGG;AAEH;;;GAGG;AACH,wBAAsB,gBAAgB,CAAC,IAAI,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,KAAK,EAAE,IAAI,CAAC,kBAAkB,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAYxI;AAED,6FAA6F;AAC7F,wBAAsB,iBAAiB,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,SAAuB,GAAG,OAAO,CAAC,IAAI,CAAC,CAG9F;AAED,0EAA0E;AAC1E,wBAAgB,OAAO,CAAC,KAAK,EAAE,IAAI,CAAC,kBAAkB,GAAG,IAAI,CAI5D"}
@@ -0,0 +1,28 @@
1
+ import type { Knex } from 'knex';
2
+ /**
3
+ * Column sets for infrastructure tables, for `.cjs` migrations. Each matches the Drizzle table of
4
+ * the same purpose in `@heavybit/shared-lib/database` (`outboxTable`, `processedEventsTable`,
5
+ * `schedulerRunsTable`); a test keeps the two in step.
6
+ *
7
+ * @example
8
+ * await knex.schema.createTable('processed_events', (t) => processedEventsColumns(t, knex));
9
+ */
10
+ type Builder = Knex.CreateTableBuilder;
11
+ /**
12
+ * Outbox columns: id, routing_key, exchange, payload, correlation_id, attempts, last_error, created_at,
13
+ * published_at (+ relay index).
14
+ */
15
+ export declare function outboxColumns(t: Builder, knex: Knex): void;
16
+ /** Consumer idempotency columns: id, consumer, event_id (unique together), processed_at. */
17
+ export declare function processedEventsColumns(t: Builder, knex: Knex): void;
18
+ /** Scheduler run columns: id, job, run_key (unique together), status, error, started_at, finished_at. */
19
+ export declare function schedulerRunsColumns(t: Builder, knex: Knex): void;
20
+ /**
21
+ * Index that always leads with the scope column (TEN-4); `columns` follow in order, duplicates of
22
+ * the scope column are dropped.
23
+ *
24
+ * @example scopedIndex(t, 'workspace_id', ['status', 'created_at'])
25
+ */
26
+ export declare function scopedIndex(t: Builder | Knex.AlterTableBuilder, scopeColumn: string, columns?: string[], indexName?: string): void;
27
+ export {};
28
+ //# sourceMappingURL=migration-helpers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"migration-helpers.d.ts","sourceRoot":"","sources":["../../../src/knex/migration-helpers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAIjC;;;;;;;GAOG;AAEH,KAAK,OAAO,GAAG,IAAI,CAAC,kBAAkB,CAAC;AAGvC;;;GAGG;AACH,wBAAgB,aAAa,CAAC,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,GAAG,IAAI,CAW1D;AAED,4FAA4F;AAC5F,wBAAgB,sBAAsB,CAAC,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,GAAG,IAAI,CAOnE;AAED,yGAAyG;AACzG,wBAAgB,oBAAoB,CAAC,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,GAAG,IAAI,CAUjE;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC,iBAAiB,EAAE,WAAW,EAAE,MAAM,EAAE,OAAO,GAAE,MAAM,EAAO,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAGtI"}
@@ -11,13 +11,13 @@ export declare const DEFAULT_ALLOW_KEYS: readonly string[];
11
11
  /** The marker that replaces a secret value. */
12
12
  export declare const REDACTED = "[REDACTED]";
13
13
  /**
14
- * Masks every digit except the last four (log rule; the UI uses `maskPhone` from common).
14
+ * Masks every digit except the last four; the same rule as `maskPhone` in common (one rule for logs and UI).
15
15
  *
16
16
  * @param value A phone number in any format.
17
17
  * @returns e.g. `********5678`; values with fewer than 5 digits are fully masked.
18
18
  * @example maskPhoneForLog('15551234567') // '*******4567'
19
19
  */
20
- export declare function maskPhoneForLog(value: string): string;
20
+ export declare const maskPhoneForLog: (value: string) => string;
21
21
  /**
22
22
  * Masks phone-number-looking runs inside free text.
23
23
  *
@@ -1 +1 @@
1
- {"version":3,"file":"redact.d.ts","sourceRoot":"","sources":["../../../src/logging/redact.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,qGAAqG;AACrG,eAAO,MAAM,mBAAmB,EAAE,SAAS,MAAM,EAGhD,CAAC;AAEF,4GAA4G;AAC5G,eAAO,MAAM,kBAAkB,EAAE,SAAS,MAAM,EAAwF,CAAC;AAQzI,+CAA+C;AAC/C,eAAO,MAAM,QAAQ,eAAe,CAAC;AAGrC;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAIrD;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAErD;AAWD,gFAAgF;AAChF,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;IAC/B,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CAC/B;AAED;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAAC,SAAS,GAAE,SAAS,MAAM,EAAO,EAAE,SAAS,GAAE,SAAS,MAAM,EAAuB,GAAG,aAAa,CAgBvI;AAED;;;;;;;;;GASG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,EAAE,UAAU,EAAE,aAAa,EAAE,KAAK,SAAI,GAAG,OAAO,CAYzF"}
1
+ {"version":3,"file":"redact.d.ts","sourceRoot":"","sources":["../../../src/logging/redact.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,qGAAqG;AACrG,eAAO,MAAM,mBAAmB,EAAE,SAAS,MAAM,EAGhD,CAAC;AAEF,4GAA4G;AAC5G,eAAO,MAAM,kBAAkB,EAAE,SAAS,MAAM,EAAwF,CAAC;AAQzI,+CAA+C;AAC/C,eAAO,MAAM,QAAQ,eAAe,CAAC;AAGrC;;;;;;GAMG;AACH,eAAO,MAAM,eAAe,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAkB,CAAC;AAEpE;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAErD;AAWD,gFAAgF;AAChF,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;IAC/B,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CAC/B;AAED;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAAC,SAAS,GAAE,SAAS,MAAM,EAAO,EAAE,SAAS,GAAE,SAAS,MAAM,EAAuB,GAAG,aAAa,CAgBvI;AAED;;;;;;;;;GASG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,EAAE,UAAU,EAAE,aAAa,EAAE,KAAK,SAAI,GAAG,OAAO,CAYzF"}
@@ -0,0 +1,33 @@
1
+ import type { InboundMessage, MessageConsumer, SubscriptionOptions } from './consumer.js';
2
+ /** Everything a subscription needs except the handler, which a {@link BaseConsumer} subclass supplies. */
3
+ export type ConsumerDefinition<T, TTx> = Omit<SubscriptionOptions<T, TTx>, 'handler'>;
4
+ /**
5
+ * Base for a service's queue consumers (one class per queue in `src/consumers/`). The subclass declares
6
+ * its queue, bindings, payload schema, retry policy, dedupe and scope in the constructor and implements
7
+ * {@link BaseConsumer.handle}; `worker.ts` registers it. Validation, dedupe, retries, dead-lettering and
8
+ * context restore are done by {@link MessageConsumer}.
9
+ *
10
+ * @typeParam T Validated payload type.
11
+ * @typeParam TTx Dedupe transaction type (`DbExecutor` with `ProcessedEventsDeduper`).
12
+ * @example
13
+ * class ThingCreatedConsumer extends BaseConsumer<ThingCreated, DbExecutor> {
14
+ * constructor(private readonly things: ThingService, dedupe: ProcessedEventsDeduper) {
15
+ * super({ queue: 'thing-created', bindings: [{ exchange: 'events', routingKey: 'thing.created' }],
16
+ * schema: thingCreatedSchema, dedupe, scopeFrom: (d) => ({ tenantId: d.tenantId }) });
17
+ * }
18
+ * protected handle({ data, tx, scope }: InboundMessage<ThingCreated, DbExecutor>) { return this.things.onCreated(scope!, data, tx!); }
19
+ * }
20
+ */
21
+ export declare abstract class BaseConsumer<T, TTx = undefined> {
22
+ protected readonly definition: ConsumerDefinition<T, TTx>;
23
+ protected constructor(definition: ConsumerDefinition<T, TTx>);
24
+ /** Handles one validated, de-duplicated message. Throw to retry (or a non-retryable error to dead-letter). */
25
+ protected abstract handle(message: InboundMessage<T, TTx>): Promise<void>;
26
+ /**
27
+ * Subscribes this consumer.
28
+ *
29
+ * @param consumer The service's {@link MessageConsumer}.
30
+ */
31
+ register(consumer: MessageConsumer): Promise<void>;
32
+ }
33
+ //# sourceMappingURL=base-consumer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"base-consumer.d.ts","sourceRoot":"","sources":["../../../src/messaging/base-consumer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AAE1F,0GAA0G;AAC1G,MAAM,MAAM,kBAAkB,CAAC,CAAC,EAAE,GAAG,IAAI,IAAI,CAAC,mBAAmB,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,SAAS,CAAC,CAAC;AAEtF;;;;;;;;;;;;;;;;GAgBG;AACH,8BAAsB,YAAY,CAAC,CAAC,EAAE,GAAG,GAAG,SAAS;IAC7B,SAAS,CAAC,QAAQ,CAAC,UAAU,EAAE,kBAAkB,CAAC,CAAC,EAAE,GAAG,CAAC;IAA/E,SAAS,aAAgC,UAAU,EAAE,kBAAkB,CAAC,CAAC,EAAE,GAAG,CAAC;IAE/E,8GAA8G;IAC9G,SAAS,CAAC,QAAQ,CAAC,MAAM,CAAC,OAAO,EAAE,cAAc,CAAC,CAAC,EAAE,GAAG,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAEzE;;;;OAIG;IACH,QAAQ,CAAC,QAAQ,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC;CAGnD"}
@@ -1,55 +1,93 @@
1
1
  import type { ConsumeMessage } from 'amqplib';
2
- import type { ZodTypeAny } from 'zod';
2
+ import type { ZodType, ZodTypeDef } from 'zod';
3
3
  import type { EventEnvelope, Logger } from '../types/index.js';
4
4
  import type { AmqpConnection } from './connection.js';
5
- import type { IdempotencyStore } from './idempotency.js';
5
+ import type { EventDeduper } from './idempotency.js';
6
6
  import { type Topology } from './topology.js';
7
+ /** One exchange binding of a subscription queue. */
7
8
  export interface Binding {
8
- /** `events` (topic, pattern keys like `auth.user.*`) or `commands` (exact command name). */
9
+ /** `events` (topic, pattern keys like `thing.*`) or `commands` (exact command name). */
9
10
  exchange: 'events' | 'commands';
10
11
  routingKey: string;
11
12
  }
12
- export interface InboundMessage<T = unknown> {
13
- /** Envelope `data`, validated against `schema` when one is given. */
13
+ /** Scope values restored from a message, e.g. `{ tenantId: '01J…' }`; the app names the keys. */
14
+ export type MessageScope = Readonly<Record<string, string>>;
15
+ /** What a consumer handler receives for one delivery. */
16
+ export interface InboundMessage<T = unknown, TTx = undefined> {
17
+ /** Payload (envelope `data` without the reserved fields), validated against `schema` when given. */
14
18
  data: T;
15
19
  envelope: EventEnvelope | null;
20
+ /** Envelope event id, else the AMQP `messageId`. */
21
+ eventId: string | undefined;
16
22
  routingKey: string;
17
- /** How many times this message has been retried before (0 on first delivery). */
23
+ /** Delivery attempt, 1-based. */
18
24
  attempt: number;
25
+ /** The dedupe ledger's open transaction (write through it so effects and ledger commit together). */
26
+ tx: TTx | undefined;
27
+ /** Scope restored by `scopeFrom`; also set as the request context's `scopes`. */
28
+ scope: MessageScope | undefined;
19
29
  raw: ConsumeMessage;
20
30
  }
21
- export interface SubscriptionOptions<T = unknown> {
22
- /** Logical queue name; the environment prefix is added. Convention: `<service>:<source>.<topic>`. */
31
+ /** One durable subscription: queue purpose, bindings, handler and delivery policy. */
32
+ export interface SubscriptionOptions<T = unknown, TTx = undefined> {
33
+ /** Queue purpose; the physical queue is `{prefix}.{service}.{purpose}`. */
23
34
  queue: string;
24
35
  bindings: Binding[];
25
- handler: (message: InboundMessage<T>) => Promise<void>;
26
- /** Validates `data`; failures dead-letter immediately (a bad payload never succeeds on retry). */
27
- schema?: ZodTypeAny;
36
+ handler: (message: InboundMessage<T, TTx>) => Promise<void>;
37
+ /** Validates the payload; failures dead-letter immediately (a bad payload never succeeds on retry). */
38
+ schema?: ZodType<T, ZodTypeDef, unknown>;
28
39
  prefetch?: number;
29
- /** Delay before each retry. Length = max retries. Default: 1s, 5s, 25s. */
30
- retryDelaysMs?: number[];
31
- /** Enables duplicate suppression keyed on `<queue>:<eventId>`. */
32
- idempotency?: IdempotencyStore;
33
- idempotencyTtlSec?: number;
40
+ /** Total deliveries before dead-lettering (default 5). Retry n waits `retryBaseDelayMs × 2^n`. */
41
+ maxAttempts?: number;
42
+ /** Base of the exponential retry delay (default 1000 ms → 2 s, 4 s, 8 s, 16 s). */
43
+ retryBaseDelayMs?: number;
44
+ /** Duplicate suppression keyed on `({service}.{purpose}, eventId)`; a message without an id dead-letters. */
45
+ dedupe?: EventDeduper<TTx>;
46
+ /**
47
+ * Re-establishes the scope (tenant) the message belongs to from its payload (TEN-7). Every returned
48
+ * value must be a non-empty string, or the message dead-letters.
49
+ *
50
+ * @example scopeFrom: (d) => ({ tenantId: d.tenantId })
51
+ */
52
+ scopeFrom?: (data: T) => Record<string, string | null | undefined>;
34
53
  }
54
+ /** Total deliveries before a failing message is dead-lettered. */
55
+ export declare const DEFAULT_MAX_ATTEMPTS = 5;
56
+ /** Base of the exponential retry delay (retry n waits base × 2^n). */
57
+ export declare const DEFAULT_RETRY_BASE_DELAY_MS = 1000;
35
58
  /**
36
- * Durable subscriptions with bounded retries and dead-lettering:
59
+ * Retry delays for `maxAttempts` deliveries: `base × 2^n` for n = 1 … maxAttempts − 1.
60
+ *
61
+ * @example retryDelays(5, 1000) // [2000, 4000, 8000, 16000]
62
+ */
63
+ export declare function retryDelays(maxAttempts?: number, baseMs?: number): number[];
64
+ /**
65
+ * Durable subscriptions with manual ack, bounded exponential retries and dead-lettering:
37
66
  *
38
67
  * <queue> ──fail──▶ <queue>.retry.<delay> (TTL, dead-letters back into <queue>)
39
- * ──fail after last retry / non-retryable──▶ <queue>.dlq
68
+ * ──last attempt / non-retryable / invalid──▶ service DLX ──▶ <queue>.dlq
40
69
  *
41
- * One fixed TTL per retry queue avoids head-of-line blocking of per-message TTLs.
70
+ * One fixed TTL per retry queue avoids head-of-line blocking of per-message TTLs. The main queue also
71
+ * names the service DLX, so broker-side rejects land in the same DLQ. Each message runs inside a request
72
+ * context carrying its correlation id, actor and restored scope.
42
73
  */
43
74
  export declare class MessageConsumer {
44
75
  private readonly connection;
45
76
  private readonly topology;
77
+ private readonly service;
46
78
  private readonly handles;
47
79
  private readonly logger;
48
- constructor(connection: AmqpConnection, topology: Topology, logger?: Logger);
49
80
  /** Physical DLQ names for every subscription (for monitoring). */
50
81
  readonly deadLetterQueues: string[];
51
- subscribe<T = unknown>(options: SubscriptionOptions<T>): Promise<void>;
82
+ constructor(connection: AmqpConnection, topology: Topology, service: string, logger?: Logger);
83
+ subscribe<T = unknown, TTx = undefined>(options: SubscriptionOptions<T, TTx>): Promise<void>;
84
+ private declare;
52
85
  private dispatch;
86
+ /** Parses, validates and extracts scope; throws `Rejected` for messages that can never succeed. */
87
+ private prepare;
88
+ private scopeOf;
89
+ private correlationIdOf;
90
+ private handle;
53
91
  private retryOrDeadLetter;
54
92
  private deadLetter;
55
93
  }
@@ -1 +1 @@
1
- {"version":3,"file":"consumer.d.ts","sourceRoot":"","sources":["../../../src/messaging/consumer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAW,cAAc,EAAE,MAAM,SAAS,CAAC;AACvD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,KAAK,CAAC;AAGtC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAC/D,OAAO,KAAK,EAAE,cAAc,EAAiB,MAAM,iBAAiB,CAAC;AAErE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACzD,OAAO,EAAuC,KAAK,QAAQ,EAAE,MAAM,eAAe,CAAC;AAEnF,MAAM,WAAW,OAAO;IACtB,4FAA4F;IAC5F,QAAQ,EAAE,QAAQ,GAAG,UAAU,CAAC;IAChC,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,cAAc,CAAC,CAAC,GAAG,OAAO;IACzC,qEAAqE;IACrE,IAAI,EAAE,CAAC,CAAC;IACR,QAAQ,EAAE,aAAa,GAAG,IAAI,CAAC;IAC/B,UAAU,EAAE,MAAM,CAAC;IACnB,iFAAiF;IACjF,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,EAAE,cAAc,CAAC;CACrB;AAED,MAAM,WAAW,mBAAmB,CAAC,CAAC,GAAG,OAAO;IAC9C,qGAAqG;IACrG,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,OAAO,EAAE,CAAC,OAAO,EAAE,cAAc,CAAC,CAAC,CAAC,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACvD,kGAAkG;IAClG,MAAM,CAAC,EAAE,UAAU,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,2EAA2E;IAC3E,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;IACzB,kEAAkE;IAClE,WAAW,CAAC,EAAE,gBAAgB,CAAC;IAC/B,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B;AAKD;;;;;;;GAOG;AACH,qBAAa,eAAe;IAId,OAAO,CAAC,QAAQ,CAAC,UAAU;IAAkB,OAAO,CAAC,QAAQ,CAAC,QAAQ;IAHlF,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAuB;IAC/C,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;gBAEH,UAAU,EAAE,cAAc,EAAmB,QAAQ,EAAE,QAAQ,EAAE,MAAM,CAAC,EAAE,MAAM;IAI7G,kEAAkE;IAClE,QAAQ,CAAC,gBAAgB,EAAE,MAAM,EAAE,CAAM;IAEnC,SAAS,CAAC,CAAC,GAAG,OAAO,EAAE,OAAO,EAAE,mBAAmB,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;YAiC9D,QAAQ;IAgDtB,OAAO,CAAC,iBAAiB;IAazB,OAAO,CAAC,UAAU;CASnB"}
1
+ {"version":3,"file":"consumer.d.ts","sourceRoot":"","sources":["../../../src/messaging/consumer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAW,cAAc,EAAE,MAAM,SAAS,CAAC;AACvD,OAAO,KAAK,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,KAAK,CAAC;AAK/C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAC/D,OAAO,KAAK,EAAE,cAAc,EAAiB,MAAM,iBAAiB,CAAC;AAErE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,EAAuC,KAAK,QAAQ,EAAE,MAAM,eAAe,CAAC;AAEnF,oDAAoD;AACpD,MAAM,WAAW,OAAO;IACtB,wFAAwF;IACxF,QAAQ,EAAE,QAAQ,GAAG,UAAU,CAAC;IAChC,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,iGAAiG;AACjG,MAAM,MAAM,YAAY,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;AAE5D,yDAAyD;AACzD,MAAM,WAAW,cAAc,CAAC,CAAC,GAAG,OAAO,EAAE,GAAG,GAAG,SAAS;IAC1D,oGAAoG;IACpG,IAAI,EAAE,CAAC,CAAC;IACR,QAAQ,EAAE,aAAa,GAAG,IAAI,CAAC;IAC/B,oDAAoD;IACpD,OAAO,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5B,UAAU,EAAE,MAAM,CAAC;IACnB,iCAAiC;IACjC,OAAO,EAAE,MAAM,CAAC;IAChB,qGAAqG;IACrG,EAAE,EAAE,GAAG,GAAG,SAAS,CAAC;IACpB,iFAAiF;IACjF,KAAK,EAAE,YAAY,GAAG,SAAS,CAAC;IAChC,GAAG,EAAE,cAAc,CAAC;CACrB;AAED,sFAAsF;AACtF,MAAM,WAAW,mBAAmB,CAAC,CAAC,GAAG,OAAO,EAAE,GAAG,GAAG,SAAS;IAC/D,2EAA2E;IAC3E,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,OAAO,EAAE,CAAC,OAAO,EAAE,cAAc,CAAC,CAAC,EAAE,GAAG,CAAC,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5D,uGAAuG;IACvG,MAAM,CAAC,EAAE,OAAO,CAAC,CAAC,EAAE,UAAU,EAAE,OAAO,CAAC,CAAC;IACzC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,kGAAkG;IAClG,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,mFAAmF;IACnF,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,6GAA6G;IAC7G,MAAM,CAAC,EAAE,YAAY,CAAC,GAAG,CAAC,CAAC;IAC3B;;;;;OAKG;IACH,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC,CAAC;CACpE;AAGD,kEAAkE;AAClE,eAAO,MAAM,oBAAoB,IAAI,CAAC;AACtC,sEAAsE;AACtE,eAAO,MAAM,2BAA2B,OAAO,CAAC;AAEhD;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,WAAW,SAAuB,EAAE,MAAM,SAA8B,GAAG,MAAM,EAAE,CAG9G;AAsBD;;;;;;;;;GASG;AACH,qBAAa,eAAe;IAQxB,OAAO,CAAC,QAAQ,CAAC,UAAU;IAC3B,OAAO,CAAC,QAAQ,CAAC,QAAQ;IACzB,OAAO,CAAC,QAAQ,CAAC,OAAO;IAT1B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAuB;IAC/C,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAEhC,kEAAkE;IAClE,QAAQ,CAAC,gBAAgB,EAAE,MAAM,EAAE,CAAM;gBAGtB,UAAU,EAAE,cAAc,EAC1B,QAAQ,EAAE,QAAQ,EAClB,OAAO,EAAE,MAAM,EAChC,MAAM,CAAC,EAAE,MAAM;IAKX,SAAS,CAAC,CAAC,GAAG,OAAO,EAAE,GAAG,GAAG,SAAS,EAAE,OAAO,EAAE,mBAAmB,CAAC,CAAC,EAAE,GAAG,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;YAsBpF,OAAO;YAoBP,QAAQ;IAwBtB,mGAAmG;IACnG,OAAO,CAAC,OAAO;IAwBf,OAAO,CAAC,OAAO;IAcf,OAAO,CAAC,eAAe;YAOT,MAAM;IAYpB,OAAO,CAAC,iBAAiB;IAczB,OAAO,CAAC,UAAU;CASnB"}
@@ -1,16 +1,55 @@
1
- import type { EventEnvelope } from '../types/index.js';
1
+ import type { EnvelopeData, EventEnvelope } from '../types/index.js';
2
+ /** Names reserved in an envelope's `data`; a payload field with one of these names is overwritten. */
3
+ export declare const RESERVED_ENVELOPE_FIELDS: readonly ["eventId", "version", "actorUserId"];
4
+ /** AMQP header that carries the correlation id between services. */
5
+ export declare const CORRELATION_HEADER: "x-correlation-id";
6
+ /**
7
+ * Optional envelope values and transport metadata.
8
+ *
9
+ * @example { actorUserId: user.id, version: 2 }
10
+ */
2
11
  export interface EnvelopeMeta {
3
- actorId?: string | null;
4
- correlationId?: string;
5
- aggregate?: {
6
- type: string;
7
- id: string;
8
- version?: number;
9
- };
10
- version?: number;
11
- /** Deterministic ID for idempotent re-publishing (defaults to a new ULID). */
12
+ /** Deterministic id for idempotent re-publishing; defaults to a new ULID. */
12
13
  eventId?: string;
14
+ /** Payload schema version; defaults to 1. */
15
+ version?: number;
16
+ /** Defaults to the request context's `userId`; pass `null` for the system. */
17
+ actorUserId?: string | null;
18
+ /** Sent as the `x-correlation-id` header (not part of the body); defaults to the request context's. */
19
+ correlationId?: string;
13
20
  }
14
- export declare function buildEnvelope<T>(type: string, data: T, source: string, meta?: EnvelopeMeta): EventEnvelope<T>;
21
+ /**
22
+ * Builds `{ routingKey, data: { ...payload, eventId, version, actorUserId? }, timestamp }`.
23
+ *
24
+ * @param routingKey Event or command name.
25
+ * @param data Payload fields (an object); {@link RESERVED_ENVELOPE_FIELDS} are set by the envelope.
26
+ * @param meta Optional id, version and actor.
27
+ * @returns The envelope.
28
+ * @example const envelope = buildEnvelope('thing.created', { thingId }, { actorUserId: user.id });
29
+ */
30
+ export declare function buildEnvelope<T extends object>(routingKey: string, data: T, meta?: EnvelopeMeta): EventEnvelope<T>;
31
+ /**
32
+ * True when `value` has the envelope shape (routing key, timestamp and `data.eventId`).
33
+ *
34
+ * @param value Parsed message body.
35
+ * @returns Whether it is an {@link EventEnvelope}.
36
+ */
15
37
  export declare function isEnvelope(value: unknown): value is EventEnvelope;
38
+ /**
39
+ * Splits an envelope's `data` into the payload and the reserved fields.
40
+ *
41
+ * @param envelope A received envelope.
42
+ * @returns `payload` (without reserved fields) and `meta` ({@link EnvelopeData}).
43
+ */
44
+ export declare function splitEnvelopeData(envelope: EventEnvelope): {
45
+ payload: Record<string, unknown>;
46
+ meta: EnvelopeData;
47
+ };
48
+ /**
49
+ * The correlation id to send: the explicit one, else the active request context's.
50
+ *
51
+ * @param meta Optional explicit value.
52
+ * @returns The id, or undefined outside a request/consumer context.
53
+ */
54
+ export declare function outgoingCorrelationId(meta?: Pick<EnvelopeMeta, 'correlationId'>): string | undefined;
16
55
  //# sourceMappingURL=envelope.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"envelope.d.ts","sourceRoot":"","sources":["../../../src/messaging/envelope.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAEvD,MAAM,WAAW,YAAY;IAC3B,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,SAAS,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAC3D,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,8EAA8E;IAC9E,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,wBAAgB,aAAa,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,GAAE,YAAiB,GAAG,aAAa,CAAC,CAAC,CAAC,CAYjH;AAED,wBAAgB,UAAU,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,aAAa,CAGjE"}
1
+ {"version":3,"file":"envelope.d.ts","sourceRoot":"","sources":["../../../src/messaging/envelope.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAErE,sGAAsG;AACtG,eAAO,MAAM,wBAAwB,gDAAiD,CAAC;AAEvF,oEAAoE;AACpE,eAAO,MAAM,kBAAkB,oBAAiC,CAAC;AAEjE;;;;GAIG;AACH,MAAM,WAAW,YAAY;IAC3B,6EAA6E;IAC7E,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,6CAA6C;IAC7C,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,8EAA8E;IAC9E,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,uGAAuG;IACvG,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED;;;;;;;;GAQG;AACH,wBAAgB,aAAa,CAAC,CAAC,SAAS,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,EAAE,IAAI,GAAE,YAAiB,GAAG,aAAa,CAAC,CAAC,CAAC,CAQtH;AAED;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,aAAa,CAOjE;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,aAAa,GAAG;IAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAAC,IAAI,EAAE,YAAY,CAAA;CAAE,CAGnH;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,CAAC,EAAE,IAAI,CAAC,YAAY,EAAE,eAAe,CAAC,GAAG,MAAM,GAAG,SAAS,CAEpG"}
@@ -1,7 +1,44 @@
1
1
  import type { Redis } from 'ioredis';
2
+ import type { RedisKeyspace } from '../cache/keyspace.js';
3
+ import type { DbExecutor } from '../database/scoped-repository.js';
4
+ import { type ProcessedEventsTable } from '../database/tables.js';
5
+ /** Result of {@link EventDeduper.run}. */
6
+ export type DedupeOutcome = 'processed' | 'duplicate';
7
+ /**
8
+ * Runs a consumer's work at most once per `(consumer, eventId)`. At-least-once delivery means duplicates;
9
+ * a deduper turns them into no-ops.
10
+ *
11
+ * @typeParam TTx What `work` receives: the open transaction for a database ledger, `undefined` otherwise.
12
+ */
13
+ export interface EventDeduper<TTx = unknown> {
14
+ /**
15
+ * @param consumer Stable consumer name (e.g. `{service}.{purpose}`).
16
+ * @param eventId The message's event id.
17
+ * @param work The handler; it throws to fail (nothing is recorded, so a retry runs it again).
18
+ * @returns `processed`, or `duplicate` when the event was already handled (work not called).
19
+ */
20
+ run(consumer: string, eventId: string, work: (tx: TTx) => Promise<void>): Promise<DedupeOutcome>;
21
+ }
22
+ /** True for a MySQL duplicate-key error, also when Drizzle wraps it in `cause`. */
23
+ export declare function isDuplicateKeyError(error: unknown): boolean;
24
+ /**
25
+ * Database ledger (`processed_events`, unique `(consumer, event_id)`): the ledger row and the handler's
26
+ * writes commit in one transaction, so an event is recorded as processed if and only if its effects
27
+ * commit. A concurrent duplicate blocks on the unique key until the first commits, then is skipped.
28
+ *
29
+ * @example const dedupe = new ProcessedEventsDeduper(db); // handler gets `tx` and writes through it
30
+ */
31
+ export declare class ProcessedEventsDeduper implements EventDeduper<DbExecutor> {
32
+ private readonly db;
33
+ private readonly table;
34
+ constructor(db: DbExecutor, table?: ProcessedEventsTable);
35
+ run(consumer: string, eventId: string, work: (tx: DbExecutor) => Promise<void>): Promise<DedupeOutcome>;
36
+ }
37
+ /** Outcome of {@link IdempotencyStore.begin}. */
2
38
  export type ClaimResult = 'claimed' | 'done' | 'in-flight';
3
39
  /**
4
- * At-least-once delivery means duplicates. Consumers claim `consumer:eventId` before handling:
40
+ * Lease-based claim store for services without a database (Redis). Not transactional with the handler's
41
+ * effects: prefer {@link ProcessedEventsDeduper} wherever the handler writes to a database.
5
42
  * - `claimed` → process it, then `complete()` (or `abandon()` on failure so a retry can run)
6
43
  * - `done` → already processed; ack and skip
7
44
  * - `in-flight` → another worker holds the lease; retry later
@@ -11,14 +48,17 @@ export interface IdempotencyStore {
11
48
  complete(key: string, ttlSec: number): Promise<void>;
12
49
  abandon(key: string): Promise<void>;
13
50
  }
51
+ /** Redis claim store; every key comes from the service keyspace (`{prefix}idem:{key}`). */
14
52
  export declare class RedisIdempotencyStore implements IdempotencyStore {
15
53
  private readonly redis;
16
- private readonly prefix;
17
- constructor(redis: Redis, prefix?: string);
54
+ private readonly keyspace;
55
+ constructor(redis: Redis, keyspace: RedisKeyspace);
56
+ private key;
18
57
  begin(key: string, leaseSec: number): Promise<ClaimResult>;
19
58
  complete(key: string, ttlSec: number): Promise<void>;
20
59
  abandon(key: string): Promise<void>;
21
60
  }
61
+ /** In-process claim store for tests and single-process tools. */
22
62
  export declare class MemoryIdempotencyStore implements IdempotencyStore {
23
63
  private readonly entries;
24
64
  private read;
@@ -26,4 +66,21 @@ export declare class MemoryIdempotencyStore implements IdempotencyStore {
26
66
  complete(key: string, ttlSec: number): Promise<void>;
27
67
  abandon(key: string): Promise<void>;
28
68
  }
69
+ /** Lease and retention for {@link StoreDeduper}. */
70
+ export interface StoreDeduperOptions {
71
+ /** How long a claim blocks other workers (default 300 s). */
72
+ leaseSec?: number;
73
+ /** How long a completed marker is kept (default 7 days). */
74
+ ttlSec?: number;
75
+ }
76
+ /**
77
+ * {@link EventDeduper} over an {@link IdempotencyStore}. A duplicate still in flight elsewhere throws a
78
+ * retryable 503, so the consumer retries it later instead of acking it unprocessed.
79
+ */
80
+ export declare class StoreDeduper implements EventDeduper<undefined> {
81
+ private readonly store;
82
+ private readonly options;
83
+ constructor(store: IdempotencyStore, options?: StoreDeduperOptions);
84
+ run(consumer: string, eventId: string, work: (tx: undefined) => Promise<void>): Promise<DedupeOutcome>;
85
+ }
29
86
  //# sourceMappingURL=idempotency.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"idempotency.d.ts","sourceRoot":"","sources":["../../../src/messaging/idempotency.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAErC,MAAM,MAAM,WAAW,GAAG,SAAS,GAAG,MAAM,GAAG,WAAW,CAAC;AAE3D;;;;;GAKG;AACH,MAAM,WAAW,gBAAgB;IAC/B,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC;IAC3D,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACrD,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACrC;AAKD,qBAAa,qBAAsB,YAAW,gBAAgB;IAChD,OAAO,CAAC,QAAQ,CAAC,KAAK;IAAS,OAAO,CAAC,QAAQ,CAAC,MAAM;gBAArC,KAAK,EAAE,KAAK,EAAmB,MAAM,SAAU;IAEtE,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC;IAO1D,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIpD,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAK1C;AAED,qBAAa,sBAAuB,YAAW,gBAAgB;IAC7D,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA2D;IAEnF,OAAO,CAAC,IAAI;IASN,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC;IAS1D,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIpD,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAG1C"}
1
+ {"version":3,"file":"idempotency.d.ts","sourceRoot":"","sources":["../../../src/messaging/idempotency.ts"],"names":[],"mappings":"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"}
@@ -10,19 +10,26 @@ export * from './topology.js';
10
10
  export * from './envelope.js';
11
11
  export * from './publisher.js';
12
12
  export * from './consumer.js';
13
+ export * from './base-consumer.js';
13
14
  export * from './idempotency.js';
14
15
  export * from './rpc.js';
15
16
  export * from './outbox.js';
16
17
  export * from './monitor.js';
17
18
  export * from './staff-notifier.js';
18
19
  export * from './staff-recipients.js';
20
+ /** Inputs for {@link createMessaging}; all values come from the service config. */
19
21
  export interface MessagingOptions {
22
+ /** AMQP URL (`RABBITMQ_URL`, or built by `amqpUrlFromEnv(env)`). */
20
23
  url: string;
24
+ /** App slug: names the exchanges. */
21
25
  app: string;
26
+ /** Environment prefix (`QUEUE_PREFIX`). */
22
27
  prefix: string;
28
+ /** This service's name: names its queues and dead-letter exchange. */
23
29
  service: string;
24
30
  logger?: Logger;
25
31
  }
32
+ /** One service's wired messaging components on a shared connection. */
26
33
  export interface Messaging {
27
34
  topology: Topology;
28
35
  connection: AmqpConnection;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/messaging/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AACjD,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAClD,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAChD,OAAO,EAAkB,KAAK,QAAQ,EAAE,MAAM,eAAe,CAAC;AAE9D,cAAc,iBAAiB,CAAC;AAChC,cAAc,eAAe,CAAC;AAC9B,cAAc,eAAe,CAAC;AAC9B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,eAAe,CAAC;AAC9B,cAAc,kBAAkB,CAAC;AACjC,cAAc,UAAU,CAAC;AACzB,cAAc,aAAa,CAAC;AAC5B,cAAc,cAAc,CAAC;AAC7B,cAAc,qBAAqB,CAAC;AACpC,cAAc,uBAAuB,CAAC;AAEtC,MAAM,WAAW,gBAAgB;IAC/B,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,SAAS;IACxB,QAAQ,EAAE,QAAQ,CAAC;IACnB,UAAU,EAAE,cAAc,CAAC;IAC3B,SAAS,EAAE,gBAAgB,CAAC;IAC5B,QAAQ,EAAE,eAAe,CAAC;IAC1B,SAAS,EAAE,SAAS,CAAC;IACrB,SAAS,EAAE,SAAS,CAAC;IACrB,OAAO,EAAE,YAAY,CAAC;IACtB,0FAA0F;IAC1F,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACvB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB;AAED,0FAA0F;AAC1F,wBAAgB,eAAe,CAAC,OAAO,EAAE,gBAAgB,GAAG,SAAS,CA0BpE"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/messaging/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AACjD,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAClD,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAChD,OAAO,EAAkB,KAAK,QAAQ,EAAE,MAAM,eAAe,CAAC;AAE9D,cAAc,iBAAiB,CAAC;AAChC,cAAc,eAAe,CAAC;AAC9B,cAAc,eAAe,CAAC;AAC9B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,eAAe,CAAC;AAC9B,cAAc,oBAAoB,CAAC;AACnC,cAAc,kBAAkB,CAAC;AACjC,cAAc,UAAU,CAAC;AACzB,cAAc,aAAa,CAAC;AAC5B,cAAc,cAAc,CAAC;AAC7B,cAAc,qBAAqB,CAAC;AACpC,cAAc,uBAAuB,CAAC;AAEtC,mFAAmF;AACnF,MAAM,WAAW,gBAAgB;IAC/B,oEAAoE;IACpE,GAAG,EAAE,MAAM,CAAC;IACZ,qCAAqC;IACrC,GAAG,EAAE,MAAM,CAAC;IACZ,2CAA2C;IAC3C,MAAM,EAAE,MAAM,CAAC;IACf,sEAAsE;IACtE,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,uEAAuE;AACvE,MAAM,WAAW,SAAS;IACxB,QAAQ,EAAE,QAAQ,CAAC;IACnB,UAAU,EAAE,cAAc,CAAC;IAC3B,SAAS,EAAE,gBAAgB,CAAC;IAC5B,QAAQ,EAAE,eAAe,CAAC;IAC1B,SAAS,EAAE,SAAS,CAAC;IACrB,SAAS,EAAE,SAAS,CAAC;IACrB,OAAO,EAAE,YAAY,CAAC;IACtB,0FAA0F;IAC1F,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACvB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB;AAED,0FAA0F;AAC1F,wBAAgB,eAAe,CAAC,OAAO,EAAE,gBAAgB,GAAG,SAAS,CA0BpE"}
@@ -1,51 +1,91 @@
1
- import type { Knex } from 'knex';
1
+ import { DEFAULT_OUTBOX_TABLE } from '../database/table-names.js';
2
+ import { type OutboxExchange, type OutboxTable } from '../database/tables.js';
3
+ import type { DbExecutor } from '../database/scoped-repository.js';
2
4
  import type { EventEnvelope, Logger } from '../types/index.js';
5
+ import { type EnvelopeMeta } from './envelope.js';
3
6
  import { type CommandBus, type EventBus } from './publisher.js';
4
- export declare const DEFAULT_OUTBOX_TABLE = "event_outbox";
5
- export type OutboxKind = 'event' | 'command';
6
- /** Outbox `routing_key` column value: the event type, or `command:<name>` for commands. */
7
- export declare function outboxRoutingKey(envelope: Pick<EventEnvelope, 'type'>, kind?: OutboxKind): string;
7
+ export { DEFAULT_OUTBOX_TABLE };
8
+ /** Options for {@link enqueueOutbox}. */
9
+ export interface EnqueueOptions extends EnvelopeMeta {
10
+ /** `events` (default, topic fan-out) or `commands` (direct, one owner). */
11
+ exchange?: OutboxExchange;
12
+ /** A service-specific outbox table; defaults to `event_outbox`. */
13
+ table?: OutboxTable;
14
+ }
8
15
  /**
9
- * Transactional outbox: write the domain change and the event in one DB transaction, then a
10
- * relay publishes pending rows with broker confirms. Closes the "row committed, event lost"
11
- * gap of publish-after-commit. Consumers must be idempotent (they are: see IdempotencyStore).
16
+ * Transactional outbox: stages a message in the caller's transaction, so it is published if and only if
17
+ * the domain change commits. The {@link OutboxRelay} publishes it later with broker confirms. The
18
+ * correlation id (explicit, else the request context's) is stored with the row and sent as a header.
12
19
  *
13
- * Table (see `createOutboxTable` in `@heavybit/shared-lib/knex`):
14
- * id CHAR(26) PK = eventId, routing_key, payload JSON, attempts, last_error, created_at, published_at
20
+ * @param tx The caller's open Drizzle transaction (or db handle for a single-statement change).
21
+ * @param routingKey Event or command name.
22
+ * @param data Payload fields.
23
+ * @param options Exchange, envelope meta and table.
24
+ * @returns The staged envelope.
25
+ * @example await db.transaction(async (tx) => { await repo.insert(ctx, row, { tx }); await enqueueOutbox(tx, 'thing.created', { thingId: row.id }); });
15
26
  */
16
- export declare function enqueueOutbox(db: Knex | Knex.Transaction, envelope: EventEnvelope, options?: {
17
- kind?: OutboxKind;
18
- table?: string;
19
- }): Promise<void>;
27
+ export declare function enqueueOutbox<T extends object>(tx: DbExecutor, routingKey: string, data: T, options?: EnqueueOptions): Promise<EventEnvelope<T>>;
28
+ /** What one relay batch did; passed to {@link OutboxRelayOptions.onMetrics}. */
29
+ export interface OutboxBatchMetrics {
30
+ /** Rows published in this batch. */
31
+ published: number;
32
+ /** Rows that failed (broker down or unroutable) and stay pending. */
33
+ failed: number;
34
+ /** Age in ms of the oldest pending row when the batch started (0 when nothing was pending). */
35
+ lagMs: number;
36
+ }
37
+ /** Dependencies and tuning for {@link OutboxRelay}. */
20
38
  export interface OutboxRelayOptions {
21
- knex: Knex;
39
+ db: DbExecutor;
22
40
  bus: EventBus & CommandBus;
23
- table?: string;
41
+ table?: OutboxTable;
42
+ /** Rows per batch (default 100). */
24
43
  batchSize?: number;
25
- /** Safety-net poll interval; `trigger()` after commits gives low latency. */
44
+ /** Poll interval when healthy (default 2000 ms); `trigger()` after commits gives low latency. */
26
45
  intervalMs?: number;
46
+ /** Ceiling for the exponential back-off after failed batches (default 60 000 ms). */
47
+ maxBackoffMs?: number;
48
+ /** Called after every batch, e.g. to export relay lag as a gauge. */
49
+ onMetrics?: (metrics: OutboxBatchMetrics) => void;
27
50
  logger?: Logger;
28
51
  }
52
+ /**
53
+ * Publishes pending outbox rows in id (= time) order. Each batch locks its rows with
54
+ * `FOR UPDATE SKIP LOCKED`, so several replicas can relay at once without double-publishing, and marks a
55
+ * row published only after the broker confirms it. Consumers still dedupe: a crash between confirm and
56
+ * commit re-sends the row.
57
+ *
58
+ * Failures: an unroutable command stays pending and the batch continues; a broker error stops the batch
59
+ * (keeping order) and the poll interval backs off exponentially until a batch succeeds.
60
+ */
29
61
  export declare class OutboxRelay {
30
62
  private readonly options;
31
63
  private timer;
32
64
  private running;
33
65
  private rerun;
66
+ private failures;
34
67
  private readonly logger;
35
68
  private readonly table;
36
69
  constructor(options: OutboxRelayOptions);
70
+ /** Starts polling. */
37
71
  start(): void;
72
+ /** Stops polling (an in-flight batch finishes). */
38
73
  stop(): void;
74
+ /** Delay before the next poll: the interval, doubled per consecutive failed batch, capped. */
75
+ nextDelayMs(): number;
39
76
  /** Publishes pending rows now. Concurrent triggers coalesce into one follow-up run. */
40
77
  trigger(): Promise<void>;
41
- /** One batch. Returns how many rows were published. */
42
- flush(): Promise<number>;
78
+ /** One batch. */
79
+ flush(): Promise<OutboxBatchMetrics>;
80
+ /** Deletes published rows older than `olderThanDays`; returns how many were removed. */
81
+ prune(olderThanDays?: number): Promise<number>;
82
+ private lockPending;
83
+ private publish;
84
+ private schedule;
43
85
  /**
44
86
  * Logged on the 1st, 2nd, 4th, 8th… failure of a row (the attempt count before this one is in `attempts`),
45
- * so a queue that stays missing for hours is still visible without a line every tick.
87
+ * so a queue that stays missing for hours is still visible without a line every poll.
46
88
  */
47
89
  private reportUnroutable;
48
- /** Deletes published rows older than `olderThanDays`. */
49
- prune(olderThanDays?: number): Promise<number>;
50
90
  }
51
91
  //# sourceMappingURL=outbox.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"outbox.d.ts","sourceRoot":"","sources":["../../../src/messaging/outbox.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAEjC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAC/D,OAAO,EAAqB,KAAK,UAAU,EAAE,KAAK,QAAQ,EAA+B,MAAM,gBAAgB,CAAC;AAEhH,eAAO,MAAM,oBAAoB,iBAAiB,CAAC;AAEnD,MAAM,MAAM,UAAU,GAAG,OAAO,GAAG,SAAS,CAAC;AAI7C,2FAA2F;AAC3F,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,IAAI,CAAC,aAAa,EAAE,MAAM,CAAC,EAAE,IAAI,GAAE,UAAoB,GAAG,MAAM,CAE1G;AAED;;;;;;;GAOG;AACH,wBAAsB,aAAa,CAAC,EAAE,EAAE,IAAI,GAAG,IAAI,CAAC,WAAW,EAAE,QAAQ,EAAE,aAAa,EAAE,OAAO,GAAE;IAAE,IAAI,CAAC,EAAE,UAAU,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAQ5J;AAED,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,IAAI,CAAC;IACX,GAAG,EAAE,QAAQ,GAAG,UAAU,CAAC;IAC3B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,6EAA6E;IAC7E,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,qBAAa,WAAW;IAOV,OAAO,CAAC,QAAQ,CAAC,OAAO;IANpC,OAAO,CAAC,KAAK,CAA+C;IAC5D,OAAO,CAAC,OAAO,CAAgC;IAC/C,OAAO,CAAC,KAAK,CAAS;IACtB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAS;gBAEF,OAAO,EAAE,kBAAkB;IAKxD,KAAK,IAAI,IAAI;IAMb,IAAI,IAAI,IAAI;IAKZ,uFAAuF;IACjF,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAgB9B,uDAAuD;IACjD,KAAK,IAAI,OAAO,CAAC,MAAM,CAAC;IAwC9B;;;OAGG;IACH,OAAO,CAAC,gBAAgB;IAQxB,yDAAyD;IACnD,KAAK,CAAC,aAAa,SAAI,GAAG,OAAO,CAAC,MAAM,CAAC;CAIhD"}
1
+ {"version":3,"file":"outbox.d.ts","sourceRoot":"","sources":["../../../src/messaging/outbox.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,oBAAoB,EAAE,MAAM,4BAA4B,CAAC;AAClE,OAAO,EAA2B,KAAK,cAAc,EAAE,KAAK,WAAW,EAAE,MAAM,uBAAuB,CAAC;AACvG,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kCAAkC,CAAC;AAEnE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAC/D,OAAO,EAAwC,KAAK,YAAY,EAAE,MAAM,eAAe,CAAC;AACxF,OAAO,EAAqB,KAAK,UAAU,EAAE,KAAK,QAAQ,EAA+B,MAAM,gBAAgB,CAAC;AAEhH,OAAO,EAAE,oBAAoB,EAAE,CAAC;AAEhC,yCAAyC;AACzC,MAAM,WAAW,cAAe,SAAQ,YAAY;IAClD,2EAA2E;IAC3E,QAAQ,CAAC,EAAE,cAAc,CAAC;IAC1B,mEAAmE;IACnE,KAAK,CAAC,EAAE,WAAW,CAAC;CACrB;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,aAAa,CAAC,CAAC,SAAS,MAAM,EAAE,EAAE,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,EAAE,OAAO,GAAE,cAAmB,GAAG,OAAO,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CAY1J;AAED,gFAAgF;AAChF,MAAM,WAAW,kBAAkB;IACjC,oCAAoC;IACpC,SAAS,EAAE,MAAM,CAAC;IAClB,qEAAqE;IACrE,MAAM,EAAE,MAAM,CAAC;IACf,+FAA+F;IAC/F,KAAK,EAAE,MAAM,CAAC;CACf;AAED,uDAAuD;AACvD,MAAM,WAAW,kBAAkB;IACjC,EAAE,EAAE,UAAU,CAAC;IACf,GAAG,EAAE,QAAQ,GAAG,UAAU,CAAC;IAC3B,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,oCAAoC;IACpC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,iGAAiG;IACjG,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,qFAAqF;IACrF,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,qEAAqE;IACrE,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,kBAAkB,KAAK,IAAI,CAAC;IAClD,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAID;;;;;;;;GAQG;AACH,qBAAa,WAAW;IAQV,OAAO,CAAC,QAAQ,CAAC,OAAO;IAPpC,OAAO,CAAC,KAAK,CAA8C;IAC3D,OAAO,CAAC,OAAO,CAA4C;IAC3D,OAAO,CAAC,KAAK,CAAS;IACtB,OAAO,CAAC,QAAQ,CAAK;IACrB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAc;gBAEP,OAAO,EAAE,kBAAkB;IAKxD,sBAAsB;IACtB,KAAK,IAAI,IAAI;IAKb,mDAAmD;IACnD,IAAI,IAAI,IAAI;IAKZ,8FAA8F;IAC9F,WAAW,IAAI,MAAM;IAKrB,uFAAuF;IACjF,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAgB9B,iBAAiB;IACX,KAAK,IAAI,OAAO,CAAC,kBAAkB,CAAC;IAuC1C,wFAAwF;IAClF,KAAK,CAAC,aAAa,SAAI,GAAG,OAAO,CAAC,MAAM,CAAC;IAO/C,OAAO,CAAC,WAAW;IAQnB,OAAO,CAAC,OAAO;IAQf,OAAO,CAAC,QAAQ;IAMhB;;;OAGG;IACH,OAAO,CAAC,gBAAgB;CAOzB"}