@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,5 +1,5 @@
1
1
  import { newId } from '../common/id.js';
2
- import { signIdentityHeaders } from '../identity/index.js';
2
+ import { signedIdentityHeaders } from '../identity/index.js';
3
3
  import { buildEnvelope } from '../messaging/envelope.js';
4
4
  /** A logger spy: records every line (level, message, meta) so tests can assert on log context. */
5
5
  export class RecordingLogger {
@@ -13,24 +13,26 @@ export class RecordingLogger {
13
13
  export function makeAuthUser(overrides = {}) {
14
14
  return { id: newId(), email: 'user@example.com', roles: [], permissions: [], scopes: {}, attrs: {}, ...overrides };
15
15
  }
16
- /** Gateway-signed identity headers for route tests (use with supertest `.set()`). */
17
- export function identityHeaders(user, secret) {
18
- return signIdentityHeaders(user, secret);
16
+ /**
17
+ * Gateway-signed (v2) identity headers for route tests (use with supertest `.set()`). The method and path
18
+ * must match the request the test sends.
19
+ *
20
+ * @example request(app).get('/v1/items').set(identityHeaders({ user, secret, protocol, method: 'GET', path: '/v1/items' }));
21
+ */
22
+ export function identityHeaders(input) {
23
+ return signedIdentityHeaders(input);
19
24
  }
20
- /** Records everything published/sent so tests can assert on events, commands and direct queue sends. */
25
+ /** Records everything published/sent so tests can assert on events, commands, queue sends and delayed sends. */
21
26
  export class RecordingBus {
22
- source;
23
27
  events = [];
24
28
  commands = [];
25
29
  queued = [];
30
+ delayed = [];
26
31
  failWith = null;
27
- constructor(source = 'test') {
28
- this.source = source;
29
- }
30
- async publish(type, data, meta) {
32
+ async publish(routingKey, data, meta) {
31
33
  if (this.failWith)
32
34
  throw this.failWith;
33
- const envelope = buildEnvelope(type, data, this.source, meta);
35
+ const envelope = buildEnvelope(routingKey, data, meta);
34
36
  this.events.push(envelope);
35
37
  return envelope;
36
38
  }
@@ -42,7 +44,7 @@ export class RecordingBus {
42
44
  async send(command, data, meta) {
43
45
  if (this.failWith)
44
46
  throw this.failWith;
45
- const envelope = buildEnvelope(command, data, this.source, meta);
47
+ const envelope = buildEnvelope(command, data, meta);
46
48
  this.commands.push(envelope);
47
49
  return envelope;
48
50
  }
@@ -56,11 +58,18 @@ export class RecordingBus {
56
58
  throw this.failWith;
57
59
  this.queued.push({ queue, payload, options });
58
60
  }
59
- eventsOfType(type) {
60
- return this.events.filter((e) => e.type === type);
61
+ async scheduleDelayed(queue, payload, delayMs, options = {}) {
62
+ if (this.failWith)
63
+ throw this.failWith;
64
+ this.delayed.push({ queue, payload, delayMs, options });
65
+ }
66
+ /** Published events with this routing key. */
67
+ eventsOfType(routingKey) {
68
+ return this.events.filter((e) => e.routingKey === routingKey);
61
69
  }
70
+ /** Sent commands with this name. */
62
71
  commandsOfType(command) {
63
- return this.commands.filter((e) => e.type === command);
72
+ return this.commands.filter((e) => e.routingKey === command);
64
73
  }
65
74
  }
66
75
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/testing/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/testing/index.ts"],"names":[],"mappings":"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"}
@@ -1,14 +1,25 @@
1
1
  import { z } from 'zod';
2
2
  import { ID_PATTERN } from '../common/id.js';
3
3
  import { ValidationError } from '../common/errors.js';
4
+ /** A ULID in canonical form (26 upper-case Crockford base32 chars), as `newId()` makes and `CHAR(26)` stores. */
4
5
  export const idSchema = z.string().regex(ID_PATTERN, 'Invalid id');
6
+ /** Route params `{ id }` where `id` is a canonical ULID. Use with `validate({ params: idParamSchema })`. */
5
7
  export const idParamSchema = z.object({ id: idSchema });
8
+ /**
9
+ * Alias of {@link idParamSchema} for `validate({ params: idParam })`. Stricter than `z.string().ulid()`:
10
+ * lower-case ULIDs are rejected, so a lookup never misses a stored upper-case id.
11
+ */
12
+ export const idParam = idParamSchema;
13
+ /** Offset pagination query: `page` ≥ 1 (default 1), `limit` 1–100 (default 20). Prefer cursor pagination (D6). */
6
14
  export const paginationSchema = z.object({
7
15
  page: z.coerce.number().int().min(1).default(1),
8
16
  limit: z.coerce.number().int().min(1).max(100).default(20),
9
17
  });
18
+ /** `asc` or `desc`, default `desc`. */
10
19
  export const sortOrderSchema = z.enum(['asc', 'desc']).default('desc');
20
+ /** Trimmed, lower-cased email address, max 191 chars. */
11
21
  export const emailSchema = z.string().trim().toLowerCase().email('Invalid email address').max(191);
22
+ /** Lower-case kebab-case slug, 1–120 chars. */
12
23
  export const slugSchema = z.string().trim().min(1).max(120).regex(/^[a-z0-9]+(?:-[a-z0-9]+)*$/, 'Invalid slug');
13
24
  /** Length-first password rule (NIST 800-63B): 8–128 chars, no composition rules. Tighten per app if needed. */
14
25
  export const passwordSchema = z.string().min(8, 'Password must be at least 8 characters').max(128, 'Password is too long');
@@ -16,7 +27,13 @@ export const passwordSchema = z.string().min(8, 'Password must be at least 8 cha
16
27
  export const booleanish = z.union([z.boolean(), z.enum(['true', 'false', '1', '0', 'yes', 'no'])]).transform((v) => v === true || v === 'true' || v === '1' || v === 'yes');
17
28
  /** Comma list or repeated query param → string[]. */
18
29
  export const csvList = z.union([z.string(), z.array(z.string())]).transform((v) => (Array.isArray(v) ? v : v.split(',')).map((s) => s.trim()).filter(Boolean));
19
- /** Converts Zod issues into the `{ field: [messages] }` shape used by the error envelope. */
30
+ /**
31
+ * Converts Zod issues into the `{ field: [messages] }` shape used by the error envelope.
32
+ *
33
+ * @param error A Zod error.
34
+ * @returns Messages keyed by dotted field path (`_` for the root).
35
+ * @example zodIssuesToErrors(result.error) // { 'items.0': ['Expected string'] }
36
+ */
20
37
  export function zodIssuesToErrors(error) {
21
38
  const errors = {};
22
39
  for (const issue of error.issues) {
@@ -25,12 +42,20 @@ export function zodIssuesToErrors(error) {
25
42
  }
26
43
  return errors;
27
44
  }
28
- /** Parses `input` or throws a ValidationError (422). */
45
+ /**
46
+ * Parses `input` or throws a ValidationError (422).
47
+ *
48
+ * @param schema Zod schema.
49
+ * @param input Untrusted value.
50
+ * @returns The parsed value.
51
+ * @example const dto = parseOrThrow(createSchema, payload);
52
+ */
29
53
  export function parseOrThrow(schema, input) {
30
54
  const result = schema.safeParse(input);
31
55
  if (!result.success)
32
56
  throw new ValidationError(zodIssuesToErrors(result.error));
33
57
  return result.data;
34
58
  }
59
+ /** Zod re-exported so services build schemas on the same zod instance as the library. */
35
60
  export { z };
36
61
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/validation/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/validation/index.ts"],"names":[],"mappings":"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"}
@@ -7,11 +7,16 @@ import type { AuthUser } from '../types/index.js';
7
7
  * wildcards: `*` grants everything; `media.*` grants any permission starting with `media.`.
8
8
  */
9
9
  export declare const WILDCARD_PERMISSION = "*";
10
- /** True when the user holds at least one of `required`. */
11
- export declare function hasPermission(user: Pick<AuthUser, 'permissions'> | null | undefined, ...required: string[]): boolean;
12
- export declare function hasAllPermissions(user: Pick<AuthUser, 'permissions'> | null | undefined, ...required: string[]): boolean;
10
+ type PermissionHolder = Pick<AuthUser, 'permissions' | 'isSuperAdmin'>;
11
+ /** True when the user holds at least one of `required`. A super admin holds every permission. */
12
+ export declare function hasPermission(user: PermissionHolder | null | undefined, ...required: string[]): boolean;
13
+ /** True when the user holds every one of `required`. A super admin holds every permission. */
14
+ export declare function hasAllPermissions(user: PermissionHolder | null | undefined, ...required: string[]): boolean;
15
+ /** True when the user has at least one of `roles`. Prefer permission checks; roles are coarse. */
13
16
  export declare function hasRole(user: Pick<AuthUser, 'roles'> | null | undefined, ...roles: string[]): boolean;
17
+ /** Returns the user, or throws UnauthorizedError (401) when there is none. */
14
18
  export declare function requireUser(user: AuthUser | null | undefined): AuthUser;
19
+ /** Returns the user when they hold any of `required` (super admin always does); throws 401/403 otherwise. */
15
20
  export declare function assertPermission(user: AuthUser | null | undefined, ...required: string[]): AuthUser;
16
21
  /** IDs the user may act on within a scope (e.g. profiles they are a member of). */
17
22
  export declare function scopeIds(user: Pick<AuthUser, 'scopes'> | null | undefined, scope: string): string[];
@@ -47,4 +52,5 @@ export type OwnerFilter = {
47
52
  * otherwise the caller's own scope IDs (possibly empty → return nothing).
48
53
  */
49
54
  export declare function ownerFilter(user: AuthUser | null | undefined, scope: string, bypassPermission?: string): OwnerFilter;
55
+ export {};
50
56
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/access/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAElD;;;;;;GAMG;AACH,eAAO,MAAM,mBAAmB,MAAM,CAAC;AAOvC,2DAA2D;AAC3D,wBAAgB,aAAa,CAAC,IAAI,EAAE,IAAI,CAAC,QAAQ,EAAE,aAAa,CAAC,GAAG,IAAI,GAAG,SAAS,EAAE,GAAG,QAAQ,EAAE,MAAM,EAAE,GAAG,OAAO,CAGpH;AAED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,IAAI,CAAC,QAAQ,EAAE,aAAa,CAAC,GAAG,IAAI,GAAG,SAAS,EAAE,GAAG,QAAQ,EAAE,MAAM,EAAE,GAAG,OAAO,CAGxH;AAED,wBAAgB,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,QAAQ,EAAE,OAAO,CAAC,GAAG,IAAI,GAAG,SAAS,EAAE,GAAG,KAAK,EAAE,MAAM,EAAE,GAAG,OAAO,CAErG;AAED,wBAAgB,WAAW,CAAC,IAAI,EAAE,QAAQ,GAAG,IAAI,GAAG,SAAS,GAAG,QAAQ,CAGvE;AAED,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,QAAQ,GAAG,IAAI,GAAG,SAAS,EAAE,GAAG,QAAQ,EAAE,MAAM,EAAE,GAAG,QAAQ,CAInG;AAED,mFAAmF;AACnF,wBAAgB,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC,QAAQ,EAAE,QAAQ,CAAC,GAAG,IAAI,GAAG,SAAS,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAEnG;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,QAAQ,GAAG,IAAI,GAAG,SAAS,EAAE,WAAW,EAAE,MAAM,EAAE,gBAAgB,CAAC,EAAE,MAAM,GAAG,QAAQ,CAKlI;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,QAAQ,GAAG,IAAI,GAAG,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,EAAE,gBAAgB,CAAC,EAAE,MAAM,GAAG,QAAQ,CAK5I;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,QAAQ,GAAG,IAAI,GAAG,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,CASxH;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,QAAQ,GAAG,IAAI,GAAG,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,CAGzH;AAED,MAAM,MAAM,WAAW,GAAG;IAAE,GAAG,EAAE,IAAI,CAAA;CAAE,GAAG;IAAE,GAAG,EAAE,KAAK,CAAC;IAAC,GAAG,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC;AAExE;;;GAGG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,QAAQ,GAAG,IAAI,GAAG,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,gBAAgB,CAAC,EAAE,MAAM,GAAG,WAAW,CAIpH"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/access/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAElD;;;;;;GAMG;AACH,eAAO,MAAM,mBAAmB,MAAM,CAAC;AAOvC,KAAK,gBAAgB,GAAG,IAAI,CAAC,QAAQ,EAAE,aAAa,GAAG,cAAc,CAAC,CAAC;AAEvE,iGAAiG;AACjG,wBAAgB,aAAa,CAAC,IAAI,EAAE,gBAAgB,GAAG,IAAI,GAAG,SAAS,EAAE,GAAG,QAAQ,EAAE,MAAM,EAAE,GAAG,OAAO,CAIvG;AAED,8FAA8F;AAC9F,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,gBAAgB,GAAG,IAAI,GAAG,SAAS,EAAE,GAAG,QAAQ,EAAE,MAAM,EAAE,GAAG,OAAO,CAI3G;AAED,kGAAkG;AAClG,wBAAgB,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,QAAQ,EAAE,OAAO,CAAC,GAAG,IAAI,GAAG,SAAS,EAAE,GAAG,KAAK,EAAE,MAAM,EAAE,GAAG,OAAO,CAErG;AAED,8EAA8E;AAC9E,wBAAgB,WAAW,CAAC,IAAI,EAAE,QAAQ,GAAG,IAAI,GAAG,SAAS,GAAG,QAAQ,CAGvE;AAED,6GAA6G;AAC7G,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,QAAQ,GAAG,IAAI,GAAG,SAAS,EAAE,GAAG,QAAQ,EAAE,MAAM,EAAE,GAAG,QAAQ,CAInG;AAED,mFAAmF;AACnF,wBAAgB,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC,QAAQ,EAAE,QAAQ,CAAC,GAAG,IAAI,GAAG,SAAS,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAEnG;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,QAAQ,GAAG,IAAI,GAAG,SAAS,EAAE,WAAW,EAAE,MAAM,EAAE,gBAAgB,CAAC,EAAE,MAAM,GAAG,QAAQ,CAKlI;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,QAAQ,GAAG,IAAI,GAAG,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,EAAE,gBAAgB,CAAC,EAAE,MAAM,GAAG,QAAQ,CAK5I;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,QAAQ,GAAG,IAAI,GAAG,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,CASxH;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,QAAQ,GAAG,IAAI,GAAG,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,CAGzH;AAED,MAAM,MAAM,WAAW,GAAG;IAAE,GAAG,EAAE,IAAI,CAAA;CAAE,GAAG;IAAE,GAAG,EAAE,KAAK,CAAC;IAAC,GAAG,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC;AAExE;;;GAGG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,QAAQ,GAAG,IAAI,GAAG,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,gBAAgB,CAAC,EAAE,MAAM,GAAG,WAAW,CAIpH"}
@@ -17,7 +17,7 @@ export interface SlackAlertPayload {
17
17
  export interface SlackAlerterOptions {
18
18
  /** The notification app's URL. Empty = not configured: alerts are logged and skipped. */
19
19
  url: string;
20
- /** Product name shown on the alert, e.g. `Showbiz`. */
20
+ /** Product name shown on the alert, e.g. `Acme Shop`. */
21
21
  app: string;
22
22
  /** `staging`, `production`… */
23
23
  environment: string;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/alerting/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAEhD,4GAA4G;AAC5G,MAAM,WAAW,WAAW;IAC1B,yGAAyG;IACzG,IAAI,CAAC,QAAQ,EAAE,MAAM,EAAE,EAAE,OAAO,CAAC,EAAE;QAAE,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CAC9E;AAED,uGAAuG;AACvG,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,OAAO,CAAC;CACtB;AAED,MAAM,WAAW,mBAAmB;IAClC,yFAAyF;IACzF,GAAG,EAAE,MAAM,CAAC;IACZ,uDAAuD;IACvD,GAAG,EAAE,MAAM,CAAC;IACZ,+BAA+B;IAC/B,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,yDAAyD;IACzD,KAAK,CAAC,EAAE,OAAO,KAAK,CAAC;CACtB;AAED;;;GAGG;AACH,qBAAa,YAAa,YAAW,WAAW;IAIlC,OAAO,CAAC,QAAQ,CAAC,OAAO;IAHpC,OAAO,CAAC,kBAAkB,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;gBAEH,OAAO,EAAE,mBAAmB;IAIzD,IAAI,UAAU,IAAI,OAAO,CAExB;IAEK,IAAI,CAAC,QAAQ,EAAE,MAAM,EAAE,EAAE,OAAO,GAAE;QAAE,SAAS,CAAC,EAAE,MAAM,CAAA;KAAO,GAAG,OAAO,CAAC,OAAO,CAAC;CAgCvF"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/alerting/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAEhD,4GAA4G;AAC5G,MAAM,WAAW,WAAW;IAC1B,yGAAyG;IACzG,IAAI,CAAC,QAAQ,EAAE,MAAM,EAAE,EAAE,OAAO,CAAC,EAAE;QAAE,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CAC9E;AAED,uGAAuG;AACvG,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,OAAO,CAAC;CACtB;AAED,MAAM,WAAW,mBAAmB;IAClC,yFAAyF;IACzF,GAAG,EAAE,MAAM,CAAC;IACZ,yDAAyD;IACzD,GAAG,EAAE,MAAM,CAAC;IACZ,+BAA+B;IAC/B,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,yDAAyD;IACzD,KAAK,CAAC,EAAE,OAAO,KAAK,CAAC;CACtB;AAED;;;GAGG;AACH,qBAAa,YAAa,YAAW,WAAW;IAIlC,OAAO,CAAC,QAAQ,CAAC,OAAO;IAHpC,OAAO,CAAC,kBAAkB,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;gBAEH,OAAO,EAAE,mBAAmB;IAIzD,IAAI,UAAU,IAAI,OAAO,CAExB;IAEK,IAAI,CAAC,QAAQ,EAAE,MAAM,EAAE,EAAE,OAAO,GAAE;QAAE,SAAS,CAAC,EAAE,MAAM,CAAA;KAAO,GAAG,OAAO,CAAC,OAAO,CAAC;CAgCvF"}
@@ -0,0 +1,99 @@
1
+ import type { AuthUser } from '../types/index.js';
2
+ /**
3
+ * Two-person approval state machine. A requester asks for a sensitive operation; a different person who
4
+ * holds both the operation's permission and the app's "decide" permission approves or rejects it; the
5
+ * approved operation is then executed once (or fails). Pending requests expire after a TTL.
6
+ *
7
+ * ```
8
+ * pending ──► approved ──► executed
9
+ * │ └──────► failed
10
+ * ├──► rejected
11
+ * ├──► cancelled
12
+ * └──► expired
13
+ * ```
14
+ *
15
+ * App values (the decide permission and the TTL) are passed in through {@link createApprovalPolicy}.
16
+ */
17
+ /** Every approval state. */
18
+ export declare const APPROVAL_STATES: readonly ["pending", "approved", "rejected", "cancelled", "expired", "executed", "failed"];
19
+ /** One approval state. */
20
+ export type ApprovalState = (typeof APPROVAL_STATES)[number];
21
+ /** The approval facts the checks need. */
22
+ export interface ApprovalSubject {
23
+ state: ApprovalState;
24
+ requesterUserId: string;
25
+ /** Permission needed to perform the operation itself, e.g. `payouts.manage`. */
26
+ operationPermission: string;
27
+ /** When a pending request stops being decidable (omit for no expiry). */
28
+ expiresAt?: Date | string | null;
29
+ }
30
+ /** The caller deciding or cancelling. */
31
+ export type ApprovalActor = Pick<AuthUser, 'id' | 'permissions' | 'isSuperAdmin'>;
32
+ /**
33
+ * True when `from → to` is an allowed move.
34
+ *
35
+ * @example canTransition('pending', 'approved') // true
36
+ */
37
+ export declare function canTransition(from: ApprovalState, to: ApprovalState): boolean;
38
+ /**
39
+ * Throws unless `from → to` is allowed.
40
+ *
41
+ * @throws ConflictError (`APPROVAL_INVALID_TRANSITION`).
42
+ */
43
+ export declare function assertTransition(from: ApprovalState, to: ApprovalState): void;
44
+ /** True when the state allows no further moves. */
45
+ export declare function isTerminalApprovalState(state: ApprovalState): boolean;
46
+ /**
47
+ * Throws unless `approver` may approve or reject `approval`: it is pending and not expired, the approver
48
+ * is not the requester (even a super admin cannot approve their own request), and holds both the
49
+ * operation permission and `decidePermission`.
50
+ *
51
+ * @param approval The request.
52
+ * @param approver The deciding user.
53
+ * @param decidePermission The app's "may decide approvals" permission.
54
+ * @param now Clock (tests).
55
+ * @throws ConflictError (`APPROVAL_NOT_PENDING`, `APPROVAL_EXPIRED`) or ForbiddenError
56
+ * (`APPROVAL_SELF_DECISION`, `APPROVAL_FORBIDDEN`).
57
+ */
58
+ export declare function assertCanDecide(approval: ApprovalSubject, approver: ApprovalActor, decidePermission: string, now?: Date): void;
59
+ /**
60
+ * Throws unless `user` may cancel `approval`: only the requester, and only while it is pending.
61
+ *
62
+ * @throws ConflictError (`APPROVAL_NOT_PENDING`) or ForbiddenError (`APPROVAL_NOT_REQUESTER`).
63
+ */
64
+ export declare function assertCanCancel(approval: ApprovalSubject, user: Pick<AuthUser, 'id'>): void;
65
+ /**
66
+ * When a request made at `from` expires.
67
+ *
68
+ * @param ttlSeconds Positive lifetime in seconds.
69
+ * @param from Creation time (default now).
70
+ * @returns The expiry instant.
71
+ * @example approvalExpiresAt(3600, new Date('2026-01-01T00:00:00Z')) // 2026-01-01T01:00:00Z
72
+ */
73
+ export declare function approvalExpiresAt(ttlSeconds: number, from?: Date): Date;
74
+ /** App settings for {@link createApprovalPolicy}. */
75
+ export interface ApprovalPolicyOptions {
76
+ /** Permission every approver must hold in addition to the operation's permission. */
77
+ decidePermission: string;
78
+ /** Lifetime of a pending request, in seconds. */
79
+ ttlSeconds: number;
80
+ }
81
+ /** The approval rules bound to one app's decide permission and TTL. */
82
+ export interface ApprovalPolicy {
83
+ readonly decidePermission: string;
84
+ readonly ttlSeconds: number;
85
+ canTransition: typeof canTransition;
86
+ assertTransition: typeof assertTransition;
87
+ assertCanDecide(approval: ApprovalSubject, approver: ApprovalActor, now?: Date): void;
88
+ assertCanCancel: typeof assertCanCancel;
89
+ expiresAt(from?: Date): Date;
90
+ }
91
+ /**
92
+ * Binds the approval rules to an app's decide permission and TTL.
93
+ *
94
+ * @param options Decide permission and TTL.
95
+ * @returns The bound policy.
96
+ * @example const approvals = createApprovalPolicy({ decidePermission: 'requests.decide', ttlSeconds: 86_400 });
97
+ */
98
+ export declare function createApprovalPolicy(options: ApprovalPolicyOptions): ApprovalPolicy;
99
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/approvals/index.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAElD;;;;;;;;;;;;;;GAcG;AAEH,4BAA4B;AAC5B,eAAO,MAAM,eAAe,4FAA6F,CAAC;AAE1H,0BAA0B;AAC1B,MAAM,MAAM,aAAa,GAAG,CAAC,OAAO,eAAe,CAAC,CAAC,MAAM,CAAC,CAAC;AAY7D,0CAA0C;AAC1C,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,aAAa,CAAC;IACrB,eAAe,EAAE,MAAM,CAAC;IACxB,gFAAgF;IAChF,mBAAmB,EAAE,MAAM,CAAC;IAC5B,yEAAyE;IACzE,SAAS,CAAC,EAAE,IAAI,GAAG,MAAM,GAAG,IAAI,CAAC;CAClC;AAED,yCAAyC;AACzC,MAAM,MAAM,aAAa,GAAG,IAAI,CAAC,QAAQ,EAAE,IAAI,GAAG,aAAa,GAAG,cAAc,CAAC,CAAC;AAElF;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,aAAa,EAAE,EAAE,EAAE,aAAa,GAAG,OAAO,CAE7E;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,aAAa,EAAE,EAAE,EAAE,aAAa,GAAG,IAAI,CAI7E;AAED,mDAAmD;AACnD,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,aAAa,GAAG,OAAO,CAErE;AAWD;;;;;;;;;;;GAWG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,eAAe,EAAE,QAAQ,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,EAAE,GAAG,GAAE,IAAiB,GAAG,IAAI,CAQ1I;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,eAAe,EAAE,IAAI,EAAE,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,GAAG,IAAI,CAO3F;AAED;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAAC,UAAU,EAAE,MAAM,EAAE,IAAI,GAAE,IAAiB,GAAG,IAAI,CAGnF;AAED,qDAAqD;AACrD,MAAM,WAAW,qBAAqB;IACpC,qFAAqF;IACrF,gBAAgB,EAAE,MAAM,CAAC;IACzB,iDAAiD;IACjD,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,uEAAuE;AACvE,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAClC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,aAAa,EAAE,OAAO,aAAa,CAAC;IACpC,gBAAgB,EAAE,OAAO,gBAAgB,CAAC;IAC1C,eAAe,CAAC,QAAQ,EAAE,eAAe,EAAE,QAAQ,EAAE,aAAa,EAAE,GAAG,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;IACtF,eAAe,EAAE,OAAO,eAAe,CAAC;IACxC,SAAS,CAAC,IAAI,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;CAC9B;AAED;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,qBAAqB,GAAG,cAAc,CAYnF"}
@@ -0,0 +1,82 @@
1
+ /**
2
+ * Business audit events: who did what to which entity, in which scope, and which fields changed. Each
3
+ * service builds the event here and stages it through its outbox for the audit store; the app's contracts
4
+ * map it to its own event payload. Secrets never reach the event: keys matching the redaction list are
5
+ * dropped (only their names are kept), and phone-like values are masked.
6
+ */
7
+ /** The tenant or other scope the audited entity belongs to, e.g. `{ key: 'tenant', id: '01J…' }`. */
8
+ export interface AuditScope {
9
+ key: string;
10
+ id: string;
11
+ }
12
+ /** Whoever performed the action; `null` (or an absent id) means the system. */
13
+ export interface AuditActor {
14
+ id: string | null;
15
+ }
16
+ /** One changed field: its value before and after (null when absent). */
17
+ export interface AuditChange {
18
+ before: unknown;
19
+ after: unknown;
20
+ }
21
+ /** Input to {@link buildAuditEvent}. */
22
+ export interface AuditEventInput {
23
+ /** What happened, as a dotted verb, e.g. `widget.price.changed`. */
24
+ action: string;
25
+ entityType: string;
26
+ entityId: string | null;
27
+ scope: AuditScope | null;
28
+ actor: AuditActor | null;
29
+ /** Entity state before the action (`null` on create). */
30
+ before?: Record<string, unknown> | null;
31
+ /** Entity state after the action (`null` on delete). */
32
+ after?: Record<string, unknown> | null;
33
+ ip?: string | null;
34
+ userAgent?: string | null;
35
+ outcome?: AuditOutcome;
36
+ /** When it happened; defaults to now. */
37
+ occurredAt?: Date | string | number;
38
+ }
39
+ /** Whether the audited action succeeded. */
40
+ export type AuditOutcome = 'success' | 'failure';
41
+ /** The built event: app-neutral, ready for an app binding to map onto its event payload. */
42
+ export interface AuditEvent {
43
+ action: string;
44
+ entityType: string;
45
+ entityId: string | null;
46
+ scope: AuditScope | null;
47
+ actorUserId: string | null;
48
+ outcome: AuditOutcome;
49
+ occurredAt: string;
50
+ /** Only the fields whose value changed (deep comparison), secrets excluded. */
51
+ changes: Record<string, AuditChange>;
52
+ /** Names of changed fields whose values were dropped because they match the redaction list. */
53
+ redactedFields: string[];
54
+ ip: string | null;
55
+ /** Truncated to {@link MAX_USER_AGENT_LENGTH} characters. */
56
+ userAgent: string | null;
57
+ }
58
+ /** Options for {@link createAuditEventBuilder}. */
59
+ export interface AuditEventBuilderOptions {
60
+ /** Extra key words to drop, on top of the logger's default redaction list (`password`, `token`, …). */
61
+ redactKeys?: readonly string[];
62
+ }
63
+ /** User-agent strings are cut to this length. */
64
+ export declare const MAX_USER_AGENT_LENGTH = 255;
65
+ /**
66
+ * Creates an audit-event builder with the app's extra redaction words.
67
+ *
68
+ * @param options Extra redaction words.
69
+ * @returns `buildAuditEvent` bound to that list.
70
+ * @example const buildAudit = createAuditEventBuilder({ redactKeys: ['nationalId'] });
71
+ */
72
+ export declare function createAuditEventBuilder(options?: AuditEventBuilderOptions): (input: AuditEventInput) => AuditEvent;
73
+ /**
74
+ * Builds an audit event with the default redaction list: the diff holds only changed fields; secret
75
+ * fields are dropped (named in `redactedFields`); phone fields are masked to their last 4 digits.
76
+ *
77
+ * @param input Action, entity, scope, actor, before/after state and request facts.
78
+ * @returns The event.
79
+ * @example buildAuditEvent({ action: 'widget.updated', entityType: 'widget', entityId, scope: { key: 'tenant', id: tenantId }, actor: req.user ?? null, before, after, ip: req.ip, userAgent: req.get('user-agent') })
80
+ */
81
+ export declare const buildAuditEvent: (input: AuditEventInput) => AuditEvent;
82
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/audit/index.ts"],"names":[],"mappings":"AAIA;;;;;GAKG;AAEH,qGAAqG;AACrG,MAAM,WAAW,UAAU;IACzB,GAAG,EAAE,MAAM,CAAC;IACZ,EAAE,EAAE,MAAM,CAAC;CACZ;AAED,+EAA+E;AAC/E,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,GAAG,IAAI,CAAC;CACnB;AAED,wEAAwE;AACxE,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,OAAO,CAAC;IAChB,KAAK,EAAE,OAAO,CAAC;CAChB;AAED,wCAAwC;AACxC,MAAM,WAAW,eAAe;IAC9B,oEAAoE;IACpE,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,KAAK,EAAE,UAAU,GAAG,IAAI,CAAC;IACzB,KAAK,EAAE,UAAU,GAAG,IAAI,CAAC;IACzB,yDAAyD;IACzD,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IACxC,wDAAwD;IACxD,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IACvC,EAAE,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,OAAO,CAAC,EAAE,YAAY,CAAC;IACvB,yCAAyC;IACzC,UAAU,CAAC,EAAE,IAAI,GAAG,MAAM,GAAG,MAAM,CAAC;CACrC;AAED,4CAA4C;AAC5C,MAAM,MAAM,YAAY,GAAG,SAAS,GAAG,SAAS,CAAC;AAEjD,4FAA4F;AAC5F,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,KAAK,EAAE,UAAU,GAAG,IAAI,CAAC;IACzB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,OAAO,EAAE,YAAY,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;IACnB,+EAA+E;IAC/E,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;IACrC,+FAA+F;IAC/F,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,EAAE,EAAE,MAAM,GAAG,IAAI,CAAC;IAClB,6DAA6D;IAC7D,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AAED,mDAAmD;AACnD,MAAM,WAAW,wBAAwB;IACvC,uGAAuG;IACvG,UAAU,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CAChC;AAED,iDAAiD;AACjD,eAAO,MAAM,qBAAqB,MAAM,CAAC;AA+BzC;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CAAC,OAAO,GAAE,wBAA6B,GAAG,CAAC,KAAK,EAAE,eAAe,KAAK,UAAU,CAmBtH;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,eAAe,EAAE,CAAC,KAAK,EAAE,eAAe,KAAK,UAAsC,CAAC"}
@@ -1,45 +1,95 @@
1
1
  import { Redis, type RedisOptions } from 'ioredis';
2
2
  import type { Logger } from '../types/index.js';
3
+ import type { RedisKey, RedisKeyspace } from './keyspace.js';
4
+ export * from './keyspace.js';
5
+ /** Per-write options: required TTL and optional invalidation tags. */
3
6
  export interface CacheSetOptions {
4
- ttlSec?: number;
7
+ /** Time to live in whole seconds; required and must be > 0 (every Redis key gets a TTL). */
8
+ ttlSec: number;
5
9
  /** Tags let one call invalidate every key derived from an entity (e.g. `user:01J…`). */
6
10
  tags?: string[];
7
11
  }
12
+ /** Cache-aside store. Keys come only from the cache's {@link RedisKeyspace}; there are no KEYS/FLUSH helpers. */
8
13
  export interface Cache {
9
- get<T>(key: string): Promise<T | undefined>;
10
- set<T>(key: string, value: T, options?: CacheSetOptions): Promise<void>;
11
- del(...keys: string[]): Promise<void>;
14
+ /** Reads a value; `undefined` on a miss. */
15
+ get<T>(key: RedisKey): Promise<T | undefined>;
16
+ /** Stores a value for `ttlSec` seconds. */
17
+ set<T>(key: RedisKey, value: T, options: CacheSetOptions): Promise<void>;
18
+ /** Deletes keys (Redis and the in-memory fallback). */
19
+ del(...keys: RedisKey[]): Promise<void>;
12
20
  /** Cache-aside: returns the cached value or loads, stores and returns it. Concurrent misses share one load. */
13
- wrap<T>(key: string, loader: () => Promise<T>, options?: CacheSetOptions & {
21
+ wrap<T>(key: RedisKey, loader: () => Promise<T>, options: CacheSetOptions & {
14
22
  cacheNull?: boolean;
15
23
  }): Promise<T>;
24
+ /** Deletes every key stored with any of `tags`. */
16
25
  invalidateTags(...tags: string[]): Promise<void>;
17
26
  }
27
+ /** Inputs to {@link createCache}. */
18
28
  export interface CacheOptions {
19
29
  /** Null → in-memory only (tests, local tooling). */
20
30
  redis: Redis | null;
21
- /** Key namespace, e.g. `auth` → keys `auth:<key>`. Combine with the client-level env prefix. */
22
- namespace: string;
23
- defaultTtlSec?: number;
31
+ /** The service keyspace; every key passed in must come from it, and tag sets live under it. */
32
+ keyspace: RedisKeyspace;
24
33
  /** Upper bound for the in-memory layer / fallback. */
25
34
  memoryMaxEntries?: number;
26
35
  logger?: Logger;
27
36
  }
37
+ /** Purpose segment for tag index sets: `{prefix}cache-tag:{tag}`. */
38
+ export declare const CACHE_TAG_PURPOSE = "cache-tag";
39
+ /**
40
+ * Throws unless `ttlSec` is a positive whole number of seconds.
41
+ *
42
+ * @param ttlSec Proposed TTL.
43
+ * @throws ConfigError for ttl ≤ 0, fractions, NaN or Infinity.
44
+ * @example assertTtl(60)
45
+ */
46
+ export declare function assertTtl(ttlSec: number): void;
28
47
  /**
29
48
  * Redis-backed cache with tag invalidation. If Redis errors, reads fall through to the loader
30
49
  * and writes go to a bounded in-memory store, so a cache outage degrades latency rather than
31
50
  * availability.
51
+ *
52
+ * @param options Redis client (or null for memory only), the service keyspace, memory bound, logger.
53
+ * @returns The {@link Cache}.
54
+ * @example
55
+ * const cache = createCache({ redis, keyspace: ks, logger });
56
+ * const widget = await cache.wrap(ks.key('widget', id), () => repo.find(id), { ttlSec: 300 });
32
57
  */
33
58
  export declare function createCache(options: CacheOptions): Cache;
59
+ /** Connection settings for {@link createRedisClient} (the parsed `redisEnv` block fits). */
60
+ export interface RedisConnectionEnv {
61
+ REDIS_URL?: string;
62
+ REDIS_HOST?: string;
63
+ REDIS_PORT?: number;
64
+ REDIS_PASSWORD?: string;
65
+ REDIS_DB?: number;
66
+ }
67
+ /** Extra inputs to {@link createRedisClient}. */
34
68
  export interface RedisClientOptions {
35
- url?: string;
36
- db?: number;
37
- /** Prepended to every key by ioredis, e.g. `showbiz:prod:`. */
38
- keyPrefix?: string;
39
69
  logger?: Logger;
70
+ /** Extra ioredis options. `keyPrefix` is always cleared: the keyspace does the prefixing. */
40
71
  options?: RedisOptions;
41
72
  }
42
- /** ioredis client that never crashes the process on connection errors and retries forever. */
43
- export declare function createRedisClient(opts: RedisClientOptions): Redis;
73
+ /**
74
+ * ioredis client that never crashes the process on connection errors and retries forever.
75
+ * It sets no `keyPrefix`: keys carry their full `{app}:{env}:{service}:` prefix from the keyspace.
76
+ *
77
+ * @param env Connection env (`REDIS_URL`, or host/port/password, and `REDIS_DB`).
78
+ * @param opts Logger and extra ioredis options.
79
+ * @returns The client.
80
+ * @example const redis = createRedisClient(config, { logger });
81
+ */
82
+ export declare function createRedisClient(env: RedisConnectionEnv, opts?: RedisClientOptions): Redis;
83
+ /**
84
+ * `SET key value EX ttlSec` for a keyspace key; refuses a TTL ≤ 0 so no key lives forever.
85
+ *
86
+ * @param redis Client (or anything with a compatible `set`).
87
+ * @param key Key from the service keyspace.
88
+ * @param value String value.
89
+ * @param ttlSec TTL in whole seconds (> 0).
90
+ * @throws ConfigError for an invalid TTL.
91
+ * @example await setWithTtl(redis, ks.key('session', id), payload, 900);
92
+ */
93
+ export declare function setWithTtl(redis: Pick<Redis, 'set'>, key: RedisKey, value: string, ttlSec: number): Promise<void>;
44
94
  export type { Redis };
45
95
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/cache/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,KAAK,YAAY,EAAE,MAAM,SAAS,CAAC;AAEnD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAEhD,MAAM,WAAW,eAAe;IAC9B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,wFAAwF;IACxF,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;CACjB;AAED,MAAM,WAAW,KAAK;IACpB,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC,CAAC;IAC5C,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE,OAAO,CAAC,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACxE,GAAG,CAAC,GAAG,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACtC,+GAA+G;IAC/G,IAAI,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,EAAE,eAAe,GAAG;QAAE,SAAS,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IAChH,cAAc,CAAC,GAAG,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAClD;AAED,MAAM,WAAW,YAAY;IAC3B,oDAAoD;IACpD,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;IACpB,gGAAgG;IAChG,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,sDAAsD;IACtD,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AA+BD;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,YAAY,GAAG,KAAK,CA+FxD;AAED,MAAM,WAAW,kBAAkB;IACjC,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,+DAA+D;IAC/D,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,YAAY,CAAC;CACxB;AAED,8FAA8F;AAC9F,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,kBAAkB,GAAG,KAAK,CAmBjE;AAED,YAAY,EAAE,KAAK,EAAE,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/cache/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,KAAK,YAAY,EAAE,MAAM,SAAS,CAAC;AAGnD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,KAAK,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAE7D,cAAc,eAAe,CAAC;AAE9B,sEAAsE;AACtE,MAAM,WAAW,eAAe;IAC9B,4FAA4F;IAC5F,MAAM,EAAE,MAAM,CAAC;IACf,wFAAwF;IACxF,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;CACjB;AAED,iHAAiH;AACjH,MAAM,WAAW,KAAK;IACpB,4CAA4C;IAC5C,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,QAAQ,GAAG,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC,CAAC;IAC9C,2CAA2C;IAC3C,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC,EAAE,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACzE,uDAAuD;IACvD,GAAG,CAAC,GAAG,IAAI,EAAE,QAAQ,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACxC,+GAA+G;IAC/G,IAAI,CAAC,CAAC,EAAE,GAAG,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,eAAe,GAAG;QAAE,SAAS,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IACjH,mDAAmD;IACnD,cAAc,CAAC,GAAG,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAClD;AAED,qCAAqC;AACrC,MAAM,WAAW,YAAY;IAC3B,oDAAoD;IACpD,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;IACpB,+FAA+F;IAC/F,QAAQ,EAAE,aAAa,CAAC;IACxB,sDAAsD;IACtD,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,qEAAqE;AACrE,eAAO,MAAM,iBAAiB,cAAc,CAAC;AAI7C;;;;;;GAMG;AACH,wBAAgB,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAE9C;AAmCD;;;;;;;;;;GAUG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,YAAY,GAAG,KAAK,CAoGxD;AAED,4FAA4F;AAC5F,MAAM,WAAW,kBAAkB;IACjC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,iDAAiD;AACjD,MAAM,WAAW,kBAAkB;IACjC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,6FAA6F;IAC7F,OAAO,CAAC,EAAE,YAAY,CAAC;CACxB;AAED;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,kBAAkB,EAAE,IAAI,GAAE,kBAAuB,GAAG,KAAK,CAsB/F;AAED;;;;;;;;;GASG;AACH,wBAAsB,UAAU,CAAC,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,KAAK,CAAC,EAAE,GAAG,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAGvH;AAED,YAAY,EAAE,KAAK,EAAE,CAAC"}
@@ -0,0 +1,84 @@
1
+ /**
2
+ * Redis keyspace: every key is `{app}:{env}:{service}:{purpose}[:{part}…]`.
3
+ *
4
+ * One Redis installation may serve several apps and environments, so keys are namespaced by the app
5
+ * slug (a parameter — shared-lib names no app), the environment (`CACHE_KEY_PREFIX`) and the owning
6
+ * service. Build every key through {@link createRedisKeyspace}; never hand-write prefixes.
7
+ *
8
+ * Dependency-free (no ioredis), so it is exported from the package root as well as `/cache`.
9
+ */
10
+ /** A key built by a {@link RedisKeyspace}. The brand stops hand-written strings reaching the cache helpers. */
11
+ export type RedisKey = string & {
12
+ readonly __redisKey: true;
13
+ };
14
+ /** A value to hash before it goes into a key (personal data or secrets). Create with {@link sensitivePart}. */
15
+ export interface SensitiveKeyPart {
16
+ readonly sensitive: string;
17
+ }
18
+ /** One variable part of a key. */
19
+ export type RedisKeyPart = string | number | SensitiveKeyPart;
20
+ /** Inputs to {@link createRedisKeyspace}. */
21
+ export interface RedisKeyspaceOptions {
22
+ /** App slug, e.g. `myapp` (lower-case kebab-case). */
23
+ app: string;
24
+ /** Owning service (the service that writes the key), e.g. `widget-service`. */
25
+ service: string;
26
+ /** Environment segment, normally the `CACHE_KEY_PREFIX` env value (`dev`, `staging`, `prod`). */
27
+ env?: string;
28
+ /** Node environment used for the deployed-environment check (default `process.env.NODE_ENV`). */
29
+ nodeEnv?: string;
30
+ /** When set, hashed parts use HMAC-SHA256 with this secret instead of plain SHA-256. */
31
+ hashSecret?: string;
32
+ }
33
+ /** One service's key builder; every key it makes starts with {@link RedisKeyspace.prefix}. */
34
+ export interface RedisKeyspace {
35
+ readonly app: string;
36
+ readonly env: string;
37
+ readonly service: string;
38
+ /** `{app}:{env}:{service}:` — every key and pattern starts with this. */
39
+ readonly prefix: string;
40
+ /**
41
+ * Builds `{prefix}{purpose}[:{part}…]`. Parts are percent-encoded so they cannot add segments or
42
+ * widen a SCAN pattern; long, email-like, phone-like and {@link sensitivePart} parts are hashed.
43
+ */
44
+ key(purpose: string, ...parts: RedisKeyPart[]): RedisKey;
45
+ /** SCAN pattern anchored on the prefix: `{prefix}{purpose}:{suffix}` (suffix defaults to `*`). */
46
+ pattern(purpose: string, suffix?: string): string;
47
+ /** True when `key` lies inside this keyspace. */
48
+ owns(key: string): key is RedisKey;
49
+ }
50
+ /** Environment segment used outside staging and production when none is given. */
51
+ export declare const DEFAULT_KEY_ENV = "dev";
52
+ /** Parts longer than this are hashed. */
53
+ export declare const MAX_PLAIN_KEY_PART_LENGTH = 64;
54
+ /** Marker that starts every hashed part. */
55
+ export declare const HASHED_PART_PREFIX = "h.";
56
+ /**
57
+ * Marks a key part as personal data or a secret so the keyspace always hashes it.
58
+ *
59
+ * @param value Raw value (e.g. an email address or a token).
60
+ * @returns A part the keyspace hashes.
61
+ * @example ks.key('otp', sensitivePart(email))
62
+ */
63
+ export declare const sensitivePart: (value: string) => SensitiveKeyPart;
64
+ /**
65
+ * Percent-encodes a plain key part, including the glob characters SCAN interprets.
66
+ *
67
+ * @param value Part text (non-empty).
68
+ * @returns The encoded part.
69
+ * @throws ConfigError when the part is empty.
70
+ * @example encodeKeyPart('a:b*') // 'a%3Ab%2A'
71
+ */
72
+ export declare function encodeKeyPart(value: string): string;
73
+ /**
74
+ * Creates the keyspace for one service.
75
+ *
76
+ * @param options App slug, service, env segment (`CACHE_KEY_PREFIX`), optional hash secret.
77
+ * @returns A {@link RedisKeyspace}.
78
+ * @throws ConfigError on an invalid segment, or when `env` is missing in staging/production.
79
+ * @example
80
+ * const ks = createRedisKeyspace({ app: 'myapp', service: 'widget-service', env: config.CACHE_KEY_PREFIX });
81
+ * ks.key('widget', widgetId) // 'myapp:prod:widget-service:widget:01J…'
82
+ */
83
+ export declare function createRedisKeyspace(options: RedisKeyspaceOptions): RedisKeyspace;
84
+ //# sourceMappingURL=keyspace.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"keyspace.d.ts","sourceRoot":"","sources":["../../../src/cache/keyspace.ts"],"names":[],"mappings":"AAGA;;;;;;;;GAQG;AAEH,+GAA+G;AAC/G,MAAM,MAAM,QAAQ,GAAG,MAAM,GAAG;IAAE,QAAQ,CAAC,UAAU,EAAE,IAAI,CAAA;CAAE,CAAC;AAE9D,+GAA+G;AAC/G,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B;AAED,kCAAkC;AAClC,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,MAAM,GAAG,gBAAgB,CAAC;AAE9D,6CAA6C;AAC7C,MAAM,WAAW,oBAAoB;IACnC,sDAAsD;IACtD,GAAG,EAAE,MAAM,CAAC;IACZ,+EAA+E;IAC/E,OAAO,EAAE,MAAM,CAAC;IAChB,iGAAiG;IACjG,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,iGAAiG;IACjG,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,wFAAwF;IACxF,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,8FAA8F;AAC9F,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,yEAAyE;IACzE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB;;;OAGG;IACH,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,KAAK,EAAE,YAAY,EAAE,GAAG,QAAQ,CAAC;IACzD,kGAAkG;IAClG,OAAO,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAClD,iDAAiD;IACjD,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,IAAI,QAAQ,CAAC;CACpC;AAED,kFAAkF;AAClF,eAAO,MAAM,eAAe,QAAQ,CAAC;AACrC,yCAAyC;AACzC,eAAO,MAAM,yBAAyB,KAAK,CAAC;AAC5C,4CAA4C;AAC5C,eAAO,MAAM,kBAAkB,OAAO,CAAC;AAWvC;;;;;;GAMG;AACH,eAAO,MAAM,aAAa,GAAI,OAAO,MAAM,KAAG,gBAA0C,CAAC;AAEzF;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAGnD;AA2BD;;;;;;;;;GASG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,oBAAoB,GAAG,aAAa,CAuBhF"}