stratal 0.0.19 → 0.0.21

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 (163) hide show
  1. package/dist/{base-email.provider-mjynzewK.mjs → base-email.provider-CfQCA08m.mjs} +1 -1
  2. package/dist/{base-email.provider-mjynzewK.mjs.map → base-email.provider-CfQCA08m.mjs.map} +1 -1
  3. package/dist/bin/cloudflare-workers-loader.mjs.map +1 -1
  4. package/dist/bin/quarry.mjs +6 -0
  5. package/dist/bin/quarry.mjs.map +1 -1
  6. package/dist/cache/index.d.mts +2 -154
  7. package/dist/cache/index.d.mts.map +1 -1
  8. package/dist/cache/index.mjs +6 -6
  9. package/dist/cache/index.mjs.map +1 -1
  10. package/dist/cache.service-DsnKuNyO.d.mts +156 -0
  11. package/dist/cache.service-DsnKuNyO.d.mts.map +1 -0
  12. package/dist/cache.tokens-B7Rw1C9Q.mjs +6 -0
  13. package/dist/cache.tokens-B7Rw1C9Q.mjs.map +1 -0
  14. package/dist/command-BgSlsS4M.mjs.map +1 -1
  15. package/dist/{command-DsQq56Lp.d.mts → command-Cmmf0oHX.d.mts} +2 -2
  16. package/dist/{command-DsQq56Lp.d.mts.map → command-Cmmf0oHX.d.mts.map} +1 -1
  17. package/dist/config/index.d.mts +3 -3
  18. package/dist/config/index.mjs +5 -3
  19. package/dist/config/index.mjs.map +1 -1
  20. package/dist/{consumer-registry-Doom7BEh.d.mts → consumer-registry-B7yUNh0q.d.mts} +1 -1
  21. package/dist/{consumer-registry-Doom7BEh.d.mts.map → consumer-registry-B7yUNh0q.d.mts.map} +1 -1
  22. package/dist/{controller.decorator-LZY9aHYG.mjs → controller.decorator-B9vwn0zK.mjs} +3 -3
  23. package/dist/{controller.decorator-LZY9aHYG.mjs.map → controller.decorator-B9vwn0zK.mjs.map} +1 -1
  24. package/dist/cron/index.d.mts +2 -2
  25. package/dist/cron/index.mjs +1 -1
  26. package/dist/{cron-manager-RuPtFVLy.d.mts → cron-manager-CmTimEjf.d.mts} +3 -3
  27. package/dist/cron-manager-CmTimEjf.d.mts.map +1 -0
  28. package/dist/{cron-manager-C30t9UZM.mjs → cron-manager-DQSK8uoV.mjs} +10 -4
  29. package/dist/cron-manager-DQSK8uoV.mjs.map +1 -0
  30. package/dist/di/index.d.mts +1 -1
  31. package/dist/di/index.mjs +2 -2
  32. package/dist/email/index.d.mts +3 -3
  33. package/dist/email/index.mjs +13 -8
  34. package/dist/email/index.mjs.map +1 -1
  35. package/dist/{en-rHmW6vD9.mjs → en-DSH_bhh6.mjs} +7 -1
  36. package/dist/en-DSH_bhh6.mjs.map +1 -0
  37. package/dist/{env-CamWD-U1.d.mts → env-D1rcZ8_r.d.mts} +1 -1
  38. package/dist/env-D1rcZ8_r.d.mts.map +1 -0
  39. package/dist/errors/index.d.mts +1 -1
  40. package/dist/errors/index.mjs +1 -1
  41. package/dist/{errors-B4pYgYON.mjs → errors-COW9-Mar.mjs} +35 -10
  42. package/dist/errors-COW9-Mar.mjs.map +1 -0
  43. package/dist/{errors-BUyUfr2Z.mjs → errors-ORxu1-Bb.mjs} +2 -2
  44. package/dist/{errors-BUyUfr2Z.mjs.map → errors-ORxu1-Bb.mjs.map} +1 -1
  45. package/dist/events/index.d.mts +2 -2
  46. package/dist/events/index.mjs +1 -1
  47. package/dist/{events-COKixqnG.mjs → events-CzCV8jI8.mjs} +4 -2
  48. package/dist/{events-COKixqnG.mjs.map → events-CzCV8jI8.mjs.map} +1 -1
  49. package/dist/{gateway-context-cqZ8wMoi.mjs → gateway-context-CXmXtaUP.mjs} +5 -8
  50. package/dist/{gateway-context-cqZ8wMoi.mjs.map → gateway-context-CXmXtaUP.mjs.map} +1 -1
  51. package/dist/guards/index.d.mts +3 -3
  52. package/dist/guards/index.mjs +1 -1
  53. package/dist/{guards-DMbsAxSX.mjs → guards-DU1_J9YA.mjs} +2 -1
  54. package/dist/{guards-DMbsAxSX.mjs.map → guards-DU1_J9YA.mjs.map} +1 -1
  55. package/dist/{http-method.decorator-BT3ufnz8.mjs → http-method.decorator-BrgHMdLQ.mjs} +3 -3
  56. package/dist/{http-method.decorator-BT3ufnz8.mjs.map → http-method.decorator-BrgHMdLQ.mjs.map} +1 -1
  57. package/dist/i18n/index.d.mts +2 -2
  58. package/dist/i18n/index.mjs +2 -2
  59. package/dist/i18n/messages/en/index.d.mts +1 -1
  60. package/dist/i18n/messages/en/index.mjs +1 -1
  61. package/dist/i18n/utils/index.mjs +1 -1
  62. package/dist/i18n/validation/index.d.mts +2 -2
  63. package/dist/i18n/validation/index.mjs +2 -2
  64. package/dist/{i18n.module-CI_prYFD.mjs → i18n.module-CzXLW9Hy.mjs} +242 -50
  65. package/dist/i18n.module-CzXLW9Hy.mjs.map +1 -0
  66. package/dist/{index-SHx31sBJ.d.mts → index-7-hU3GTV.d.mts} +1 -1
  67. package/dist/{index-SHx31sBJ.d.mts.map → index-7-hU3GTV.d.mts.map} +1 -1
  68. package/dist/{index-B437eK7p.d.mts → index-Bnpfq6uk.d.mts} +58 -10
  69. package/dist/index-Bnpfq6uk.d.mts.map +1 -0
  70. package/dist/{index-DPFqRs8L.d.mts → index-ByOyTmqf.d.mts} +317 -205
  71. package/dist/index-ByOyTmqf.d.mts.map +1 -0
  72. package/dist/{index-DFhEeFfC.d.mts → index-C1KvMncZ.d.mts} +7 -1
  73. package/dist/{index-DFhEeFfC.d.mts.map → index-C1KvMncZ.d.mts.map} +1 -1
  74. package/dist/{index-CWRS7Ri3.d.mts → index-DBd_2wv8.d.mts} +1 -1
  75. package/dist/{index-CWRS7Ri3.d.mts.map → index-DBd_2wv8.d.mts.map} +1 -1
  76. package/dist/{index-Dnqm9ZB6.d.mts → index-DUzWs0z7.d.mts} +5 -5
  77. package/dist/{index-Dnqm9ZB6.d.mts.map → index-DUzWs0z7.d.mts.map} +1 -1
  78. package/dist/index.d.mts +3 -3
  79. package/dist/index.mjs +1 -1
  80. package/dist/is-command-C6a7WTPw.mjs.map +1 -1
  81. package/dist/is-seeder-CebjZCDn.mjs.map +1 -1
  82. package/dist/logger/index.d.mts +1 -1
  83. package/dist/logger/index.mjs +1 -1
  84. package/dist/{logger-V6Ms3QnQ.mjs → logger-DlV7NtvD.mjs} +8 -4
  85. package/dist/{logger-V6Ms3QnQ.mjs.map → logger-DlV7NtvD.mjs.map} +1 -1
  86. package/dist/macroable/index.d.mts +1 -1
  87. package/dist/macroable-BmufBshB.mjs.map +1 -1
  88. package/dist/module/index.d.mts +2 -2
  89. package/dist/module/index.mjs +1 -1
  90. package/dist/{module-qGE_1duv.mjs → module-BzLg57FK.mjs} +139 -5
  91. package/dist/module-BzLg57FK.mjs.map +1 -0
  92. package/dist/openapi/index.d.mts +3 -3
  93. package/dist/openapi/index.mjs +2 -2
  94. package/dist/{openapi-tools.service-CYWGuhue.mjs → openapi-tools.service-Zs-Ewv7F.mjs} +1 -1
  95. package/dist/{openapi-tools.service-CYWGuhue.mjs.map → openapi-tools.service-Zs-Ewv7F.mjs.map} +1 -1
  96. package/dist/{openapi.service-Bv_NioM9.d.mts → openapi.service-Bt9bCIrd.d.mts} +3 -3
  97. package/dist/{openapi.service-Bv_NioM9.d.mts.map → openapi.service-Bt9bCIrd.d.mts.map} +1 -1
  98. package/dist/quarry/index.d.mts +6 -6
  99. package/dist/quarry/index.mjs +2 -2
  100. package/dist/{quarry-registry-DFfRRkA7.mjs → quarry-registry-BwY2hOxm.mjs} +18 -7
  101. package/dist/{quarry-registry-DFfRRkA7.mjs.map → quarry-registry-BwY2hOxm.mjs.map} +1 -1
  102. package/dist/queue/index.d.mts +2 -2
  103. package/dist/queue/index.mjs +3 -2
  104. package/dist/queue/index.mjs.map +1 -1
  105. package/dist/{queue.module-P-G-nCYz.mjs → queue.module-BhCjZp6H.mjs} +13 -4
  106. package/dist/{queue.module-P-G-nCYz.mjs.map → queue.module-BhCjZp6H.mjs.map} +1 -1
  107. package/dist/{r2-storage.provider-LdzK9tfG.mjs → r2-storage.provider-DuonKeYm.mjs} +6 -3
  108. package/dist/{r2-storage.provider-LdzK9tfG.mjs.map → r2-storage.provider-DuonKeYm.mjs.map} +1 -1
  109. package/dist/rate-limit.decorator-6qzNcSOt.mjs +55 -0
  110. package/dist/rate-limit.decorator-6qzNcSOt.mjs.map +1 -0
  111. package/dist/rate-limiter/index.d.mts +420 -0
  112. package/dist/rate-limiter/index.d.mts.map +1 -0
  113. package/dist/rate-limiter/index.mjs +374 -0
  114. package/dist/rate-limiter/index.mjs.map +1 -0
  115. package/dist/{resend.provider-bwILp0WI.mjs → resend.provider-DB4IlFjG.mjs} +3 -2
  116. package/dist/{resend.provider-bwILp0WI.mjs.map → resend.provider-DB4IlFjG.mjs.map} +1 -1
  117. package/dist/router/index.d.mts +2 -2
  118. package/dist/router/index.mjs +6 -6
  119. package/dist/seeder/index.d.mts +3 -3
  120. package/dist/seeder/index.mjs +1 -1
  121. package/dist/{seeder-BcqIFa2X.mjs → seeder-zoEfEw9i.mjs} +6 -3
  122. package/dist/{seeder-BcqIFa2X.mjs.map → seeder-zoEfEw9i.mjs.map} +1 -1
  123. package/dist/{setup-CtekcwuO.mjs → setup-CefZKV_e.mjs} +1 -1
  124. package/dist/{setup-CtekcwuO.mjs.map → setup-CefZKV_e.mjs.map} +1 -1
  125. package/dist/{signed-url-COX7cCWR.mjs → signed-url-BQPbv2In.mjs} +1 -1
  126. package/dist/{signed-url-COX7cCWR.mjs.map → signed-url-BQPbv2In.mjs.map} +1 -1
  127. package/dist/{smtp.provider-B07yuARi.mjs → smtp.provider-B6D7zuWX.mjs} +3 -2
  128. package/dist/{smtp.provider-B07yuARi.mjs.map → smtp.provider-B6D7zuWX.mjs.map} +1 -1
  129. package/dist/storage/index.d.mts +3 -3
  130. package/dist/storage/index.mjs +2 -2
  131. package/dist/storage/providers/index.d.mts +2 -2
  132. package/dist/storage/providers/index.mjs +1 -1
  133. package/dist/{storage-P6X4h9So.mjs → storage-D8CBP72Z.mjs} +14 -9
  134. package/dist/{storage-P6X4h9So.mjs.map → storage-D8CBP72Z.mjs.map} +1 -1
  135. package/dist/{storage-provider.interface-CC1nniHk.d.mts → storage-provider.interface-Bd6vA4ak.d.mts} +2 -2
  136. package/dist/{storage-provider.interface-CC1nniHk.d.mts.map → storage-provider.interface-Bd6vA4ak.d.mts.map} +1 -1
  137. package/dist/{stratal-BCiwCFN9.mjs → stratal-CNwpbSZl.mjs} +12 -10
  138. package/dist/stratal-CNwpbSZl.mjs.map +1 -0
  139. package/dist/{types-DIWemRad.d.mts → types-cySNS_lp.d.mts} +1 -1
  140. package/dist/types-cySNS_lp.d.mts.map +1 -0
  141. package/dist/{usage-generator-MBcRo0Q2.mjs → usage-generator-BUdlhnCK.mjs} +1 -1
  142. package/dist/{usage-generator-MBcRo0Q2.mjs.map → usage-generator-BUdlhnCK.mjs.map} +1 -1
  143. package/dist/{validation-Dbg3ehdP.mjs → validation-DtJwAv7O.mjs} +62 -8
  144. package/dist/validation-DtJwAv7O.mjs.map +1 -0
  145. package/dist/websocket/index.d.mts +8 -3
  146. package/dist/websocket/index.d.mts.map +1 -1
  147. package/dist/websocket/index.mjs +1 -1
  148. package/dist/workers/index.d.mts +2 -2
  149. package/dist/workers/index.mjs +2 -2
  150. package/dist/workers/index.mjs.map +1 -1
  151. package/package.json +18 -14
  152. package/dist/cron-manager-C30t9UZM.mjs.map +0 -1
  153. package/dist/cron-manager-RuPtFVLy.d.mts.map +0 -1
  154. package/dist/en-rHmW6vD9.mjs.map +0 -1
  155. package/dist/env-CamWD-U1.d.mts.map +0 -1
  156. package/dist/errors-B4pYgYON.mjs.map +0 -1
  157. package/dist/i18n.module-CI_prYFD.mjs.map +0 -1
  158. package/dist/index-B437eK7p.d.mts.map +0 -1
  159. package/dist/index-DPFqRs8L.d.mts.map +0 -1
  160. package/dist/module-qGE_1duv.mjs.map +0 -1
  161. package/dist/stratal-BCiwCFN9.mjs.map +0 -1
  162. package/dist/types-DIWemRad.d.mts.map +0 -1
  163. package/dist/validation-Dbg3ehdP.mjs.map +0 -1
@@ -1,6 +1,6 @@
1
1
  import { n as __reExport, t as __exportAll } from "./chunk-D1SwGrFN.mjs";
2
2
  import { AsyncLocalStorage } from "node:async_hooks";
3
- import { OpenAPIHono, z as z$1 } from "@hono/zod-openapi";
3
+ import { OpenAPIHono, z, z as z$2 } from "@hono/zod-openapi";
4
4
  import { ZodError } from "zod";
5
5
  //#region src/i18n/validation/validation.error-map.ts
6
6
  /**
@@ -163,32 +163,86 @@ function runWithErrorMapContext(context, fn) {
163
163
  * const schema = z.string().email(withI18n('validation.email'))
164
164
  * ```
165
165
  *
166
- * Note: This is the backend version using AsyncLocalStorage.
167
- * For frontend, use withI18nFrontend from the frontend validation module
168
- *
169
166
  * @param key - Message key from shared i18n messages (type-safe via MessageKeys)
170
167
  * @param params - Optional interpolation parameters for the message
171
168
  * @returns Zod error configuration object with translated message
172
169
  */
173
170
  function withI18n(key, params) {
174
- return { error: () => {
171
+ return { error: (_issue) => {
175
172
  const context = errorMapContextStorage.getStore();
176
173
  return context ? context.t(key, params) : "Invalid input";
177
174
  } };
178
175
  }
179
176
  //#endregion
177
+ //#region src/i18n/validation/cuid2.ts
178
+ /**
179
+ * Default cuid2 shape: 24-32 lowercase-alphanumeric chars, starting with a
180
+ * letter. Matches what `@paralleldrive/cuid2` produces with default settings
181
+ * and what most apps expect when they say "cuid2".
182
+ *
183
+ * Used as the default for {@link cuid2}; exposed for callers who want to
184
+ * compose it into other patterns.
185
+ */
186
+ const CUID2_REGEX = /^[a-z][0-9a-z]{23,31}$/;
187
+ /**
188
+ * Stratal's cuid2 validator — a stricter drop-in for Zod 4.3.6's `z.cuid2()`.
189
+ *
190
+ * **Why this exists.** Zod 4.3.6's built-in `z.cuid2()` uses the regex
191
+ * `/^[0-9a-z]+$/`, which accepts *any* non-empty lowercase-alphanumeric
192
+ * string (including 2-letter locale prefixes like `'sw'`). That makes it
193
+ * effectively useless as a tenant-id / external-id validator.
194
+ *
195
+ * This helper layers the proper shape regex on top of `z.cuid2()`, so:
196
+ * - validation actually enforces cuid2 shape;
197
+ * - the schema still carries `z.cuid2()`'s OpenAPI metadata (i.e. spec
198
+ * output keeps `format: 'cuid2'`).
199
+ *
200
+ * @example
201
+ * ```ts
202
+ * import { z, withI18n } from 'stratal/validation'
203
+ * import { cuid2 } from 'stratal/validation'
204
+ *
205
+ * // Default 24-32 char cuid2:
206
+ * router.prefix('/:tenantId', z.object({ tenantId: cuid2() }))
207
+ *
208
+ * // Custom regex (e.g. fixed-length 24):
209
+ * router.prefix('/:tenantId', z.object({
210
+ * tenantId: cuid2({ pattern: /^[a-z][0-9a-z]{23}$/ }),
211
+ * }))
212
+ *
213
+ * // Custom error message:
214
+ * cuid2(withI18n('tenants.errors.invalidId'))
215
+ *
216
+ * // Compose: chain anything Zod string accepts
217
+ * cuid2().describe('Tenant ID')
218
+ * ```
219
+ *
220
+ * @param options.pattern - Override the shape regex. Defaults to {@link CUID2_REGEX}.
221
+ * @param options.error - Custom error message (string or i18n key) for the regex check.
222
+ */
223
+ function cuid2(options) {
224
+ const pattern = options?.pattern ?? CUID2_REGEX;
225
+ return z.cuid2({
226
+ pattern,
227
+ error: typeof options?.error === "string" ? options?.error : void 0,
228
+ ...typeof options?.error === "function" ? { error: options?.error } : {}
229
+ });
230
+ }
231
+ //#endregion
180
232
  //#region src/i18n/validation/index.ts
181
233
  var validation_exports = /* @__PURE__ */ __exportAll({
234
+ CUID2_REGEX: () => CUID2_REGEX,
182
235
  OpenAPIHono: () => OpenAPIHono,
183
236
  ZodError: () => ZodError,
184
237
  backendErrorMap: () => backendErrorMap,
238
+ cuid2: () => cuid2,
185
239
  runWithErrorMapContext: () => runWithErrorMapContext,
186
240
  withI18n: () => withI18n,
187
- z: () => z$1
241
+ z: () => z$2
188
242
  });
189
243
  import * as import__hono_zod_openapi from "@hono/zod-openapi";
190
244
  __reExport(validation_exports, import__hono_zod_openapi);
191
245
  //#endregion
192
- export { withI18n as a, z$1 as i, ZodError as n, backendErrorMap as o, validation_exports as r, runWithErrorMapContext as s, OpenAPIHono as t };
246
+ export { CUID2_REGEX as a, backendErrorMap as c, z$2 as i, runWithErrorMapContext as l, ZodError as n, cuid2 as o, validation_exports as r, withI18n as s, OpenAPIHono as t };
193
247
 
194
- //# sourceMappingURL=validation-Dbg3ehdP.mjs.map
248
+ //# sourceMappingURL=validation-DtJwAv7O.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validation-DtJwAv7O.mjs","names":[],"sources":["../src/i18n/validation/validation.error-map.ts","../src/i18n/validation/validation.context.ts","../src/i18n/validation/with-i18n.ts","../src/i18n/validation/cuid2.ts","../src/i18n/validation/index.ts"],"sourcesContent":["import type {\n $ZodErrorMap,\n $ZodIssueInvalidKey,\n $ZodIssueInvalidStringFormat,\n $ZodIssueInvalidType,\n $ZodIssueInvalidUnion,\n $ZodIssueNotMultipleOf,\n $ZodIssueTooBig,\n $ZodIssueTooSmall,\n $ZodIssueUnrecognizedKeys,\n $ZodRawIssue,\n} from 'zod/v4/core'\nimport type { MessageKeys } from '../i18n.types'\nimport type { ErrorMapContext } from './validation.types'\n\n/**\n * Type guards for narrowing Zod v4 issue types\n */\nfunction isInvalidType(issue: $ZodRawIssue): issue is $ZodRawIssue<$ZodIssueInvalidType> {\n return issue.code === 'invalid_type'\n}\n\nfunction isTooBig(issue: $ZodRawIssue): issue is $ZodRawIssue<$ZodIssueTooBig> {\n return issue.code === 'too_big'\n}\n\nfunction isTooSmall(issue: $ZodRawIssue): issue is $ZodRawIssue<$ZodIssueTooSmall> {\n return issue.code === 'too_small'\n}\n\nfunction isInvalidFormat(issue: $ZodRawIssue): issue is $ZodRawIssue<$ZodIssueInvalidStringFormat> {\n return issue.code === 'invalid_format'\n}\n\nfunction isNotMultipleOf(issue: $ZodRawIssue): issue is $ZodRawIssue<$ZodIssueNotMultipleOf> {\n return issue.code === 'not_multiple_of'\n}\n\nfunction isUnrecognizedKeys(issue: $ZodRawIssue): issue is $ZodRawIssue<$ZodIssueUnrecognizedKeys> {\n return issue.code === 'unrecognized_keys'\n}\n\nfunction isInvalidUnion(issue: $ZodRawIssue): issue is $ZodRawIssue<$ZodIssueInvalidUnion> {\n return issue.code === 'invalid_union'\n}\n\nfunction isInvalidKey(issue: $ZodRawIssue): issue is $ZodRawIssue<$ZodIssueInvalidKey> {\n return issue.code === 'invalid_key'\n}\n\n/**\n * Maps Zod issue codes to zodI18n message keys\n * Adapted for Zod v4's simpler issue code system\n */\nfunction getMessageKey(issue: $ZodRawIssue): MessageKeys {\n if (isInvalidType(issue)) {\n if (issue.input === undefined) {\n return 'zodI18n.errors.required'\n }\n return 'zodI18n.errors.invalid_type'\n }\n\n if (isTooSmall(issue)) {\n const typeKey = issue.origin || 'string'\n const exactKey = issue.exact\n ? 'exact'\n : issue.inclusive\n ? 'inclusive'\n : 'not_inclusive'\n return `zodI18n.errors.too_small.${typeKey}.${exactKey}` as MessageKeys\n }\n\n if (isTooBig(issue)) {\n const typeKey = issue.origin || 'string'\n const exactKey = issue.exact\n ? 'exact'\n : issue.inclusive\n ? 'inclusive'\n : 'not_inclusive'\n return `zodI18n.errors.too_big.${typeKey}.${exactKey}` as MessageKeys\n }\n\n if (isInvalidFormat(issue)) {\n // Map v4's format to v3-style validation keys\n return `zodI18n.errors.invalid_string.${issue.format}` as MessageKeys\n }\n\n if (isNotMultipleOf(issue)) {\n return 'zodI18n.errors.not_multiple_of'\n }\n\n if (isUnrecognizedKeys(issue)) {\n return 'zodI18n.errors.unrecognized_keys'\n }\n\n if (isInvalidUnion(issue)) {\n return 'zodI18n.errors.invalid_union'\n }\n\n if (isInvalidKey(issue)) {\n // v4: Replaces invalid_enum_value, invalid_literal\n return 'zodI18n.errors.invalid_enum_value'\n }\n\n // invalid_element, invalid_value, or unknown codes\n return 'zodI18n.errors.custom.default'\n}\n\n/**\n * Extracts interpolation parameters from Zod issue\n * Uses proper type narrowing for v4\n */\nfunction getMessageParams(issue: $ZodRawIssue): Record<string, unknown> {\n const params: Record<string, unknown> = {}\n\n if (isInvalidType(issue)) {\n params.expected = issue.expected\n params.received = String(issue.input)\n return params\n }\n\n if (isUnrecognizedKeys(issue)) {\n params.keys = issue.keys.join(', ')\n return params\n }\n\n if (isInvalidKey(issue)) {\n // v4: For enums and records\n // Since v4 doesn't have options field, we'll use generic message\n return params\n }\n\n if (isInvalidFormat(issue)) {\n params.validation = issue.format\n\n // Check for specific string format issues with additional fields\n if ('prefix' in issue) {\n params.startsWith = (issue as { prefix?: string }).prefix\n }\n if ('suffix' in issue) {\n params.endsWith = (issue as { suffix?: string }).suffix\n }\n if ('includes' in issue) {\n params.includes = (issue as { includes?: string }).includes\n }\n if (issue.pattern) {\n params.pattern = issue.pattern\n }\n\n return params\n }\n\n if (isTooSmall(issue)) {\n params.minimum = issue.minimum\n params.type = issue.origin\n if (issue.origin === 'date') {\n params.minimum = new Date(Number(issue.minimum)).toLocaleDateString()\n }\n return params\n }\n\n if (isTooBig(issue)) {\n params.maximum = issue.maximum\n params.type = issue.origin\n if (issue.origin === 'date') {\n params.maximum = new Date(Number(issue.maximum)).toLocaleDateString()\n }\n return params\n }\n\n if (isNotMultipleOf(issue)) {\n params.multipleOf = issue.divisor\n return params\n }\n\n return params\n}\n\n/**\n * Creates a Zod error map that uses i18n for translation\n * Uses Zod v4 native $ZodErrorMap signature (no ctx parameter)\n */\nexport function createI18nErrorMap(getContext: () => ErrorMapContext | undefined): $ZodErrorMap {\n return (issue: $ZodRawIssue): { message: string } => {\n // Get message key and params for this issue\n const messageKey = getMessageKey(issue)\n const messageParams = getMessageParams(issue)\n\n // Get translation context\n const context = getContext()\n\n if (!context) {\n // Fallback: Use English messages directly\n // This handles config validation at startup, tests, etc.\n return { message: 'Invalid input' }\n }\n\n // Normal flow: Use context-aware i18n (respects user locale)\n return {\n message: context.t(messageKey, messageParams),\n }\n }\n}\n","import { AsyncLocalStorage } from 'node:async_hooks'\nimport type { ErrorMapContext } from './validation.types'\nimport { createI18nErrorMap } from './validation.error-map'\n\n/**\n * AsyncLocalStorage for storing request-scoped ErrorMapContext\n * Allows error map to access I18nService without passing through function calls\n */\nexport const errorMapContextStorage = new AsyncLocalStorage<ErrorMapContext>()\n\n/**\n * Gets the current error map context from AsyncLocalStorage\n */\nfunction getErrorMapContext(): ErrorMapContext | undefined {\n return errorMapContextStorage.getStore()\n}\n\n/**\n * Backend error map that accesses I18nService from AsyncLocalStorage\n * Must be initialized in router middleware after locale detection\n */\nexport const backendErrorMap = createI18nErrorMap(getErrorMapContext)\n\n/**\n * Runs a function within an error map context\n * Used by router middleware to provide I18nService to validation\n *\n * @example\n * ```typescript\n * router.use('*', async (c, next) => {\n * const i18nService = c.get('i18nService')\n * const locale = c.get('locale')\n *\n * return runWithErrorMapContext(\n * {\n * t: (key, params) => i18nService.t(key, params),\n * locale,\n * },\n * () => next()\n * )\n * })\n * ```\n */\nexport function runWithErrorMapContext<T>(\n context: ErrorMapContext,\n fn: () => T\n): T {\n return errorMapContextStorage.run(context, fn)\n}\n","import { type $ZodRawIssue } from 'zod/v4/core'\nimport type { MessageKeys } from '../i18n.types'\nimport { errorMapContextStorage } from './validation.context'\n\n/**\n * Type-safe helper for creating custom Zod error messages with i18n support (Backend)\n *\n * Usage with .refine():\n * ```typescript\n * const schema = z.string().refine(\n * (val) => val.length > 5,\n * withI18n('validation.minLength', { min: 5 })\n * )\n * ```\n *\n * Usage with built-in validators:\n * ```typescript\n * const schema = z.string().min(5, withI18n('validation.minLength', { min: 5 }))\n * const schema = z.string().email(withI18n('validation.email'))\n * ```\n *\n * @param key - Message key from shared i18n messages (type-safe via MessageKeys)\n * @param params - Optional interpolation parameters for the message\n * @returns Zod error configuration object with translated message\n */\nexport function withI18n(\n key: MessageKeys,\n params?: Record<string, unknown>\n): { error: (_issue: $ZodRawIssue) => string } {\n return {\n error: (_issue: $ZodRawIssue) => {\n // Get i18n context from AsyncLocalStorage (backend)\n // This is set by the router middleware before validation\n const context = errorMapContextStorage.getStore()\n\n // Translate using context if available, otherwise fallback to generic message\n const message = context\n ? context.t(key, params as Record<string, string | number> | undefined)\n : 'Invalid input'\n\n return message\n },\n }\n}\n","import { z } from '@hono/zod-openapi';\nimport { type $ZodRawIssue } from 'zod/v4/core';\n\n/**\n * Default cuid2 shape: 24-32 lowercase-alphanumeric chars, starting with a\n * letter. Matches what `@paralleldrive/cuid2` produces with default settings\n * and what most apps expect when they say \"cuid2\".\n *\n * Used as the default for {@link cuid2}; exposed for callers who want to\n * compose it into other patterns.\n */\nexport const CUID2_REGEX = /^[a-z][0-9a-z]{23,31}$/\n\n/**\n * Stratal's cuid2 validator — a stricter drop-in for Zod 4.3.6's `z.cuid2()`.\n *\n * **Why this exists.** Zod 4.3.6's built-in `z.cuid2()` uses the regex\n * `/^[0-9a-z]+$/`, which accepts *any* non-empty lowercase-alphanumeric\n * string (including 2-letter locale prefixes like `'sw'`). That makes it\n * effectively useless as a tenant-id / external-id validator.\n *\n * This helper layers the proper shape regex on top of `z.cuid2()`, so:\n * - validation actually enforces cuid2 shape;\n * - the schema still carries `z.cuid2()`'s OpenAPI metadata (i.e. spec\n * output keeps `format: 'cuid2'`).\n *\n * @example\n * ```ts\n * import { z, withI18n } from 'stratal/validation'\n * import { cuid2 } from 'stratal/validation'\n *\n * // Default 24-32 char cuid2:\n * router.prefix('/:tenantId', z.object({ tenantId: cuid2() }))\n *\n * // Custom regex (e.g. fixed-length 24):\n * router.prefix('/:tenantId', z.object({\n * tenantId: cuid2({ pattern: /^[a-z][0-9a-z]{23}$/ }),\n * }))\n *\n * // Custom error message:\n * cuid2(withI18n('tenants.errors.invalidId'))\n *\n * // Compose: chain anything Zod string accepts\n * cuid2().describe('Tenant ID')\n * ```\n *\n * @param options.pattern - Override the shape regex. Defaults to {@link CUID2_REGEX}.\n * @param options.error - Custom error message (string or i18n key) for the regex check.\n */\nexport function cuid2(options?: { pattern?: RegExp; error?: string | ((_issue: $ZodRawIssue) => string) }) {\n const pattern = options?.pattern ?? CUID2_REGEX\n return z.cuid2({\n pattern: pattern,\n error: typeof options?.error === 'string' ? options?.error : undefined,\n ...(typeof options?.error === 'function' ? { error: options?.error } : {})\n })\n}\n","// Consolidated zod - single source from @hono/zod-openapi (superset of zod)\nexport { OpenAPIHono, z } from '@hono/zod-openapi'\nexport type * from 'zod'\nexport { ZodError } from 'zod'\n\n// OpenAPI utilities\nexport * from '@hono/zod-openapi'\nexport type { OpenAPIObject, PathItemObject } from 'openapi3-ts/oas30'\n\n// Helpers\nexport { withI18n } from './with-i18n'\nexport { CUID2_REGEX, cuid2 } from './cuid2'\n\n// Backend utilities\nexport { backendErrorMap, runWithErrorMapContext } from './validation.context'\n\n// Types\nexport type { ErrorMapContext, I18nErrorMetadata, LocaleProvider, ZodCustomIssue } from './validation.types'\n\n"],"mappings":"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"}
@@ -1,5 +1,5 @@
1
- import { B as ContextQueryResult, Bn as ControllerOptions, V as RouterContext, Xn as RouterEnv, d as ApplicationError } from "../index-DPFqRs8L.mjs";
2
- import { t as Constructor } from "../types-DIWemRad.mjs";
1
+ import { B as ContextQueryResult, Bn as ControllerOptions, V as RouterContext, Xn as RouterEnv, d as ApplicationError } from "../index-ByOyTmqf.mjs";
2
+ import { t as Constructor } from "../types-cySNS_lp.mjs";
3
3
  import { Context } from "hono";
4
4
  import { WSContext, WSContext as WSContext$1, WSEvents, WSMessageReceive, WSReadyState, WSReadyState as WSReadyState$1 } from "hono/ws";
5
5
 
@@ -145,10 +145,15 @@ declare class GatewayContext extends RouterContext {
145
145
  /** Current WebSocket ready state */
146
146
  get readyState(): WSReadyState$1;
147
147
  /**
148
- * Get route parameter value from the raw request (no OpenAPI validation)
148
+ * Get route parameter value(s) from the raw request WebSocket gateways are
149
+ * not OpenAPI-registered, so reads come straight from Hono's matcher.
150
+ *
151
+ * - With a key → single string value.
152
+ * - With no args → full `Record<string, string>` (or `{}` when none).
149
153
  *
150
154
  * @param key - Parameter name (e.g., 'id' for /ws/chat/:id)
151
155
  */
156
+ param(): Record<string, string>;
152
157
  param(key: string): string;
153
158
  /**
154
159
  * Get query parameter value from the raw request (no OpenAPI validation)
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","names":[],"sources":["../../src/websocket/types.ts","../../src/websocket/decorators/gateway.decorator.ts","../../src/websocket/decorators/ws-event.decorator.ts","../../src/websocket/errors/websocket-body-not-available.error.ts","../../src/websocket/errors/websocket-duplicate-event-handler.error.ts","../../src/websocket/gateway-context.ts"],"mappings":";;;;;;;;;;KAMY,cAAA,GAAiB,IAAA,CAAK,iBAAA;;;;;;;AAAlC;;;;;;;;AC6BA;;;;;;;;;;;;;;;;iBAAgB,OAAA,CAAQ,KAAA,UAAe,OAAA,GAAU,cAAA,cACpB,WAAA,EAAa,MAAA,EAAQ,CAAA,KAAC,CAAA;;;AAanD;;;;iBAAgB,SAAA,CAAU,MAAA;;;;;;;;AD3C1B;;;;;;;;AC6BA;iBCDgB,SAAA,CAAA,GAAa,eAAA;;;;;;;;;;;;;;;iBAsBb,OAAA,CAAA,GAAW,eAAA;;;ADP3B;;;;;;;;ACfA;;;;iBA0CgB,OAAA,CAAA,GAAW,eAAA;AApB3B;;;AAAA,iBA6BgB,oBAAA,CAAqB,MAAA,EAAQ,WAAA;;AAT7C;;iBAgBgB,kBAAA,CAAmB,MAAA,EAAQ,WAAA;;;AAP3C;iBAcgB,kBAAA,CAAmB,MAAA,EAAQ,WAAA;;;cCjG9B,8BAAA,SAAuC,gBAAA;EAAA,WAAA,CAAA;AAAA;;;cCAvC,mCAAA,SAA4C,gBAAA;cAC3C,SAAA,UAAmB,cAAA;AAAA;;;;AJGjC;;;;;;;;AC6BA;;;;;;;;cIXa,cAAA,SAAuB,aAAA;EAAA,SACiB,EAAA,EAAI,WAAA;cAA3C,CAAA,EAAG,OAAA,CAAQ,SAAA,GAA4B,EAAA,EAAI,WAAA;EJUlB;EILrC,IAAA,CAAK,IAAA,WAAe,WAAA,GAAc,UAAA,CAAW,WAAA;EJMlB;EID3B,KAAA,CAAM,IAAA,WAAe,MAAA;EJCmB;EAAA,IIIpC,UAAA,CAAA,GAAc,cAAA;EJJ+B;;AAanD;;;EIAW,KAAA,CAAM,GAAA;EJAuB;;;;ACfxC;EGwBW,KAAA,WAAgB,MAAA,oFAAA,CAA2F,GAAA,GAAM,CAAA,GAAI,kBAAA,CAAmB,CAAA,EAAG,CAAA;;;;AHFtJ;;EGcW,IAAA,GAAA,CAAA,GAAW,OAAA,CAAQ,CAAA;AAAA"}
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../../src/websocket/types.ts","../../src/websocket/decorators/gateway.decorator.ts","../../src/websocket/decorators/ws-event.decorator.ts","../../src/websocket/errors/websocket-body-not-available.error.ts","../../src/websocket/errors/websocket-duplicate-event-handler.error.ts","../../src/websocket/gateway-context.ts"],"mappings":";;;;;;;;;;KAMY,cAAA,GAAiB,IAAA,CAAK,iBAAA;;;;;;;AAAlC;;;;;;;;AC6BA;;;;;;;;;;;;;;;;iBAAgB,OAAA,CAAQ,KAAA,UAAe,OAAA,GAAU,cAAA,cACpB,WAAA,EAAa,MAAA,EAAQ,CAAA,KAAC,CAAA;;;AAanD;;;;iBAAgB,SAAA,CAAU,MAAA;;;;;;;;AD3C1B;;;;;;;;AC6BA;iBCDgB,SAAA,CAAA,GAAa,eAAA;;;;;;;;;;;;;;;iBAsBb,OAAA,CAAA,GAAW,eAAA;;;ADP3B;;;;;;;;ACfA;;;;iBA0CgB,OAAA,CAAA,GAAW,eAAA;AApB3B;;;AAAA,iBA6BgB,oBAAA,CAAqB,MAAA,EAAQ,WAAA;;AAT7C;;iBAgBgB,kBAAA,CAAmB,MAAA,EAAQ,WAAA;;;AAP3C;iBAcgB,kBAAA,CAAmB,MAAA,EAAQ,WAAA;;;cCjG9B,8BAAA,SAAuC,gBAAA;EAAA,WAAA,CAAA;AAAA;;;cCAvC,mCAAA,SAA4C,gBAAA;cAC3C,SAAA,UAAmB,cAAA;AAAA;;;;AJGjC;;;;;;;;AC6BA;;;;;;;;cIXa,cAAA,SAAuB,aAAA;EAAA,SACiB,EAAA,EAAI,WAAA;cAA3C,CAAA,EAAG,OAAA,CAAQ,SAAA,GAA4B,EAAA,EAAI,WAAA;EJUlB;EILrC,IAAA,CAAK,IAAA,WAAe,WAAA,GAAc,UAAA,CAAW,WAAA;EJMlB;EID3B,KAAA,CAAM,IAAA,WAAe,MAAA;EJCmB;EAAA,IIIpC,UAAA,CAAA,GAAc,cAAA;EJJ+B;;AAanD;;;;;;;EIIW,KAAA,CAAA,GAAS,MAAA;EACT,KAAA,CAAM,GAAA;EHpBQ;;;;AAsBzB;EGSW,KAAA,WAAgB,MAAA,oFAAA,CAA2F,GAAA,GAAM,CAAA,GAAI,kBAAA,CAAmB,CAAA,EAAG,CAAA;;;;AHWtJ;;EGCW,IAAA,GAAA,CAAA,GAAW,OAAA,CAAQ,CAAA;AAAA"}
@@ -1,2 +1,2 @@
1
- import { a as OnMessage, c as getWsOnMessageMethod, d as isGateway, i as OnError, l as WebSocketDuplicateEventHandlerError, n as WebSocketBodyNotAvailableError, o as getWsOnCloseMethod, r as OnClose, s as getWsOnErrorMethod, t as GatewayContext, u as Gateway } from "../gateway-context-cqZ8wMoi.mjs";
1
+ import { a as OnMessage, c as getWsOnMessageMethod, d as isGateway, i as OnError, l as WebSocketDuplicateEventHandlerError, n as WebSocketBodyNotAvailableError, o as getWsOnCloseMethod, r as OnClose, s as getWsOnErrorMethod, t as GatewayContext, u as Gateway } from "../gateway-context-CXmXtaUP.mjs";
2
2
  export { Gateway, GatewayContext, OnClose, OnError, OnMessage, WebSocketBodyNotAvailableError, WebSocketDuplicateEventHandlerError, getWsOnCloseMethod, getWsOnErrorMethod, getWsOnMessageMethod, isGateway };
@@ -1,5 +1,5 @@
1
- import { Cr as Container } from "../index-DPFqRs8L.mjs";
2
- import { t as StratalEnv } from "../env-CamWD-U1.mjs";
1
+ import { Dr as Container } from "../index-ByOyTmqf.mjs";
2
+ import { t as StratalEnv } from "../env-D1rcZ8_r.mjs";
3
3
  import { DurableObject, WorkerEntrypoint, WorkflowEntrypoint } from "cloudflare:workers";
4
4
 
5
5
  //#region src/workers/run-in-scope.d.ts
@@ -1,5 +1,5 @@
1
- import { f as DI_TOKENS } from "../logger-V6Ms3QnQ.mjs";
2
- import { t as Stratal } from "../stratal-BCiwCFN9.mjs";
1
+ import { f as DI_TOKENS } from "../logger-DlV7NtvD.mjs";
2
+ import { t as Stratal } from "../stratal-CNwpbSZl.mjs";
3
3
  import { DurableObject, WorkerEntrypoint, WorkflowEntrypoint } from "cloudflare:workers";
4
4
  //#region src/workers/run-in-scope.ts
5
5
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../../src/workers/run-in-scope.ts","../../src/workers/stratal-durable-object.ts","../../src/workers/stratal-worker-entrypoint.ts","../../src/workers/stratal-workflow.ts"],"sourcesContent":["import type { Container } from '../di/container'\nimport { Stratal } from '../stratal'\n\n/**\n * Shared helper that creates a request-scoped DI container by accessing the\n * Stratal Application via the static singleton.\n *\n * Works with Durable Objects, Workflows, and WorkerEntrypoints.\n */\nexport async function runInScope<T>(\n callback: (container: Container) => T | Promise<T>\n): Promise<T> {\n const app = await Stratal.resolveApplication()\n const mockCtx = app.createMockRouterContext('en')\n return app.container.runInRequestScope(mockCtx, callback)\n}\n","import { DurableObject } from 'cloudflare:workers'\nimport type { Container } from '../di/container'\nimport { DI_TOKENS } from '../di/tokens'\nimport type { StratalEnv } from '../env'\nimport { runInScope } from './run-in-scope'\n\n/**\n * Base class for Durable Objects with full DI access.\n *\n * Extends Cloudflare's `DurableObject` and provides a `runInScope()` helper\n * that creates a request-scoped container with `DurableObjectState` and\n * `DurableObjectId` tokens registered.\n *\n * @example\n * ```typescript\n * import { StratalDurableObject } from 'stratal/workers'\n *\n * export class Counter extends StratalDurableObject {\n * async increment() {\n * return this.runInScope(async (container) => {\n * const svc = container.resolve(CounterService)\n * return svc.increment()\n * })\n * }\n * }\n * ```\n */\nexport abstract class StratalDurableObject<\n Env extends StratalEnv = StratalEnv\n> extends DurableObject<Env> {\n protected async runInScope<T>(\n callback: (container: Container) => T | Promise<T>\n ): Promise<T> {\n return runInScope(async (requestContainer) => {\n requestContainer.registerValue(DI_TOKENS.DurableObjectState, this.ctx)\n requestContainer.registerValue(DI_TOKENS.DurableObjectId, this.ctx.id)\n return callback(requestContainer)\n })\n }\n}\n","import { WorkerEntrypoint } from 'cloudflare:workers'\nimport type { Container } from '../di/container'\nimport type { StratalEnv } from '../env'\nimport { runInScope } from './run-in-scope'\n\n/**\n * Base class for Cloudflare WorkerEntrypoints (Service Bindings / RPC) with full DI access.\n *\n * Extends Cloudflare's `WorkerEntrypoint` and provides a `runInScope()` helper\n * that creates a request-scoped DI container.\n *\n * @example\n * ```typescript\n * import { StratalWorkerEntrypoint } from 'stratal/workers'\n *\n * export class AuthRpc extends StratalWorkerEntrypoint {\n * async verifyToken(token: string) {\n * return this.runInScope(async (container) => {\n * const auth = container.resolve(AuthService)\n * return auth.verify(token)\n * })\n * }\n * }\n * ```\n */\nexport abstract class StratalWorkerEntrypoint<\n Env extends StratalEnv = StratalEnv\n> extends WorkerEntrypoint<Env> {\n protected runInScope<T>(\n callback: (container: Container) => T | Promise<T>\n ): Promise<T> {\n return runInScope(callback)\n }\n}\n","import { WorkflowEntrypoint } from 'cloudflare:workers'\nimport type { Container } from '../di/container'\nimport type { StratalEnv } from '../env'\nimport { runInScope } from './run-in-scope'\n\n/**\n * Base class for Cloudflare Workflows with full DI access.\n *\n * Extends Cloudflare's `WorkflowEntrypoint` and provides a `runInScope()` helper\n * that creates a request-scoped DI container.\n *\n * @example\n * ```typescript\n * import { StratalWorkflow } from 'stratal/workers'\n *\n * export class MyWorkflow extends StratalWorkflow<Env, { userId: string }> {\n * async run(event: WorkflowEvent<{ userId: string }>, step: WorkflowStep) {\n * await this.runInScope(async (container) => {\n * const svc = container.resolve(UserService)\n * await svc.process(event.payload.userId)\n * })\n * }\n * }\n * ```\n */\nexport abstract class StratalWorkflow<\n Env extends StratalEnv = StratalEnv,\n Params = unknown\n> extends WorkflowEntrypoint<Env, Params> {\n protected runInScope<T>(\n callback: (container: Container) => T | Promise<T>\n ): Promise<T> {\n return runInScope(callback)\n }\n}\n"],"mappings":";;;;;;;;;;AASA,eAAsB,WACpB,UACY;CACZ,MAAM,MAAM,MAAM,QAAQ,oBAAoB;CAC9C,MAAM,UAAU,IAAI,wBAAwB,KAAK;AACjD,QAAO,IAAI,UAAU,kBAAkB,SAAS,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;ACa3D,IAAsB,uBAAtB,cAEU,cAAmB;CAC3B,MAAgB,WACd,UACY;AACZ,SAAO,WAAW,OAAO,qBAAqB;AAC5C,oBAAiB,cAAc,UAAU,oBAAoB,KAAK,IAAI;AACtE,oBAAiB,cAAc,UAAU,iBAAiB,KAAK,IAAI,GAAG;AACtE,UAAO,SAAS,iBAAiB;IACjC;;;;;;;;;;;;;;;;;;;;;;;;;ACZN,IAAsB,0BAAtB,cAEU,iBAAsB;CAC9B,WACE,UACY;AACZ,SAAO,WAAW,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;ACN/B,IAAsB,kBAAtB,cAGU,mBAAgC;CACxC,WACE,UACY;AACZ,SAAO,WAAW,SAAS"}
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../../src/workers/run-in-scope.ts","../../src/workers/stratal-durable-object.ts","../../src/workers/stratal-worker-entrypoint.ts","../../src/workers/stratal-workflow.ts"],"sourcesContent":["import type { Container } from '../di/container'\nimport { Stratal } from '../stratal'\n\n/**\n * Shared helper that creates a request-scoped DI container by accessing the\n * Stratal Application via the static singleton.\n *\n * Works with Durable Objects, Workflows, and WorkerEntrypoints.\n */\nexport async function runInScope<T>(\n callback: (container: Container) => T | Promise<T>\n): Promise<T> {\n const app = await Stratal.resolveApplication()\n const mockCtx = app.createMockRouterContext('en')\n return app.container.runInRequestScope(mockCtx, callback)\n}\n","import { DurableObject } from 'cloudflare:workers'\nimport type { Container } from '../di/container'\nimport { DI_TOKENS } from '../di/tokens'\nimport type { StratalEnv } from '../env'\nimport { runInScope } from './run-in-scope'\n\n/**\n * Base class for Durable Objects with full DI access.\n *\n * Extends Cloudflare's `DurableObject` and provides a `runInScope()` helper\n * that creates a request-scoped container with `DurableObjectState` and\n * `DurableObjectId` tokens registered.\n *\n * @example\n * ```typescript\n * import { StratalDurableObject } from 'stratal/workers'\n *\n * export class Counter extends StratalDurableObject {\n * async increment() {\n * return this.runInScope(async (container) => {\n * const svc = container.resolve(CounterService)\n * return svc.increment()\n * })\n * }\n * }\n * ```\n */\nexport abstract class StratalDurableObject<\n Env extends StratalEnv = StratalEnv\n> extends DurableObject<Env> {\n protected async runInScope<T>(\n callback: (container: Container) => T | Promise<T>\n ): Promise<T> {\n return runInScope(async (requestContainer) => {\n requestContainer.registerValue(DI_TOKENS.DurableObjectState, this.ctx)\n requestContainer.registerValue(DI_TOKENS.DurableObjectId, this.ctx.id)\n return callback(requestContainer)\n })\n }\n}\n","import { WorkerEntrypoint } from 'cloudflare:workers'\nimport type { Container } from '../di/container'\nimport type { StratalEnv } from '../env'\nimport { runInScope } from './run-in-scope'\n\n/**\n * Base class for Cloudflare WorkerEntrypoints (Service Bindings / RPC) with full DI access.\n *\n * Extends Cloudflare's `WorkerEntrypoint` and provides a `runInScope()` helper\n * that creates a request-scoped DI container.\n *\n * @example\n * ```typescript\n * import { StratalWorkerEntrypoint } from 'stratal/workers'\n *\n * export class AuthRpc extends StratalWorkerEntrypoint {\n * async verifyToken(token: string) {\n * return this.runInScope(async (container) => {\n * const auth = container.resolve(AuthService)\n * return auth.verify(token)\n * })\n * }\n * }\n * ```\n */\nexport abstract class StratalWorkerEntrypoint<\n Env extends StratalEnv = StratalEnv\n> extends WorkerEntrypoint<Env> {\n protected runInScope<T>(\n callback: (container: Container) => T | Promise<T>\n ): Promise<T> {\n return runInScope(callback)\n }\n}\n","import { WorkflowEntrypoint } from 'cloudflare:workers'\nimport type { Container } from '../di/container'\nimport type { StratalEnv } from '../env'\nimport { runInScope } from './run-in-scope'\n\n/**\n * Base class for Cloudflare Workflows with full DI access.\n *\n * Extends Cloudflare's `WorkflowEntrypoint` and provides a `runInScope()` helper\n * that creates a request-scoped DI container.\n *\n * @example\n * ```typescript\n * import { StratalWorkflow } from 'stratal/workers'\n *\n * export class MyWorkflow extends StratalWorkflow<Env, { userId: string }> {\n * async run(event: WorkflowEvent<{ userId: string }>, step: WorkflowStep) {\n * await this.runInScope(async (container) => {\n * const svc = container.resolve(UserService)\n * await svc.process(event.payload.userId)\n * })\n * }\n * }\n * ```\n */\nexport abstract class StratalWorkflow<\n Env extends StratalEnv = StratalEnv,\n Params = unknown\n> extends WorkflowEntrypoint<Env, Params> {\n protected runInScope<T>(\n callback: (container: Container) => T | Promise<T>\n ): Promise<T> {\n return runInScope(callback)\n }\n}\n"],"mappings":";;;;;;;;;;AASA,eAAsB,WACpB,UACY;CACZ,MAAM,MAAM,MAAM,QAAQ,oBAAoB;CAC9C,MAAM,UAAU,IAAI,wBAAwB,KAAK;CACjD,OAAO,IAAI,UAAU,kBAAkB,SAAS,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;ACa3D,IAAsB,uBAAtB,cAEU,cAAmB;CAC3B,MAAgB,WACd,UACY;EACZ,OAAO,WAAW,OAAO,qBAAqB;GAC5C,iBAAiB,cAAc,UAAU,oBAAoB,KAAK,IAAI;GACtE,iBAAiB,cAAc,UAAU,iBAAiB,KAAK,IAAI,GAAG;GACtE,OAAO,SAAS,iBAAiB;IACjC;;;;;;;;;;;;;;;;;;;;;;;;;ACZN,IAAsB,0BAAtB,cAEU,iBAAsB;CAC9B,WACE,UACY;EACZ,OAAO,WAAW,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;ACN/B,IAAsB,kBAAtB,cAGU,mBAAgC;CACxC,WACE,UACY;EACZ,OAAO,WAAW,SAAS"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "stratal",
3
- "version": "0.0.19",
3
+ "version": "0.0.21",
4
4
  "description": "A modular Cloudflare Workers framework with dependency injection, queue-based events, and type-safe configuration",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -114,6 +114,10 @@
114
114
  "types": "./dist/queue/index.d.mts",
115
115
  "import": "./dist/queue/index.mjs"
116
116
  },
117
+ "./rate-limiter": {
118
+ "types": "./dist/rate-limiter/index.d.mts",
119
+ "import": "./dist/rate-limiter/index.mjs"
120
+ },
117
121
  "./router": {
118
122
  "types": "./dist/router/index.d.mts",
119
123
  "import": "./dist/router/index.mjs"
@@ -160,17 +164,17 @@
160
164
  },
161
165
  "dependencies": {
162
166
  "@hono/swagger-ui": "^0.6.1",
163
- "@hono/zod-openapi": "^1.3.0",
164
- "@intlify/core-base": "^11.4.0",
165
- "@intlify/message-compiler": "^11.4.0",
167
+ "@hono/zod-openapi": "^1.4.0",
168
+ "@intlify/core-base": "^11.4.2",
169
+ "@intlify/message-compiler": "^11.4.2",
166
170
  "@modelcontextprotocol/sdk": "^1.29.0",
167
171
  "@swc-node/register": "^1.11.1",
168
- "@swc/core": "^1.15.32",
172
+ "@swc/core": "^1.15.33",
169
173
  "@swc/helpers": "^0.5.21",
170
174
  "clipanion": "^4.0.0-rc.4",
171
- "hono": "^4.12.15",
175
+ "hono": "^4.12.18",
172
176
  "tsyringe": "^4.10.0",
173
- "zod": "^4.3.6"
177
+ "zod": "^4.4.3"
174
178
  },
175
179
  "peerDependencies": {
176
180
  "@react-email/components": ">=1",
@@ -198,11 +202,11 @@
198
202
  }
199
203
  },
200
204
  "devDependencies": {
201
- "@cloudflare/vitest-pool-workers": "^0.15.0",
202
- "@cloudflare/workers-types": "4.20260426.1",
205
+ "@cloudflare/vitest-pool-workers": "^0.16.3",
206
+ "@cloudflare/workers-types": "4.20260510.1",
203
207
  "@react-email/components": "^1.0.12",
204
208
  "@stratal/testing": "workspace:*",
205
- "@types/node": "^25.6.0",
209
+ "@types/node": "^25.6.2",
206
210
  "@types/nodemailer": "^8.0.0",
207
211
  "@types/react": "^19.2.14",
208
212
  "@types/react-dom": "^19.2.3",
@@ -211,11 +215,11 @@
211
215
  "@vitest/snapshot": "~4.1.5",
212
216
  "jsonc-parser": "^3.3.1",
213
217
  "nodemailer": "^8.0.7",
214
- "react": "^19.2.5",
215
- "react-dom": "^19.2.5",
218
+ "react": "^19.2.6",
219
+ "react-dom": "^19.2.6",
216
220
  "reflect-metadata": "^0.2.2",
217
- "resend": "^6.12.2",
218
- "tsdown": "^0.21.10",
221
+ "resend": "^6.12.3",
222
+ "tsdown": "^0.22.0",
219
223
  "typescript": "^6.0.3",
220
224
  "vitest": "~4.1.5"
221
225
  }
@@ -1 +0,0 @@
1
- {"version":3,"file":"cron-manager-C30t9UZM.mjs","names":[],"sources":["../src/cron/errors/cron-execution.error.ts","../src/cron/cron-manager.ts"],"sourcesContent":["import { ApplicationError } from '../../errors'\nimport { ERROR_CODES } from '../../errors'\n\n/**\n * Error thrown when one or more cron jobs fail execution\n *\n * This error aggregates failures from multiple jobs that share the same schedule.\n */\nexport class CronExecutionError extends ApplicationError {\n\tconstructor(\n\t\tschedule: string,\n\t\tfailedJobsCount: number,\n\t\tjobNames: string\n\t) {\n\t\tsuper(\n\t\t\t'errors.cronExecutionFailed',\n\t\t\tERROR_CODES.SYSTEM.CRON_EXECUTION_FAILED,\n\t\t\t{\n\t\t\t\tschedule,\n\t\t\t\tcount: failedJobsCount,\n\t\t\t\tjobs: jobNames\n\t\t\t}\n\t\t)\n\t}\n}\n","import type { Container } from '../di/container'\nimport { Transient } from '../di/decorators'\nimport type { CronJob, RegisteredJob } from './cron-job'\nimport { CronExecutionError } from './errors/cron-execution.error'\n\n/**\n * Manages cron job registration and execution\n *\n * CronManager is a singleton service that:\n * - Registers cron job class references from modules\n * - Routes scheduled events to matching jobs\n * - Resolves jobs from a request-scoped container at execution time\n *\n * Jobs are grouped by their cron expression, allowing multiple jobs\n * to run on the same schedule.\n */\n@Transient()\nexport class CronManager {\n\t/**\n\t * Map of cron expressions to registered job entries\n\t * Key: Cron expression (e.g., '0 2 * * *')\n\t * Value: Array of registered jobs (class ref + schedule)\n\t */\n\tprivate jobs = new Map<string, RegisteredJob[]>()\n\n\t/**\n\t * Register a cron job class\n\t *\n\t * Jobs with the same schedule are grouped together and executed\n\t * sequentially when the trigger fires.\n\t *\n\t * @param schedule - Cron expression (e.g., '0 2 * * *')\n\t * @param jobClass - CronJob class constructor (resolved at execution time)\n\t */\n\tregisterJob(schedule: string, jobClass: RegisteredJob['jobClass']): void {\n\t\tconst existing = this.jobs.get(schedule) ?? []\n\t\texisting.push({ schedule, jobClass })\n\t\tthis.jobs.set(schedule, existing)\n\t}\n\n\t/**\n\t * Execute all jobs matching the triggered cron expression\n\t *\n\t * Jobs are resolved from the provided request-scoped container,\n\t * ensuring dependencies (e.g. database) are properly scoped.\n\t *\n\t * Jobs are executed sequentially. If a job fails:\n\t * - Its onError() hook is called (if defined)\n\t * - Execution continues with the next job\n\t * - Errors are collected and thrown as CronExecutionError\n\t *\n\t * @param controller - Cloudflare ScheduledController\n\t * @param container - Request-scoped container to resolve jobs from\n\t */\n\tasync executeScheduled(controller: ScheduledController, container: Container): Promise<void> {\n\t\tconst { cron } = controller\n\t\tconst matchingJobs = this.jobs.get(cron) ?? []\n\n\t\tif (matchingJobs.length === 0) {\n\t\t\treturn\n\t\t}\n\n\t\tconst errors: { job: string; error: Error }[] = []\n\n\t\tfor (const { jobClass } of matchingJobs) {\n\t\t\tconst jobName = jobClass.name\n\n\t\t\ttry {\n\t\t\t\t// Register the job class in the request-scoped container so its\n\t\t\t\t// dependencies are resolved from request scope (not the parent).\n\t\t\t\t// Without this, tsyringe falls through to the parent container\n\t\t\t\t// and request-scoped services (e.g. database) get stale instances.\n\t\t\t\tcontainer.register(jobClass, jobClass)\n\t\t\t\tconst job = container.resolve<CronJob>(jobClass)\n\t\t\t\tawait job.execute(controller)\n\t\t\t} catch (error) {\n\t\t\t\tconst err = error as Error\n\t\t\t\terrors.push({ job: jobName, error: err })\n\n\t\t\t\t// Try to resolve and call onError if possible\n\t\t\t\ttry {\n\t\t\t\t\tconst job = container.resolve<CronJob>(jobClass)\n\t\t\t\t\tif (job.onError) {\n\t\t\t\t\t\tawait job.onError(err, controller)\n\t\t\t\t\t}\n\t\t\t\t} catch {\n\t\t\t\t\t// If resolution or onError fails, continue\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// If any jobs failed, throw an aggregate error\n\t\t// This ensures the error is logged by ExceptionHandler\n\t\tif (errors.length > 0) {\n\t\t\tconst jobNames = errors\n\t\t\t\t.map(({ job, error }) => `${job}: ${error.message}`)\n\t\t\t\t.join('; ')\n\n\t\t\tthrow new CronExecutionError(cron, errors.length, jobNames)\n\t\t}\n\t}\n\n\t/**\n\t * Get all registered jobs for a specific cron expression\n\t *\n\t * @param schedule - Cron expression\n\t * @returns Array of registered jobs, or empty array if none\n\t */\n\tgetJobsForSchedule(schedule: string): RegisteredJob[] {\n\t\treturn this.jobs.get(schedule) ?? []\n\t}\n\n\t/**\n\t * Get all registered cron expressions\n\t *\n\t * @returns Array of unique cron expressions\n\t */\n\tgetAllSchedules(): string[] {\n\t\treturn Array.from(this.jobs.keys())\n\t}\n\n\t/**\n\t * Get total number of registered jobs across all schedules\n\t *\n\t * @returns Total job count\n\t */\n\tgetTotalJobCount(): number {\n\t\tlet count = 0\n\t\tfor (const jobs of this.jobs.values()) {\n\t\t\tcount += jobs.length\n\t\t}\n\t\treturn count\n\t}\n}\n"],"mappings":";;;;;;;;AAQA,IAAa,qBAAb,cAAwC,iBAAiB;CACxD,YACC,UACA,iBACA,UACC;AACD,QACC,8BACA,YAAY,OAAO,uBACnB;GACC;GACA,OAAO;GACP,MAAM;GACN,CACD;;;;;ACLI,IAAA,cAAA,MAAM,YAAY;;;;;;CAMxB,uBAAe,IAAI,KAA8B;;;;;;;;;;CAWjD,YAAY,UAAkB,UAA2C;EACxE,MAAM,WAAW,KAAK,KAAK,IAAI,SAAS,IAAI,EAAE;AAC9C,WAAS,KAAK;GAAE;GAAU;GAAU,CAAC;AACrC,OAAK,KAAK,IAAI,UAAU,SAAS;;;;;;;;;;;;;;;;CAiBlC,MAAM,iBAAiB,YAAiC,WAAqC;EAC5F,MAAM,EAAE,SAAS;EACjB,MAAM,eAAe,KAAK,KAAK,IAAI,KAAK,IAAI,EAAE;AAE9C,MAAI,aAAa,WAAW,EAC3B;EAGD,MAAM,SAA0C,EAAE;AAElD,OAAK,MAAM,EAAE,cAAc,cAAc;GACxC,MAAM,UAAU,SAAS;AAEzB,OAAI;AAKH,cAAU,SAAS,UAAU,SAAS;AAEtC,UADY,UAAU,QAAiB,SAC9B,CAAC,QAAQ,WAAW;YACrB,OAAO;IACf,MAAM,MAAM;AACZ,WAAO,KAAK;KAAE,KAAK;KAAS,OAAO;KAAK,CAAC;AAGzC,QAAI;KACH,MAAM,MAAM,UAAU,QAAiB,SAAS;AAChD,SAAI,IAAI,QACP,OAAM,IAAI,QAAQ,KAAK,WAAW;YAE5B;;;AAQV,MAAI,OAAO,SAAS,GAAG;GACtB,MAAM,WAAW,OACf,KAAK,EAAE,KAAK,YAAY,GAAG,IAAI,IAAI,MAAM,UAAU,CACnD,KAAK,KAAK;AAEZ,SAAM,IAAI,mBAAmB,MAAM,OAAO,QAAQ,SAAS;;;;;;;;;CAU7D,mBAAmB,UAAmC;AACrD,SAAO,KAAK,KAAK,IAAI,SAAS,IAAI,EAAE;;;;;;;CAQrC,kBAA4B;AAC3B,SAAO,MAAM,KAAK,KAAK,KAAK,MAAM,CAAC;;;;;;;CAQpC,mBAA2B;EAC1B,IAAI,QAAQ;AACZ,OAAK,MAAM,QAAQ,KAAK,KAAK,QAAQ,CACpC,UAAS,KAAK;AAEf,SAAO;;;0BAnHR,WAAW,CAAA,EAAA,YAAA"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"cron-manager-RuPtFVLy.d.mts","names":[],"sources":["../src/cron/cron-job.ts","../src/cron/cron-manager.ts"],"mappings":";;;;;;;AA6BA;;;;;;;;;;AAOA;;;;;;;;;;;;;;UAPiB,aAAA;EAiChB;EA/BA,QAAA;EA+BS;EA7BT,QAAA,EAAU,WAAA,CAAY,OAAA;AAAA;AAAA,UAGN,OAAA;EA0BgD;;;;;AC9CjE;;ED8CiE,SAlBvD,QAAA;ECV+B;;;;;;EDkBxC,OAAA,CAAQ,UAAA,EAAY,mBAAA,GAAsB,OAAA;EC7BlC;;;;;;;;EDuCR,OAAA,EAAS,KAAA,EAAO,KAAA,EAAO,UAAA,EAAY,mBAAA,GAAsB,OAAA;AAAA;;;;;AAjC1D;;;;;;;;;cCZa,WAAA;EDmBI;;;;;EAAA,QCbR,IAAA;EDuC2B;;;;;;;;;EC5BnC,WAAA,CAAY,QAAA,UAAkB,QAAA,EAAU,aAAA;ED4BxB;;;;;;;;;;AC9CjB;;;;EAsCO,gBAAA,CAAiB,UAAA,EAAY,mBAAA,EAAqB,SAAA,EAAW,SAAA,GAAY,OAAA;EAAZ;;;;;;EAsDnE,kBAAA,CAAmB,QAAA,WAAmB,aAAA;EA1E1B;;;;;EAmFZ,eAAA,CAAA;EA/DmE;;;;;EAwEnE,gBAAA,CAAA;AAAA"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"en-rHmW6vD9.mjs","names":[],"sources":["../src/i18n/messages/en/common.ts","../src/i18n/messages/en/errors.ts","../src/i18n/messages/en/emails.ts","../src/i18n/messages/en/validation.ts","../src/i18n/messages/en/zod.ts","../src/i18n/messages/en/index.ts"],"sourcesContent":["/**\n * System Common Messages - English\n *\n * Common messages used by packages/modules infrastructure.\n * These are automatically merged with application-specific messages.\n */\n\nexport const common = {\n api: {\n title: 'Stratal API',\n description: 'Platform API',\n serverDescription: 'API server',\n security: {\n bearerAuth: 'JWT Bearer token authentication',\n apiKey: 'API key for service authentication',\n sessionCookie: 'Session cookie for browser authentication'\n }\n }\n} as const\n","/**\n * System Error Messages - English\n *\n * Error messages used by packages/modules infrastructure.\n * These are automatically merged with application-specific messages.\n */\n\nexport const errors = {\n // Generic errors\n internalError: 'An internal error occurred',\n notFound: 'Resource not found',\n unauthorized: 'Unauthorized. Please sign in.',\n forbidden: 'Access denied',\n\n // Router errors\n honoAppAlreadyConfigured: 'HonoApp has already been configured and can only be configured once',\n routeNotFound: 'Route not found: {method} {path}',\n routeAccessDenied: 'Resource not found',\n controllerMethodNotFound: 'Method {methodName} not found on {controllerName}',\n controllerRegistration: 'Failed to register controller {controllerName}: {reason}',\n duplicateRouteName: 'Duplicate route name \"{name}\". Already registered by {existingHandler}, cannot register {newHandler}.',\n routeNameNotFound: 'Route \"{name}\" not found in registry.',\n missingRouteParam: 'Missing required parameter \"{param}\" for route \"{name}\" (path: {path}).',\n routerUseScopeViolation: 'router.use() can only be called on the root Router, not inside group() callbacks. Use router.middleware() for scoped middleware.',\n middlewareNextCalledMultipleTimes: 'next() was called multiple times in \"{middlewareName}\" middleware. Ensure each middleware calls next() at most once.',\n missingEnvironmentVariable: 'Environment variable \"{variable}\" is required but not set.',\n\n // WebSocket errors\n websocketBodyNotAvailable: 'body() is not available in WebSocket gateways. Use WebSocket messages instead.',\n websocketDuplicateEventHandler: '@{decorator}() is already applied to \\'{existingMethod}\\'. Only one method per gateway can handle this event.',\n\n // Context errors\n contextNotInitialized: 'Context has not been initialized',\n userNotAuthenticated: 'User is not authenticated',\n insufficientPermissions: 'Insufficient permissions to perform this action',\n requestContainerNotInitialized: 'Request container has not been initialized',\n requestScopeOperationNotAllowed: '{methodName}() cannot be called on this container scope. Check if you are calling it on the correct container (global vs request-scoped).',\n conditionalBindingFallback: 'Conditional binding predicate returned false for token \"{token}\" but no fallback was provided and no existing registration exists.',\n\n // Configuration errors\n configNotInitialized: 'Configuration service has not been initialized',\n configModuleNotInitialized: 'ConfigModule.forRoot() was not called before module initialization',\n stratalNotInitialized: 'Stratal has not been instantiated. Ensure you export a Stratal instance as the default export.',\n\n // Module errors\n moduleAlreadyRegistered: 'Module {moduleName} is already registered',\n moduleDependencyNotFound: 'Module dependency {dependency} not found for module {moduleName}',\n moduleCircularDependency: 'Circular dependency detected: {cycle}',\n invalidModuleProvider: 'Invalid module provider configuration: {provider}',\n\n // Database errors\n databaseGeneric: 'Database error occurred',\n databaseRecordNotFound: 'Record not found in database',\n databaseUniqueConstraint: 'Record already exists',\n databaseForeignKeyConstraint: 'Related record not found',\n databaseConnectionFailed: 'Failed to connect to database',\n databaseTimeout: 'Database query timeout',\n databaseNullConstraint: 'Required field is missing',\n databaseTooManyConnections: 'Too many database connections',\n databaseTransactionConflict: 'Transaction conflict or deadlock',\n databaseConstraintFailed: 'A database constraint was violated',\n databaseTableNotFound: 'The specified table does not exist in the database',\n databaseColumnNotFound: 'The specified column does not exist in the table',\n databaseInvalidQuery: 'The database query is invalid or malformed',\n invalidErrorCodeRange: 'Invalid error code range: {code}',\n\n // Queue errors\n queueBindingNotFound: 'Queue binding {queueName} not found in environment',\n queueProviderNotSupported: 'Queue provider \"{provider}\" is not supported. Valid providers: cloudflare, sync',\n\n // Cron errors\n cronExecutionFailed: '{count} cron job(s) failed for schedule \"{schedule}\": {jobs}',\n\n // i18n errors\n localeNotSupported: \"Locale '{locale}' is not supported. Supported locales: {supportedLocales}\",\n translationMissing: \"Translation missing for key '{key}' in locale '{locale}'\",\n\n // Container errors\n containerNotInitialized: 'Application container has not been initialized. Ensure Application.initialize() has been called.',\n\n // Domain routing errors\n domainMismatch: 'The requested domain does not match any configured route',\n\n // Signature errors\n invalidSignature: 'The URL signature is invalid or has expired',\n\n // Schema validation errors\n schemaValidation: 'Schema validation failed',\n responseValidation: 'Response validation failed',\n\n // OpenAPI errors\n openapiValidation: 'OpenAPI validation failed: {details}',\n openapiRouteRegistration: 'Failed to register OpenAPI route {path}: {reason}',\n\n // Email errors\n email: {\n resendApiKeyMissing: 'Resend API key not configured. Set RESEND_EMAIL_API_KEY environment variable.',\n smtpConfigurationMissing: 'SMTP configuration missing. Set SMTP_URL environment variable.',\n smtpHostMissing: 'SMTP host not configured. Check SMTP_URL format (smtp://user:pass@host:port).',\n smtpConnectionFailed: 'Failed to connect to SMTP server {smtpHost}:{smtpPort}',\n resendApiFailed: 'Resend API error',\n providerNotSupported: 'Unsupported email provider: {provider}. Supported providers: resend, smtp'\n },\n\n // Storage errors\n storage: {\n fileNotFound: 'File at path \"{path}\" was not found',\n invalidDisk: 'Storage disk \"{disk}\" is not configured',\n invalidFileType: 'File type \"{mimeType}\" is not allowed',\n fileTooLarge: 'File size {size} exceeds maximum allowed size of {maxSize}',\n presignedUrlInvalidExpiry: 'Expiry must be between {min} and {max} seconds',\n diskNotConfigured: 'Disk \"{disk}\" is not configured',\n responseBodyMissing: 'No body in storage response for path: {path}',\n r2BindingNotFound: 'R2 binding \"{binding}\" was not found in the environment',\n r2PresignedUrlSecretMissing: 'APP_SECRET environment variable is required for presigned URLs',\n },\n\n // Cache errors\n cache: {\n getFailed: \"Failed to retrieve value from cache for key '{key}'\",\n putFailed: \"Failed to store value in cache for key '{key}'\",\n deleteFailed: \"Failed to delete value from cache for key '{key}'\",\n listFailed: 'Failed to list cache keys'\n },\n\n // Seeder errors\n seederNameCollision: 'Seeder name collision: \"{name}\" is already registered. Use distinct class names for each seeder.',\n seederNotRegistered: 'Seeder \"{name}\" is not registered',\n\n // Migration errors\n migration: {\n failed: 'Migration {migrationName} failed: {error}',\n checksumMismatch: 'Migration {migrationName} checksum mismatch (expected: {expected}, actual: {actual})',\n alreadyApplied: 'Migration {migrationName} has already been applied',\n notFound: 'Migration {migrationName} not found',\n },\n} as const\n","/**\n * System Email Messages - English\n *\n * Email-related messages used by packages/modules infrastructure.\n * These are automatically merged with application-specific messages.\n */\n\nexport const emails = {\n magicLink: {\n subject: 'Your sign-in link'\n }\n} as const\n","/**\n * Form validation messages - English\n */\n\nexport const validation = {\n required: 'This field is required',\n email: 'Invalid email address',\n minLength: 'Must be at least {min} characters',\n maxLength: 'Must not exceed {max} characters',\n min: 'Must be at least {min}',\n max: 'Must not exceed {max}',\n pattern: 'Invalid format',\n numeric: 'Must be a number',\n url: 'Invalid URL',\n date: 'Invalid date',\n passwordStrength: 'Password must contain at least one uppercase letter, one lowercase letter, and one number',\n passwordMatch: 'Passwords do not match',\n unique: 'This value already exists',\n phone: 'Invalid phone number',\n fileRequired: 'Please upload a file',\n fileTooLarge: 'File must be smaller than {max}',\n invalidFileType: 'Please upload a PDF, JPG, or PNG file',\n schoolTypes: {\n required: 'School type is required',\n atLeastOne: 'Please select at least one school type',\n invalidCode: 'Invalid school type: {code}',\n notAvailableInCountry: '{schoolType} is not available in {country}',\n countryNotSupported: 'Country {country} is not supported'\n },\n timezone: {\n required: 'Timezone is required',\n invalid: 'Invalid timezone. Please select a valid IANA timezone.'\n },\n locale: {\n required: 'Language is required',\n invalid: 'Invalid language. Supported languages: {locales}'\n }\n} as const\n","/**\n * Zod validation error messages - English\n *\n * Comprehensive messages for all Zod validation error codes\n * Structured to match Zod's issue types and validation contexts\n */\n\nexport const zodI18n = {\n errors: {\n // General errors\n required: 'Required',\n invalid_type: 'Expected {expected}, received {received}',\n invalid_literal: 'Invalid literal value, expected {expected}',\n custom: {\n default: 'Invalid value',\n // Email validation\n emailOrTextRequired: 'Either html or text content must be provided',\n invalidFromEmail: 'Invalid from email address',\n // Storage validation\n fileInstanceRequired: 'File must be a File instance',\n filePathRequired: 'File path is required',\n diskNameRequired: 'Disk name is required',\n endpointRequired: 'Endpoint URL is required for S3',\n bucketNameRequired: 'Bucket name is required',\n accessKeyRequired: 'Access key ID is required',\n secretKeyRequired: 'Secret access key is required',\n storageDiskRequired: 'At least one storage disk is required',\n // Database validation\n databaseUrlRequired: 'Database URL is required',\n // Domain validation\n domainRequired: 'Domain is required',\n domainTooLong: 'Domain too long',\n invalidDomainFormat: 'Invalid domain format',\n },\n invalid_union: 'Invalid input',\n invalid_union_discriminator: 'Invalid discriminator value. Expected {options}',\n invalid_enum_value: 'Invalid enum value. Expected {options}, received {received}',\n unrecognized_keys: 'Unrecognized key(s) in object: {keys}',\n invalid_arguments: 'Invalid function arguments',\n invalid_return_type: 'Invalid function return type',\n invalid_date: 'Invalid date',\n invalid_intersection_types: 'Intersection results could not be merged',\n not_multiple_of: 'Number must be a multiple of {multipleOf}',\n not_finite: 'Number must be finite',\n\n // String-specific validation errors\n invalid_string: {\n email: 'Invalid email address',\n url: 'Invalid URL',\n uuid: 'Invalid UUID',\n cuid: 'Invalid CUID',\n cuid2: 'Invalid CUID2',\n ulid: 'Invalid ULID',\n regex: 'Invalid format',\n datetime: 'Invalid datetime',\n ip: 'Invalid IP address',\n emoji: 'Invalid emoji',\n startsWith: 'Must start with \"{startsWith}\"',\n endsWith: 'Must end with \"{endsWith}\"',\n includes: 'Must include \"{includes}\"',\n base64: 'Invalid Base64',\n nanoid: 'Invalid NanoID',\n cidr: 'Invalid CIDR',\n jwt: 'Invalid JWT',\n time: 'Invalid time',\n },\n\n // Size validation errors (strings, arrays, numbers)\n too_small: {\n string: {\n exact: 'Must be exactly {minimum} characters',\n inclusive: 'Must be at least {minimum} characters',\n not_inclusive: 'Must be more than {minimum} characters',\n },\n number: {\n exact: 'Must be exactly {minimum}',\n inclusive: 'Must be at least {minimum}',\n not_inclusive: 'Must be greater than {minimum}',\n },\n array: {\n exact: 'Must contain exactly {minimum} item(s)',\n inclusive: 'Must contain at least {minimum} item(s)',\n not_inclusive: 'Must contain more than {minimum} item(s)',\n },\n set: {\n exact: 'Must contain exactly {minimum} item(s)',\n inclusive: 'Must contain at least {minimum} item(s)',\n not_inclusive: 'Must contain more than {minimum} item(s)',\n },\n date: {\n exact: 'Date must be {minimum}',\n inclusive: 'Date must be {minimum} or later',\n not_inclusive: 'Date must be after {minimum}',\n },\n bigint: {\n exact: 'Must be exactly {minimum}',\n inclusive: 'Must be at least {minimum}',\n not_inclusive: 'Must be greater than {minimum}',\n },\n },\n\n too_big: {\n string: {\n exact: 'Must be exactly {maximum} characters',\n inclusive: 'Must be at most {maximum} characters',\n not_inclusive: 'Must be less than {maximum} characters',\n },\n number: {\n exact: 'Must be exactly {maximum}',\n inclusive: 'Must be at most {maximum}',\n not_inclusive: 'Must be less than {maximum}',\n },\n array: {\n exact: 'Must contain exactly {maximum} item(s)',\n inclusive: 'Must contain at most {maximum} item(s)',\n not_inclusive: 'Must contain less than {maximum} item(s)',\n },\n set: {\n exact: 'Must contain exactly {maximum} item(s)',\n inclusive: 'Must contain at most {maximum} item(s)',\n not_inclusive: 'Must contain less than {maximum} item(s)',\n },\n date: {\n exact: 'Date must be {maximum}',\n inclusive: 'Date must be {maximum} or earlier',\n not_inclusive: 'Date must be before {maximum}',\n },\n bigint: {\n exact: 'Must be exactly {maximum}',\n inclusive: 'Must be at most {maximum}',\n not_inclusive: 'Must be less than {maximum}',\n },\n },\n },\n} as const\n","/**\n * System Messages - English\n *\n * Re-exports all system message categories.\n * These messages are used by packages/modules infrastructure\n * and are automatically merged with application-specific messages.\n */\n\nexport { common } from './common'\nexport { errors } from './errors'\nexport { emails } from './emails'\nexport { validation } from './validation'\nexport { zodI18n } from './zod'\n"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"env-CamWD-U1.d.mts","names":[],"sources":["../src/env.ts"],"mappings":";;AAgBA;;;;;;;;;;;;;;;UAAiB,UAAA;EACf,WAAA;EACA,KAAA,EAAO,WAAA;EACP,UAAA;AAAA"}