@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,63 +1,110 @@
1
1
  import { hasAllPermissions, hasPermission, hasRole } from '../access/index.js';
2
- import { safeEqual } from '../common/crypto.js';
3
- import { errorResponse } from '../common/response.js';
4
- import { CONTEXT_HEADERS, verifyIdentityHeaders } from '../identity/index.js';
5
- function resolveIdentity(req, options) {
6
- const result = verifyIdentityHeaders(req.headers, options.secret, { maxSkewMs: options.maxSkewMs });
7
- if (result.status === 'authenticated') {
8
- req.user = result.user;
9
- return 'ok';
10
- }
11
- if (result.status === 'invalid') {
12
- options.logger?.warn('Rejected forged or stale gateway identity', { reason: result.reason, path: req.path });
13
- return 'invalid';
14
- }
15
- return 'anonymous';
2
+ import { errorBody } from '../common/response.js';
3
+ import { correlationId } from './middleware.js';
4
+ import { updateRequestContext } from '../context/index.js';
5
+ import { assertGatewaySecret, canonicalPath, verifyGatewayIdentity, } from '../identity/index.js';
6
+ /** Collapses the user's scope id lists into the request context's `key → id` map (lists are comma-joined). */
7
+ function contextScopes(user) {
8
+ return Object.fromEntries(Object.entries(user.scopes).filter(([, ids]) => ids.length).map(([key, ids]) => [key, ids.join(',')]));
16
9
  }
17
- /** Requires a valid gateway-signed identity; 401 otherwise. */
10
+ function createResolver(options) {
11
+ const secret = assertGatewaySecret(options.secret ?? process.env.GATEWAY_SIGNING_SECRET);
12
+ const reject = (req, reason) => {
13
+ const event = { reason, method: req.method, path: canonicalPath(req.originalUrl), requestId: correlationId(req) };
14
+ options.logger?.warn('Rejected gateway identity', { reason, method: event.method, path: event.path });
15
+ if (!options.onRejected)
16
+ return;
17
+ Promise.resolve()
18
+ .then(() => options.onRejected?.(event, req))
19
+ .catch((error) => options.logger?.error('Identity onRejected hook failed', { error: error.message }));
20
+ };
21
+ return (req) => {
22
+ const result = verifyGatewayIdentity({
23
+ headers: req.headers, method: req.method, path: req.originalUrl, secret, protocol: options.protocol,
24
+ maxAgeMs: options.maxAgeMs, maxFutureMs: options.maxFutureMs,
25
+ });
26
+ if (result.status === 'authenticated') {
27
+ req.user = result.user;
28
+ updateRequestContext({ userId: result.user.id, scopes: contextScopes(result.user) });
29
+ return 'ok';
30
+ }
31
+ if (result.status === 'invalid')
32
+ reject(req, result.reason);
33
+ return result.status === 'invalid' ? 'invalid' : 'anonymous';
34
+ };
35
+ }
36
+ const unauthenticated = (req, res, message = 'Authentication required') => res.status(401).json(errorBody('UNAUTHENTICATED', message, { requestId: correlationId(req) }));
37
+ /**
38
+ * Requires a valid gateway-signed (v2) identity and sets `req.user`; 401 UNAUTHENTICATED (with `requestId`)
39
+ * otherwise. Also fills the request context's `userId` and `scopes` so log lines carry them.
40
+ * Throws at startup when the secret is missing or shorter than 32 characters.
41
+ *
42
+ * @param options Protocol, secret, replay window, logger and rejection hook.
43
+ * @returns Express middleware.
44
+ * @example router.use(authenticateFromGateway({ protocol, logger, onRejected: publishSecurityEvent }));
45
+ */
18
46
  export function authenticateFromGateway(options) {
47
+ const resolve = createResolver(options);
19
48
  return (req, res, next) => {
20
- if (resolveIdentity(req, options) === 'ok')
49
+ if (resolve(req) === 'ok')
21
50
  return next();
22
- res.status(401).json(errorResponse('Authentication required', undefined, 'UNAUTHENTICATED'));
51
+ unauthenticated(req, res);
23
52
  };
24
53
  }
25
- /** Attaches `req.user` when a valid identity is present; continues anonymously otherwise. */
54
+ /**
55
+ * Attaches `req.user` when a valid identity is present and continues anonymously when none is sent,
56
+ * for public routes that personalise. A forged or stale identity is still rejected with 401, never
57
+ * downgraded to anonymous.
58
+ *
59
+ * @param options Same as {@link authenticateFromGateway}.
60
+ * @returns Express middleware.
61
+ * @example router.get('/items', optionalAuthFromGateway({ protocol }), controller.list);
62
+ */
26
63
  export function optionalAuthFromGateway(options) {
64
+ const resolve = createResolver(options);
27
65
  return (req, res, next) => {
28
- if (resolveIdentity(req, options) === 'invalid') {
29
- res.status(401).json(errorResponse('Invalid identity', undefined, 'UNAUTHENTICATED'));
30
- return;
31
- }
66
+ if (resolve(req) === 'invalid')
67
+ return unauthenticated(req, res, 'Invalid identity');
32
68
  next();
33
69
  };
34
70
  }
35
71
  function guard(check, message = 'You do not have permission to perform this action') {
36
72
  return (req, res, next) => {
37
- if (!req.user) {
38
- res.status(401).json(errorResponse('Authentication required', undefined, 'UNAUTHENTICATED'));
39
- return;
40
- }
73
+ if (!req.user)
74
+ return unauthenticated(req, res);
41
75
  if (!check(req.user)) {
42
- res.status(403).json(errorResponse(message, undefined, 'FORBIDDEN'));
76
+ res.status(403).json(errorBody('FORBIDDEN', message, { requestId: correlationId(req) }));
43
77
  return;
44
78
  }
45
79
  next();
46
80
  };
47
81
  }
48
- /** Allows callers holding any of `permissions`. */
82
+ /**
83
+ * Allows callers holding any of `permissions` (401 without identity, 403 FORBIDDEN otherwise).
84
+ * A super admin passes every permission guard.
85
+ *
86
+ * @example router.post('/refunds', requirePermission('refunds.create'), controller.create);
87
+ */
49
88
  export const requirePermission = (...permissions) => guard((u) => hasPermission(u, ...permissions));
50
- /** Allows callers holding every one of `permissions`. */
51
- export const requireAllPermissions = (...permissions) => guard((u) => hasAllPermissions(u, ...permissions));
52
- /** Allows callers with any of `roles`. Prefer permissions; roles are for coarse gates only. */
89
+ /**
90
+ * Allows callers holding every one of `permissions`. Accepts a list or rest arguments.
91
+ *
92
+ * @example router.delete('/x/:id', requireAllPermissions('x.read', 'x.delete'), controller.remove);
93
+ */
94
+ export const requireAllPermissions = (...permissions) => {
95
+ const required = permissions.flat();
96
+ return guard((u) => hasAllPermissions(u, ...required));
97
+ };
98
+ /**
99
+ * Allows callers whose session passed the one-time-code (second factor) check; 403 FORBIDDEN otherwise.
100
+ *
101
+ * @example router.post('/security/settings', requireOtpVerified(), controller.update);
102
+ */
103
+ export const requireOtpVerified = () => guard((u) => u.otpVerified === true, 'One-time code verification is required');
104
+ /**
105
+ * Allows callers with any of `roles`. Prefer permissions; roles are for coarse gates only.
106
+ *
107
+ * @example router.use('/ops', requireRole('platform_admin'));
108
+ */
53
109
  export const requireRole = (...roles) => guard((u) => hasRole(u, ...roles), 'Insufficient role');
54
- /** Guards `/internal/*` routes: requires the shared service secret header. */
55
- export function requireInternalServiceSecret(secret) {
56
- return (req, res, next) => {
57
- const provided = req.headers[CONTEXT_HEADERS.INTERNAL_SECRET];
58
- if (typeof provided === 'string' && secret && safeEqual(provided, secret))
59
- return next();
60
- res.status(401).json(errorResponse('Invalid internal service credentials', undefined, 'UNAUTHENTICATED'));
61
- };
62
- }
63
110
  //# sourceMappingURL=auth.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"auth.js","sourceRoot":"","sources":["../../../src/express/auth.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"auth.js","sourceRoot":"","sources":["../../../src/express/auth.ts"],"names":[],"mappings":"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"}
@@ -2,4 +2,7 @@ export * from './auth.js';
2
2
  export * from './middleware.js';
3
3
  export * from './health.js';
4
4
  export * from './security.js';
5
+ export * from './respond.js';
6
+ export * from './step-up.js';
7
+ export * from './internal-caller.js';
5
8
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/express/index.ts"],"names":[],"mappings":"AAAA,cAAc,WAAW,CAAC;AAC1B,cAAc,iBAAiB,CAAC;AAChC,cAAc,aAAa,CAAC;AAC5B,cAAc,eAAe,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/express/index.ts"],"names":[],"mappings":"AAAA,cAAc,WAAW,CAAC;AAC1B,cAAc,iBAAiB,CAAC;AAChC,cAAc,aAAa,CAAC;AAC5B,cAAc,eAAe,CAAC;AAC9B,cAAc,cAAc,CAAC;AAC7B,cAAc,cAAc,CAAC;AAC7B,cAAc,sBAAsB,CAAC"}
@@ -0,0 +1,49 @@
1
+ import { errorBody } from '../common/response.js';
2
+ import { internalServiceAuthConfigFromEnv, verifyInternalCaller } from '../common/internal-service-auth.js';
3
+ import { canonicalPath } from '../identity/index.js';
4
+ import { correlationId } from './middleware.js';
5
+ const isUnderPrefix = (path, prefix) => path === prefix || path.startsWith(`${prefix}/`);
6
+ /**
7
+ * Builds the internal-caller guard for a service, bound to its internal path prefix and accepted credentials.
8
+ *
9
+ * @param options Path prefix, credentials and logger.
10
+ * @returns `{ requireInternalCaller }`.
11
+ * @throws Error when the prefix is not an absolute path; ConfigError when env credentials are malformed.
12
+ * @example
13
+ * const { requireInternalCaller } = createInternalCallerGuards({ pathPrefix: '/internal', logger });
14
+ * router.get('/internal/records/:id', requireInternalCaller(['alpha-service']), controller.get);
15
+ */
16
+ export function createInternalCallerGuards(options) {
17
+ const prefix = options.pathPrefix.replace(/\/+$/, '');
18
+ if (!prefix.startsWith('/'))
19
+ throw new Error('createInternalCallerGuards: pathPrefix must be an absolute path such as /internal');
20
+ const credentials = options.credentials ?? internalServiceAuthConfigFromEnv().credentials;
21
+ return {
22
+ requireInternalCaller(allowed) {
23
+ if (!allowed.length)
24
+ throw new Error('requireInternalCaller: list at least one caller');
25
+ const callers = [...allowed];
26
+ return (req, res, next) => {
27
+ const path = canonicalPath(req.originalUrl);
28
+ const requestId = correlationId(req);
29
+ if (!isUnderPrefix(path, prefix)) {
30
+ options.logger?.warn('Internal guard outside the internal prefix', { path });
31
+ res.status(403).json(errorBody('FORBIDDEN', 'Not an internal route', { requestId }));
32
+ return;
33
+ }
34
+ const verdict = verifyInternalCaller({ headers: req.headers, credentials, allowed: callers });
35
+ if (verdict.ok) {
36
+ req.internalCaller = verdict.caller;
37
+ return next();
38
+ }
39
+ options.logger?.warn('Rejected internal caller', { reason: verdict.reason, caller: verdict.caller, path });
40
+ if (verdict.status === 401) {
41
+ res.status(401).json(errorBody('UNAUTHENTICATED', 'Invalid internal service credentials', { requestId }));
42
+ return;
43
+ }
44
+ res.status(403).json(errorBody('FORBIDDEN', 'This service may not call this route', { requestId }));
45
+ };
46
+ },
47
+ };
48
+ }
49
+ //# sourceMappingURL=internal-caller.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"internal-caller.js","sourceRoot":"","sources":["../../../src/express/internal-caller.ts"],"names":[],"mappings":"AAYA,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAClD,OAAO,EAAE,gCAAgC,EAAE,oBAAoB,EAAE,MAAM,oCAAoC,CAAC;AAC5G,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAErD,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAkChD,MAAM,aAAa,GAAG,CAAC,IAAY,EAAE,MAAc,EAAW,EAAE,CAAC,IAAI,KAAK,MAAM,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,MAAM,GAAG,CAAC,CAAC;AAElH;;;;;;;;;GASG;AACH,MAAM,UAAU,0BAA0B,CAAC,OAAmC;IAC5E,MAAM,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;IACtD,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,GAAG,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,mFAAmF,CAAC,CAAC;IAClI,MAAM,WAAW,GAAG,OAAO,CAAC,WAAW,IAAI,gCAAgC,EAAE,CAAC,WAAW,CAAC;IAE1F,OAAO;QACL,qBAAqB,CAAC,OAAO;YAC3B,IAAI,CAAC,OAAO,CAAC,MAAM;gBAAE,MAAM,IAAI,KAAK,CAAC,iDAAiD,CAAC,CAAC;YACxF,MAAM,OAAO,GAAG,CAAC,GAAG,OAAO,CAAC,CAAC;YAC7B,OAAO,CAAC,GAAY,EAAE,GAAa,EAAE,IAAkB,EAAE,EAAE;gBACzD,MAAM,IAAI,GAAG,aAAa,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;gBAC5C,MAAM,SAAS,GAAG,aAAa,CAAC,GAAG,CAAC,CAAC;gBACrC,IAAI,CAAC,aAAa,CAAC,IAAI,EAAE,MAAM,CAAC,EAAE,CAAC;oBACjC,OAAO,CAAC,MAAM,EAAE,IAAI,CAAC,4CAA4C,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC;oBAC7E,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,WAAW,EAAE,uBAAuB,EAAE,EAAE,SAAS,EAAE,CAAC,CAAC,CAAC;oBACrF,OAAO;gBACT,CAAC;gBACD,MAAM,OAAO,GAAG,oBAAoB,CAAC,EAAE,OAAO,EAAE,GAAG,CAAC,OAAO,EAAE,WAAW,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC,CAAC;gBAC9F,IAAI,OAAO,CAAC,EAAE,EAAE,CAAC;oBACf,GAAG,CAAC,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC;oBACpC,OAAO,IAAI,EAAE,CAAC;gBAChB,CAAC;gBACD,OAAO,CAAC,MAAM,EAAE,IAAI,CAAC,0BAA0B,EAAE,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;gBAC3G,IAAI,OAAO,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;oBAC3B,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,iBAAiB,EAAE,sCAAsC,EAAE,EAAE,SAAS,EAAE,CAAC,CAAC,CAAC;oBAC1G,OAAO;gBACT,CAAC;gBACD,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,WAAW,EAAE,sCAAsC,EAAE,EAAE,SAAS,EAAE,CAAC,CAAC,CAAC;YACtG,CAAC,CAAC;QACJ,CAAC;KACF,CAAC;AACJ,CAAC"}
@@ -1,84 +1,203 @@
1
- import { isAppError, publicMessage } from '../common/errors.js';
1
+ import { GENERIC_ERROR_MESSAGE, ValidationError, isAppError, publicMessage } from '../common/errors.js';
2
2
  import { newId } from '../common/id.js';
3
- import { errorResponse } from '../common/response.js';
3
+ import { errorBody } from '../common/response.js';
4
+ import { getRequestContext, runWithContext } from '../context/index.js';
4
5
  import { CONTEXT_HEADERS } from '../identity/index.js';
5
6
  import { zodIssuesToErrors } from '../validation/index.js';
6
- /** Ensures every request carries `x-correlation-id` (reused from the gateway when present). */
7
- export function correlationMiddleware() {
7
+ /** An incoming correlation id is trusted only if it is 8–64 chars of `[A-Za-z0-9-]` (no log injection). */
8
+ export const CORRELATION_ID_PATTERN = /^[A-Za-z0-9-]{8,64}$/;
9
+ /**
10
+ * Gives every request one correlation id and runs the rest of the request inside a
11
+ * {@link RequestContext}. A valid incoming `x-correlation-id` (see {@link CORRELATION_ID_PATTERN}) is
12
+ * kept; anything else is replaced with a new ULID. The id is echoed on the response, written back to
13
+ * the request header (so outbound calls forward it) and becomes every error body's `requestId`.
14
+ * Mount it first: correlation → json → routes → notFoundHandler → errorHandler.
15
+ *
16
+ * @param serviceName Recorded in the context (and so in log lines written without a service).
17
+ * @returns Express middleware.
18
+ * @example app.use(correlationMiddleware('orders'));
19
+ */
20
+ export function correlationMiddleware(serviceName) {
8
21
  return (req, res, next) => {
9
22
  const incoming = req.headers[CONTEXT_HEADERS.CORRELATION_ID];
10
- const id = typeof incoming === 'string' && incoming.length <= 128 ? incoming : newId();
23
+ const id = typeof incoming === 'string' && CORRELATION_ID_PATTERN.test(incoming) ? incoming : newId();
11
24
  req.headers[CONTEXT_HEADERS.CORRELATION_ID] = id;
12
25
  res.setHeader(CONTEXT_HEADERS.CORRELATION_ID, id);
13
- next();
26
+ runWithContext({ correlationId: id, service: serviceName }, next);
14
27
  };
15
28
  }
29
+ /**
30
+ * The request's correlation id (the source of every error body's `requestId`): the active request
31
+ * context's id, else the `x-correlation-id` header.
32
+ *
33
+ * @param req Express request.
34
+ * @returns The id, or undefined when neither is present.
35
+ * @example logger.info('Order placed', { correlationId: correlationId(req) });
36
+ */
16
37
  export function correlationId(req) {
38
+ const fromContext = getRequestContext()?.correlationId;
39
+ if (fromContext)
40
+ return fromContext;
17
41
  const value = req.headers[CONTEXT_HEADERS.CORRELATION_ID];
18
42
  return typeof value === 'string' ? value : undefined;
19
43
  }
20
- /** Real client IP: gateway-forwarded header first, then Express's `req.ip`. */
44
+ /**
45
+ * Real client IP: gateway-forwarded header first, then Express's `req.ip`.
46
+ *
47
+ * @param req Express request.
48
+ * @returns The IP, or null when unknown.
49
+ * @example const ip = clientIp(req);
50
+ */
21
51
  export function clientIp(req) {
22
52
  const forwarded = req.headers[CONTEXT_HEADERS.CLIENT_IP];
23
53
  if (typeof forwarded === 'string' && forwarded)
24
54
  return forwarded;
25
55
  return req.ip ?? null;
26
56
  }
57
+ /**
58
+ * The caller's IANA time zone as forwarded by the gateway.
59
+ *
60
+ * @param req Express request.
61
+ * @returns e.g. `Europe/Paris`, or null when not sent.
62
+ * @example formatInTimeZone(date, clientTimezone(req) ?? 'UTC');
63
+ */
27
64
  export function clientTimezone(req) {
28
65
  const tz = req.headers[CONTEXT_HEADERS.TIMEZONE];
29
66
  return typeof tz === 'string' && tz ? tz : null;
30
67
  }
68
+ /**
69
+ * The anonymous visitor id forwarded by the gateway (for anonymous visitor state, rate limits and analytics).
70
+ *
71
+ * @param req Express request.
72
+ * @returns The id, or null when not sent.
73
+ * @example const visitor = req.user?.id ?? anonId(req);
74
+ */
31
75
  export function anonId(req) {
32
76
  const id = req.headers[CONTEXT_HEADERS.ANON_ID];
33
77
  return typeof id === 'string' && id ? id : null;
34
78
  }
35
- /** Validates and replaces `req[part]` with the parsed value; 422 with field errors on failure. */
36
- export function validate(schema, part = 'body') {
37
- return (req, res, next) => {
38
- const result = schema.safeParse(req[part]);
39
- if (!result.success) {
40
- res.status(422).json(errorResponse('Validation failed', zodIssuesToErrors(result.error), 'VALIDATION_FAILED'));
79
+ const PART_ORDER = ['params', 'query', 'body'];
80
+ /** Field-error keys: body fields keep their own path; params/query are prefixed (`query.limit`). */
81
+ function prefixErrors(part, errors) {
82
+ if (part === 'body')
83
+ return errors;
84
+ return Object.fromEntries(Object.entries(errors).map(([key, messages]) => [key === '_' ? part : `${part}.${key}`, messages]));
85
+ }
86
+ function isSchema(value) {
87
+ return typeof value?.safeParse === 'function';
88
+ }
89
+ /**
90
+ * Validates the request with Zod at the controller boundary. Each given part is parsed and
91
+ * `req[part]` is replaced with the parsed value, so unknown keys are stripped (or rejected with
92
+ * `.strict()`) and coercions/defaults apply. Every part is checked before failing, and the combined
93
+ * field errors are thrown as a 422 {@link ValidationError} for the error handler (body fields keep
94
+ * their path; params and query fields are prefixed, e.g. `params.id`).
95
+ *
96
+ * The older single-schema form `validate(schema, part = 'body')` still works.
97
+ *
98
+ * @param schemas `{ params?, query?, body? }`, or one schema.
99
+ * @param part With a single schema: the part it checks (default `body`).
100
+ * @returns Express middleware.
101
+ * @example router.patch('/:id', validate({ params: idParam, body: updateSchema }), controller.update);
102
+ */
103
+ export function validate(schemas, part = 'body') {
104
+ const parts = isSchema(schemas) ? { [part]: schemas } : schemas;
105
+ return (req, _res, next) => {
106
+ let errors = {};
107
+ const parsed = {};
108
+ for (const name of PART_ORDER) {
109
+ const schema = parts[name];
110
+ if (!schema)
111
+ continue;
112
+ const result = schema.safeParse(req[name]);
113
+ if (result.success)
114
+ parsed[name] = result.data;
115
+ else
116
+ errors = { ...errors, ...prefixErrors(name, zodIssuesToErrors(result.error)) };
117
+ }
118
+ if (Object.keys(errors).length > 0) {
119
+ next(new ValidationError(errors));
41
120
  return;
42
121
  }
43
- // Express 5 exposes req.query as a getter; redefine to store the parsed value.
44
- Object.defineProperty(req, part, { value: result.data, writable: true, configurable: true, enumerable: true });
122
+ for (const [name, value] of Object.entries(parsed)) {
123
+ // Express 5 exposes req.query as a getter; redefine to store the parsed value.
124
+ Object.defineProperty(req, name, { value, writable: true, configurable: true, enumerable: true });
125
+ }
45
126
  next();
46
127
  };
47
128
  }
48
- /** Wraps an async handler so rejections reach the error middleware (needed on Express 4). */
129
+ /**
130
+ * Wraps an async handler so rejections reach the error middleware (needed on Express 4; harmless on 5).
131
+ *
132
+ * @param fn Async route handler.
133
+ * @returns A handler that forwards rejections to `next(err)`.
134
+ * @example router.get('/:id', asyncHandler(controller.get));
135
+ */
49
136
  export function asyncHandler(fn) {
50
137
  return (req, res, next) => {
51
138
  fn(req, res, next).catch(next);
52
139
  };
53
140
  }
54
- /** Maps AppErrors to their status; everything else becomes a logged 500 with a generic message. */
141
+ /** Zod's error, recognised structurally so a service on another zod copy/version still maps to 422. */
142
+ function isZodError(err) {
143
+ return err instanceof Error && err.name === 'ZodError' && Array.isArray(err.issues);
144
+ }
145
+ /**
146
+ * Last error middleware. Every body is `{ success: false, code, message, errors?, requestId }`:
147
+ * - body-parser failures → 400 BAD_REQUEST / 413 PAYLOAD_TOO_LARGE;
148
+ * - a thrown ZodError (e.g. from `schema.parse`) → 422 VALIDATION_FAILED with field errors;
149
+ * - AppError → its status, code and errors; 4xx keep their message, 5xx get a generic one; a
150
+ * `retryAfterSeconds` becomes the `Retry-After` header;
151
+ * - anything else → logged in full, answered 500 INTERNAL_ERROR with a generic message.
152
+ * `requestId` is the request context's correlation id (set by {@link correlationMiddleware}).
153
+ *
154
+ * @param logger Receives 5xx details (message, stack, path, correlation id); never sent to the client.
155
+ * @returns Express error middleware.
156
+ * @example app.use(notFoundHandler()); app.use(errorHandler(logger));
157
+ */
55
158
  export function errorHandler(logger) {
56
- return (err, req, res, _next) => {
57
- if (res.headersSent)
159
+ return (err, req, res, next) => {
160
+ if (res.headersSent) {
161
+ next(err);
58
162
  return;
163
+ }
164
+ const requestId = correlationId(req);
59
165
  const bodyError = err;
60
166
  if (bodyError?.type === 'entity.parse.failed') {
61
- res.status(400).json(errorResponse('Malformed JSON body', undefined, 'BAD_REQUEST'));
167
+ res.status(400).json(errorBody('BAD_REQUEST', 'Malformed JSON body', { requestId }));
62
168
  return;
63
169
  }
64
170
  if (bodyError?.type === 'entity.too.large') {
65
- res.status(413).json(errorResponse('Request body too large', undefined, 'PAYLOAD_TOO_LARGE'));
171
+ res.status(413).json(errorBody('PAYLOAD_TOO_LARGE', 'Request body too large', { requestId }));
172
+ return;
173
+ }
174
+ if (isZodError(err)) {
175
+ res.status(422).json(errorBody('VALIDATION_FAILED', 'Validation failed', { errors: zodIssuesToErrors(err), requestId }));
66
176
  return;
67
177
  }
68
178
  if (isAppError(err)) {
69
179
  if (err.statusCode >= 500)
70
- logger?.error(err.message, { path: req.path, code: err.code, correlationId: correlationId(req) });
71
- res.status(err.statusCode).json(errorResponse(publicMessage(err), err.errors, err.code));
180
+ logger?.error(err.message, { path: req.path, errorCode: err.code, stack: err.stack, correlationId: requestId });
181
+ if (err.retryAfterSeconds !== undefined)
182
+ res.setHeader('Retry-After', String(Math.max(0, Math.ceil(err.retryAfterSeconds))));
183
+ const errors = err.statusCode < 500 ? err.errors : undefined;
184
+ res.status(err.statusCode).json(errorBody(err.code, publicMessage(err), { errors, requestId }));
72
185
  return;
73
186
  }
74
187
  const error = err instanceof Error ? err : new Error(String(err));
75
- logger?.error('Unhandled error', { error: error.message, stack: error.stack, path: req.path, correlationId: correlationId(req) });
76
- res.status(500).json(errorResponse('Internal server error', undefined, 'INTERNAL_ERROR'));
188
+ logger?.error('Unhandled error', { error: error.message, stack: error.stack, path: req.path, correlationId: requestId });
189
+ res.status(500).json(errorBody('INTERNAL_ERROR', GENERIC_ERROR_MESSAGE, { requestId }));
77
190
  };
78
191
  }
192
+ /**
193
+ * Catch-all for unmatched routes: 404 NOT_FOUND with the request id. Mount after every router.
194
+ *
195
+ * @returns Express middleware.
196
+ * @example app.use(notFoundHandler()); app.use(errorHandler(logger));
197
+ */
79
198
  export function notFoundHandler() {
80
- return (_req, res) => {
81
- res.status(404).json(errorResponse('Resource not found', undefined, 'NOT_FOUND'));
199
+ return (req, res) => {
200
+ res.status(404).json(errorBody('NOT_FOUND', 'Resource not found', { requestId: correlationId(req) }));
82
201
  };
83
202
  }
84
203
  //# sourceMappingURL=middleware.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"middleware.js","sourceRoot":"","sources":["../../../src/express/middleware.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAChE,OAAO,EAAE,KAAK,EAAE,MAAM,iBAAiB,CAAC;AACxC,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AACtD,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAEvD,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAE3D,+FAA+F;AAC/F,MAAM,UAAU,qBAAqB;IACnC,OAAO,CAAC,GAAY,EAAE,GAAa,EAAE,IAAkB,EAAE,EAAE;QACzD,MAAM,QAAQ,GAAG,GAAG,CAAC,OAAO,CAAC,eAAe,CAAC,cAAc,CAAC,CAAC;QAC7D,MAAM,EAAE,GAAG,OAAO,QAAQ,KAAK,QAAQ,IAAI,QAAQ,CAAC,MAAM,IAAI,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC;QACvF,GAAG,CAAC,OAAO,CAAC,eAAe,CAAC,cAAc,CAAC,GAAG,EAAE,CAAC;QACjD,GAAG,CAAC,SAAS,CAAC,eAAe,CAAC,cAAc,EAAE,EAAE,CAAC,CAAC;QAClD,IAAI,EAAE,CAAC;IACT,CAAC,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,GAAY;IACxC,MAAM,KAAK,GAAG,GAAG,CAAC,OAAO,CAAC,eAAe,CAAC,cAAc,CAAC,CAAC;IAC1D,OAAO,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC;AACvD,CAAC;AAED,+EAA+E;AAC/E,MAAM,UAAU,QAAQ,CAAC,GAAY;IACnC,MAAM,SAAS,GAAG,GAAG,CAAC,OAAO,CAAC,eAAe,CAAC,SAAS,CAAC,CAAC;IACzD,IAAI,OAAO,SAAS,KAAK,QAAQ,IAAI,SAAS;QAAE,OAAO,SAAS,CAAC;IACjE,OAAO,GAAG,CAAC,EAAE,IAAI,IAAI,CAAC;AACxB,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,GAAY;IACzC,MAAM,EAAE,GAAG,GAAG,CAAC,OAAO,CAAC,eAAe,CAAC,QAAQ,CAAC,CAAC;IACjD,OAAO,OAAO,EAAE,KAAK,QAAQ,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;AAClD,CAAC;AAED,MAAM,UAAU,MAAM,CAAC,GAAY;IACjC,MAAM,EAAE,GAAG,GAAG,CAAC,OAAO,CAAC,eAAe,CAAC,OAAO,CAAC,CAAC;IAChD,OAAO,OAAO,EAAE,KAAK,QAAQ,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;AAClD,CAAC;AAID,kGAAkG;AAClG,MAAM,UAAU,QAAQ,CAAC,MAAkB,EAAE,OAAoB,MAAM;IACrE,OAAO,CAAC,GAAY,EAAE,GAAa,EAAE,IAAkB,EAAE,EAAE;QACzD,MAAM,MAAM,GAAG,MAAM,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC;QAC3C,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;YACpB,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,mBAAmB,EAAE,iBAAiB,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,mBAAmB,CAAC,CAAC,CAAC;YAC/G,OAAO;QACT,CAAC;QACD,+EAA+E;QAC/E,MAAM,CAAC,cAAc,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE,KAAK,EAAE,MAAM,CAAC,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,YAAY,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC,CAAC;QAC/G,IAAI,EAAE,CAAC;IACT,CAAC,CAAC;AACJ,CAAC;AAED,6FAA6F;AAC7F,MAAM,UAAU,YAAY,CAAgC,EAAqE;IAC/H,OAAO,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE;QACxB,EAAE,CAAC,GAAU,EAAE,GAAG,EAAE,IAAI,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IACxC,CAAC,CAAC;AACJ,CAAC;AAED,mGAAmG;AACnG,MAAM,UAAU,YAAY,CAAC,MAAe;IAC1C,OAAO,CAAC,GAAY,EAAE,GAAY,EAAE,GAAa,EAAE,KAAmB,EAAE,EAAE;QACxE,IAAI,GAAG,CAAC,WAAW;YAAE,OAAO;QAC5B,MAAM,SAAS,GAAG,GAAyC,CAAC;QAC5D,IAAI,SAAS,EAAE,IAAI,KAAK,qBAAqB,EAAE,CAAC;YAC9C,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,qBAAqB,EAAE,SAAS,EAAE,aAAa,CAAC,CAAC,CAAC;YACrF,OAAO;QACT,CAAC;QACD,IAAI,SAAS,EAAE,IAAI,KAAK,kBAAkB,EAAE,CAAC;YAC3C,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,wBAAwB,EAAE,SAAS,EAAE,mBAAmB,CAAC,CAAC,CAAC;YAC9F,OAAO;QACT,CAAC;QACD,IAAI,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;YACpB,IAAI,GAAG,CAAC,UAAU,IAAI,GAAG;gBAAE,MAAM,EAAE,KAAK,CAAC,GAAG,CAAC,OAAO,EAAE,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,aAAa,EAAE,aAAa,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YAC7H,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,aAAa,CAAC,GAAG,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC;YACzF,OAAO;QACT,CAAC;QACD,MAAM,KAAK,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;QAClE,MAAM,EAAE,KAAK,CAAC,iBAAiB,EAAE,EAAE,KAAK,EAAE,KAAK,CAAC,OAAO,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,aAAa,EAAE,aAAa,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAClI,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,uBAAuB,EAAE,SAAS,EAAE,gBAAgB,CAAC,CAAC,CAAC;IAC5F,CAAC,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,eAAe;IAC7B,OAAO,CAAC,IAAa,EAAE,GAAa,EAAE,EAAE;QACtC,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,oBAAoB,EAAE,SAAS,EAAE,WAAW,CAAC,CAAC,CAAC;IACpF,CAAC,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"middleware.js","sourceRoot":"","sources":["../../../src/express/middleware.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,42 @@
1
+ import { cursorPaginatedResponse, successResponse } from '../common/response.js';
2
+ /**
3
+ * 200 with the success envelope.
4
+ *
5
+ * @param res Express response.
6
+ * @param data The payload placed under `data`.
7
+ * @param message Optional human-readable message (default `Success`).
8
+ * @param meta Optional list metadata.
9
+ * @returns The Express response.
10
+ * @example ok(res, product, 'Product updated');
11
+ */
12
+ export function ok(res, data, message, meta) {
13
+ const body = successResponse(data, message);
14
+ return res.status(200).json(meta ? { ...body, meta } : body);
15
+ }
16
+ /**
17
+ * 201 with the success envelope, for a newly created resource.
18
+ *
19
+ * @param res Express response.
20
+ * @param data The created resource (or its id).
21
+ * @param message Optional message (default `Created`).
22
+ * @returns The Express response.
23
+ *
24
+ * @example created(res, { id }, 'Shop created');
25
+ */
26
+ export function created(res, data, message = 'Created') {
27
+ return res.status(201).json(successResponse(data, message));
28
+ }
29
+ /**
30
+ * 200 with a cursor-paginated list: `data` is the items, `meta` is `{ limit, nextCursor, hasMore }`.
31
+ *
32
+ * @param res Express response.
33
+ * @param page The page from the service (see `buildCursorPageMeta`).
34
+ * @param message Optional message.
35
+ * @returns The Express response.
36
+ *
37
+ * @example okPage(res, await service.list(scope, { cursor, limit }));
38
+ */
39
+ export function okPage(res, page, message) {
40
+ return res.status(200).json(cursorPaginatedResponse(page, message));
41
+ }
42
+ //# sourceMappingURL=respond.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"respond.js","sourceRoot":"","sources":["../../../src/express/respond.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,uBAAuB,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAGjF;;;;;;;;;GASG;AACH,MAAM,UAAU,EAAE,CAAI,GAAa,EAAE,IAAO,EAAE,OAAgB,EAAE,IAAsC;IACpG,MAAM,IAAI,GAAG,eAAe,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IAC5C,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,GAAG,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;AAC/D,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,OAAO,CAAI,GAAa,EAAE,IAAO,EAAE,OAAO,GAAG,SAAS;IACpE,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,eAAe,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC;AAC9D,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,MAAM,CAAI,GAAa,EAAE,IAAmB,EAAE,OAAgB;IAC5E,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,uBAAuB,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC;AACtE,CAAC"}
@@ -0,0 +1,38 @@
1
+ import { errorBody } from '../common/response.js';
2
+ import { DEFAULT_STEP_UP_WINDOW_MS, STEP_UP_REQUIRED_CODE, stepUpStatus, } from '../identity/step-up.js';
3
+ import { correlationId } from './middleware.js';
4
+ /**
5
+ * Builds the step-up guard for an app's catalogue. Must run after `authenticateFromGateway`, which sets
6
+ * `req.user.stepUpAt` only from the signed `x-step-up-at` header. Single-use proofs are consumed by the
7
+ * gateway; the service sees only the verified time. Throws at startup for an unknown scope or a bad window.
8
+ *
9
+ * @param options Catalogue, freshness window and clock.
10
+ * @returns `requireStepUp(scope, route?)` and `declaredStepUpRoutes()`.
11
+ * @example
12
+ * const { requireStepUp } = createStepUpGuards({ catalogue: stepUpScopes });
13
+ * router.post('/refunds/:id', requirePermission('refunds.create'), requireStepUp('refund-large', 'POST /refunds/:id'), controller.refund);
14
+ */
15
+ export function createStepUpGuards(options) {
16
+ const windowMs = options.windowMs ?? DEFAULT_STEP_UP_WINDOW_MS;
17
+ if (!Number.isFinite(windowMs) || windowMs <= 0)
18
+ throw new Error(`createStepUpGuards: windowMs must be positive (got ${windowMs})`);
19
+ const now = options.now ?? Date.now;
20
+ const declared = [];
21
+ const requireStepUp = (scope, route) => {
22
+ const rules = options.catalogue[scope];
23
+ if (!rules)
24
+ throw new Error(`requireStepUp: unknown step-up scope "${scope}"`);
25
+ declared.push({ scope, singleUse: rules.singleUse === true, ...(route ? { route } : {}) });
26
+ return (req, res, next) => {
27
+ if (!req.user) {
28
+ res.status(401).json(errorBody('UNAUTHENTICATED', 'Authentication required', { requestId: correlationId(req) }));
29
+ return;
30
+ }
31
+ if (stepUpStatus(req.user.stepUpAt, windowMs, now()) === 'fresh')
32
+ return next();
33
+ res.status(403).json({ ...errorBody(STEP_UP_REQUIRED_CODE, 'Please confirm it is you to continue', { requestId: correlationId(req) }), scope });
34
+ };
35
+ };
36
+ return { requireStepUp, declaredStepUpRoutes: () => declared.map((entry) => ({ ...entry })) };
37
+ }
38
+ //# sourceMappingURL=step-up.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"step-up.js","sourceRoot":"","sources":["../../../src/express/step-up.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAClD,OAAO,EACL,yBAAyB,EAAE,qBAAqB,EAAE,YAAY,GAC/D,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AA4BhD;;;;;;;;;;GAUG;AACH,MAAM,UAAU,kBAAkB,CAAmB,OAA8B;IACjF,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,IAAI,yBAAyB,CAAC;IAC/D,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,QAAQ,IAAI,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,sDAAsD,QAAQ,GAAG,CAAC,CAAC;IACpI,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC;IACpC,MAAM,QAAQ,GAAkC,EAAE,CAAC;IAEnD,MAAM,aAAa,GAAG,CAAC,KAAQ,EAAE,KAAc,EAAkB,EAAE;QACjE,MAAM,KAAK,GAAG,OAAO,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;QACvC,IAAI,CAAC,KAAK;YAAE,MAAM,IAAI,KAAK,CAAC,yCAAyC,KAAK,GAAG,CAAC,CAAC;QAC/E,QAAQ,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,CAAC,SAAS,KAAK,IAAI,EAAE,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;QAC3F,OAAO,CAAC,GAAY,EAAE,GAAa,EAAE,IAAkB,EAAE,EAAE;YACzD,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC;gBACd,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,iBAAiB,EAAE,yBAAyB,EAAE,EAAE,SAAS,EAAE,aAAa,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC;gBACjH,OAAO;YACT,CAAC;YACD,IAAI,YAAY,CAAC,GAAG,CAAC,IAAI,CAAC,QAAQ,EAAE,QAAQ,EAAE,GAAG,EAAE,CAAC,KAAK,OAAO;gBAAE,OAAO,IAAI,EAAE,CAAC;YAChF,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,GAAG,SAAS,CAAC,qBAAqB,EAAE,sCAAsC,EAAE,EAAE,SAAS,EAAE,aAAa,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC;QAClJ,CAAC,CAAC;IACJ,CAAC,CAAC;IAEF,OAAO,EAAE,aAAa,EAAE,oBAAoB,EAAE,GAAG,EAAE,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,KAAK,EAAE,CAAC,CAAC,EAAE,CAAC;AAChG,CAAC"}