@heavybit/shared-lib 1.3.2 → 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 (366) hide show
  1. package/README.md +261 -0
  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/errors.js +44 -5
  15. package/dist/cjs/common/errors.js.map +1 -1
  16. package/dist/cjs/common/index.js +4 -0
  17. package/dist/cjs/common/index.js.map +1 -1
  18. package/dist/cjs/common/internal-client.js +177 -0
  19. package/dist/cjs/common/internal-client.js.map +1 -0
  20. package/dist/cjs/common/internal-constants.js +12 -0
  21. package/dist/cjs/common/internal-constants.js.map +1 -0
  22. package/dist/cjs/common/internal-service-auth.js +160 -0
  23. package/dist/cjs/common/internal-service-auth.js.map +1 -0
  24. package/dist/cjs/common/money.js +215 -0
  25. package/dist/cjs/common/money.js.map +1 -0
  26. package/dist/cjs/common/phone.js +63 -11
  27. package/dist/cjs/common/phone.js.map +1 -1
  28. package/dist/cjs/common/response.js +78 -5
  29. package/dist/cjs/common/response.js.map +1 -1
  30. package/dist/cjs/common/time.js +28 -2
  31. package/dist/cjs/common/time.js.map +1 -1
  32. package/dist/cjs/config/index.js +140 -20
  33. package/dist/cjs/config/index.js.map +1 -1
  34. package/dist/cjs/context/index.js +49 -0
  35. package/dist/cjs/context/index.js.map +1 -0
  36. package/dist/cjs/contracts/index.js +0 -2
  37. package/dist/cjs/contracts/index.js.map +1 -1
  38. package/dist/cjs/contracts/notification-dispatch.js +87 -63
  39. package/dist/cjs/contracts/notification-dispatch.js.map +1 -1
  40. package/dist/cjs/database/db.js +37 -0
  41. package/dist/cjs/database/db.js.map +1 -0
  42. package/dist/cjs/database/index.js +22 -51
  43. package/dist/cjs/database/index.js.map +1 -1
  44. package/dist/cjs/database/pagination.js +88 -0
  45. package/dist/cjs/database/pagination.js.map +1 -0
  46. package/dist/cjs/database/pool.js +60 -0
  47. package/dist/cjs/database/pool.js.map +1 -0
  48. package/dist/cjs/database/scoped-repository.js +132 -0
  49. package/dist/cjs/database/scoped-repository.js.map +1 -0
  50. package/dist/cjs/database/scopes.js +166 -0
  51. package/dist/cjs/database/scopes.js.map +1 -0
  52. package/dist/cjs/database/table-names.js +14 -0
  53. package/dist/cjs/database/table-names.js.map +1 -0
  54. package/dist/cjs/database/tables.js +71 -0
  55. package/dist/cjs/database/tables.js.map +1 -0
  56. package/dist/cjs/express/auth.js +88 -41
  57. package/dist/cjs/express/auth.js.map +1 -1
  58. package/dist/cjs/express/index.js +3 -0
  59. package/dist/cjs/express/index.js.map +1 -1
  60. package/dist/cjs/express/internal-caller.js +52 -0
  61. package/dist/cjs/express/internal-caller.js.map +1 -0
  62. package/dist/cjs/express/middleware.js +154 -34
  63. package/dist/cjs/express/middleware.js.map +1 -1
  64. package/dist/cjs/express/respond.js +47 -0
  65. package/dist/cjs/express/respond.js.map +1 -0
  66. package/dist/cjs/express/step-up.js +41 -0
  67. package/dist/cjs/express/step-up.js.map +1 -0
  68. package/dist/cjs/http/index.js +3 -71
  69. package/dist/cjs/http/index.js.map +1 -1
  70. package/dist/cjs/identity/index.js +269 -67
  71. package/dist/cjs/identity/index.js.map +1 -1
  72. package/dist/cjs/identity/step-up.js +52 -0
  73. package/dist/cjs/identity/step-up.js.map +1 -0
  74. package/dist/cjs/index.js +7 -0
  75. package/dist/cjs/index.js.map +1 -1
  76. package/dist/cjs/knex/columns.js +36 -0
  77. package/dist/cjs/knex/columns.js.map +1 -0
  78. package/dist/cjs/knex/index.js +26 -43
  79. package/dist/cjs/knex/index.js.map +1 -1
  80. package/dist/cjs/knex/migration-helpers.js +57 -0
  81. package/dist/cjs/knex/migration-helpers.js.map +1 -0
  82. package/dist/cjs/logging/index.js +76 -40
  83. package/dist/cjs/logging/index.js.map +1 -1
  84. package/dist/cjs/logging/redact.js +112 -0
  85. package/dist/cjs/logging/redact.js.map +1 -0
  86. package/dist/cjs/messaging/base-consumer.js +36 -0
  87. package/dist/cjs/messaging/base-consumer.js.map +1 -0
  88. package/dist/cjs/messaging/consumer.js +142 -73
  89. package/dist/cjs/messaging/consumer.js.map +1 -1
  90. package/dist/cjs/messaging/envelope.js +53 -10
  91. package/dist/cjs/messaging/envelope.js.map +1 -1
  92. package/dist/cjs/messaging/idempotency.js +95 -8
  93. package/dist/cjs/messaging/idempotency.js.map +1 -1
  94. package/dist/cjs/messaging/index.js +3 -1
  95. package/dist/cjs/messaging/index.js.map +1 -1
  96. package/dist/cjs/messaging/outbox.js +97 -55
  97. package/dist/cjs/messaging/outbox.js.map +1 -1
  98. package/dist/cjs/messaging/publisher.js +86 -38
  99. package/dist/cjs/messaging/publisher.js.map +1 -1
  100. package/dist/cjs/messaging/staff-notifier.js +36 -44
  101. package/dist/cjs/messaging/staff-notifier.js.map +1 -1
  102. package/dist/cjs/messaging/staff-recipients.js +57 -0
  103. package/dist/cjs/messaging/staff-recipients.js.map +1 -0
  104. package/dist/cjs/messaging/topology.js +41 -5
  105. package/dist/cjs/messaging/topology.js.map +1 -1
  106. package/dist/cjs/rate-limit/index.js +57 -6
  107. package/dist/cjs/rate-limit/index.js.map +1 -1
  108. package/dist/cjs/rate-limit/keys.js +47 -0
  109. package/dist/cjs/rate-limit/keys.js.map +1 -0
  110. package/dist/cjs/socket/index.js +127 -16
  111. package/dist/cjs/socket/index.js.map +1 -1
  112. package/dist/cjs/socket/ticket.js +124 -0
  113. package/dist/cjs/socket/ticket.js.map +1 -0
  114. package/dist/cjs/testing/index.js +23 -14
  115. package/dist/cjs/testing/index.js.map +1 -1
  116. package/dist/cjs/validation/index.js +27 -3
  117. package/dist/cjs/validation/index.js.map +1 -1
  118. package/dist/esm/access/index.js +9 -1
  119. package/dist/esm/access/index.js.map +1 -1
  120. package/dist/esm/approvals/index.js +127 -0
  121. package/dist/esm/approvals/index.js.map +1 -0
  122. package/dist/esm/audit/index.js +70 -0
  123. package/dist/esm/audit/index.js.map +1 -0
  124. package/dist/esm/cache/index.js +70 -13
  125. package/dist/esm/cache/index.js.map +1 -1
  126. package/dist/esm/cache/keyspace.js +95 -0
  127. package/dist/esm/cache/keyspace.js.map +1 -0
  128. package/dist/esm/common/circuit-breaker.js +70 -0
  129. package/dist/esm/common/circuit-breaker.js.map +1 -0
  130. package/dist/esm/common/errors.js +42 -4
  131. package/dist/esm/common/errors.js.map +1 -1
  132. package/dist/esm/common/index.js +4 -0
  133. package/dist/esm/common/index.js.map +1 -1
  134. package/dist/esm/common/internal-client.js +173 -0
  135. package/dist/esm/common/internal-client.js.map +1 -0
  136. package/dist/esm/common/internal-constants.js +9 -0
  137. package/dist/esm/common/internal-constants.js.map +1 -0
  138. package/dist/esm/common/internal-service-auth.js +150 -0
  139. package/dist/esm/common/internal-service-auth.js.map +1 -0
  140. package/dist/esm/common/money.js +207 -0
  141. package/dist/esm/common/money.js.map +1 -0
  142. package/dist/esm/common/phone.js +63 -11
  143. package/dist/esm/common/phone.js.map +1 -1
  144. package/dist/esm/common/response.js +75 -5
  145. package/dist/esm/common/response.js.map +1 -1
  146. package/dist/esm/common/time.js +27 -2
  147. package/dist/esm/common/time.js.map +1 -1
  148. package/dist/esm/config/index.js +136 -19
  149. package/dist/esm/config/index.js.map +1 -1
  150. package/dist/esm/context/index.js +44 -0
  151. package/dist/esm/context/index.js.map +1 -0
  152. package/dist/esm/contracts/index.js +0 -2
  153. package/dist/esm/contracts/index.js.map +1 -1
  154. package/dist/esm/contracts/notification-dispatch.js +85 -62
  155. package/dist/esm/contracts/notification-dispatch.js.map +1 -1
  156. package/dist/esm/database/db.js +32 -0
  157. package/dist/esm/database/db.js.map +1 -0
  158. package/dist/esm/database/index.js +9 -45
  159. package/dist/esm/database/index.js.map +1 -1
  160. package/dist/esm/database/pagination.js +81 -0
  161. package/dist/esm/database/pagination.js.map +1 -0
  162. package/dist/esm/database/pool.js +51 -0
  163. package/dist/esm/database/pool.js.map +1 -0
  164. package/dist/esm/database/scoped-repository.js +128 -0
  165. package/dist/esm/database/scoped-repository.js.map +1 -0
  166. package/dist/esm/database/scopes.js +157 -0
  167. package/dist/esm/database/scopes.js.map +1 -0
  168. package/dist/esm/database/table-names.js +11 -0
  169. package/dist/esm/database/table-names.js.map +1 -0
  170. package/dist/esm/database/tables.js +61 -0
  171. package/dist/esm/database/tables.js.map +1 -0
  172. package/dist/esm/express/auth.js +87 -40
  173. package/dist/esm/express/auth.js.map +1 -1
  174. package/dist/esm/express/index.js +3 -0
  175. package/dist/esm/express/index.js.map +1 -1
  176. package/dist/esm/express/internal-caller.js +49 -0
  177. package/dist/esm/express/internal-caller.js.map +1 -0
  178. package/dist/esm/express/middleware.js +146 -27
  179. package/dist/esm/express/middleware.js.map +1 -1
  180. package/dist/esm/express/respond.js +42 -0
  181. package/dist/esm/express/respond.js.map +1 -0
  182. package/dist/esm/express/step-up.js +38 -0
  183. package/dist/esm/express/step-up.js.map +1 -0
  184. package/dist/esm/http/index.js +3 -70
  185. package/dist/esm/http/index.js.map +1 -1
  186. package/dist/esm/identity/index.js +246 -64
  187. package/dist/esm/identity/index.js.map +1 -1
  188. package/dist/esm/identity/step-up.js +47 -0
  189. package/dist/esm/identity/step-up.js.map +1 -0
  190. package/dist/esm/index.js +7 -0
  191. package/dist/esm/index.js.map +1 -1
  192. package/dist/esm/knex/columns.js +28 -0
  193. package/dist/esm/knex/columns.js.map +1 -0
  194. package/dist/esm/knex/index.js +8 -34
  195. package/dist/esm/knex/index.js.map +1 -1
  196. package/dist/esm/knex/migration-helpers.js +51 -0
  197. package/dist/esm/knex/migration-helpers.js.map +1 -0
  198. package/dist/esm/logging/index.js +68 -39
  199. package/dist/esm/logging/index.js.map +1 -1
  200. package/dist/esm/logging/redact.js +106 -0
  201. package/dist/esm/logging/redact.js.map +1 -0
  202. package/dist/esm/messaging/base-consumer.js +32 -0
  203. package/dist/esm/messaging/base-consumer.js.map +1 -0
  204. package/dist/esm/messaging/consumer.js +139 -71
  205. package/dist/esm/messaging/consumer.js.map +1 -1
  206. package/dist/esm/messaging/envelope.js +50 -10
  207. package/dist/esm/messaging/envelope.js.map +1 -1
  208. package/dist/esm/messaging/idempotency.js +91 -7
  209. package/dist/esm/messaging/idempotency.js.map +1 -1
  210. package/dist/esm/messaging/index.js +3 -1
  211. package/dist/esm/messaging/index.js.map +1 -1
  212. package/dist/esm/messaging/outbox.js +97 -54
  213. package/dist/esm/messaging/outbox.js.map +1 -1
  214. package/dist/esm/messaging/publisher.js +86 -38
  215. package/dist/esm/messaging/publisher.js.map +1 -1
  216. package/dist/esm/messaging/staff-notifier.js +36 -43
  217. package/dist/esm/messaging/staff-notifier.js.map +1 -1
  218. package/dist/esm/messaging/staff-recipients.js +53 -0
  219. package/dist/esm/messaging/staff-recipients.js.map +1 -0
  220. package/dist/esm/messaging/topology.js +39 -4
  221. package/dist/esm/messaging/topology.js.map +1 -1
  222. package/dist/esm/rate-limit/index.js +41 -6
  223. package/dist/esm/rate-limit/index.js.map +1 -1
  224. package/dist/esm/rate-limit/keys.js +43 -0
  225. package/dist/esm/rate-limit/keys.js.map +1 -0
  226. package/dist/esm/socket/index.js +112 -17
  227. package/dist/esm/socket/index.js.map +1 -1
  228. package/dist/esm/socket/ticket.js +117 -0
  229. package/dist/esm/socket/ticket.js.map +1 -0
  230. package/dist/esm/testing/index.js +24 -15
  231. package/dist/esm/testing/index.js.map +1 -1
  232. package/dist/esm/validation/index.js +27 -2
  233. package/dist/esm/validation/index.js.map +1 -1
  234. package/dist/types/access/index.d.ts +9 -3
  235. package/dist/types/access/index.d.ts.map +1 -1
  236. package/dist/types/alerting/index.d.ts +1 -1
  237. package/dist/types/alerting/index.d.ts.map +1 -1
  238. package/dist/types/approvals/index.d.ts +99 -0
  239. package/dist/types/approvals/index.d.ts.map +1 -0
  240. package/dist/types/audit/index.d.ts +82 -0
  241. package/dist/types/audit/index.d.ts.map +1 -0
  242. package/dist/types/cache/index.d.ts +64 -14
  243. package/dist/types/cache/index.d.ts.map +1 -1
  244. package/dist/types/cache/keyspace.d.ts +84 -0
  245. package/dist/types/cache/keyspace.d.ts.map +1 -0
  246. package/dist/types/common/circuit-breaker.d.ts +51 -0
  247. package/dist/types/common/circuit-breaker.d.ts.map +1 -0
  248. package/dist/types/common/errors.d.ts +48 -7
  249. package/dist/types/common/errors.d.ts.map +1 -1
  250. package/dist/types/common/index.d.ts +4 -0
  251. package/dist/types/common/index.d.ts.map +1 -1
  252. package/dist/types/common/internal-client.d.ts +116 -0
  253. package/dist/types/common/internal-client.d.ts.map +1 -0
  254. package/dist/types/common/internal-constants.d.ts +9 -0
  255. package/dist/types/common/internal-constants.d.ts.map +1 -0
  256. package/dist/types/common/internal-service-auth.d.ts +102 -0
  257. package/dist/types/common/internal-service-auth.d.ts.map +1 -0
  258. package/dist/types/common/money.d.ts +104 -0
  259. package/dist/types/common/money.d.ts.map +1 -0
  260. package/dist/types/common/phone.d.ts +45 -6
  261. package/dist/types/common/phone.d.ts.map +1 -1
  262. package/dist/types/common/response.d.ts +66 -5
  263. package/dist/types/common/response.d.ts.map +1 -1
  264. package/dist/types/common/time.d.ts +20 -2
  265. package/dist/types/common/time.d.ts.map +1 -1
  266. package/dist/types/config/index.d.ts +252 -28
  267. package/dist/types/config/index.d.ts.map +1 -1
  268. package/dist/types/context/index.d.ts +39 -0
  269. package/dist/types/context/index.d.ts.map +1 -0
  270. package/dist/types/contracts/index.d.ts +0 -2
  271. package/dist/types/contracts/index.d.ts.map +1 -1
  272. package/dist/types/contracts/notification-dispatch.d.ts +204 -86
  273. package/dist/types/contracts/notification-dispatch.d.ts.map +1 -1
  274. package/dist/types/database/db.d.ts +46 -0
  275. package/dist/types/database/db.d.ts.map +1 -0
  276. package/dist/types/database/index.d.ts +9 -22
  277. package/dist/types/database/index.d.ts.map +1 -1
  278. package/dist/types/database/pagination.d.ts +68 -0
  279. package/dist/types/database/pagination.d.ts.map +1 -0
  280. package/dist/types/database/pool.d.ts +39 -0
  281. package/dist/types/database/pool.d.ts.map +1 -0
  282. package/dist/types/database/scoped-repository.d.ts +92 -0
  283. package/dist/types/database/scoped-repository.d.ts.map +1 -0
  284. package/dist/types/database/scopes.d.ts +159 -0
  285. package/dist/types/database/scopes.d.ts.map +1 -0
  286. package/dist/types/database/table-names.d.ts +13 -0
  287. package/dist/types/database/table-names.d.ts.map +1 -0
  288. package/dist/types/database/tables.d.ts +748 -0
  289. package/dist/types/database/tables.d.ts.map +1 -0
  290. package/dist/types/express/auth.d.ts +63 -11
  291. package/dist/types/express/auth.d.ts.map +1 -1
  292. package/dist/types/express/index.d.ts +3 -0
  293. package/dist/types/express/index.d.ts.map +1 -1
  294. package/dist/types/express/internal-caller.d.ts +53 -0
  295. package/dist/types/express/internal-caller.d.ts.map +1 -0
  296. package/dist/types/express/middleware.d.ts +86 -7
  297. package/dist/types/express/middleware.d.ts.map +1 -1
  298. package/dist/types/express/respond.d.ts +41 -0
  299. package/dist/types/express/respond.d.ts.map +1 -0
  300. package/dist/types/express/step-up.d.ts +38 -0
  301. package/dist/types/express/step-up.d.ts.map +1 -0
  302. package/dist/types/http/index.d.ts +3 -31
  303. package/dist/types/http/index.d.ts.map +1 -1
  304. package/dist/types/identity/index.d.ts +159 -21
  305. package/dist/types/identity/index.d.ts.map +1 -1
  306. package/dist/types/identity/step-up.d.ts +41 -0
  307. package/dist/types/identity/step-up.d.ts.map +1 -0
  308. package/dist/types/index.d.ts +7 -0
  309. package/dist/types/index.d.ts.map +1 -1
  310. package/dist/types/knex/columns.d.ts +15 -0
  311. package/dist/types/knex/columns.d.ts.map +1 -0
  312. package/dist/types/knex/index.d.ts +3 -11
  313. package/dist/types/knex/index.d.ts.map +1 -1
  314. package/dist/types/knex/migration-helpers.d.ts +28 -0
  315. package/dist/types/knex/migration-helpers.d.ts.map +1 -0
  316. package/dist/types/logging/index.d.ts +30 -4
  317. package/dist/types/logging/index.d.ts.map +1 -1
  318. package/dist/types/logging/redact.d.ts +54 -0
  319. package/dist/types/logging/redact.d.ts.map +1 -0
  320. package/dist/types/messaging/base-consumer.d.ts +33 -0
  321. package/dist/types/messaging/base-consumer.d.ts.map +1 -0
  322. package/dist/types/messaging/consumer.d.ts +59 -21
  323. package/dist/types/messaging/consumer.d.ts.map +1 -1
  324. package/dist/types/messaging/envelope.d.ts +50 -11
  325. package/dist/types/messaging/envelope.d.ts.map +1 -1
  326. package/dist/types/messaging/idempotency.d.ts +60 -3
  327. package/dist/types/messaging/idempotency.d.ts.map +1 -1
  328. package/dist/types/messaging/index.d.ts +8 -0
  329. package/dist/types/messaging/index.d.ts.map +1 -1
  330. package/dist/types/messaging/outbox.d.ts +62 -22
  331. package/dist/types/messaging/outbox.d.ts.map +1 -1
  332. package/dist/types/messaging/publisher.d.ts +54 -21
  333. package/dist/types/messaging/publisher.d.ts.map +1 -1
  334. package/dist/types/messaging/staff-notifier.d.ts +69 -38
  335. package/dist/types/messaging/staff-notifier.d.ts.map +1 -1
  336. package/dist/types/messaging/staff-recipients.d.ts +50 -0
  337. package/dist/types/messaging/staff-recipients.d.ts.map +1 -0
  338. package/dist/types/messaging/topology.d.ts +40 -8
  339. package/dist/types/messaging/topology.d.ts.map +1 -1
  340. package/dist/types/rate-limit/index.d.ts +40 -4
  341. package/dist/types/rate-limit/index.d.ts.map +1 -1
  342. package/dist/types/rate-limit/keys.d.ts +52 -0
  343. package/dist/types/rate-limit/keys.d.ts.map +1 -0
  344. package/dist/types/socket/index.d.ts +86 -2
  345. package/dist/types/socket/index.d.ts.map +1 -1
  346. package/dist/types/socket/ticket.d.ts +90 -0
  347. package/dist/types/socket/ticket.d.ts.map +1 -0
  348. package/dist/types/testing/index.d.ts +31 -10
  349. package/dist/types/testing/index.d.ts.map +1 -1
  350. package/dist/types/types/index.d.ts +62 -15
  351. package/dist/types/types/index.d.ts.map +1 -1
  352. package/dist/types/validation/index.d.ts +33 -2
  353. package/dist/types/validation/index.d.ts.map +1 -1
  354. package/package.json +3 -1
  355. package/dist/cjs/contracts/editorial-draft.js +0 -120
  356. package/dist/cjs/contracts/editorial-draft.js.map +0 -1
  357. package/dist/cjs/contracts/upload-import.js +0 -54
  358. package/dist/cjs/contracts/upload-import.js.map +0 -1
  359. package/dist/esm/contracts/editorial-draft.js +0 -117
  360. package/dist/esm/contracts/editorial-draft.js.map +0 -1
  361. package/dist/esm/contracts/upload-import.js +0 -51
  362. package/dist/esm/contracts/upload-import.js.map +0 -1
  363. package/dist/types/contracts/editorial-draft.d.ts +0 -610
  364. package/dist/types/contracts/editorial-draft.d.ts.map +0 -1
  365. package/dist/types/contracts/upload-import.d.ts +0 -96
  366. package/dist/types/contracts/upload-import.d.ts.map +0 -1
@@ -1,33 +1,51 @@
1
+ /**
2
+ * Notification dispatch: a generic command contract (shared plumbing, no app vocabulary).
3
+ *
4
+ * Services never send email, SMS or push themselves. They stage one dispatch command (default
5
+ * `notification:dispatch`) through their outbox, and the app's notification-service renders a seeded
6
+ * template and delivers it. Both sides build their schema from {@link createNotificationDispatchSchema},
7
+ * so producers and the consumer cannot drift.
8
+ *
9
+ * App-specific parts are parameters: the app passes its own categories (e.g. `security`, `order`) and
10
+ * channels (e.g. `email`, `sms`, `in_app`). Template slugs and their variables also belong to the app
11
+ * (keep a table next to the notification-service seeds and test both sides against it).
12
+ */
1
13
  import { z } from 'zod';
2
14
  /**
3
- * `notification:dispatch` — the one command every Showbiz service stages (through its outbox) to have
4
- * notification-service render a seeded template and deliver it by email and/or the in-app feed. No
5
- * other service sends email itself. One contract for both sides: producers type their payload with it
6
- * and notification-service validates against it, so the two cannot drift.
15
+ * Default command name for a dispatch. An app may use another name and pass it to its producers.
16
+ *
17
+ * @example await outbox.command(tx, NOTIFICATION_DISPATCH, dispatch, { correlationId });
7
18
  */
8
19
  export declare const NOTIFICATION_DISPATCH = "notification:dispatch";
9
- /** Channels a dispatch may ask for. `in_app` needs a recipient `userId` (the bell feed is per user). */
10
- export declare const DISPATCH_CHANNELS: readonly ["email", "in_app"];
11
- export type DispatchChannel = (typeof DISPATCH_CHANNELS)[number];
12
20
  /**
13
- * What a dispatch is about. Account mail (`security`, `account`, `team`, `marketing`) goes to a user;
14
- * `staff` is an operational alert to a configured staff address.
21
+ * Size limits that keep a dispatch small enough for a queue message and safe to render.
22
+ *
23
+ * @example z.string().max(DISPATCH_LIMITS.template)
15
24
  */
16
- export declare const NOTIFICATION_CATEGORIES: readonly ["security", "account", "team", "marketing", "staff"];
17
- export type NotificationCategory = (typeof NOTIFICATION_CATEGORIES)[number];
18
25
  export declare const DISPATCH_LIMITS: {
19
26
  readonly template: 100;
20
27
  readonly email: 254;
21
28
  readonly name: 200;
29
+ readonly userId: 36;
22
30
  readonly dataKeys: 40;
23
31
  readonly dataKey: 64;
24
32
  readonly dataValue: 2000;
25
33
  readonly secretKeys: 10;
26
34
  readonly permission: 100;
27
35
  };
28
- /** Template variables are flat scalars: they are printed into the copy, never serialised objects. */
36
+ /**
37
+ * Template variables are flat scalars: they are printed into the copy, never serialised objects.
38
+ *
39
+ * @example dispatchValueSchema.parse('1250.00')
40
+ */
29
41
  export declare const dispatchValueSchema: z.ZodUnion<[z.ZodString, z.ZodNumber, z.ZodBoolean, z.ZodNull]>;
30
42
  export type DispatchValue = z.infer<typeof dispatchValueSchema>;
43
+ /**
44
+ * Who receives a dispatch. `userId` is needed for per-user channels such as an in-app feed;
45
+ * addresses with no account behind them carry only `email`.
46
+ *
47
+ * @example { email: 'ada@example.com', name: 'Ada', userId: '01J…', timezone: 'Europe/Lisbon' }
48
+ */
31
49
  export declare const dispatchRecipientSchema: z.ZodObject<{
32
50
  email: z.ZodString;
33
51
  name: z.ZodOptional<z.ZodString>;
@@ -49,10 +67,46 @@ export declare const dispatchRecipientSchema: z.ZodObject<{
49
67
  timezone?: string | null | undefined;
50
68
  }>;
51
69
  export type DispatchRecipient = z.infer<typeof dispatchRecipientSchema>;
52
- export declare const notificationDispatchSchema: z.ZodEffects<z.ZodObject<{
53
- /** Seeded `notification_templates.slug`. */
70
+ /**
71
+ * A template variable name the renderer can match.
72
+ *
73
+ * @example dispatchDataKeySchema.parse('expiresInMinutes')
74
+ */
75
+ export declare const dispatchDataKeySchema: z.ZodString;
76
+ /**
77
+ * The app's vocabulary: which categories and channels a dispatch may name.
78
+ *
79
+ * @example { categories: ['security', 'order', 'staff'], channels: ['email', 'sms'] }
80
+ */
81
+ export interface DispatchVocabulary<C extends string, Ch extends string> {
82
+ /** What a dispatch is about, e.g. `['security', 'account', 'order', 'staff']`. */
83
+ categories: readonly [C, ...C[]];
84
+ /** Delivery channels the notification-service supports, e.g. `['email', 'sms', 'in_app']`. */
85
+ channels: readonly [Ch, ...Ch[]];
86
+ }
87
+ /**
88
+ * Build the dispatch schema for an app's categories and channels. Producers type their payload with it;
89
+ * the notification-service validates incoming commands with the same schema.
90
+ *
91
+ * Rules every app gets: a seeded `template` slug; one or more distinct channels; at most 40 flat-scalar `data`
92
+ * variables with renderer-safe names; `secretKeys` (one-time codes, reset links) must be keys of `data`;
93
+ * the consumer must use them only to render, never store them or show them in a feed.
94
+ *
95
+ * @typeParam C The app's categories (inferred as literals).
96
+ * @typeParam Ch The app's channels (inferred as literals).
97
+ * @param vocabulary The app's categories and channels.
98
+ * @returns A Zod schema; `data` defaults to `{}` and `secretKeys` to `[]`.
99
+ * @example
100
+ * export const dispatchSchema = createNotificationDispatchSchema({
101
+ * categories: ['security', 'account', 'order', 'staff'],
102
+ * channels: ['email', 'sms'],
103
+ * });
104
+ * export type Dispatch = z.input<typeof dispatchSchema>;
105
+ */
106
+ export declare function createNotificationDispatchSchema<const C extends string, const Ch extends string>(vocabulary: DispatchVocabulary<C, Ch>): z.ZodEffects<z.ZodObject<{
107
+ /** Seeded template slug in the notification-service. */
54
108
  template: z.ZodString;
55
- category: z.ZodEnum<["security", "account", "team", "marketing", "staff"]>;
109
+ category: z.ZodEnum<[C, ...C[]]>;
56
110
  recipient: z.ZodObject<{
57
111
  email: z.ZodString;
58
112
  name: z.ZodOptional<z.ZodString>;
@@ -73,103 +127,167 @@ export declare const notificationDispatchSchema: z.ZodEffects<z.ZodObject<{
73
127
  locale?: string | null | undefined;
74
128
  timezone?: string | null | undefined;
75
129
  }>;
76
- channels: z.ZodArray<z.ZodEnum<["email", "in_app"]>, "many">;
130
+ channels: z.ZodEffects<z.ZodArray<z.ZodEnum<[Ch, ...Ch[]]>, "many">, Ch[], Ch[]>;
77
131
  data: z.ZodEffects<z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodUnion<[z.ZodString, z.ZodNumber, z.ZodBoolean, z.ZodNull]>>>, Record<string, string | number | boolean | null>, Record<string, string | number | boolean | null> | undefined>;
78
- /**
79
- * Keys of `data` holding a one-time secret (a sign-in code, a reset or confirm link). They are used to
80
- * render the email and nothing else: notification-service never stores them and never puts them in the
81
- * in-app feed, and email-service masks them in what it keeps.
82
- */
132
+ /** Keys of `data` holding a one-time secret: used to render, never stored or shown in a feed. */
83
133
  secretKeys: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
84
- /** Security and staff alerts are always delivered, whatever the recipient's preferences. */
134
+ /** Deliver whatever the recipient's notification preferences say (for mail the recipient cannot opt out of). */
85
135
  bypassPreferences: z.ZodOptional<z.ZodBoolean>;
86
- /** The permission staff recipients hold; informational until recipients are resolved by permission. */
136
+ /** The permission the recipients were resolved by, when they were (traceability only). */
87
137
  targetPermission: z.ZodOptional<z.ZodString>;
88
- }, "strip", z.ZodTypeAny, {
89
- data: Record<string, string | number | boolean | null>;
90
- category: "security" | "account" | "team" | "marketing" | "staff";
91
- template: string;
92
- recipient: {
138
+ }, "strip", z.ZodTypeAny, z.objectUtil.addQuestionMarks<z.baseObjectOutputType<{
139
+ /** Seeded template slug in the notification-service. */
140
+ template: z.ZodString;
141
+ category: z.ZodEnum<[C, ...C[]]>;
142
+ recipient: z.ZodObject<{
143
+ email: z.ZodString;
144
+ name: z.ZodOptional<z.ZodString>;
145
+ userId: z.ZodOptional<z.ZodNullable<z.ZodString>>;
146
+ locale: z.ZodOptional<z.ZodNullable<z.ZodString>>;
147
+ /** IANA zone; time variables are rendered in it. */
148
+ timezone: z.ZodOptional<z.ZodNullable<z.ZodString>>;
149
+ }, "strip", z.ZodTypeAny, {
93
150
  email: string;
94
151
  name?: string | undefined;
95
152
  userId?: string | null | undefined;
96
153
  locale?: string | null | undefined;
97
154
  timezone?: string | null | undefined;
98
- };
99
- channels: ("email" | "in_app")[];
100
- secretKeys: string[];
101
- bypassPreferences?: boolean | undefined;
102
- targetPermission?: string | undefined;
103
- }, {
104
- category: "security" | "account" | "team" | "marketing" | "staff";
105
- template: string;
106
- recipient: {
155
+ }, {
107
156
  email: string;
108
157
  name?: string | undefined;
109
158
  userId?: string | null | undefined;
110
159
  locale?: string | null | undefined;
111
160
  timezone?: string | null | undefined;
112
- };
113
- channels: ("email" | "in_app")[];
114
- data?: Record<string, string | number | boolean | null> | undefined;
115
- secretKeys?: string[] | undefined;
116
- bypassPreferences?: boolean | undefined;
117
- targetPermission?: string | undefined;
118
- }>, {
119
- data: Record<string, string | number | boolean | null>;
120
- category: "security" | "account" | "team" | "marketing" | "staff";
121
- template: string;
122
- recipient: {
161
+ }>;
162
+ channels: z.ZodEffects<z.ZodArray<z.ZodEnum<[Ch, ...Ch[]]>, "many">, Ch[], Ch[]>;
163
+ data: z.ZodEffects<z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodUnion<[z.ZodString, z.ZodNumber, z.ZodBoolean, z.ZodNull]>>>, Record<string, string | number | boolean | null>, Record<string, string | number | boolean | null> | undefined>;
164
+ /** Keys of `data` holding a one-time secret: used to render, never stored or shown in a feed. */
165
+ secretKeys: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
166
+ /** Deliver whatever the recipient's notification preferences say (for mail the recipient cannot opt out of). */
167
+ bypassPreferences: z.ZodOptional<z.ZodBoolean>;
168
+ /** The permission the recipients were resolved by, when they were (traceability only). */
169
+ targetPermission: z.ZodOptional<z.ZodString>;
170
+ }>, any> extends infer T ? { [k in keyof T]: T[k]; } : never, z.baseObjectInputType<{
171
+ /** Seeded template slug in the notification-service. */
172
+ template: z.ZodString;
173
+ category: z.ZodEnum<[C, ...C[]]>;
174
+ recipient: z.ZodObject<{
175
+ email: z.ZodString;
176
+ name: z.ZodOptional<z.ZodString>;
177
+ userId: z.ZodOptional<z.ZodNullable<z.ZodString>>;
178
+ locale: z.ZodOptional<z.ZodNullable<z.ZodString>>;
179
+ /** IANA zone; time variables are rendered in it. */
180
+ timezone: z.ZodOptional<z.ZodNullable<z.ZodString>>;
181
+ }, "strip", z.ZodTypeAny, {
123
182
  email: string;
124
183
  name?: string | undefined;
125
184
  userId?: string | null | undefined;
126
185
  locale?: string | null | undefined;
127
186
  timezone?: string | null | undefined;
128
- };
129
- channels: ("email" | "in_app")[];
130
- secretKeys: string[];
131
- bypassPreferences?: boolean | undefined;
132
- targetPermission?: string | undefined;
133
- }, {
134
- category: "security" | "account" | "team" | "marketing" | "staff";
135
- template: string;
136
- recipient: {
187
+ }, {
137
188
  email: string;
138
189
  name?: string | undefined;
139
190
  userId?: string | null | undefined;
140
191
  locale?: string | null | undefined;
141
192
  timezone?: string | null | undefined;
142
- };
143
- channels: ("email" | "in_app")[];
144
- data?: Record<string, string | number | boolean | null> | undefined;
145
- secretKeys?: string[] | undefined;
146
- bypassPreferences?: boolean | undefined;
147
- targetPermission?: string | undefined;
148
- }>;
193
+ }>;
194
+ channels: z.ZodEffects<z.ZodArray<z.ZodEnum<[Ch, ...Ch[]]>, "many">, Ch[], Ch[]>;
195
+ data: z.ZodEffects<z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodUnion<[z.ZodString, z.ZodNumber, z.ZodBoolean, z.ZodNull]>>>, Record<string, string | number | boolean | null>, Record<string, string | number | boolean | null> | undefined>;
196
+ /** Keys of `data` holding a one-time secret: used to render, never stored or shown in a feed. */
197
+ secretKeys: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
198
+ /** Deliver whatever the recipient's notification preferences say (for mail the recipient cannot opt out of). */
199
+ bypassPreferences: z.ZodOptional<z.ZodBoolean>;
200
+ /** The permission the recipients were resolved by, when they were (traceability only). */
201
+ targetPermission: z.ZodOptional<z.ZodString>;
202
+ }> extends infer T_1 ? { [k_1 in keyof T_1]: T_1[k_1]; } : never>, z.objectUtil.addQuestionMarks<z.baseObjectOutputType<{
203
+ /** Seeded template slug in the notification-service. */
204
+ template: z.ZodString;
205
+ category: z.ZodEnum<[C, ...C[]]>;
206
+ recipient: z.ZodObject<{
207
+ email: z.ZodString;
208
+ name: z.ZodOptional<z.ZodString>;
209
+ userId: z.ZodOptional<z.ZodNullable<z.ZodString>>;
210
+ locale: z.ZodOptional<z.ZodNullable<z.ZodString>>;
211
+ /** IANA zone; time variables are rendered in it. */
212
+ timezone: z.ZodOptional<z.ZodNullable<z.ZodString>>;
213
+ }, "strip", z.ZodTypeAny, {
214
+ email: string;
215
+ name?: string | undefined;
216
+ userId?: string | null | undefined;
217
+ locale?: string | null | undefined;
218
+ timezone?: string | null | undefined;
219
+ }, {
220
+ email: string;
221
+ name?: string | undefined;
222
+ userId?: string | null | undefined;
223
+ locale?: string | null | undefined;
224
+ timezone?: string | null | undefined;
225
+ }>;
226
+ channels: z.ZodEffects<z.ZodArray<z.ZodEnum<[Ch, ...Ch[]]>, "many">, Ch[], Ch[]>;
227
+ data: z.ZodEffects<z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodUnion<[z.ZodString, z.ZodNumber, z.ZodBoolean, z.ZodNull]>>>, Record<string, string | number | boolean | null>, Record<string, string | number | boolean | null> | undefined>;
228
+ /** Keys of `data` holding a one-time secret: used to render, never stored or shown in a feed. */
229
+ secretKeys: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
230
+ /** Deliver whatever the recipient's notification preferences say (for mail the recipient cannot opt out of). */
231
+ bypassPreferences: z.ZodOptional<z.ZodBoolean>;
232
+ /** The permission the recipients were resolved by, when they were (traceability only). */
233
+ targetPermission: z.ZodOptional<z.ZodString>;
234
+ }>, any> extends infer T_2 ? { [k in keyof T_2]: T_2[k]; } : never, z.baseObjectInputType<{
235
+ /** Seeded template slug in the notification-service. */
236
+ template: z.ZodString;
237
+ category: z.ZodEnum<[C, ...C[]]>;
238
+ recipient: z.ZodObject<{
239
+ email: z.ZodString;
240
+ name: z.ZodOptional<z.ZodString>;
241
+ userId: z.ZodOptional<z.ZodNullable<z.ZodString>>;
242
+ locale: z.ZodOptional<z.ZodNullable<z.ZodString>>;
243
+ /** IANA zone; time variables are rendered in it. */
244
+ timezone: z.ZodOptional<z.ZodNullable<z.ZodString>>;
245
+ }, "strip", z.ZodTypeAny, {
246
+ email: string;
247
+ name?: string | undefined;
248
+ userId?: string | null | undefined;
249
+ locale?: string | null | undefined;
250
+ timezone?: string | null | undefined;
251
+ }, {
252
+ email: string;
253
+ name?: string | undefined;
254
+ userId?: string | null | undefined;
255
+ locale?: string | null | undefined;
256
+ timezone?: string | null | undefined;
257
+ }>;
258
+ channels: z.ZodEffects<z.ZodArray<z.ZodEnum<[Ch, ...Ch[]]>, "many">, Ch[], Ch[]>;
259
+ data: z.ZodEffects<z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodUnion<[z.ZodString, z.ZodNumber, z.ZodBoolean, z.ZodNull]>>>, Record<string, string | number | boolean | null>, Record<string, string | number | boolean | null> | undefined>;
260
+ /** Keys of `data` holding a one-time secret: used to render, never stored or shown in a feed. */
261
+ secretKeys: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
262
+ /** Deliver whatever the recipient's notification preferences say (for mail the recipient cannot opt out of). */
263
+ bypassPreferences: z.ZodOptional<z.ZodBoolean>;
264
+ /** The permission the recipients were resolved by, when they were (traceability only). */
265
+ targetPermission: z.ZodOptional<z.ZodString>;
266
+ }> extends infer T_3 ? { [k_1 in keyof T_3]: T_3[k_1]; } : never>;
149
267
  /**
150
- * The seeded templates Showbiz services dispatch, with the variables (`data` keys) each dispatch carries.
151
- * Producers send exactly these keys; a seeded template may use any subset (notification-service dead-letters
152
- * a dispatch missing a variable its template uses). Both sides test against this table.
268
+ * What a producer stages, as a plain type (usable while categories/channels are still generic, e.g. in
269
+ * shared helpers). Equivalent to {@link NotificationDispatchInput} for a concrete vocabulary.
270
+ *
271
+ * @example
272
+ * const dispatch: NotificationDispatchPayload<'security', 'email'> = {
273
+ * template: 'login-otp', category: 'security', recipient: { email }, channels: ['email'],
274
+ * data: { code }, secretKeys: ['code'], bypassPreferences: true,
275
+ * };
153
276
  */
154
- export declare const DISPATCH_TEMPLATE_VARIABLES: {
155
- readonly 'account-activation': readonly ["name", "code", "expiresInHours"];
156
- readonly 'login-otp': readonly ["name", "code", "expiresInMinutes", "device"];
157
- readonly 'password-reset': readonly ["name", "resetUrl", "expiresInMinutes"];
158
- readonly 'staff-invitation': readonly ["name", "resetUrl", "expiresInMinutes"];
159
- readonly 'password-changed': readonly ["name", "changedAt"];
160
- readonly 'new-device-login': readonly ["name", "device", "ip", "country", "occurredAt", "confirmUrl", "disputeUrl"];
161
- readonly 'team-member-added': readonly ["name", "profileName", "role", "invitedBy"];
162
- readonly 'account-suspended': readonly ["name", "reason"];
163
- readonly 'welcome-listener': readonly ["name"];
164
- readonly 'welcome-creator': readonly ["name"];
165
- readonly ai_budget_threshold: readonly ["requester", "period", "threshold_pct", "spent_usd", "limit_usd"];
166
- readonly newswire_source_unhealthy: readonly ["source_name", "source_slug", "consecutive_failures", "last_error", "last_success_at"];
167
- readonly newswire_block_rate_high: readonly ["blocked", "total", "block_rate_pct", "window_hours"];
168
- readonly autopublish_audit_alert: readonly ["problem_rate_pct", "problem_count", "sample_count", "window_days", "action_taken"];
169
- };
170
- export type DispatchTemplate = keyof typeof DISPATCH_TEMPLATE_VARIABLES;
277
+ export interface NotificationDispatchPayload<C extends string = string, Ch extends string = string> {
278
+ template: string;
279
+ category: C;
280
+ recipient: DispatchRecipient;
281
+ channels: Ch[];
282
+ data?: Record<string, DispatchValue>;
283
+ secretKeys?: string[];
284
+ bypassPreferences?: boolean;
285
+ targetPermission?: string;
286
+ }
287
+ /** The schema type for a given vocabulary. */
288
+ export type NotificationDispatchSchema<C extends string, Ch extends string> = ReturnType<typeof createNotificationDispatchSchema<C, Ch>>;
171
289
  /** The validated command (defaults applied). */
172
- export type NotificationDispatch = z.infer<typeof notificationDispatchSchema>;
290
+ export type NotificationDispatch<C extends string = string, Ch extends string = string> = z.output<NotificationDispatchSchema<C, Ch>>;
173
291
  /** What a producer stages: `data` and `secretKeys` may be omitted. */
174
- export type NotificationDispatchInput = z.input<typeof notificationDispatchSchema>;
292
+ export type NotificationDispatchInput<C extends string = string, Ch extends string = string> = z.input<NotificationDispatchSchema<C, Ch>>;
175
293
  //# sourceMappingURL=notification-dispatch.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"notification-dispatch.d.ts","sourceRoot":"","sources":["../../../src/contracts/notification-dispatch.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;;;;GAKG;AACH,eAAO,MAAM,qBAAqB,0BAA0B,CAAC;AAE7D,wGAAwG;AACxG,eAAO,MAAM,iBAAiB,8BAA+B,CAAC;AAC9D,MAAM,MAAM,eAAe,GAAG,CAAC,OAAO,iBAAiB,CAAC,CAAC,MAAM,CAAC,CAAC;AAEjE;;;GAGG;AACH,eAAO,MAAM,uBAAuB,gEAAiE,CAAC;AACtG,MAAM,MAAM,oBAAoB,GAAG,CAAC,OAAO,uBAAuB,CAAC,CAAC,MAAM,CAAC,CAAC;AAE5E,eAAO,MAAM,eAAe;;;;;;;;;CASlB,CAAC;AAEX,qGAAqG;AACrG,eAAO,MAAM,mBAAmB,iEAAmG,CAAC;AACpI,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAEhE,eAAO,MAAM,uBAAuB;;;;;IAKlC,oDAAoD;;;;;;;;;;;;;;EAEpD,CAAC;AACH,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,uBAAuB,CAAC,CAAC;AAIxE,eAAO,MAAM,0BAA0B;IAEnC,4CAA4C;;;;;;;;QAT9C,oDAAoD;;;;;;;;;;;;;;;;;IAkBlD;;;;OAIG;;IAEH,4FAA4F;;IAE5F,uGAAuG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EASvG,CAAC;AAEL;;;;GAIG;AACH,eAAO,MAAM,2BAA2B;;;;;;;;;;;;;;;CAiBc,CAAC;AACvD,MAAM,MAAM,gBAAgB,GAAG,MAAM,OAAO,2BAA2B,CAAC;AAExE,gDAAgD;AAChD,MAAM,MAAM,oBAAoB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,0BAA0B,CAAC,CAAC;AAC9E,sEAAsE;AACtE,MAAM,MAAM,yBAAyB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,0BAA0B,CAAC,CAAC"}
1
+ {"version":3,"file":"notification-dispatch.d.ts","sourceRoot":"","sources":["../../../src/contracts/notification-dispatch.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;;;GAIG;AACH,eAAO,MAAM,qBAAqB,0BAA0B,CAAC;AAE7D;;;;GAIG;AACH,eAAO,MAAM,eAAe;;;;;;;;;;CAUlB,CAAC;AAEX;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,iEAAmG,CAAC;AACpI,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAEhE;;;;;GAKG;AACH,eAAO,MAAM,uBAAuB;;;;;IAKlC,oDAAoD;;;;;;;;;;;;;;EAEpD,CAAC;AACH,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,uBAAuB,CAAC,CAAC;AAExE;;;;GAIG;AACH,eAAO,MAAM,qBAAqB,aAA2G,CAAC;AAE9I;;;;GAIG;AACH,MAAM,WAAW,kBAAkB,CAAC,CAAC,SAAS,MAAM,EAAE,EAAE,SAAS,MAAM;IACrE,kFAAkF;IAClF,UAAU,EAAE,SAAS,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC;IACjC,8FAA8F;IAC9F,QAAQ,EAAE,SAAS,CAAC,EAAE,EAAE,GAAG,EAAE,EAAE,CAAC,CAAC;CAClC;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,gCAAgC,CAAC,KAAK,CAAC,CAAC,SAAS,MAAM,EAAE,KAAK,CAAC,EAAE,SAAS,MAAM,EAAE,UAAU,EAAE,kBAAkB,CAAC,CAAC,EAAE,EAAE,CAAC;IAGjI,wDAAwD;;;;;;;;QA9C5D,oDAAoD;;;;;;;;;;;;;;;;;IA2DhD,iGAAiG;;IAEjG,gHAAgH;;IAEhH,0FAA0F;;;IAjB1F,wDAAwD;;;;;;;;QA9C5D,oDAAoD;;;;;;;;;;;;;;;;;IA2DhD,iGAAiG;;IAEjG,gHAAgH;;IAEhH,0FAA0F;;;IAjB1F,wDAAwD;;;;;;;;QA9C5D,oDAAoD;;;;;;;;;;;;;;;;;IA2DhD,iGAAiG;;IAEjG,gHAAgH;;IAEhH,0FAA0F;;;IAjB1F,wDAAwD;;;;;;;;QA9C5D,oDAAoD;;;;;;;;;;;;;;;;;IA2DhD,iGAAiG;;IAEjG,gHAAgH;;IAEhH,0FAA0F;;;IAjB1F,wDAAwD;;;;;;;;QA9C5D,oDAAoD;;;;;;;;;;;;;;;;;IA2DhD,iGAAiG;;IAEjG,gHAAgH;;IAEhH,0FAA0F;;kEAU/F;AAED;;;;;;;;;GASG;AACH,MAAM,WAAW,2BAA2B,CAAC,CAAC,SAAS,MAAM,GAAG,MAAM,EAAE,EAAE,SAAS,MAAM,GAAG,MAAM;IAChG,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,CAAC,CAAC;IACZ,SAAS,EAAE,iBAAiB,CAAC;IAC7B,QAAQ,EAAE,EAAE,EAAE,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;IACrC,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,8CAA8C;AAC9C,MAAM,MAAM,0BAA0B,CAAC,CAAC,SAAS,MAAM,EAAE,EAAE,SAAS,MAAM,IAAI,UAAU,CAAC,OAAO,gCAAgC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;AACzI,gDAAgD;AAChD,MAAM,MAAM,oBAAoB,CAAC,CAAC,SAAS,MAAM,GAAG,MAAM,EAAE,EAAE,SAAS,MAAM,GAAG,MAAM,IAAI,CAAC,CAAC,MAAM,CAAC,0BAA0B,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;AACtI,sEAAsE;AACtE,MAAM,MAAM,yBAAyB,CAAC,CAAC,SAAS,MAAM,GAAG,MAAM,EAAE,EAAE,SAAS,MAAM,GAAG,MAAM,IAAI,CAAC,CAAC,KAAK,CAAC,0BAA0B,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC"}
@@ -0,0 +1,46 @@
1
+ import type { Knex } from 'knex';
2
+ import { type MySql2Database } from 'drizzle-orm/mysql2';
3
+ import type mysql from 'mysql2/promise';
4
+ import type { databaseEnv } from '../config/index.js';
5
+ import type { z } from 'zod';
6
+ import { type MysqlConnectionOptions } from './pool.js';
7
+ /** The `DB_*` variables of `baseEnv` (see `databaseEnv` in the config module). */
8
+ export type DatabaseEnv = z.infer<typeof databaseEnv>;
9
+ /** A Drizzle database over a mysql2 pool. */
10
+ export type Database<TSchema extends Record<string, unknown> = Record<string, never>> = MySql2Database<TSchema>;
11
+ /** The transaction handle passed to `db.transaction(async (tx) => …)`. */
12
+ export type Tx<TSchema extends Record<string, unknown> = Record<string, never>> = Parameters<Parameters<Database<TSchema>['transaction']>[0]>[0];
13
+ /** What {@link createDb} returns. The service container owns it and closes it on shutdown. */
14
+ export interface DbHandle<TSchema extends Record<string, unknown>> {
15
+ db: Database<TSchema>;
16
+ /** Readiness probe (`SELECT 1`); rejects when the database is unreachable. */
17
+ ping(): Promise<void>;
18
+ /** Drains and closes the pool. */
19
+ close(): Promise<void>;
20
+ }
21
+ /** Options for {@link createDb}. */
22
+ export interface CreateDbOptions<TSchema extends Record<string, unknown>> {
23
+ /** Validated env (`config` from `loadEnv`). */
24
+ env: DatabaseEnv;
25
+ /** The service's Drizzle schema (tables and relations). */
26
+ schema: TSchema;
27
+ /** Pre-built pool (tests). Default: a utf8mb4, UTC pool sized by `DB_POOL_MAX`. */
28
+ pool?: mysql.Pool;
29
+ }
30
+ /**
31
+ * Creates the service's Drizzle database. There is no module-level singleton: call it once in
32
+ * `container.ts` and inject `db` into repositories.
33
+ *
34
+ * @example const { db, ping, close } = createDb({ env: config, schema });
35
+ */
36
+ export declare function createDb<TSchema extends Record<string, unknown>>(options: CreateDbOptions<TSchema>): DbHandle<TSchema>;
37
+ /**
38
+ * Knex config for a service's `knexfile.js`. Knex runs **migrations and seeds only** (`.cjs` files
39
+ * in `./migrations` and `./seeds`); repositories use Drizzle.
40
+ *
41
+ * @example module.exports = knexConfig(config);
42
+ */
43
+ export declare function knexConfig(env: DatabaseEnv): Knex.Config;
44
+ /** Maps the validated `DB_*` env to connection options. */
45
+ export declare function connectionOptions(env: DatabaseEnv): MysqlConnectionOptions;
46
+ //# sourceMappingURL=db.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"db.d.ts","sourceRoot":"","sources":["../../../src/database/db.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AACjC,OAAO,EAAW,KAAK,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAClE,OAAO,KAAK,KAAK,MAAM,gBAAgB,CAAC;AACxC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,KAAK,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAC7B,OAAO,EAAgD,KAAK,sBAAsB,EAAE,MAAM,WAAW,CAAC;AAEtG,kFAAkF;AAClF,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,WAAW,CAAC,CAAC;AAEtD,6CAA6C;AAC7C,MAAM,MAAM,QAAQ,CAAC,OAAO,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,IAAI,cAAc,CAAC,OAAO,CAAC,CAAC;AAEhH,0EAA0E;AAC1E,MAAM,MAAM,EAAE,CAAC,OAAO,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,IAAI,UAAU,CAAC,UAAU,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAEjJ,8FAA8F;AAC9F,MAAM,WAAW,QAAQ,CAAC,OAAO,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAC/D,EAAE,EAAE,QAAQ,CAAC,OAAO,CAAC,CAAC;IACtB,8EAA8E;IAC9E,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACtB,kCAAkC;IAClC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB;AAED,oCAAoC;AACpC,MAAM,WAAW,eAAe,CAAC,OAAO,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IACtE,+CAA+C;IAC/C,GAAG,EAAE,WAAW,CAAC;IACjB,2DAA2D;IAC3D,MAAM,EAAE,OAAO,CAAC;IAChB,mFAAmF;IACnF,IAAI,CAAC,EAAE,KAAK,CAAC,IAAI,CAAC;CACnB;AAED;;;;;GAKG;AACH,wBAAgB,QAAQ,CAAC,OAAO,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,OAAO,EAAE,eAAe,CAAC,OAAO,CAAC,GAAG,QAAQ,CAAC,OAAO,CAAC,CAStH;AAED;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,GAAG,EAAE,WAAW,GAAG,IAAI,CAAC,MAAM,CAExD;AAED,2DAA2D;AAC3D,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,WAAW,GAAG,sBAAsB,CAE1E"}
@@ -1,25 +1,12 @@
1
- import type { Knex } from 'knex';
2
- import mysql from 'mysql2/promise';
3
- export interface MysqlConnectionOptions {
4
- host: string;
5
- port: number;
6
- user: string;
7
- password: string;
8
- database: string;
9
- poolMax?: number;
10
- }
11
1
  /**
12
- * mysql2 pool for Drizzle. Sessions run in UTC and utf8mb4 so timestamps and emoji round-trip
13
- * unchanged regardless of server defaults.
2
+ * `@heavybit/shared-lib/database`: Drizzle data access for services. Repositories use Drizzle
3
+ * through {@link ScopedRepository} and the scopes in `scopes.ts`; raw scoped queries and Knex in
4
+ * `src/` are forbidden. Knex is for migrations and seeds only (`knexConfig`, `@heavybit/shared-lib/knex`).
14
5
  */
15
- export declare function createMysqlPool(options: MysqlConnectionOptions): mysql.Pool;
16
- /** Knex config for migrations/seeds and query-builder use (same session settings as the pool). */
17
- export declare function createKnexConfig(options: MysqlConnectionOptions & {
18
- migrationsDir?: string;
19
- seedsDir?: string;
20
- }): Knex.Config;
21
- /** Readiness probe helper. */
22
- export declare function pingMysql(pool: mysql.Pool): Promise<void>;
23
- /** Escapes `%`, `_` and `\` for use inside a LIKE pattern. */
24
- export declare function escapeLike(value: string): string;
6
+ export * from './pool.js';
7
+ export * from './db.js';
8
+ export * from './scopes.js';
9
+ export * from './scoped-repository.js';
10
+ export * from './pagination.js';
11
+ export * from './tables.js';
25
12
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/database/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AACjC,OAAO,KAAK,MAAM,gBAAgB,CAAC;AAEnC,MAAM,WAAW,sBAAsB;IACrC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,sBAAsB,GAAG,KAAK,CAAC,IAAI,CAe3E;AAED,kGAAkG;AAClG,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,sBAAsB,GAAG;IAAE,aAAa,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAAC,MAAM,CAgB7H;AAED,8BAA8B;AAC9B,wBAAsB,SAAS,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAE/D;AAED,8DAA8D;AAC9D,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAEhD"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/database/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,cAAc,WAAW,CAAC;AAC1B,cAAc,SAAS,CAAC;AACxB,cAAc,aAAa,CAAC;AAC5B,cAAc,wBAAwB,CAAC;AACvC,cAAc,iBAAiB,CAAC;AAChC,cAAc,aAAa,CAAC"}
@@ -0,0 +1,68 @@
1
+ import { z } from 'zod';
2
+ import type { CursorPage } from '../types/index.js';
3
+ /**
4
+ * Cursor (keyset) pagination (D6). Clients send `?cursor=&limit=`; the cursor is an opaque
5
+ * base64url token of `{ sortKey, id }` (the last row's sort value plus an id tie-breaker).
6
+ * Dependency-light: usable from controllers without the database subpath.
7
+ */
8
+ /** Largest page size a client may ask for. */
9
+ export declare const MAX_PAGE_LIMIT = 100;
10
+ /** Page size when the client sends none. */
11
+ export declare const DEFAULT_PAGE_LIMIT = 20;
12
+ /** `{ cursor?, limit }` query schema: limit 1–100, default 20; unknown keys are stripped. */
13
+ export declare const cursorQuerySchema: z.ZodObject<{
14
+ cursor: z.ZodOptional<z.ZodString>;
15
+ limit: z.ZodDefault<z.ZodNumber>;
16
+ }, "strip", z.ZodTypeAny, {
17
+ limit: number;
18
+ cursor?: string | undefined;
19
+ }, {
20
+ limit?: number | undefined;
21
+ cursor?: string | undefined;
22
+ }>;
23
+ /** Parsed cursor query. */
24
+ export type CursorQuery = z.infer<typeof cursorQuerySchema>;
25
+ /** Sort values a cursor can carry. Dates are carried as ISO strings. */
26
+ export type CursorSortKey = string | number | null;
27
+ /** The decoded content of a cursor. */
28
+ export interface DecodedCursor {
29
+ sortKey: CursorSortKey;
30
+ id: string;
31
+ }
32
+ /**
33
+ * Validates `{ cursor, limit }` from a query string.
34
+ *
35
+ * @throws ValidationError (422) with field errors when invalid.
36
+ * @example const { cursor, limit } = parseCursorQuery(req.query);
37
+ */
38
+ export declare function parseCursorQuery(input: {
39
+ cursor?: unknown;
40
+ limit?: unknown;
41
+ } | undefined): CursorQuery;
42
+ /**
43
+ * Encodes a cursor. Date sort keys become ISO strings.
44
+ *
45
+ * @example encodeCursor({ sortKey: row.createdAt, id: row.id })
46
+ */
47
+ export declare function encodeCursor(cursor: {
48
+ sortKey: CursorSortKey | Date;
49
+ id: string;
50
+ }): string;
51
+ /**
52
+ * Decodes a cursor produced by {@link encodeCursor}.
53
+ *
54
+ * @throws ValidationError (422, field `cursor`) for anything malformed or tampered with.
55
+ */
56
+ export declare function decodeCursor(token: string): DecodedCursor;
57
+ /**
58
+ * Builds a page from `limit + 1` fetched rows: the extra row only signals that more exist.
59
+ *
60
+ * @param rows Rows fetched with `limit + 1`.
61
+ * @param limit The page size.
62
+ * @param cursorOf Extracts `{ sortKey, id }` from a row for the next cursor.
63
+ */
64
+ export declare function buildCursorPage<T>(rows: T[], limit: number, cursorOf: (row: T) => {
65
+ sortKey: CursorSortKey | Date;
66
+ id: string;
67
+ }): CursorPage<T>;
68
+ //# sourceMappingURL=pagination.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pagination.d.ts","sourceRoot":"","sources":["../../../src/database/pagination.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAGpD;;;;GAIG;AAEH,8CAA8C;AAC9C,eAAO,MAAM,cAAc,MAAM,CAAC;AAClC,4CAA4C;AAC5C,eAAO,MAAM,kBAAkB,KAAK,CAAC;AAGrC,6FAA6F;AAC7F,eAAO,MAAM,iBAAiB;;;;;;;;;EAG5B,CAAC;AAEH,2BAA2B;AAC3B,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAE5D,wEAAwE;AACxE,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;AAEnD,uCAAuC;AACvC,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,aAAa,CAAC;IACvB,EAAE,EAAE,MAAM,CAAC;CACZ;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE;IAAE,MAAM,CAAC,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,SAAS,GAAG,WAAW,CAItG;AAMD;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE;IAAE,OAAO,EAAE,aAAa,GAAG,IAAI,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,GAAG,MAAM,CAG1F;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,aAAa,CAczD;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,GAAG,EAAE,CAAC,KAAK;IAAE,OAAO,EAAE,aAAa,GAAG,IAAI,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,GAAG,UAAU,CAAC,CAAC,CAAC,CAK/I"}
@@ -0,0 +1,39 @@
1
+ import type { Knex } from 'knex';
2
+ import mysql from 'mysql2/promise';
3
+ /** Connection settings for {@link createMysqlPool} and {@link createKnexConfig}. */
4
+ export interface MysqlConnectionOptions {
5
+ /** Server host name. */
6
+ host: string;
7
+ /** Server port. */
8
+ port: number;
9
+ /** Database user. */
10
+ user: string;
11
+ /** Database password (never logged). */
12
+ password: string;
13
+ /** The service's own database. */
14
+ database: string;
15
+ /** Maximum pool connections (default 10). */
16
+ poolMax?: number;
17
+ }
18
+ /** Knex-only options for {@link createKnexConfig}. */
19
+ export interface KnexConfigOptions extends MysqlConnectionOptions {
20
+ /** Migrations folder (default `./migrations`). */
21
+ migrationsDir?: string;
22
+ /** Seeds folder (default `./seeds`). */
23
+ seedsDir?: string;
24
+ }
25
+ /**
26
+ * mysql2 pool for Drizzle. Sessions run in UTC and utf8mb4 so timestamps and emoji round-trip
27
+ * unchanged regardless of server defaults.
28
+ */
29
+ export declare function createMysqlPool(options: MysqlConnectionOptions): mysql.Pool;
30
+ /**
31
+ * Knex config for migrations and seeds: mysql2, utf8mb4, UTC, `.cjs` files only. Services call it
32
+ * through `knexConfig(env)` in their `knexfile.js`.
33
+ */
34
+ export declare function createKnexConfig(options: KnexConfigOptions): Knex.Config;
35
+ /** Readiness probe helper. */
36
+ export declare function pingMysql(pool: mysql.Pool): Promise<void>;
37
+ /** Escapes `%`, `_` and `\` for use inside a LIKE pattern. */
38
+ export declare function escapeLike(value: string): string;
39
+ //# sourceMappingURL=pool.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pool.d.ts","sourceRoot":"","sources":["../../../src/database/pool.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AACjC,OAAO,KAAK,MAAM,gBAAgB,CAAC;AAEnC,oFAAoF;AACpF,MAAM,WAAW,sBAAsB;IACrC,wBAAwB;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,mBAAmB;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,qBAAqB;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,wCAAwC;IACxC,QAAQ,EAAE,MAAM,CAAC;IACjB,kCAAkC;IAClC,QAAQ,EAAE,MAAM,CAAC;IACjB,6CAA6C;IAC7C,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,sDAAsD;AACtD,MAAM,WAAW,iBAAkB,SAAQ,sBAAsB;IAC/D,kDAAkD;IAClD,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,wCAAwC;IACxC,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,sBAAsB,GAAG,KAAK,CAAC,IAAI,CAe3E;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,iBAAiB,GAAG,IAAI,CAAC,MAAM,CAgBxE;AAED,8BAA8B;AAC9B,wBAAsB,SAAS,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAE/D;AAED,8DAA8D;AAC9D,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAEhD"}