@heavybit/shared-lib 2.1.0 → 2.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (296) hide show
  1. package/README.md +163 -2
  2. package/dist/cjs/access/index.js +9 -1
  3. package/dist/cjs/access/index.js.map +1 -1
  4. package/dist/cjs/approvals/index.js +137 -0
  5. package/dist/cjs/approvals/index.js.map +1 -0
  6. package/dist/cjs/audit/index.js +74 -0
  7. package/dist/cjs/audit/index.js.map +1 -0
  8. package/dist/cjs/cache/index.js +90 -16
  9. package/dist/cjs/cache/index.js.map +1 -1
  10. package/dist/cjs/cache/keyspace.js +101 -0
  11. package/dist/cjs/cache/keyspace.js.map +1 -0
  12. package/dist/cjs/common/circuit-breaker.js +74 -0
  13. package/dist/cjs/common/circuit-breaker.js.map +1 -0
  14. package/dist/cjs/common/index.js +4 -0
  15. package/dist/cjs/common/index.js.map +1 -1
  16. package/dist/cjs/common/internal-client.js +177 -0
  17. package/dist/cjs/common/internal-client.js.map +1 -0
  18. package/dist/cjs/common/internal-constants.js +12 -0
  19. package/dist/cjs/common/internal-constants.js.map +1 -0
  20. package/dist/cjs/common/internal-service-auth.js +160 -0
  21. package/dist/cjs/common/internal-service-auth.js.map +1 -0
  22. package/dist/cjs/common/money.js +215 -0
  23. package/dist/cjs/common/money.js.map +1 -0
  24. package/dist/cjs/common/phone.js +41 -22
  25. package/dist/cjs/common/phone.js.map +1 -1
  26. package/dist/cjs/common/time.js +28 -2
  27. package/dist/cjs/common/time.js.map +1 -1
  28. package/dist/cjs/config/index.js +139 -19
  29. package/dist/cjs/config/index.js.map +1 -1
  30. package/dist/cjs/contracts/notification-dispatch.js +1 -1
  31. package/dist/cjs/database/db.js +37 -0
  32. package/dist/cjs/database/db.js.map +1 -0
  33. package/dist/cjs/database/index.js +22 -51
  34. package/dist/cjs/database/index.js.map +1 -1
  35. package/dist/cjs/database/pagination.js +88 -0
  36. package/dist/cjs/database/pagination.js.map +1 -0
  37. package/dist/cjs/database/pool.js +60 -0
  38. package/dist/cjs/database/pool.js.map +1 -0
  39. package/dist/cjs/database/scoped-repository.js +132 -0
  40. package/dist/cjs/database/scoped-repository.js.map +1 -0
  41. package/dist/cjs/database/scopes.js +166 -0
  42. package/dist/cjs/database/scopes.js.map +1 -0
  43. package/dist/cjs/database/table-names.js +14 -0
  44. package/dist/cjs/database/table-names.js.map +1 -0
  45. package/dist/cjs/database/tables.js +71 -0
  46. package/dist/cjs/database/tables.js.map +1 -0
  47. package/dist/cjs/express/auth.js +64 -50
  48. package/dist/cjs/express/auth.js.map +1 -1
  49. package/dist/cjs/express/index.js +2 -0
  50. package/dist/cjs/express/index.js.map +1 -1
  51. package/dist/cjs/express/internal-caller.js +52 -0
  52. package/dist/cjs/express/internal-caller.js.map +1 -0
  53. package/dist/cjs/express/step-up.js +41 -0
  54. package/dist/cjs/express/step-up.js.map +1 -0
  55. package/dist/cjs/http/index.js +3 -71
  56. package/dist/cjs/http/index.js.map +1 -1
  57. package/dist/cjs/identity/index.js +269 -67
  58. package/dist/cjs/identity/index.js.map +1 -1
  59. package/dist/cjs/identity/step-up.js +52 -0
  60. package/dist/cjs/identity/step-up.js.map +1 -0
  61. package/dist/cjs/index.js +6 -0
  62. package/dist/cjs/index.js.map +1 -1
  63. package/dist/cjs/knex/columns.js +36 -0
  64. package/dist/cjs/knex/columns.js.map +1 -0
  65. package/dist/cjs/knex/index.js +26 -43
  66. package/dist/cjs/knex/index.js.map +1 -1
  67. package/dist/cjs/knex/migration-helpers.js +57 -0
  68. package/dist/cjs/knex/migration-helpers.js.map +1 -0
  69. package/dist/cjs/logging/redact.js +6 -11
  70. package/dist/cjs/logging/redact.js.map +1 -1
  71. package/dist/cjs/messaging/base-consumer.js +36 -0
  72. package/dist/cjs/messaging/base-consumer.js.map +1 -0
  73. package/dist/cjs/messaging/consumer.js +142 -73
  74. package/dist/cjs/messaging/consumer.js.map +1 -1
  75. package/dist/cjs/messaging/envelope.js +53 -10
  76. package/dist/cjs/messaging/envelope.js.map +1 -1
  77. package/dist/cjs/messaging/idempotency.js +95 -8
  78. package/dist/cjs/messaging/idempotency.js.map +1 -1
  79. package/dist/cjs/messaging/index.js +2 -1
  80. package/dist/cjs/messaging/index.js.map +1 -1
  81. package/dist/cjs/messaging/outbox.js +97 -55
  82. package/dist/cjs/messaging/outbox.js.map +1 -1
  83. package/dist/cjs/messaging/publisher.js +86 -38
  84. package/dist/cjs/messaging/publisher.js.map +1 -1
  85. package/dist/cjs/messaging/staff-notifier.js +4 -4
  86. package/dist/cjs/messaging/staff-notifier.js.map +1 -1
  87. package/dist/cjs/messaging/topology.js +41 -5
  88. package/dist/cjs/messaging/topology.js.map +1 -1
  89. package/dist/cjs/rate-limit/index.js +57 -6
  90. package/dist/cjs/rate-limit/index.js.map +1 -1
  91. package/dist/cjs/rate-limit/keys.js +47 -0
  92. package/dist/cjs/rate-limit/keys.js.map +1 -0
  93. package/dist/cjs/socket/index.js +127 -16
  94. package/dist/cjs/socket/index.js.map +1 -1
  95. package/dist/cjs/socket/ticket.js +124 -0
  96. package/dist/cjs/socket/ticket.js.map +1 -0
  97. package/dist/cjs/testing/index.js +23 -14
  98. package/dist/cjs/testing/index.js.map +1 -1
  99. package/dist/esm/access/index.js +9 -1
  100. package/dist/esm/access/index.js.map +1 -1
  101. package/dist/esm/approvals/index.js +127 -0
  102. package/dist/esm/approvals/index.js.map +1 -0
  103. package/dist/esm/audit/index.js +70 -0
  104. package/dist/esm/audit/index.js.map +1 -0
  105. package/dist/esm/cache/index.js +70 -13
  106. package/dist/esm/cache/index.js.map +1 -1
  107. package/dist/esm/cache/keyspace.js +95 -0
  108. package/dist/esm/cache/keyspace.js.map +1 -0
  109. package/dist/esm/common/circuit-breaker.js +70 -0
  110. package/dist/esm/common/circuit-breaker.js.map +1 -0
  111. package/dist/esm/common/index.js +4 -0
  112. package/dist/esm/common/index.js.map +1 -1
  113. package/dist/esm/common/internal-client.js +173 -0
  114. package/dist/esm/common/internal-client.js.map +1 -0
  115. package/dist/esm/common/internal-constants.js +9 -0
  116. package/dist/esm/common/internal-constants.js.map +1 -0
  117. package/dist/esm/common/internal-service-auth.js +150 -0
  118. package/dist/esm/common/internal-service-auth.js.map +1 -0
  119. package/dist/esm/common/money.js +207 -0
  120. package/dist/esm/common/money.js.map +1 -0
  121. package/dist/esm/common/phone.js +41 -22
  122. package/dist/esm/common/phone.js.map +1 -1
  123. package/dist/esm/common/time.js +27 -2
  124. package/dist/esm/common/time.js.map +1 -1
  125. package/dist/esm/config/index.js +135 -18
  126. package/dist/esm/config/index.js.map +1 -1
  127. package/dist/esm/contracts/notification-dispatch.js +1 -1
  128. package/dist/esm/database/db.js +32 -0
  129. package/dist/esm/database/db.js.map +1 -0
  130. package/dist/esm/database/index.js +9 -45
  131. package/dist/esm/database/index.js.map +1 -1
  132. package/dist/esm/database/pagination.js +81 -0
  133. package/dist/esm/database/pagination.js.map +1 -0
  134. package/dist/esm/database/pool.js +51 -0
  135. package/dist/esm/database/pool.js.map +1 -0
  136. package/dist/esm/database/scoped-repository.js +128 -0
  137. package/dist/esm/database/scoped-repository.js.map +1 -0
  138. package/dist/esm/database/scopes.js +157 -0
  139. package/dist/esm/database/scopes.js.map +1 -0
  140. package/dist/esm/database/table-names.js +11 -0
  141. package/dist/esm/database/table-names.js.map +1 -0
  142. package/dist/esm/database/tables.js +61 -0
  143. package/dist/esm/database/tables.js.map +1 -0
  144. package/dist/esm/express/auth.js +62 -48
  145. package/dist/esm/express/auth.js.map +1 -1
  146. package/dist/esm/express/index.js +2 -0
  147. package/dist/esm/express/index.js.map +1 -1
  148. package/dist/esm/express/internal-caller.js +49 -0
  149. package/dist/esm/express/internal-caller.js.map +1 -0
  150. package/dist/esm/express/step-up.js +38 -0
  151. package/dist/esm/express/step-up.js.map +1 -0
  152. package/dist/esm/http/index.js +3 -70
  153. package/dist/esm/http/index.js.map +1 -1
  154. package/dist/esm/identity/index.js +246 -64
  155. package/dist/esm/identity/index.js.map +1 -1
  156. package/dist/esm/identity/step-up.js +47 -0
  157. package/dist/esm/identity/step-up.js.map +1 -0
  158. package/dist/esm/index.js +6 -0
  159. package/dist/esm/index.js.map +1 -1
  160. package/dist/esm/knex/columns.js +28 -0
  161. package/dist/esm/knex/columns.js.map +1 -0
  162. package/dist/esm/knex/index.js +8 -34
  163. package/dist/esm/knex/index.js.map +1 -1
  164. package/dist/esm/knex/migration-helpers.js +51 -0
  165. package/dist/esm/knex/migration-helpers.js.map +1 -0
  166. package/dist/esm/logging/redact.js +3 -7
  167. package/dist/esm/logging/redact.js.map +1 -1
  168. package/dist/esm/messaging/base-consumer.js +32 -0
  169. package/dist/esm/messaging/base-consumer.js.map +1 -0
  170. package/dist/esm/messaging/consumer.js +139 -71
  171. package/dist/esm/messaging/consumer.js.map +1 -1
  172. package/dist/esm/messaging/envelope.js +50 -10
  173. package/dist/esm/messaging/envelope.js.map +1 -1
  174. package/dist/esm/messaging/idempotency.js +91 -7
  175. package/dist/esm/messaging/idempotency.js.map +1 -1
  176. package/dist/esm/messaging/index.js +2 -1
  177. package/dist/esm/messaging/index.js.map +1 -1
  178. package/dist/esm/messaging/outbox.js +97 -54
  179. package/dist/esm/messaging/outbox.js.map +1 -1
  180. package/dist/esm/messaging/publisher.js +86 -38
  181. package/dist/esm/messaging/publisher.js.map +1 -1
  182. package/dist/esm/messaging/staff-notifier.js +4 -4
  183. package/dist/esm/messaging/staff-notifier.js.map +1 -1
  184. package/dist/esm/messaging/topology.js +39 -4
  185. package/dist/esm/messaging/topology.js.map +1 -1
  186. package/dist/esm/rate-limit/index.js +41 -6
  187. package/dist/esm/rate-limit/index.js.map +1 -1
  188. package/dist/esm/rate-limit/keys.js +43 -0
  189. package/dist/esm/rate-limit/keys.js.map +1 -0
  190. package/dist/esm/socket/index.js +112 -17
  191. package/dist/esm/socket/index.js.map +1 -1
  192. package/dist/esm/socket/ticket.js +117 -0
  193. package/dist/esm/socket/ticket.js.map +1 -0
  194. package/dist/esm/testing/index.js +24 -15
  195. package/dist/esm/testing/index.js.map +1 -1
  196. package/dist/types/access/index.d.ts +9 -3
  197. package/dist/types/access/index.d.ts.map +1 -1
  198. package/dist/types/approvals/index.d.ts +99 -0
  199. package/dist/types/approvals/index.d.ts.map +1 -0
  200. package/dist/types/audit/index.d.ts +82 -0
  201. package/dist/types/audit/index.d.ts.map +1 -0
  202. package/dist/types/cache/index.d.ts +64 -14
  203. package/dist/types/cache/index.d.ts.map +1 -1
  204. package/dist/types/cache/keyspace.d.ts +84 -0
  205. package/dist/types/cache/keyspace.d.ts.map +1 -0
  206. package/dist/types/common/circuit-breaker.d.ts +51 -0
  207. package/dist/types/common/circuit-breaker.d.ts.map +1 -0
  208. package/dist/types/common/index.d.ts +4 -0
  209. package/dist/types/common/index.d.ts.map +1 -1
  210. package/dist/types/common/internal-client.d.ts +116 -0
  211. package/dist/types/common/internal-client.d.ts.map +1 -0
  212. package/dist/types/common/internal-constants.d.ts +9 -0
  213. package/dist/types/common/internal-constants.d.ts.map +1 -0
  214. package/dist/types/common/internal-service-auth.d.ts +102 -0
  215. package/dist/types/common/internal-service-auth.d.ts.map +1 -0
  216. package/dist/types/common/money.d.ts +104 -0
  217. package/dist/types/common/money.d.ts.map +1 -0
  218. package/dist/types/common/phone.d.ts +25 -15
  219. package/dist/types/common/phone.d.ts.map +1 -1
  220. package/dist/types/common/time.d.ts +20 -2
  221. package/dist/types/common/time.d.ts.map +1 -1
  222. package/dist/types/config/index.d.ts +251 -27
  223. package/dist/types/config/index.d.ts.map +1 -1
  224. package/dist/types/contracts/notification-dispatch.d.ts +13 -13
  225. package/dist/types/database/db.d.ts +46 -0
  226. package/dist/types/database/db.d.ts.map +1 -0
  227. package/dist/types/database/index.d.ts +9 -22
  228. package/dist/types/database/index.d.ts.map +1 -1
  229. package/dist/types/database/pagination.d.ts +68 -0
  230. package/dist/types/database/pagination.d.ts.map +1 -0
  231. package/dist/types/database/pool.d.ts +39 -0
  232. package/dist/types/database/pool.d.ts.map +1 -0
  233. package/dist/types/database/scoped-repository.d.ts +92 -0
  234. package/dist/types/database/scoped-repository.d.ts.map +1 -0
  235. package/dist/types/database/scopes.d.ts +159 -0
  236. package/dist/types/database/scopes.d.ts.map +1 -0
  237. package/dist/types/database/table-names.d.ts +13 -0
  238. package/dist/types/database/table-names.d.ts.map +1 -0
  239. package/dist/types/database/tables.d.ts +748 -0
  240. package/dist/types/database/tables.d.ts.map +1 -0
  241. package/dist/types/express/auth.d.ts +40 -24
  242. package/dist/types/express/auth.d.ts.map +1 -1
  243. package/dist/types/express/index.d.ts +2 -0
  244. package/dist/types/express/index.d.ts.map +1 -1
  245. package/dist/types/express/internal-caller.d.ts +53 -0
  246. package/dist/types/express/internal-caller.d.ts.map +1 -0
  247. package/dist/types/express/step-up.d.ts +38 -0
  248. package/dist/types/express/step-up.d.ts.map +1 -0
  249. package/dist/types/http/index.d.ts +3 -31
  250. package/dist/types/http/index.d.ts.map +1 -1
  251. package/dist/types/identity/index.d.ts +159 -21
  252. package/dist/types/identity/index.d.ts.map +1 -1
  253. package/dist/types/identity/step-up.d.ts +41 -0
  254. package/dist/types/identity/step-up.d.ts.map +1 -0
  255. package/dist/types/index.d.ts +6 -0
  256. package/dist/types/index.d.ts.map +1 -1
  257. package/dist/types/knex/columns.d.ts +15 -0
  258. package/dist/types/knex/columns.d.ts.map +1 -0
  259. package/dist/types/knex/index.d.ts +3 -11
  260. package/dist/types/knex/index.d.ts.map +1 -1
  261. package/dist/types/knex/migration-helpers.d.ts +28 -0
  262. package/dist/types/knex/migration-helpers.d.ts.map +1 -0
  263. package/dist/types/logging/redact.d.ts +2 -2
  264. package/dist/types/logging/redact.d.ts.map +1 -1
  265. package/dist/types/messaging/base-consumer.d.ts +33 -0
  266. package/dist/types/messaging/base-consumer.d.ts.map +1 -0
  267. package/dist/types/messaging/consumer.d.ts +59 -21
  268. package/dist/types/messaging/consumer.d.ts.map +1 -1
  269. package/dist/types/messaging/envelope.d.ts +50 -11
  270. package/dist/types/messaging/envelope.d.ts.map +1 -1
  271. package/dist/types/messaging/idempotency.d.ts +60 -3
  272. package/dist/types/messaging/idempotency.d.ts.map +1 -1
  273. package/dist/types/messaging/index.d.ts +7 -0
  274. package/dist/types/messaging/index.d.ts.map +1 -1
  275. package/dist/types/messaging/outbox.d.ts +62 -22
  276. package/dist/types/messaging/outbox.d.ts.map +1 -1
  277. package/dist/types/messaging/publisher.d.ts +54 -21
  278. package/dist/types/messaging/publisher.d.ts.map +1 -1
  279. package/dist/types/messaging/staff-notifier.d.ts +9 -9
  280. package/dist/types/messaging/staff-notifier.d.ts.map +1 -1
  281. package/dist/types/messaging/staff-recipients.d.ts +1 -1
  282. package/dist/types/messaging/topology.d.ts +40 -8
  283. package/dist/types/messaging/topology.d.ts.map +1 -1
  284. package/dist/types/rate-limit/index.d.ts +40 -4
  285. package/dist/types/rate-limit/index.d.ts.map +1 -1
  286. package/dist/types/rate-limit/keys.d.ts +52 -0
  287. package/dist/types/rate-limit/keys.d.ts.map +1 -0
  288. package/dist/types/socket/index.d.ts +86 -2
  289. package/dist/types/socket/index.d.ts.map +1 -1
  290. package/dist/types/socket/ticket.d.ts +90 -0
  291. package/dist/types/socket/ticket.d.ts.map +1 -0
  292. package/dist/types/testing/index.d.ts +31 -10
  293. package/dist/types/testing/index.d.ts.map +1 -1
  294. package/dist/types/types/index.d.ts +26 -14
  295. package/dist/types/types/index.d.ts.map +1 -1
  296. package/package.json +3 -1
@@ -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. `myapp: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,6DAA6D;IAC7D,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"}
@@ -0,0 +1,51 @@
1
+ /**
2
+ * A minimal in-process circuit breaker for outbound calls.
3
+ *
4
+ * Layer: plumbing. States: `closed` (calls flow) → `open` after `failureThreshold` consecutive failures
5
+ * (calls are refused without touching the network) → `half-open` once `resetAfterMs` has passed, when exactly
6
+ * one trial call is let through: success closes the circuit, failure re-opens it for another `resetAfterMs`.
7
+ * State is per instance (per process), so each client owns its breaker; nothing is shared between targets.
8
+ */
9
+ /** Breaker state. */
10
+ export type CircuitState = 'closed' | 'open' | 'half-open';
11
+ /** Breaker settings. */
12
+ export interface CircuitBreakerOptions {
13
+ /** Consecutive failures that open the circuit (default 5). */
14
+ failureThreshold?: number;
15
+ /** How long the circuit stays open before one trial call is allowed (default 30 000 ms). */
16
+ resetAfterMs?: number;
17
+ /** Clock, for tests (default `Date.now`). */
18
+ now?: () => number;
19
+ }
20
+ /**
21
+ * Counts consecutive failures and refuses calls while open.
22
+ *
23
+ * @example
24
+ * const breaker = new CircuitBreaker({ failureThreshold: 5, resetAfterMs: 30_000 });
25
+ * if (!breaker.tryAcquire()) throw new TargetUnavailableError();
26
+ * try { const r = await call(); breaker.onSuccess(); return r; } catch (e) { breaker.onFailure(); throw e; }
27
+ */
28
+ export declare class CircuitBreaker {
29
+ private readonly failureThreshold;
30
+ private readonly resetAfterMs;
31
+ private readonly now;
32
+ private failures;
33
+ private openedAt;
34
+ private current;
35
+ private trialInFlight;
36
+ /** @param options Threshold, reset time and clock. @throws Error when a setting is not a positive number. */
37
+ constructor(options?: CircuitBreakerOptions);
38
+ /** The current state (an open circuit whose reset time has passed reports `half-open`). */
39
+ get state(): CircuitState;
40
+ /**
41
+ * Asks to make a call. While half-open only the first caller gets the trial; the rest are refused until it settles.
42
+ *
43
+ * @returns True when the call may go ahead; the caller must then report {@link onSuccess} or {@link onFailure}.
44
+ */
45
+ tryAcquire(): boolean;
46
+ /** Records a successful call: closes the circuit and clears the failure count. */
47
+ onSuccess(): void;
48
+ /** Records a failed call: a failed trial re-opens at once; otherwise opens after `failureThreshold` in a row. */
49
+ onFailure(): void;
50
+ }
51
+ //# sourceMappingURL=circuit-breaker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"circuit-breaker.d.ts","sourceRoot":"","sources":["../../../src/common/circuit-breaker.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,qBAAqB;AACrB,MAAM,MAAM,YAAY,GAAG,QAAQ,GAAG,MAAM,GAAG,WAAW,CAAC;AAE3D,wBAAwB;AACxB,MAAM,WAAW,qBAAqB;IACpC,8DAA8D;IAC9D,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,4FAA4F;IAC5F,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,6CAA6C;IAC7C,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;CACpB;AAED;;;;;;;GAOG;AACH,qBAAa,cAAc;IACzB,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAS;IAC1C,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAS;IACtC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAe;IACnC,OAAO,CAAC,QAAQ,CAAK;IACrB,OAAO,CAAC,QAAQ,CAAK;IACrB,OAAO,CAAC,OAAO,CAA0B;IACzC,OAAO,CAAC,aAAa,CAAS;IAE9B,6GAA6G;gBACjG,OAAO,GAAE,qBAA0B;IAO/C,2FAA2F;IAC3F,IAAI,KAAK,IAAI,YAAY,CAGxB;IAED;;;;OAIG;IACH,UAAU,IAAI,OAAO;IASrB,kFAAkF;IAClF,SAAS,IAAI,IAAI;IAMjB,iHAAiH;IACjH,SAAS,IAAI,IAAI;CAQlB"}
@@ -7,6 +7,10 @@ export * from './jwt.js';
7
7
  export * from './text.js';
8
8
  export * from './async.js';
9
9
  export * from './phone.js';
10
+ export * from './money.js';
10
11
  export * from './time.js';
11
12
  export * from './audit.js';
13
+ export * from './circuit-breaker.js';
14
+ export * from './internal-service-auth.js';
15
+ export * from './internal-client.js';
12
16
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/common/index.ts"],"names":[],"mappings":"AAAA,cAAc,SAAS,CAAC;AACxB,cAAc,aAAa,CAAC;AAC5B,cAAc,eAAe,CAAC;AAC9B,cAAc,aAAa,CAAC;AAC5B,cAAc,eAAe,CAAC;AAC9B,cAAc,UAAU,CAAC;AACzB,cAAc,WAAW,CAAC;AAC1B,cAAc,YAAY,CAAC;AAC3B,cAAc,YAAY,CAAC;AAC3B,cAAc,WAAW,CAAC;AAC1B,cAAc,YAAY,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/common/index.ts"],"names":[],"mappings":"AAAA,cAAc,SAAS,CAAC;AACxB,cAAc,aAAa,CAAC;AAC5B,cAAc,eAAe,CAAC;AAC9B,cAAc,aAAa,CAAC;AAC5B,cAAc,eAAe,CAAC;AAC9B,cAAc,UAAU,CAAC;AACzB,cAAc,WAAW,CAAC;AAC1B,cAAc,YAAY,CAAC;AAC3B,cAAc,YAAY,CAAC;AAC3B,cAAc,YAAY,CAAC;AAC3B,cAAc,WAAW,CAAC;AAC1B,cAAc,YAAY,CAAC;AAC3B,cAAc,sBAAsB,CAAC;AACrC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,sBAAsB,CAAC"}
@@ -0,0 +1,116 @@
1
+ /**
2
+ * Typed HTTP client base for service-to-service (`internal`) calls.
3
+ *
4
+ * Layer: plumbing for a service's `src/clients/{target}.client.ts`. Key rules:
5
+ * - Sends the caller's own credential (`x-internal-service-name` + `x-internal-service-secret`) on every call,
6
+ * the current correlation id (INT-12) and, when given, the end user's signed identity headers unchanged.
7
+ * - Callers can tell "service down" from "service said no": network errors, timeouts, 5xx, unreadable bodies
8
+ * and an open circuit all throw the target's `{Target}UnavailableError` (503); a 4xx throws an `AppError`
9
+ * carrying the remote status, code, message and field errors.
10
+ * - Only GET is retried (default 2 retries, 100 ms then 400 ms back-off), and only for network errors,
11
+ * timeouts and 502/503/504. Mutating calls are never retried here; they accept an idempotency key instead.
12
+ * - Each client owns one {@link CircuitBreaker}; a call that ends unavailable counts as a failure, any HTTP
13
+ * answer below 500 counts as a success.
14
+ * - Secrets and header values are never logged; failure logs carry target, method, path and reason only.
15
+ */
16
+ import type { ZodType } from 'zod';
17
+ import { type CircuitBreakerOptions, type CircuitState } from './circuit-breaker.js';
18
+ import { ServiceUnavailableError } from './errors.js';
19
+ import { type InternalCallerCredential } from './internal-service-auth.js';
20
+ import type { Logger } from '../types/index.js';
21
+ /** An unavailable-error class bound to one target service. Instances are 503 `ServiceUnavailableError`s. */
22
+ export type UnavailableErrorClass = new (message?: string, options?: {
23
+ cause?: unknown;
24
+ }) => ServiceUnavailableError & {
25
+ readonly target: string;
26
+ };
27
+ /**
28
+ * Returns the `{Target}UnavailableError` class for a target service (the same class on every call, so
29
+ * `instanceof` works anywhere in the process).
30
+ *
31
+ * @param target The target service name, e.g. `records-service`.
32
+ * @returns A class named e.g. `RecordsServiceUnavailableError`, extending `ServiceUnavailableError` (503).
33
+ * @example
34
+ * const RecordsUnavailable = makeUnavailableError('records-service');
35
+ * try { await records.get('/records/1'); } catch (e) { if (e instanceof RecordsUnavailable) useCachedCopy(); }
36
+ */
37
+ export declare function makeUnavailableError(target: string): UnavailableErrorClass;
38
+ /** Settings for {@link createInternalClient}. */
39
+ export interface InternalClientOptions {
40
+ /** Target service name (compose name), e.g. `records-service`; names the unavailable error. */
41
+ target: string;
42
+ /** Target base URL on the private network, e.g. `http://records-service:4000/internal`. */
43
+ baseUrl: string;
44
+ /** This service's name and secret; defaults to env `INTERNAL_SERVICE_NAME` / `INTERNAL_SERVICE_CREDENTIAL`. */
45
+ caller?: InternalCallerCredential;
46
+ /** Per-attempt timeout (default 3000 ms). */
47
+ timeoutMs?: number;
48
+ /** Extra attempts for GET (default 2). */
49
+ retries?: number;
50
+ /** Delay before retry n (1-based) is `backoffMs[n-1]`, the last value repeating (default `[100, 400]`). */
51
+ backoffMs?: readonly number[];
52
+ /** Breaker settings (default `{ failureThreshold: 5, resetAfterMs: 30000 }`). */
53
+ breaker?: CircuitBreakerOptions;
54
+ /** Receives a warning per unavailable call (target, method, path, reason). */
55
+ logger?: Logger;
56
+ /** Fetch implementation, for tests (default global `fetch`). */
57
+ fetch?: typeof fetch;
58
+ }
59
+ /** Options for one call. */
60
+ export interface InternalRequestOptions<T = unknown> {
61
+ query?: Record<string, string | number | boolean | undefined>;
62
+ body?: unknown;
63
+ /** Overrides the correlation id taken from the request context. */
64
+ correlationId?: string;
65
+ /**
66
+ * The end user's gateway identity headers, forwarded unchanged. Gateway signatures are bound to method and
67
+ * path, so a service acting for a user re-signs them for this exact call with `signedIdentityHeaders`.
68
+ */
69
+ identityHeaders?: Record<string, string>;
70
+ /** Sent as `idempotency-key` so the target can dedupe a mutating call the caller repeats. */
71
+ idempotencyKey?: string;
72
+ /** Overrides the client's per-attempt timeout. */
73
+ timeoutMs?: number;
74
+ /** Validates the unwrapped `data`; a mismatch throws the unavailable error (the contract is broken). */
75
+ schema?: ZodType<T>;
76
+ }
77
+ /** A client bound to one target service. */
78
+ export interface InternalClient {
79
+ readonly target: string;
80
+ /** This target's unavailable error class, for `instanceof` checks. */
81
+ readonly UnavailableError: UnavailableErrorClass;
82
+ /** Current breaker state, for health and metrics. */
83
+ readonly circuitState: CircuitState;
84
+ /**
85
+ * Sends one call to the target. `get` is retried on transient failures; `post`/`put`/`patch`/`delete` never are.
86
+ *
87
+ * @param path Path under `baseUrl`, e.g. `/records/01J…`.
88
+ * @param options Query, body, correlation/identity headers, idempotency key, timeout and response schema.
89
+ * @returns The unwrapped `data` of the target's success envelope (validated when `schema` is given).
90
+ * @throws `{Target}UnavailableError` (503) when the target is down, slow, answers 5xx, sends an unreadable or
91
+ * schema-mismatched body, or the circuit is open; `AppError` with the remote status/code for a 4xx.
92
+ * @example const record = await records.get('/records/01J…', { schema: recordSchema });
93
+ */
94
+ get<T>(path: string, options?: InternalRequestOptions<T>): Promise<T>;
95
+ /** Same as {@link InternalClient.get}, never retried; pass `idempotencyKey` so the target can dedupe a repeat. */
96
+ post<T>(path: string, options?: InternalRequestOptions<T>): Promise<T>;
97
+ /** Same as {@link InternalClient.post}. */
98
+ put<T>(path: string, options?: InternalRequestOptions<T>): Promise<T>;
99
+ /** Same as {@link InternalClient.post}. */
100
+ patch<T>(path: string, options?: InternalRequestOptions<T>): Promise<T>;
101
+ /** Same as {@link InternalClient.post}. */
102
+ delete<T>(path: string, options?: InternalRequestOptions<T>): Promise<T>;
103
+ }
104
+ /**
105
+ * Creates a client for one target service.
106
+ *
107
+ * @param options Target, base URL, caller credential, timeout, retry, breaker and logger settings.
108
+ * @returns An {@link InternalClient}.
109
+ * @throws ConfigError when the caller name or credential is missing.
110
+ * @example
111
+ * // src/clients/records.client.ts
112
+ * const http = createInternalClient({ target: 'records-service', baseUrl: config.RECORDS_INTERNAL_URL, logger });
113
+ * export const getRecord = (id: string) => http.get(`/records/${id}`, { schema: recordSchema });
114
+ */
115
+ export declare function createInternalClient(options: InternalClientOptions): InternalClient;
116
+ //# sourceMappingURL=internal-client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"internal-client.d.ts","sourceRoot":"","sources":["../../../src/common/internal-client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,KAAK,CAAC;AAEnC,OAAO,EAAkB,KAAK,qBAAqB,EAAE,KAAK,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACrG,OAAO,EAAY,uBAAuB,EAAE,MAAM,aAAa,CAAC;AAChE,OAAO,EAA4D,KAAK,wBAAwB,EAAE,MAAM,4BAA4B,CAAC;AAIrI,OAAO,KAAK,EAAe,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAE7D,4GAA4G;AAC5G,MAAM,MAAM,qBAAqB,GAAG,KAAK,OAAO,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;IAAE,KAAK,CAAC,EAAE,OAAO,CAAA;CAAE,KAAK,uBAAuB,GAAG;IAAE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAOnJ;;;;;;;;;GASG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,MAAM,GAAG,qBAAqB,CAa1E;AAED,iDAAiD;AACjD,MAAM,WAAW,qBAAqB;IACpC,+FAA+F;IAC/F,MAAM,EAAE,MAAM,CAAC;IACf,2FAA2F;IAC3F,OAAO,EAAE,MAAM,CAAC;IAChB,+GAA+G;IAC/G,MAAM,CAAC,EAAE,wBAAwB,CAAC;IAClC,6CAA6C;IAC7C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,0CAA0C;IAC1C,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,2GAA2G;IAC3G,SAAS,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC9B,iFAAiF;IACjF,OAAO,CAAC,EAAE,qBAAqB,CAAC;IAChC,8EAA8E;IAC9E,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,gEAAgE;IAChE,KAAK,CAAC,EAAE,OAAO,KAAK,CAAC;CACtB;AAED,4BAA4B;AAC5B,MAAM,WAAW,sBAAsB,CAAC,CAAC,GAAG,OAAO;IACjD,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,SAAS,CAAC,CAAC;IAC9D,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,mEAAmE;IACnE,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;;OAGG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACzC,6FAA6F;IAC7F,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,kDAAkD;IAClD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,wGAAwG;IACxG,MAAM,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC;CACrB;AAED,4CAA4C;AAC5C,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,sEAAsE;IACtE,QAAQ,CAAC,gBAAgB,EAAE,qBAAqB,CAAC;IACjD,qDAAqD;IACrD,QAAQ,CAAC,YAAY,EAAE,YAAY,CAAC;IACpC;;;;;;;;;OASG;IACH,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,sBAAsB,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IACtE,kHAAkH;IAClH,IAAI,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,sBAAsB,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IACvE,2CAA2C;IAC3C,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,sBAAsB,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IACtE,2CAA2C;IAC3C,KAAK,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,sBAAsB,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IACxE,2CAA2C;IAC3C,MAAM,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,sBAAsB,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;CAC1E;AAUD;;;;;;;;;;GAUG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,qBAAqB,GAAG,cAAc,CA4GnF"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Leaf constants for internal-service auth, shared by `common/internal-service-auth` and the `config` env blocks
3
+ * (kept dependency-free so neither module imports the other).
4
+ */
5
+ /** Shortest internal credential accepted in staging and production. */
6
+ export declare const MIN_INTERNAL_CREDENTIAL_LENGTH = 32;
7
+ /** Service names are compose-style: lower-case letters, digits and hyphens, starting with a letter. */
8
+ export declare const SERVICE_NAME_PATTERN: RegExp;
9
+ //# sourceMappingURL=internal-constants.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"internal-constants.d.ts","sourceRoot":"","sources":["../../../src/common/internal-constants.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,uEAAuE;AACvE,eAAO,MAAM,8BAA8B,KAAK,CAAC;AACjD,uGAAuG;AACvG,eAAO,MAAM,oBAAoB,QAA2B,CAAC"}