stratal 0.0.17 → 0.0.19

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 (200) hide show
  1. package/README.md +8 -8
  2. package/dist/{base-email.provider-DypUAfWm.mjs → base-email.provider-mjynzewK.mjs} +1 -1
  3. package/dist/{base-email.provider-DypUAfWm.mjs.map → base-email.provider-mjynzewK.mjs.map} +1 -1
  4. package/dist/bin/cloudflare-workers-loader.mjs +33 -1
  5. package/dist/bin/cloudflare-workers-loader.mjs.map +1 -1
  6. package/dist/bin/quarry.mjs +169 -7
  7. package/dist/bin/quarry.mjs.map +1 -1
  8. package/dist/cache/index.d.mts +3 -2
  9. package/dist/cache/index.d.mts.map +1 -1
  10. package/dist/cache/index.mjs +3 -10
  11. package/dist/cache/index.mjs.map +1 -1
  12. package/dist/{colors-Y7WIFXs7.mjs → colors-DJaRDXoS.mjs} +1 -1
  13. package/dist/{colors-Y7WIFXs7.mjs.map → colors-DJaRDXoS.mjs.map} +1 -1
  14. package/dist/{command-B1CPgsrU.mjs → command-BgSlsS4M.mjs} +3 -3
  15. package/dist/command-BgSlsS4M.mjs.map +1 -0
  16. package/dist/{command-TnkPYWta.d.mts → command-DsQq56Lp.d.mts} +2 -2
  17. package/dist/{command-TnkPYWta.d.mts.map → command-DsQq56Lp.d.mts.map} +1 -1
  18. package/dist/config/index.d.mts +81 -37
  19. package/dist/config/index.d.mts.map +1 -1
  20. package/dist/config/index.mjs +135 -61
  21. package/dist/config/index.mjs.map +1 -1
  22. package/dist/{consumer-registry-Bymm6ff4.d.mts → consumer-registry-Doom7BEh.d.mts} +1 -1
  23. package/dist/{consumer-registry-Bymm6ff4.d.mts.map → consumer-registry-Doom7BEh.d.mts.map} +1 -1
  24. package/dist/controller.decorator-LZY9aHYG.mjs +66 -0
  25. package/dist/controller.decorator-LZY9aHYG.mjs.map +1 -0
  26. package/dist/cron/index.d.mts +4 -3
  27. package/dist/cron/index.d.mts.map +1 -1
  28. package/dist/cron/index.mjs +1 -3
  29. package/dist/{cron-manager-CFBamKKk.mjs → cron-manager-C30t9UZM.mjs} +29 -19
  30. package/dist/cron-manager-C30t9UZM.mjs.map +1 -0
  31. package/dist/{cron-manager-D7imGwUT.d.mts → cron-manager-RuPtFVLy.d.mts} +28 -14
  32. package/dist/cron-manager-RuPtFVLy.d.mts.map +1 -0
  33. package/dist/di/index.d.mts +2 -2
  34. package/dist/di/index.mjs +3 -3
  35. package/dist/email/index.d.mts +3 -3
  36. package/dist/email/index.mjs +87 -18
  37. package/dist/email/index.mjs.map +1 -1
  38. package/dist/{en-DaewN8hc.mjs → en-rHmW6vD9.mjs} +14 -31
  39. package/dist/en-rHmW6vD9.mjs.map +1 -0
  40. package/dist/env-CamWD-U1.d.mts +25 -0
  41. package/dist/env-CamWD-U1.d.mts.map +1 -0
  42. package/dist/errors/index.d.mts +2 -2
  43. package/dist/errors/index.mjs +2 -3
  44. package/dist/errors-B4pYgYON.mjs +1714 -0
  45. package/dist/errors-B4pYgYON.mjs.map +1 -0
  46. package/dist/{errors-DuAR5Wke.mjs → errors-BUyUfr2Z.mjs} +14 -7
  47. package/dist/errors-BUyUfr2Z.mjs.map +1 -0
  48. package/dist/events/index.d.mts +2 -2
  49. package/dist/events/index.mjs +1 -2
  50. package/dist/{events-CvUSgEuN.mjs → events-COKixqnG.mjs} +2 -2
  51. package/dist/{events-CvUSgEuN.mjs.map → events-COKixqnG.mjs.map} +1 -1
  52. package/dist/{gateway-context-CNOLkLUC.mjs → gateway-context-cqZ8wMoi.mjs} +4 -9
  53. package/dist/gateway-context-cqZ8wMoi.mjs.map +1 -0
  54. package/dist/guards/index.d.mts +14 -5
  55. package/dist/guards/index.d.mts.map +1 -1
  56. package/dist/guards/index.mjs +1 -1
  57. package/dist/{guards-DUk_Kzst.mjs → guards-DMbsAxSX.mjs} +1 -1
  58. package/dist/guards-DMbsAxSX.mjs.map +1 -0
  59. package/dist/http-method.decorator-BT3ufnz8.mjs +96 -0
  60. package/dist/http-method.decorator-BT3ufnz8.mjs.map +1 -0
  61. package/dist/i18n/index.d.mts +3 -3
  62. package/dist/i18n/index.mjs +3 -16
  63. package/dist/i18n/messages/en/index.d.mts +1 -1
  64. package/dist/i18n/messages/en/index.mjs +1 -1
  65. package/dist/i18n/utils/index.d.mts +30 -0
  66. package/dist/i18n/utils/index.d.mts.map +1 -0
  67. package/dist/i18n/utils/index.mjs +2 -0
  68. package/dist/i18n/validation/index.d.mts +1 -1
  69. package/dist/i18n/validation/index.mjs +1 -1
  70. package/dist/i18n.module-CI_prYFD.mjs +2340 -0
  71. package/dist/i18n.module-CI_prYFD.mjs.map +1 -0
  72. package/dist/{index-NGxg-KP_.d.mts → index-B437eK7p.d.mts} +59 -16
  73. package/dist/index-B437eK7p.d.mts.map +1 -0
  74. package/dist/{index-Dp6A5ywM.d.mts → index-CWRS7Ri3.d.mts} +1 -1
  75. package/dist/{index-Dp6A5ywM.d.mts.map → index-CWRS7Ri3.d.mts.map} +1 -1
  76. package/dist/{index-D_w_Rmtd.d.mts → index-DFhEeFfC.d.mts} +13 -30
  77. package/dist/{index-D_w_Rmtd.d.mts.map → index-DFhEeFfC.d.mts.map} +1 -1
  78. package/dist/index-DPFqRs8L.d.mts +4318 -0
  79. package/dist/index-DPFqRs8L.d.mts.map +1 -0
  80. package/dist/{index-DGRe6Yoa.d.mts → index-Dnqm9ZB6.d.mts} +5 -4
  81. package/dist/index-Dnqm9ZB6.d.mts.map +1 -0
  82. package/dist/index-SHx31sBJ.d.mts +101 -0
  83. package/dist/index-SHx31sBJ.d.mts.map +1 -0
  84. package/dist/index.d.mts +5 -3
  85. package/dist/index.d.mts.map +1 -1
  86. package/dist/index.mjs +1 -20
  87. package/dist/{is-command-DJVI6wEJ.mjs → is-command-C6a7WTPw.mjs} +2 -2
  88. package/dist/{is-command-DJVI6wEJ.mjs.map → is-command-C6a7WTPw.mjs.map} +1 -1
  89. package/dist/{is-seeder-D5MIEcdz.mjs → is-seeder-CebjZCDn.mjs} +1 -1
  90. package/dist/{is-seeder-D5MIEcdz.mjs.map → is-seeder-CebjZCDn.mjs.map} +1 -1
  91. package/dist/logger/index.d.mts +1 -1
  92. package/dist/logger/index.mjs +1 -1
  93. package/dist/{logger-CGT91VY6.mjs → logger-V6Ms3QnQ.mjs} +63 -45
  94. package/dist/logger-V6Ms3QnQ.mjs.map +1 -0
  95. package/dist/macroable/index.d.mts +2 -0
  96. package/dist/macroable/index.mjs +2 -0
  97. package/dist/macroable-BmufBshB.mjs +122 -0
  98. package/dist/macroable-BmufBshB.mjs.map +1 -0
  99. package/dist/module/index.d.mts +3 -4
  100. package/dist/module/index.d.mts.map +1 -1
  101. package/dist/module/index.mjs +1 -10
  102. package/dist/module-qGE_1duv.mjs +732 -0
  103. package/dist/module-qGE_1duv.mjs.map +1 -0
  104. package/dist/openapi/index.d.mts +3 -3
  105. package/dist/openapi/index.mjs +2 -15
  106. package/dist/{openapi-tools.service-B3TxYKoQ.mjs → openapi-tools.service-CYWGuhue.mjs} +4 -1
  107. package/dist/{openapi-tools.service-B3TxYKoQ.mjs.map → openapi-tools.service-CYWGuhue.mjs.map} +1 -1
  108. package/dist/{openapi.service-DGnX3Fc4.d.mts → openapi.service-Bv_NioM9.d.mts} +9 -17
  109. package/dist/openapi.service-Bv_NioM9.d.mts.map +1 -0
  110. package/dist/quarry/index.d.mts +26 -12
  111. package/dist/quarry/index.d.mts.map +1 -1
  112. package/dist/quarry/index.mjs +4 -8
  113. package/dist/{quarry-registry-B2rkO-JS.mjs → quarry-registry-DFfRRkA7.mjs} +45 -40
  114. package/dist/quarry-registry-DFfRRkA7.mjs.map +1 -0
  115. package/dist/queue/index.d.mts +2 -2
  116. package/dist/queue/index.mjs +3 -13
  117. package/dist/queue/index.mjs.map +1 -1
  118. package/dist/{queue.module-BtI8f4Jo.mjs → queue.module-P-G-nCYz.mjs} +39 -42
  119. package/dist/queue.module-P-G-nCYz.mjs.map +1 -0
  120. package/dist/r2-storage.provider-LdzK9tfG.mjs +244 -0
  121. package/dist/r2-storage.provider-LdzK9tfG.mjs.map +1 -0
  122. package/dist/{resend.provider-bXMEkdRJ.mjs → resend.provider-bwILp0WI.mjs} +2 -4
  123. package/dist/{resend.provider-bXMEkdRJ.mjs.map → resend.provider-bwILp0WI.mjs.map} +1 -1
  124. package/dist/router/index.d.mts +2 -2
  125. package/dist/router/index.mjs +7 -16
  126. package/dist/seeder/index.d.mts +3 -4
  127. package/dist/seeder/index.d.mts.map +1 -1
  128. package/dist/seeder/index.mjs +2 -6
  129. package/dist/{seeder-R7RXJC35.mjs → seeder-BcqIFa2X.mjs} +5 -5
  130. package/dist/{seeder-R7RXJC35.mjs.map → seeder-BcqIFa2X.mjs.map} +1 -1
  131. package/dist/setup-CtekcwuO.mjs +37 -0
  132. package/dist/setup-CtekcwuO.mjs.map +1 -0
  133. package/dist/signed-url-COX7cCWR.mjs +74 -0
  134. package/dist/signed-url-COX7cCWR.mjs.map +1 -0
  135. package/dist/{smtp.provider-DrbHQztF.mjs → smtp.provider-B07yuARi.mjs} +2 -4
  136. package/dist/{smtp.provider-DrbHQztF.mjs.map → smtp.provider-B07yuARi.mjs.map} +1 -1
  137. package/dist/storage/index.d.mts +39 -17
  138. package/dist/storage/index.d.mts.map +1 -1
  139. package/dist/storage/index.mjs +3 -14
  140. package/dist/storage/providers/index.d.mts +30 -69
  141. package/dist/storage/providers/index.d.mts.map +1 -1
  142. package/dist/storage/providers/index.mjs +2 -5
  143. package/dist/{storage-CZKHOhci.mjs → storage-P6X4h9So.mjs} +102 -29
  144. package/dist/storage-P6X4h9So.mjs.map +1 -0
  145. package/dist/{storage-provider.interface-0IqcdhBf.d.mts → storage-provider.interface-CC1nniHk.d.mts} +20 -15
  146. package/dist/storage-provider.interface-CC1nniHk.d.mts.map +1 -0
  147. package/dist/stratal-BCiwCFN9.mjs +533 -0
  148. package/dist/stratal-BCiwCFN9.mjs.map +1 -0
  149. package/dist/{types-DahElfUw.d.mts → types-DIWemRad.d.mts} +2 -2
  150. package/dist/types-DIWemRad.d.mts.map +1 -0
  151. package/dist/{usage-generator-CVIsENuE.mjs → usage-generator-MBcRo0Q2.mjs} +2 -2
  152. package/dist/{usage-generator-CVIsENuE.mjs.map → usage-generator-MBcRo0Q2.mjs.map} +1 -1
  153. package/dist/{validation-DQTC259A.mjs → validation-Dbg3ehdP.mjs} +2 -2
  154. package/dist/{validation-DQTC259A.mjs.map → validation-Dbg3ehdP.mjs.map} +1 -1
  155. package/dist/websocket/index.d.mts +3 -3
  156. package/dist/websocket/index.d.mts.map +1 -1
  157. package/dist/websocket/index.mjs +1 -4
  158. package/dist/workers/index.d.mts +2 -1
  159. package/dist/workers/index.d.mts.map +1 -1
  160. package/dist/workers/index.mjs +2 -20
  161. package/dist/workers/index.mjs.map +1 -1
  162. package/package.json +41 -50
  163. package/dist/application-DfPtIzxF.d.mts +0 -177
  164. package/dist/application-DfPtIzxF.d.mts.map +0 -1
  165. package/dist/command-B1CPgsrU.mjs.map +0 -1
  166. package/dist/cron-manager-CFBamKKk.mjs.map +0 -1
  167. package/dist/cron-manager-D7imGwUT.d.mts.map +0 -1
  168. package/dist/en-DaewN8hc.mjs.map +0 -1
  169. package/dist/errors-DSKapqD8.mjs +0 -707
  170. package/dist/errors-DSKapqD8.mjs.map +0 -1
  171. package/dist/errors-DuAR5Wke.mjs.map +0 -1
  172. package/dist/gateway-context-CNOLkLUC.mjs.map +0 -1
  173. package/dist/guards-DUk_Kzst.mjs.map +0 -1
  174. package/dist/i18n.module-Dn9SrFdS.mjs +0 -1841
  175. package/dist/i18n.module-Dn9SrFdS.mjs.map +0 -1
  176. package/dist/index-BFCxSp_f.d.mts +0 -2625
  177. package/dist/index-BFCxSp_f.d.mts.map +0 -1
  178. package/dist/index-DGRe6Yoa.d.mts.map +0 -1
  179. package/dist/index-NGxg-KP_.d.mts.map +0 -1
  180. package/dist/logger-CGT91VY6.mjs.map +0 -1
  181. package/dist/middleware/index.d.mts +0 -2
  182. package/dist/middleware/index.mjs +0 -5
  183. package/dist/middleware-Bl-b5pkt.mjs +0 -362
  184. package/dist/middleware-Bl-b5pkt.mjs.map +0 -1
  185. package/dist/module-registry-CmjBX6ol.d.mts +0 -121
  186. package/dist/module-registry-CmjBX6ol.d.mts.map +0 -1
  187. package/dist/module-tUtyVJ5E.mjs +0 -371
  188. package/dist/module-tUtyVJ5E.mjs.map +0 -1
  189. package/dist/openapi.service-DGnX3Fc4.d.mts.map +0 -1
  190. package/dist/quarry-registry-B2rkO-JS.mjs.map +0 -1
  191. package/dist/queue.module-BtI8f4Jo.mjs.map +0 -1
  192. package/dist/router-context-D9R1v2Ac.mjs +0 -267
  193. package/dist/router-context-D9R1v2Ac.mjs.map +0 -1
  194. package/dist/s3-storage.provider-CttzNnDR.mjs +0 -335
  195. package/dist/s3-storage.provider-CttzNnDR.mjs.map +0 -1
  196. package/dist/storage-CZKHOhci.mjs.map +0 -1
  197. package/dist/storage-provider.interface-0IqcdhBf.d.mts.map +0 -1
  198. package/dist/stratal-D5smIU1y.mjs +0 -315
  199. package/dist/stratal-D5smIU1y.mjs.map +0 -1
  200. package/dist/types-DahElfUw.d.mts.map +0 -1
@@ -0,0 +1,1714 @@
1
+ import { a as __decorate, d as CONTAINER_TOKEN, f as DI_TOKENS, o as __decorateParam, p as Transient, s as __decorateMetadata, u as LOGGER_TOKENS } from "./logger-V6Ms3QnQ.mjs";
2
+ import { t as Macroable } from "./macroable-BmufBshB.mjs";
3
+ import { Lifecycle, container as container$1, delay, inject, inject as inject$1, injectable as injectable$1, instancePerContainerCachingFactory as instancePerContainerCachingFactory$1, predicateAwareClassFactory, singleton } from "tsyringe";
4
+ import { AsyncLocalStorage } from "node:async_hooks";
5
+ import { stream, streamSSE, streamText } from "hono/streaming";
6
+ //#region src/errors/application-error.ts
7
+ /**
8
+ * ApplicationError
9
+ *
10
+ * Abstract base class for all application errors.
11
+ *
12
+ * @deprecated Use {@link HttpException} for new error classes. `HttpException` provides
13
+ * a simpler constructor that takes `(httpStatus, message?)` and derives the error code
14
+ * automatically. Existing subclasses will continue to work but should be migrated over time.
15
+ *
16
+ * Features:
17
+ * - Type-safe error codes from ERROR_CODES registry
18
+ * - Type-safe message keys from i18n module
19
+ * - Localized message keys (translated by ExceptionHandler)
20
+ * - Structured metadata for logging and interpolation
21
+ * - Proper Error prototype chain
22
+ * - Automatic timestamp generation
23
+ * - Serialization for RPC transmission
24
+ * - Optional self-reporting via `report()` method
25
+ * - Optional self-rendering via `render()` method
26
+ *
27
+ * Message Localization:
28
+ * - Each error class passes an i18n key (e.g., 'errors.userNotFound') to super()
29
+ * - `Error.message` contains the i18n key for useful stack traces and fallback display
30
+ * - Metadata provides interpolation parameters (e.g., { userId: '123' })
31
+ * - ExceptionHandler translates the message key using I18nService before sending response
32
+ * - This ensures errors are localized based on the user's locale
33
+ */
34
+ var ApplicationError = class ApplicationError extends Error {
35
+ /**
36
+ * Controls whether stack traces are captured.
37
+ * Set to false in production to skip the expensive Error.captureStackTrace() call,
38
+ * since stack traces are stripped from responses in production anyway.
39
+ */
40
+ static captureStackTraces = true;
41
+ /**
42
+ * Type-safe error code from ERROR_CODES registry
43
+ * See error-codes.ts for the complete registry
44
+ */
45
+ code;
46
+ /**
47
+ * ISO timestamp when the error was created
48
+ */
49
+ timestamp;
50
+ /**
51
+ * Additional structured data about the error
52
+ * Used for:
53
+ * 1. Logging and debugging
54
+ * 2. Message interpolation (e.g., { userId: '123', email: 'user@example.com' })
55
+ */
56
+ metadata;
57
+ /**
58
+ * @param i18nKey - Type-safe i18n message key (e.g., 'errors.userNotFound')
59
+ * @param code - Type-safe error code from ERROR_CODES registry
60
+ * @param metadata - Optional data for logging and interpolation
61
+ */
62
+ constructor(i18nKey, code, metadata) {
63
+ super(i18nKey);
64
+ Object.setPrototypeOf(this, new.target.prototype);
65
+ this.name = this.constructor.name;
66
+ this.code = code;
67
+ this.timestamp = (/* @__PURE__ */ new Date()).toISOString();
68
+ this.metadata = metadata;
69
+ if (ApplicationError.captureStackTraces && Error.captureStackTrace) Error.captureStackTrace(this, this.constructor);
70
+ }
71
+ /**
72
+ * Filter metadata to include only user-facing properties
73
+ *
74
+ * User-facing properties (validation/constraint errors):
75
+ * - issues: Validation errors from SchemaValidationError
76
+ * - fields: Constraint violation fields
77
+ * - field: Single field constraint/foreign key
78
+ *
79
+ * Internal properties (excluded from response):
80
+ * - path, method: Route debugging
81
+ * - controllerName, reason: Controller errors
82
+ * - details, etc.: Internal debugging info
83
+ *
84
+ * @param metadata - Raw metadata object
85
+ * @returns Filtered metadata with only whitelisted properties
86
+ */
87
+ static filterMetadata(metadata) {
88
+ if (!metadata) return void 0;
89
+ const whitelist = [
90
+ "issues",
91
+ "fields",
92
+ "field"
93
+ ];
94
+ const filtered = {};
95
+ let hasUserFacingData = false;
96
+ for (const key of whitelist) if (key in metadata && metadata[key] !== void 0) {
97
+ filtered[key] = metadata[key];
98
+ hasUserFacingData = true;
99
+ }
100
+ return hasUserFacingData ? filtered : void 0;
101
+ }
102
+ /**
103
+ * Serialize error to ErrorResponse format for RPC transmission
104
+ *
105
+ * @param env - Environment (development | production)
106
+ * @param translatedMessage - Optional translated message (from ExceptionHandler)
107
+ * @returns ErrorResponse object suitable for JSON serialization
108
+ */
109
+ toErrorResponse(env, translatedMessage) {
110
+ const message = translatedMessage ?? this.message;
111
+ return {
112
+ code: this.code,
113
+ message,
114
+ timestamp: this.timestamp,
115
+ metadata: ApplicationError.filterMetadata(this.metadata),
116
+ stack: env === "development" ? this.stack?.replace(this.message, message) : void 0
117
+ };
118
+ }
119
+ /**
120
+ * JSON serialization (used by JSON.stringify)
121
+ * Defaults to development mode for backward compatibility
122
+ * Note: This will use the untranslated message key - use ExceptionHandler for proper localization
123
+ */
124
+ toJSON() {
125
+ return this.toErrorResponse("development");
126
+ }
127
+ };
128
+ //#endregion
129
+ //#region src/router/router.tokens.ts
130
+ /**
131
+ * Dependency injection tokens for the router system
132
+ */
133
+ const ROUTER_TOKENS = {
134
+ /**
135
+ * Token for RouterContext (request-scoped)
136
+ * Contains Hono context wrapper with helper methods
137
+ */
138
+ RouterContext: Symbol.for("stratal:router:context"),
139
+ /**
140
+ * Token for RouteRegistry (singleton)
141
+ * Central registry of all application routes — source of truth for route:list, route:types, URL generation
142
+ */
143
+ RouteRegistry: Symbol.for("stratal:router:route-registry"),
144
+ /**
145
+ * Token for VersioningService (singleton)
146
+ * Resolves version prefixes for route paths
147
+ */
148
+ VersioningService: Symbol.for("stratal:router:versioning-service"),
149
+ /**
150
+ * Token for LocalePathService (singleton)
151
+ * Resolves locale path variants and computes LocalePathConfig
152
+ */
153
+ LocalePathService: Symbol.for("stratal:router:locale-path-service"),
154
+ /**
155
+ * Token for RouterResolver (singleton, may be null)
156
+ * Internal resolver that computes effective Router config per controller
157
+ */
158
+ RouterResolver: Symbol.for("stratal:router:router-resolver"),
159
+ /**
160
+ * Token for HonoApp (singleton)
161
+ * The Hono application instance with Stratal-specific setup
162
+ */
163
+ HonoApp: Symbol.for("stratal:router:hono-app"),
164
+ /**
165
+ * Token for Uri (request-scoped)
166
+ * URL generation service — route URLs, signed URLs, current/previous URL access
167
+ */
168
+ Uri: Symbol.for("stratal:router:uri")
169
+ };
170
+ //#endregion
171
+ //#region src/di/errors/conditional-binding-fallback.error.ts
172
+ /**
173
+ * ConditionalBindingFallbackError
174
+ *
175
+ * Thrown when a conditional binding predicate returns false but no fallback
176
+ * implementation was provided and no existing registration exists for the token.
177
+ *
178
+ * This typically indicates a misconfiguration in the DI setup where:
179
+ * - A `when().use().give()` chain was used without `otherwise()`
180
+ * - AND the token wasn't previously registered
181
+ * - AND the predicate evaluated to false at resolution time
182
+ */
183
+ var ConditionalBindingFallbackError = class extends ApplicationError {
184
+ constructor(token) {
185
+ super("errors.conditionalBindingFallback", ERROR_CODES.SYSTEM.INFRASTRUCTURE_ERROR, { token });
186
+ }
187
+ };
188
+ //#endregion
189
+ //#region src/di/errors/request-scope-operation-not-allowed.error.ts
190
+ /**
191
+ * RequestScopeOperationNotAllowedError
192
+ *
193
+ * Thrown when attempting to call a method that is not allowed on the current container scope.
194
+ * - `createRequestScope()` and `runInRequestScope()` can only be called on global containers
195
+ */
196
+ var RequestScopeOperationNotAllowedError = class extends ApplicationError {
197
+ constructor(methodName) {
198
+ super("errors.requestScopeOperationNotAllowed", ERROR_CODES.SYSTEM.INFRASTRUCTURE_ERROR, { methodName });
199
+ }
200
+ };
201
+ //#endregion
202
+ //#region src/di/conditional-binding-builder.ts
203
+ /**
204
+ * Implementation of ConditionalBindingBuilder
205
+ *
206
+ * @internal
207
+ */
208
+ var ConditionalBindingBuilderImpl = class {
209
+ constructor(tsyringeContainer, predicateContainer, predicate, options) {
210
+ this.tsyringeContainer = tsyringeContainer;
211
+ this.predicateContainer = predicateContainer;
212
+ this.predicate = predicate;
213
+ this.options = options;
214
+ }
215
+ use(token) {
216
+ return new ConditionalBindingUseImpl(this.tsyringeContainer, this.predicateContainer, this.predicate, this.options, token);
217
+ }
218
+ };
219
+ /**
220
+ * Implementation of ConditionalBindingUse
221
+ *
222
+ * @internal
223
+ */
224
+ var ConditionalBindingUseImpl = class {
225
+ constructor(tsyringeContainer, predicateContainer, predicate, options, token) {
226
+ this.tsyringeContainer = tsyringeContainer;
227
+ this.predicateContainer = predicateContainer;
228
+ this.predicate = predicate;
229
+ this.options = options;
230
+ this.token = token;
231
+ }
232
+ give(trueImplementation) {
233
+ const falseImplementation = this.getFallbackImplementation();
234
+ this.registerWithPredicate(trueImplementation, falseImplementation);
235
+ return { otherwise: (implementation) => {
236
+ this.registerWithPredicate(trueImplementation, implementation);
237
+ } };
238
+ }
239
+ /**
240
+ * Get fallback implementation: existing registration or throw-on-resolve class
241
+ */
242
+ getFallbackImplementation() {
243
+ if (this.tsyringeContainer.isRegistered(this.token)) {
244
+ const existingInstance = this.tsyringeContainer.resolve(this.token);
245
+ return class ExistingInstanceWrapper {
246
+ static instance = existingInstance;
247
+ constructor() {
248
+ return ExistingInstanceWrapper.instance;
249
+ }
250
+ };
251
+ }
252
+ const tokenStr = typeof this.token === "symbol" ? this.token.description ?? "unknown" : typeof this.token === "function" ? this.token.name : String(this.token);
253
+ return class NoFallbackError {
254
+ constructor() {
255
+ throw new ConditionalBindingFallbackError(tokenStr);
256
+ }
257
+ };
258
+ }
259
+ registerWithPredicate(trueImplementation, falseImplementation) {
260
+ const { predicate, predicateContainer, options } = this;
261
+ this.tsyringeContainer.register(this.token, { useFactory: predicateAwareClassFactory(() => predicate(predicateContainer), trueImplementation, falseImplementation, options.cache ?? false) });
262
+ }
263
+ };
264
+ //#endregion
265
+ //#region src/di/container.ts
266
+ /**
267
+ * Unified Container for DI management
268
+ *
269
+ * Manages the two-tier container hierarchy:
270
+ * - Global scope: Singletons, base instances of request-scoped services
271
+ * - Request scope: Context-enriched instances per HTTP request
272
+ *
273
+ * @example Basic registration
274
+ * ```typescript
275
+ * import { container as tsyringeRootContainer } from 'tsyringe'
276
+ *
277
+ * const container = new Container({
278
+ * container: tsyringeRootContainer.createChildContainer()
279
+ * })
280
+ *
281
+ * container.register(I18nService)
282
+ * container.register(MY_TOKEN, MyService)
283
+ * container.registerSingleton(ConfigService)
284
+ * container.registerValue(MY_TOKEN, myInstance)
285
+ * ```
286
+ *
287
+ * @example Request scope (automatic lifecycle)
288
+ * ```typescript
289
+ * await container.runInRequestScope(routerContext, async (requestContainer) => {
290
+ * const i18n = requestContainer.resolve(I18N_TOKEN)
291
+ * })
292
+ * ```
293
+ */
294
+ var Container = class Container {
295
+ container;
296
+ isRequestScoped;
297
+ constructor(options) {
298
+ this.isRequestScoped = options.isRequestScoped ?? false;
299
+ this.container = options.container;
300
+ if (!this.isRequestScoped) this.container.register(CONTAINER_TOKEN, { useValue: this });
301
+ }
302
+ register(tokenOrClass, serviceClassOrScope, scope) {
303
+ let token;
304
+ let serviceClass;
305
+ let lifecycle;
306
+ if (typeof serviceClassOrScope === "function") {
307
+ token = tokenOrClass;
308
+ serviceClass = serviceClassOrScope;
309
+ lifecycle = scope;
310
+ } else {
311
+ token = tokenOrClass;
312
+ serviceClass = tokenOrClass;
313
+ lifecycle = serviceClassOrScope;
314
+ }
315
+ if (lifecycle !== void 0) this.container.register(token, { useClass: serviceClass }, { lifecycle });
316
+ else this.container.register(token, { useClass: serviceClass });
317
+ }
318
+ registerSingleton(tokenOrClass, serviceClass) {
319
+ if (serviceClass !== void 0) this.container.registerSingleton(tokenOrClass, serviceClass);
320
+ else {
321
+ const targetClass = tokenOrClass;
322
+ this.container.registerSingleton(targetClass, targetClass);
323
+ }
324
+ }
325
+ /**
326
+ * Register a value (instance) directly
327
+ */
328
+ registerValue(token, value) {
329
+ this.container.register(token, { useValue: value });
330
+ }
331
+ /**
332
+ * Register with factory function
333
+ */
334
+ registerFactory(token, factory) {
335
+ this.container.register(token, { useFactory: () => factory(this) });
336
+ }
337
+ /**
338
+ * Register an alias to an existing token
339
+ */
340
+ registerExisting(alias, target) {
341
+ this.container.register(alias, { useToken: target });
342
+ }
343
+ /**
344
+ * Resolve a service from the container
345
+ */
346
+ resolve(token) {
347
+ return this.container.resolve(token);
348
+ }
349
+ /**
350
+ * Check if a token is registered
351
+ */
352
+ isRegistered(token) {
353
+ return this.container.isRegistered(token);
354
+ }
355
+ /**
356
+ * Start a conditional binding with predicate evaluation
357
+ */
358
+ when(predicate, options = {}) {
359
+ return new ConditionalBindingBuilderImpl(this.container, this, predicate, options);
360
+ }
361
+ /**
362
+ * Replace a service registration with a decorated version
363
+ */
364
+ extend(token, decorator) {
365
+ const decoratedInstance = decorator(this.container.resolve(token), this);
366
+ this.container.register(token, { useValue: decoratedInstance });
367
+ }
368
+ /**
369
+ * Run callback within request scope
370
+ *
371
+ * Creates a child container with fresh instances for services registered with `scope: Scope.Request`.
372
+ * Callback receives the request-scoped container as argument.
373
+ *
374
+ * Can only be called on global container (not request-scoped).
375
+ */
376
+ async runInRequestScope(routerContext, callback) {
377
+ if (this.isRequestScoped) throw new RequestScopeOperationNotAllowedError("runInRequestScope");
378
+ const requestContainer = this.createRequestScope(routerContext);
379
+ try {
380
+ return await callback(requestContainer);
381
+ } finally {
382
+ await requestContainer.dispose();
383
+ }
384
+ }
385
+ /**
386
+ * Create request scope container
387
+ *
388
+ * Can only be called on global container (not request-scoped).
389
+ */
390
+ createRequestScope(routerContext) {
391
+ if (this.isRequestScoped) throw new RequestScopeOperationNotAllowedError("createRequestScope");
392
+ const childContainer = this.container.createChildContainer();
393
+ childContainer.register(ROUTER_TOKENS.RouterContext, { useValue: routerContext });
394
+ return new Container({
395
+ container: childContainer,
396
+ isRequestScoped: true
397
+ });
398
+ }
399
+ /**
400
+ * Get underlying tsyringe container
401
+ */
402
+ getTsyringeContainer() {
403
+ return this.container;
404
+ }
405
+ dispose() {
406
+ return this.container.dispose();
407
+ }
408
+ };
409
+ //#endregion
410
+ //#region src/di/types.ts
411
+ /**
412
+ * DI Type Definitions
413
+ *
414
+ * Core type definitions for the dependency injection system.
415
+ * Simplified after removing LazyProxy - no more type wrappers needed.
416
+ */
417
+ /**
418
+ * Service scope for DI registration
419
+ *
420
+ * Maps directly to tsyringe's Lifecycle enum.
421
+ * Scope is specified at registration time via provider configuration,
422
+ * not at class decoration time.
423
+ *
424
+ * @example
425
+ * ```typescript
426
+ * // In module providers:
427
+ * { provide: MY_TOKEN, useClass: MyService, scope: Scope.Singleton }
428
+ *
429
+ * // In Application.ts:
430
+ * container.register(MY_TOKEN, MyService, Scope.Request)
431
+ * ```
432
+ */
433
+ let Scope = /* @__PURE__ */ function(Scope) {
434
+ /** New instance per resolution (default) */
435
+ Scope[Scope["Transient"] = Lifecycle.Transient] = "Transient";
436
+ /** Single instance shared globally */
437
+ Scope[Scope["Singleton"] = Lifecycle.Singleton] = "Singleton";
438
+ /** New instance per child container (per request) */
439
+ Scope[Scope["Request"] = Lifecycle.ContainerScoped] = "Request";
440
+ return Scope;
441
+ }({});
442
+ //#endregion
443
+ //#region src/errors/error-codes.ts
444
+ /**
445
+ * Centralized Error Code Registry
446
+ *
447
+ * Error codes are organized by category with specific ranges:
448
+ * - 1000-1999: Validation errors
449
+ * - 2000-2999: Database errors (generic)
450
+ * - 3000-3999: Authentication & Authorization
451
+ * - 4000-4999: Resource errors
452
+ * - 5000-5999: Domain-specific business logic (per module)
453
+ * - 9000-9999: System/Internal errors
454
+ * - 9000-9099: Router errors
455
+ * - 9100-9199: Configuration errors
456
+ * - 9200-9299: Infrastructure errors
457
+ * - 9300-9399: I18n errors
458
+ */
459
+ const ERROR_CODES = {
460
+ /**
461
+ * Database Errors (2000-2999)
462
+ * Generic database errors thrown by Prisma client extensions
463
+ */
464
+ DATABASE: {
465
+ /** Generic database error */
466
+ GENERIC: 2e3,
467
+ /** Record not found in database */
468
+ RECORD_NOT_FOUND: 2001,
469
+ /** Unique constraint violation */
470
+ UNIQUE_CONSTRAINT: 2002,
471
+ /** Foreign key constraint violation */
472
+ FOREIGN_KEY_CONSTRAINT: 2003,
473
+ /** Database connection failed */
474
+ CONNECTION_FAILED: 2004,
475
+ /** Database timeout */
476
+ TIMEOUT: 2005,
477
+ /** Null constraint violation */
478
+ NULL_CONSTRAINT: 2006,
479
+ /** Too many database connections */
480
+ TOO_MANY_CONNECTIONS: 2007,
481
+ /** Transaction conflict or deadlock */
482
+ TRANSACTION_CONFLICT: 2008
483
+ },
484
+ /**
485
+ * Authentication Errors (3000-3099)
486
+ * Authentication-related failures
487
+ */
488
+ AUTH: {
489
+ /** Invalid credentials provided */
490
+ INVALID_CREDENTIALS: 3e3,
491
+ /** Session expired or invalid */
492
+ SESSION_EXPIRED: 3001,
493
+ /** Account locked or disabled */
494
+ ACCOUNT_LOCKED: 3002,
495
+ /** Invalid or expired token */
496
+ INVALID_TOKEN: 3003,
497
+ /** Context not initialized */
498
+ CONTEXT_NOT_INITIALIZED: 3004,
499
+ /** User not authenticated */
500
+ USER_NOT_AUTHENTICATED: 3005,
501
+ /** Email verification required before login */
502
+ EMAIL_NOT_VERIFIED: 3007,
503
+ /** Password doesn't meet minimum length */
504
+ PASSWORD_TOO_SHORT: 3008,
505
+ /** Password exceeds maximum length */
506
+ PASSWORD_TOO_LONG: 3009,
507
+ /** Account with email already exists */
508
+ ACCOUNT_ALREADY_EXISTS: 3010,
509
+ /** User creation failed */
510
+ FAILED_TO_CREATE_USER: 3011,
511
+ /** Session creation failed */
512
+ FAILED_TO_CREATE_SESSION: 3012,
513
+ /** User update failed */
514
+ FAILED_TO_UPDATE_USER: 3013,
515
+ /** Social account already linked */
516
+ SOCIAL_ACCOUNT_LINKED: 3014,
517
+ /** Last account cannot be unlinked */
518
+ CANNOT_UNLINK_LAST_ACCOUNT: 3015,
519
+ /** Organization not found */
520
+ ORGANIZATION_NOT_FOUND: 3020,
521
+ /** Organization member not found */
522
+ MEMBER_NOT_FOUND: 3021,
523
+ /** Organization invitation not found */
524
+ INVITATION_NOT_FOUND: 3022,
525
+ /** Invitation recipient mismatch */
526
+ INVITATION_RECIPIENT_MISMATCH: 3023,
527
+ /** Organization limit reached */
528
+ ORGANIZATION_LIMIT_REACHED: 3024,
529
+ /** Organization membership constraint violation */
530
+ ORGANIZATION_MEMBERSHIP_REQUIRED: 3025
531
+ },
532
+ /**
533
+ * Authorization Errors (3100-3199)
534
+ * Permission and access control failures
535
+ */
536
+ AUTHZ: {
537
+ /** Insufficient permissions */
538
+ FORBIDDEN: 3100,
539
+ /** Resource access denied */
540
+ ACCESS_DENIED: 3101,
541
+ /** User lacks required role */
542
+ INSUFFICIENT_PERMISSIONS: 3102
543
+ },
544
+ /**
545
+ * Resource Errors (4000-4999)
546
+ * Generic resource-related errors
547
+ */
548
+ RESOURCE: {
549
+ /** Generic resource not found */
550
+ NOT_FOUND: 4e3,
551
+ /** Route/endpoint not found */
552
+ ROUTE_NOT_FOUND: 4004,
553
+ /** Resource conflict or duplicate */
554
+ CONFLICT: 4100,
555
+ /** Resource already exists */
556
+ ALREADY_EXISTS: 4101
557
+ },
558
+ /**
559
+ * Validation Errors (1000-1999)
560
+ * Input validation failures
561
+ */
562
+ VALIDATION: {
563
+ /** Generic validation error */
564
+ GENERIC: 1e3,
565
+ /** Required field missing */
566
+ REQUIRED_FIELD: 1001,
567
+ /** Invalid format */
568
+ INVALID_FORMAT: 1002,
569
+ /** Schema validation failed */
570
+ SCHEMA_VALIDATION: 1003,
571
+ /** Request validation failed (OpenAPI, etc.) */
572
+ REQUEST_VALIDATION: 1004,
573
+ /** Response validation failed (response body doesn't match declared schema) */
574
+ RESPONSE_VALIDATION: 1005
575
+ },
576
+ /**
577
+ * Router Errors (9000-9099)
578
+ * Router and controller-related INTERNAL errors
579
+ */
580
+ ROUTER: {
581
+ /** Controller registration error */
582
+ CONTROLLER_REGISTRATION_ERROR: 9005,
583
+ /** Controller method not found */
584
+ CONTROLLER_METHOD_NOT_FOUND: 9006,
585
+ /** OpenAPI route registration failed */
586
+ OPENAPI_ROUTE_REGISTRATION: 9008,
587
+ /** Duplicate route name in RouteRegistry */
588
+ DUPLICATE_ROUTE_NAME: 9010,
589
+ /** Named route not found in RouteRegistry */
590
+ ROUTE_NAME_NOT_FOUND: 9011,
591
+ /** Required route parameter missing during URL generation */
592
+ MISSING_ROUTE_PARAM: 9012,
593
+ /** router.use() called inside group() callback */
594
+ USE_SCOPE_VIOLATION: 9013,
595
+ /** next() called more than once in a middleware */
596
+ MIDDLEWARE_NEXT_CALLED_MULTIPLE_TIMES: 9014
597
+ },
598
+ /**
599
+ * I18n Errors (9300-9399)
600
+ * Internationalization and localization errors
601
+ */
602
+ I18N: {
603
+ /** Translation key missing from all locales */
604
+ TRANSLATION_MISSING: 9300,
605
+ /** Requested locale not supported */
606
+ LOCALE_NOT_SUPPORTED: 9301
607
+ },
608
+ /**
609
+ * System Errors (9000-9999)
610
+ * Internal system errors and unexpected failures
611
+ */
612
+ SYSTEM: {
613
+ /** Internal server error */
614
+ INTERNAL_ERROR: 9e3,
615
+ /** Generic configuration error */
616
+ CONFIGURATION_ERROR: 9100,
617
+ /** ConfigService not initialized */
618
+ CONFIG_NOT_INITIALIZED: 9101,
619
+ /** Module already registered */
620
+ MODULE_ALREADY_REGISTERED: 9102,
621
+ /** Circular module dependency detected */
622
+ MODULE_CIRCULAR_DEPENDENCY: 9103,
623
+ /** Module dependency not found */
624
+ MODULE_DEPENDENCY_NOT_FOUND: 9104,
625
+ /** Invalid error code range */
626
+ INVALID_ERROR_CODE_RANGE: 9105,
627
+ /** Invalid module provider configuration */
628
+ INVALID_MODULE_PROVIDER: 9106,
629
+ /** ConfigModule.forRoot() was not called */
630
+ CONFIG_MODULE_NOT_INITIALIZED: 9107,
631
+ /** Generic infrastructure error */
632
+ INFRASTRUCTURE_ERROR: 9200,
633
+ /** Execution context not initialized */
634
+ EXECUTION_CONTEXT_NOT_INITIALIZED: 9201,
635
+ /** Request container not initialized */
636
+ REQUEST_CONTAINER_NOT_INITIALIZED: 9202,
637
+ /** Queue binding not found */
638
+ QUEUE_BINDING_NOT_FOUND: 9203,
639
+ /** Cron job execution failed */
640
+ CRON_EXECUTION_FAILED: 9204,
641
+ /** Queue provider not supported */
642
+ QUEUE_PROVIDER_NOT_SUPPORTED: 9205,
643
+ /** body() called on WebSocket gateway context */
644
+ WEBSOCKET_BODY_NOT_AVAILABLE: 9206,
645
+ /** Duplicate WebSocket event decorator on a gateway */
646
+ WEBSOCKET_DUPLICATE_EVENT_HANDLER: 9207,
647
+ /** Seeder name collision — two seeders share the same class name */
648
+ SEEDER_NAME_COLLISION: 9208,
649
+ /** Seeder not registered in the SeederRegistry */
650
+ SEEDER_NOT_REGISTERED: 9209,
651
+ /** Application container not initialized (AsyncLocalStorage) */
652
+ CONTAINER_NOT_INITIALIZED: 9210,
653
+ /** Required environment variable not set */
654
+ MISSING_ENVIRONMENT_VARIABLE: 9211
655
+ }
656
+ };
657
+ //#endregion
658
+ //#region src/errors/container-not-initialized.error.ts
659
+ /**
660
+ * Thrown when attempting to access the application container via AsyncLocalStorage
661
+ * before `Application.initialize()` has been called.
662
+ *
663
+ * This typically means `route()` or another standalone function is being called
664
+ * outside the application lifecycle.
665
+ */
666
+ var ContainerNotInitializedError = class extends ApplicationError {
667
+ constructor() {
668
+ super("errors.containerNotInitialized", ERROR_CODES.SYSTEM.CONTAINER_NOT_INITIALIZED);
669
+ }
670
+ };
671
+ //#endregion
672
+ //#region src/di/container-storage.ts
673
+ /**
674
+ * AsyncLocalStorage for the application container.
675
+ *
676
+ * Set by `Application.initialize()` — all code from that point onward
677
+ * (Stratal handlers, TestingModuleBuilder, standalone functions like `route()`)
678
+ * can access the container without DI or static singletons.
679
+ *
680
+ * Follows the same pattern as `errorMapContextStorage` in `i18n/validation/validation.context.ts`.
681
+ */
682
+ const containerStorage = new AsyncLocalStorage();
683
+ /**
684
+ * Get the application container from AsyncLocalStorage.
685
+ *
686
+ * @throws ContainerNotInitializedError if called outside `Application.initialize()` scope
687
+ */
688
+ function getContainer() {
689
+ const container = containerStorage.getStore();
690
+ if (!container) throw new ContainerNotInitializedError();
691
+ return container;
692
+ }
693
+ /**
694
+ * Run a function within a container context.
695
+ *
696
+ * @param container - The application container to store
697
+ * @param fn - The function to execute with container access
698
+ */
699
+ function runWithContainer(container, fn) {
700
+ return containerStorage.run(container, fn);
701
+ }
702
+ //#endregion
703
+ //#region src/i18n/i18n.tokens.ts
704
+ /**
705
+ * I18n Module DI Tokens
706
+ * Symbol-based tokens to avoid string collisions
707
+ */
708
+ const I18N_TOKENS = {
709
+ /** MessageLoaderService - loads and caches locale messages */
710
+ MessageLoader: Symbol.for("stratal:i18n:message:loader"),
711
+ /** I18nService - request-scoped translation service */
712
+ I18nService: Symbol.for("stratal:i18n:service"),
713
+ /** I18nModuleOptions - configuration options from forRoot() */
714
+ Options: Symbol.for("stratal:i18n:options"),
715
+ /** MessageRegistry - singleton accumulator for registerMessages() contributions */
716
+ MessageRegistry: Symbol.for("stratal:i18n:message:registry")
717
+ };
718
+ //#endregion
719
+ //#region src/errors/http-exception.ts
720
+ /**
721
+ * Maps common HTTP status codes to their default error codes.
722
+ * Used by {@link HttpException} to derive the error code automatically.
723
+ */
724
+ const HTTP_STATUS_TO_ERROR_CODE = {
725
+ 400: ERROR_CODES.VALIDATION.GENERIC,
726
+ 401: ERROR_CODES.AUTH.USER_NOT_AUTHENTICATED,
727
+ 403: ERROR_CODES.AUTHZ.FORBIDDEN,
728
+ 404: ERROR_CODES.RESOURCE.NOT_FOUND,
729
+ 409: ERROR_CODES.RESOURCE.CONFLICT,
730
+ 422: ERROR_CODES.VALIDATION.GENERIC,
731
+ 500: ERROR_CODES.SYSTEM.INTERNAL_ERROR
732
+ };
733
+ /**
734
+ * Default human-readable messages for common HTTP status codes.
735
+ * Used as fallback when no message is provided to {@link HttpException}.
736
+ */
737
+ const HTTP_STATUS_MESSAGES = {
738
+ 400: "Bad Request",
739
+ 401: "Unauthorized",
740
+ 403: "Forbidden",
741
+ 404: "Not Found",
742
+ 409: "Conflict",
743
+ 422: "Unprocessable Entity",
744
+ 500: "Internal Server Error"
745
+ };
746
+ /**
747
+ * HTTP-centric exception base class.
748
+ *
749
+ * Unlike {@link ApplicationError} which requires `(i18nKey, code, metadata)`,
750
+ * `HttpException` takes just `(httpStatus, message?)` and derives the error code
751
+ * from the HTTP status automatically.
752
+ *
753
+ * The message can be a plain string or an i18n key — the {@link ExceptionHandler}
754
+ * tries to translate it via `i18n.t()`, falling back to the raw string if the
755
+ * key is not found.
756
+ *
757
+ * Existing {@link ApplicationError} subclasses can be migrated to this gradually.
758
+ *
759
+ * @example
760
+ * ```typescript
761
+ * // Simple usage with plain message
762
+ * throw new HttpException(404, 'User not found')
763
+ *
764
+ * // With i18n key (auto-translated if key exists)
765
+ * throw new HttpException(422, 'errors.invalidInput')
766
+ *
767
+ * // Default message for status code
768
+ * throw new HttpException(500)
769
+ *
770
+ * // Subclass for domain-specific errors
771
+ * class PaymentDeclinedError extends HttpException {
772
+ * constructor() {
773
+ * super(402, 'errors.paymentDeclined')
774
+ * }
775
+ * }
776
+ * ```
777
+ */
778
+ var HttpException = class extends ApplicationError {
779
+ /**
780
+ * The HTTP status code for this exception.
781
+ * Used by the {@link ExceptionHandler} to set the response status.
782
+ */
783
+ httpStatus;
784
+ /**
785
+ * @param httpStatus - HTTP status code (e.g., 404, 422, 500)
786
+ * @param message - Optional message string or i18n key. Defaults to the
787
+ * standard HTTP status message (e.g., "Not Found" for 404).
788
+ */
789
+ constructor(httpStatus, message) {
790
+ const code = HTTP_STATUS_TO_ERROR_CODE[httpStatus] ?? ERROR_CODES.SYSTEM.INTERNAL_ERROR;
791
+ const messageStr = message ?? HTTP_STATUS_MESSAGES[httpStatus] ?? "Internal Server Error";
792
+ super(messageStr, code);
793
+ this.httpStatus = httpStatus;
794
+ }
795
+ };
796
+ /**
797
+ * Throw an HTTP exception from anywhere in the application.
798
+ *
799
+ * The message can be a plain string or an i18n key — the {@link ExceptionHandler}
800
+ * translates it automatically, falling back to the raw string if the key is not found.
801
+ *
802
+ * @param status - HTTP status code
803
+ * @param message - Optional message (plain string or i18n key)
804
+ * @throws {@link HttpException} — always throws, never returns
805
+ *
806
+ * @example
807
+ * ```typescript
808
+ * // With plain message
809
+ * abort(404, 'User not found')
810
+ *
811
+ * // Default message for status
812
+ * abort(403)
813
+ *
814
+ * // With i18n key
815
+ * abort(422, 'errors.invalidInput')
816
+ * ```
817
+ */
818
+ function abort(status, message) {
819
+ throw new HttpException(status, message);
820
+ }
821
+ //#endregion
822
+ //#region src/errors/get-http-status.ts
823
+ /**
824
+ * Maps error codes to HTTP status codes
825
+ *
826
+ * This utility is used by the frontend to set appropriate HTTP status codes
827
+ * when returning errors from API routes.
828
+ *
829
+ * @param code - Numeric error code from ERROR_CODES registry
830
+ * @returns HTTP status code (200-599)
831
+ */
832
+ function getHttpStatus(code) {
833
+ if (code >= 1e3 && code < 2e3) return 400;
834
+ if (code >= 2e3 && code < 3e3) {
835
+ if (code === ERROR_CODES.DATABASE.RECORD_NOT_FOUND) return 404;
836
+ if (code === ERROR_CODES.DATABASE.UNIQUE_CONSTRAINT) return 409;
837
+ return 500;
838
+ }
839
+ if (code >= 3e3 && code < 3100) return 401;
840
+ if (code >= 3100 && code < 3200) return 403;
841
+ if (code >= 4e3 && code < 4100) return 404;
842
+ if (code >= 4100 && code < 4200) return 409;
843
+ if (code >= 5e3 && code < 6e3) {
844
+ if (code === 5e3 || code === 5100 || code === 5200) return 404;
845
+ return 422;
846
+ }
847
+ if (code >= 9e3) return 500;
848
+ return 500;
849
+ }
850
+ /**
851
+ * Resolve the HTTP status code for an ApplicationError.
852
+ *
853
+ * If the error is an {@link HttpException}, its `httpStatus` property takes precedence.
854
+ * Otherwise, falls back to the code-range-based mapping via {@link getHttpStatus}.
855
+ *
856
+ * @param error - The application error to resolve the status for
857
+ * @returns HTTP status code
858
+ */
859
+ function resolveHttpStatus(error) {
860
+ if (error instanceof HttpException) return error.httpStatus;
861
+ return getHttpStatus(error.code);
862
+ }
863
+ //#endregion
864
+ //#region src/errors/internal-error.ts
865
+ /**
866
+ * InternalError
867
+ *
868
+ * Represents an unexpected internal server error.
869
+ * Used to wrap unknown errors that don't fit into specific error categories.
870
+ *
871
+ * This error is thrown when:
872
+ * - An unexpected exception occurs
873
+ * - An error type is not recognized
874
+ * - A system-level failure happens
875
+ */
876
+ var InternalError = class extends ApplicationError {
877
+ constructor(metadata) {
878
+ super("errors.internalError", ERROR_CODES.SYSTEM.INTERNAL_ERROR, metadata);
879
+ }
880
+ };
881
+ //#endregion
882
+ //#region src/errors/is-application-error.ts
883
+ /**
884
+ * Type guard to check if an error is an ApplicationError.
885
+ *
886
+ * Uses `instanceof` first, then falls back to a structural check
887
+ * for the `code` and `timestamp` properties that all ApplicationError
888
+ * instances have. This handles cross-module boundary cases where
889
+ * `instanceof` fails due to duplicate class identities (e.g., Vite's
890
+ * module graph in workerd).
891
+ *
892
+ * @param error - The error to check
893
+ * @returns True if the error is an ApplicationError instance
894
+ */
895
+ function isApplicationError(error) {
896
+ if (error instanceof ApplicationError) return true;
897
+ return error instanceof Error && typeof error.code === "number" && typeof error.timestamp === "string";
898
+ }
899
+ //#endregion
900
+ //#region src/errors/exception-handler.ts
901
+ let ExceptionHandler = class ExceptionHandler {
902
+ reportables = [];
903
+ renderables = [];
904
+ dontReportSet = /* @__PURE__ */ new Set();
905
+ levelOverrides = /* @__PURE__ */ new Map();
906
+ contextCallbacks = [];
907
+ respondCallbacks = [];
908
+ environment;
909
+ constructor(logger, env, container, executionContext) {
910
+ this.logger = logger;
911
+ this.env = env;
912
+ this.container = container;
913
+ this.executionContext = executionContext;
914
+ this.environment = this.env.ENVIRONMENT;
915
+ }
916
+ /**
917
+ * Register a custom reporting callback for a specific exception type.
918
+ *
919
+ * The callback is invoked when an error matching `errorClass` (via `instanceof`)
920
+ * is thrown. Chain `.stop()` to prevent the default logger from also reporting.
921
+ *
922
+ * @typeParam T - The exception type to match
923
+ * @param errorClass - Constructor of the exception to match
924
+ * @param callback - Reporting function receiving the typed error and context
925
+ * @returns A {@link Reportable} with a `stop()` method
926
+ *
927
+ * @example
928
+ * ```typescript
929
+ * this.reportable(PaymentError, (e, ctx) => {
930
+ * sentry.captureException(e)
931
+ * }).stop() // skip default logging
932
+ * ```
933
+ */
934
+ reportable(errorClass, callback) {
935
+ const entry = {
936
+ errorClass,
937
+ callback,
938
+ shouldStop: false
939
+ };
940
+ this.reportables.push(entry);
941
+ return { stop: () => {
942
+ entry.shouldStop = true;
943
+ } };
944
+ }
945
+ /**
946
+ * Register a custom rendering callback for a specific exception type.
947
+ *
948
+ * The callback should return a `Response` (for HTTP contexts), an `ErrorResponse`,
949
+ * or `undefined` to fall through to the default renderer.
950
+ *
951
+ * @typeParam T - The exception type to match
952
+ * @param errorClass - Constructor of the exception to match
953
+ * @param callback - Rendering function receiving the typed error and context
954
+ *
955
+ * @example
956
+ * ```typescript
957
+ * this.renderable(MaintenanceError, (e, ctx) => {
958
+ * if (ctx.type === 'http') {
959
+ * return ctx.ctx.html('<h1>Down for maintenance</h1>', 503)
960
+ * }
961
+ * })
962
+ * ```
963
+ */
964
+ renderable(errorClass, callback) {
965
+ this.renderables.push({
966
+ errorClass,
967
+ callback
968
+ });
969
+ }
970
+ /**
971
+ * Suppress reporting (logging) for the given exception types.
972
+ *
973
+ * Errors matching these classes will still be rendered into responses
974
+ * but will not be logged or sent to external reporters.
975
+ *
976
+ * @param errorClasses - Array of exception constructors to suppress
977
+ *
978
+ * @example
979
+ * ```typescript
980
+ * this.dontReport([RouteNotFoundError, SchemaValidationError])
981
+ * ```
982
+ */
983
+ dontReport(errorClasses) {
984
+ for (const cls of errorClasses) this.dontReportSet.add(cls);
985
+ }
986
+ /**
987
+ * Override the log severity for a specific exception type.
988
+ *
989
+ * By default, severity is derived from the error code range.
990
+ * Use this to promote or demote specific errors.
991
+ *
992
+ * @param errorClass - Constructor of the exception to override
993
+ * @param severity - The log severity to use
994
+ *
995
+ * @example
996
+ * ```typescript
997
+ * this.level(RecordNotFoundError, 'warn')
998
+ * ```
999
+ */
1000
+ level(errorClass, severity) {
1001
+ this.levelOverrides.set(errorClass, severity);
1002
+ }
1003
+ /**
1004
+ * Add global context data to all exception log entries.
1005
+ *
1006
+ * The callback is invoked on every reported error and its return value
1007
+ * is merged into the log data.
1008
+ *
1009
+ * @param callback - Function returning key-value pairs to include in logs
1010
+ *
1011
+ * @example
1012
+ * ```typescript
1013
+ * this.context(() => ({
1014
+ * appVersion: '1.2.3',
1015
+ * region: env.CF_REGION,
1016
+ * }))
1017
+ * ```
1018
+ */
1019
+ context(callback) {
1020
+ this.contextCallbacks.push(callback);
1021
+ }
1022
+ /**
1023
+ * Register a callback to post-process every error Response before it is returned.
1024
+ *
1025
+ * Use this to add headers, modify the body, change content type, or
1026
+ * transform the response in any way.
1027
+ *
1028
+ * @param callback - Function receiving (response, error, context) and returning a Response
1029
+ *
1030
+ * @example
1031
+ * ```typescript
1032
+ * this.respond((response, error, ctx) => {
1033
+ * response.headers.set('X-Error-Code', String(error.code))
1034
+ * return response
1035
+ * })
1036
+ * ```
1037
+ */
1038
+ respond(callback) {
1039
+ this.respondCallbacks.push(callback);
1040
+ }
1041
+ /**
1042
+ * Resolve a service from the DI container.
1043
+ *
1044
+ * Useful inside `register()` callbacks for accessing injected services
1045
+ * (e.g., Sentry, analytics, custom loggers).
1046
+ *
1047
+ * @typeParam T - The type of the service to resolve
1048
+ * @param token - DI token (symbol or constructor)
1049
+ * @returns The resolved service instance
1050
+ *
1051
+ * @example
1052
+ * ```typescript
1053
+ * this.reportable(CriticalError, (e) => {
1054
+ * this.resolve(SentryService).captureException(e)
1055
+ * })
1056
+ * ```
1057
+ */
1058
+ resolve(token) {
1059
+ return this.container.resolve(token);
1060
+ }
1061
+ /**
1062
+ * Handle an error through the full exception pipeline.
1063
+ *
1064
+ * This is the single entry point used by all contexts (HTTP, queue, cron, CLI).
1065
+ * It normalizes the error, reports it (non-blocking via `waitUntil`),
1066
+ * renders it into a Response, and applies post-processing.
1067
+ *
1068
+ * @param error - The thrown error (may or may not be an ApplicationError)
1069
+ * @param context - The execution context where the error occurred
1070
+ * @returns A Response (JSON by default, customizable via renderable/respond)
1071
+ */
1072
+ async handle(error, context) {
1073
+ const appError = this.normalizeError(error);
1074
+ this.executionContext.waitUntil(this.performReport(appError, context));
1075
+ const response = await this.performRender(appError, context);
1076
+ return this.applyRespondCallbacks(response, appError, context);
1077
+ }
1078
+ /**
1079
+ * Normalize an unknown error into an ApplicationError.
1080
+ * Non-ApplicationError values are wrapped in InternalError.
1081
+ */
1082
+ normalizeError(error) {
1083
+ if (isApplicationError(error)) return error;
1084
+ const originalMessage = error instanceof Error ? error.message : String(error);
1085
+ const internalError = new InternalError({
1086
+ originalError: originalMessage,
1087
+ stack: error instanceof Error ? error.stack : void 0
1088
+ });
1089
+ if (this.environment === "development") {
1090
+ internalError.message = originalMessage;
1091
+ if (error instanceof Error && error.stack) internalError.stack = error.stack;
1092
+ }
1093
+ return internalError;
1094
+ }
1095
+ /**
1096
+ * Run the reporting pipeline for an error.
1097
+ */
1098
+ async performReport(error, context) {
1099
+ if (typeof error.report === "function") {
1100
+ if (error.report() !== false) return;
1101
+ }
1102
+ if (this.shouldNotReport(error)) return;
1103
+ const entry = this.findReportable(error);
1104
+ if (entry) {
1105
+ await entry.callback(error, context);
1106
+ if (entry.shouldStop) return;
1107
+ }
1108
+ this.defaultReport(error, context);
1109
+ }
1110
+ /**
1111
+ * Run the rendering pipeline for an error, producing a Response.
1112
+ */
1113
+ async performRender(error, context) {
1114
+ if (typeof error.render === "function") {
1115
+ const result = error.render(context);
1116
+ if (result !== void 0) return this.toResponse(result, error);
1117
+ }
1118
+ const entry = this.findRenderable(error);
1119
+ if (entry) {
1120
+ const result = entry.callback(error, context);
1121
+ if (result !== void 0) return this.toResponse(await result, error);
1122
+ }
1123
+ return this.defaultRender(error, context);
1124
+ }
1125
+ /**
1126
+ * Apply all respond() callbacks to post-process a Response.
1127
+ */
1128
+ applyRespondCallbacks(response, error, context) {
1129
+ let result = response;
1130
+ for (const callback of this.respondCallbacks) result = callback(result, error, context);
1131
+ return result;
1132
+ }
1133
+ /**
1134
+ * Check if an error is in the dontReport set.
1135
+ */
1136
+ shouldNotReport(error) {
1137
+ for (const cls of this.dontReportSet) if (error instanceof cls) return true;
1138
+ return false;
1139
+ }
1140
+ /**
1141
+ * Find the most-specific reportable entry for an error.
1142
+ * Walks entries in registration order; picks the most-specific `instanceof` match.
1143
+ */
1144
+ findReportable(error) {
1145
+ let best;
1146
+ for (const entry of this.reportables) if (error instanceof entry.errorClass) {
1147
+ if (!best || !(error instanceof best.errorClass) || entry.errorClass.prototype instanceof best.errorClass) best = entry;
1148
+ }
1149
+ return best;
1150
+ }
1151
+ /**
1152
+ * Find the most-specific renderable entry for an error.
1153
+ */
1154
+ findRenderable(error) {
1155
+ let best;
1156
+ for (const entry of this.renderables) if (error instanceof entry.errorClass) {
1157
+ if (!best || !(error instanceof best.errorClass) || entry.errorClass.prototype instanceof best.errorClass) best = entry;
1158
+ }
1159
+ return best;
1160
+ }
1161
+ /**
1162
+ * Default reporting — log with appropriate severity and i18n translation.
1163
+ */
1164
+ defaultReport(error, context) {
1165
+ const translatedMessage = this.translateError(error, context);
1166
+ const severity = this.resolveSeverity(error);
1167
+ const globalContext = this.gatherContext();
1168
+ const logData = {
1169
+ code: error.code,
1170
+ message: translatedMessage,
1171
+ timestamp: error.timestamp,
1172
+ metadata: error.metadata,
1173
+ name: error.name,
1174
+ ...globalContext
1175
+ };
1176
+ switch (severity) {
1177
+ case "debug":
1178
+ this.logger.debug("[ApplicationError]", logData);
1179
+ break;
1180
+ case "info":
1181
+ this.logger.info("[ApplicationError]", logData);
1182
+ break;
1183
+ case "warn":
1184
+ this.logger.warn("[ApplicationError]", logData);
1185
+ break;
1186
+ case "error":
1187
+ this.logger.error("[ApplicationError]", logData);
1188
+ break;
1189
+ }
1190
+ }
1191
+ /**
1192
+ * Default rendering — content-negotiated.
1193
+ *
1194
+ * For HTTP requests that accept HTML: renders a minimal branded HTML page.
1195
+ * For everything else (API, queue, cron, CLI): returns JSON.
1196
+ *
1197
+ * Errors are always logged via `performReport` (non-blocking waitUntil),
1198
+ * so they appear in the console regardless of the rendered response format.
1199
+ */
1200
+ defaultRender(error, context) {
1201
+ const translatedMessage = this.translateError(error, context);
1202
+ const errorResponse = error.toErrorResponse(this.environment, translatedMessage);
1203
+ const status = resolveHttpStatus(error);
1204
+ if (context.type === "http" && this.wantsHtml(context)) return this.renderDefaultHtml(errorResponse, status);
1205
+ return Response.json(errorResponse, { status });
1206
+ }
1207
+ /**
1208
+ * Check if the HTTP request prefers an HTML response.
1209
+ *
1210
+ * Uses the `Accept` header to determine format. Inertia v3 XHR requests
1211
+ * send `Accept: text/html, application/xhtml+xml`, so they naturally
1212
+ * receive HTML error pages (displayed in Inertia's error modal in dev).
1213
+ *
1214
+ * Override in a subclass to customize content negotiation logic.
1215
+ */
1216
+ wantsHtml(context) {
1217
+ return (context.ctx.c.req.header("accept") ?? "").includes("text/html");
1218
+ }
1219
+ /**
1220
+ * Minimal production HTML error page with inline styles.
1221
+ */
1222
+ renderDefaultHtml(errorResponse, status) {
1223
+ const title = this.escapeHtml(errorResponse.message);
1224
+ const html = `<!DOCTYPE html>
1225
+ <html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
1226
+ <title>${status} - ${title}</title>
1227
+ <style>*{margin:0;padding:0;box-sizing:border-box}body{font-family:system-ui,-apple-system,sans-serif;min-height:100vh;display:flex;align-items:center;justify-content:center;background:#f8fafc;color:#334155}.container{text-align:center;padding:2rem}.status{font-size:6rem;font-weight:800;color:#13c397;line-height:1}.message{font-size:1.25rem;color:#64748b;margin-top:1rem}</style>
1228
+ </head><body><div class="container"><div class="status">${status}</div><div class="message">${title}</div></div></body></html>`;
1229
+ return new Response(html, {
1230
+ status,
1231
+ headers: { "content-type": "text/html; charset=utf-8" }
1232
+ });
1233
+ }
1234
+ escapeHtml(str) {
1235
+ return str.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
1236
+ }
1237
+ /**
1238
+ * Convert a render result (Response or ErrorResponse) into a Response.
1239
+ */
1240
+ toResponse(result, error) {
1241
+ if (result instanceof Response) return result;
1242
+ const status = resolveHttpStatus(error);
1243
+ return Response.json(result, { status });
1244
+ }
1245
+ /**
1246
+ * Translate an error's message key via i18n.
1247
+ * Uses the request container (from HTTP context) for correct locale,
1248
+ * falling back to the global container or raw message string.
1249
+ */
1250
+ translateError(error, context) {
1251
+ try {
1252
+ const i18n = (context.type === "http" ? context.ctx.getContainer() : this.container).resolve(I18N_TOKENS.I18nService);
1253
+ const params = error.metadata;
1254
+ return i18n.t(error.message, params);
1255
+ } catch {
1256
+ return error.message;
1257
+ }
1258
+ }
1259
+ /**
1260
+ * Resolve the log severity for an error.
1261
+ * Checks level overrides first, then falls back to code-range-based severity.
1262
+ */
1263
+ resolveSeverity(error) {
1264
+ let bestClass;
1265
+ let bestSeverity;
1266
+ for (const [cls, severity] of this.levelOverrides) if (error instanceof cls) {
1267
+ if (!bestClass || cls.prototype instanceof bestClass) {
1268
+ bestClass = cls;
1269
+ bestSeverity = severity;
1270
+ }
1271
+ }
1272
+ return bestSeverity ?? this.getDefaultSeverity(error.code);
1273
+ }
1274
+ /**
1275
+ * Determine default log severity based on error code range.
1276
+ */
1277
+ getDefaultSeverity(code) {
1278
+ if (code >= 9e3) return "error";
1279
+ if (code >= 2e3 && code < 3e3) return "error";
1280
+ if (code >= 5e3 && code < 6e3) return "warn";
1281
+ if (code >= 1e3 && code < 2e3) return "info";
1282
+ if (code >= 3e3 && code < 5e3) return "warn";
1283
+ return "error";
1284
+ }
1285
+ /**
1286
+ * Gather all global context data from registered callbacks.
1287
+ */
1288
+ gatherContext() {
1289
+ if (this.contextCallbacks.length === 0) return {};
1290
+ const merged = {};
1291
+ for (const callback of this.contextCallbacks) Object.assign(merged, callback());
1292
+ return merged;
1293
+ }
1294
+ };
1295
+ ExceptionHandler = __decorate([
1296
+ Transient(),
1297
+ __decorateParam(0, inject(LOGGER_TOKENS.LoggerService)),
1298
+ __decorateParam(1, inject(DI_TOKENS.CloudflareEnv)),
1299
+ __decorateParam(2, inject(CONTAINER_TOKEN)),
1300
+ __decorateParam(3, inject(DI_TOKENS.ExecutionContext)),
1301
+ __decorateMetadata("design:paramtypes", [
1302
+ Object,
1303
+ Object,
1304
+ Object,
1305
+ Object
1306
+ ])
1307
+ ], ExceptionHandler);
1308
+ //#endregion
1309
+ //#region src/errors/default-exception-handler.ts
1310
+ let DefaultExceptionHandler = class DefaultExceptionHandler extends ExceptionHandler {
1311
+ register() {}
1312
+ };
1313
+ DefaultExceptionHandler = __decorate([Transient()], DefaultExceptionHandler);
1314
+ //#endregion
1315
+ //#region src/errors/error-response.ts
1316
+ /**
1317
+ * Type guard to check if an object is an ErrorResponse
1318
+ */
1319
+ function isErrorResponse(obj) {
1320
+ return typeof obj === "object" && obj !== null && "code" in obj && typeof obj.code === "number" && "message" in obj && typeof obj.message === "string" && "timestamp" in obj && typeof obj.timestamp === "string";
1321
+ }
1322
+ //#endregion
1323
+ //#region src/router/constants.ts
1324
+ /**
1325
+ * Type-safe context keys for Hono router variables
1326
+ * Using symbols to avoid string collisions
1327
+ */
1328
+ const ROUTER_CONTEXT_KEYS = {
1329
+ REQUEST_CONTAINER: "requestContainer",
1330
+ LOCALE: "locale"
1331
+ };
1332
+ /**
1333
+ * Metadata keys for storing route and controller configuration
1334
+ * Using symbols to avoid collisions with other decorators
1335
+ */
1336
+ const ROUTE_METADATA_KEYS = {
1337
+ CONTROLLER_ROUTE: Symbol.for("stratal:controller:route"),
1338
+ CONTROLLER_OPTIONS: Symbol.for("stratal:controller:options"),
1339
+ CONTROLLER_MIDDLEWARES: Symbol.for("stratal:controller:middlewares"),
1340
+ ROUTE_CONFIG: Symbol.for("stratal:route:config"),
1341
+ DECORATED_METHODS: Symbol.for("stratal:decorated:methods"),
1342
+ AUTH_GUARD: Symbol.for("stratal:auth:guard"),
1343
+ GATEWAY_MARKER: Symbol.for("stratal:gateway:marker"),
1344
+ WS_ON_MESSAGE: Symbol.for("stratal:ws:on-message"),
1345
+ WS_ON_CLOSE: Symbol.for("stratal:ws:on-close"),
1346
+ WS_ON_ERROR: Symbol.for("stratal:ws:on-error")
1347
+ };
1348
+ /**
1349
+ * Security scheme identifiers for OpenAPI
1350
+ * These reference the security scheme definitions in security.schemas.ts
1351
+ */
1352
+ const SECURITY_SCHEMES = {
1353
+ BEARER_AUTH: "bearerAuth",
1354
+ API_KEY: "apiKey",
1355
+ SESSION_COOKIE: "sessionCookie"
1356
+ };
1357
+ /**
1358
+ * HTTP method mapping for RESTful controller methods
1359
+ * Maps controller method names to HTTP verbs and path patterns
1360
+ */
1361
+ const HTTP_METHODS = {
1362
+ index: {
1363
+ method: "get",
1364
+ path: ""
1365
+ },
1366
+ show: {
1367
+ method: "get",
1368
+ path: "/:id"
1369
+ },
1370
+ create: {
1371
+ method: "post",
1372
+ path: ""
1373
+ },
1374
+ update: {
1375
+ method: "put",
1376
+ path: "/:id"
1377
+ },
1378
+ patch: {
1379
+ method: "patch",
1380
+ path: "/:id"
1381
+ },
1382
+ destroy: {
1383
+ method: "delete",
1384
+ path: "/:id"
1385
+ }
1386
+ };
1387
+ /**
1388
+ * Default success status codes for RESTful controller methods
1389
+ * Used by @Route() decorator to auto-derive response status
1390
+ */
1391
+ const METHOD_STATUS_CODES = {
1392
+ index: 200,
1393
+ show: 200,
1394
+ create: 201,
1395
+ update: 200,
1396
+ patch: 200,
1397
+ destroy: 200
1398
+ };
1399
+ /**
1400
+ * Sentinel symbol to opt a controller out of versioning.
1401
+ * When used as the version, no prefix is applied even when defaultVersion is set.
1402
+ */
1403
+ const VERSION_NEUTRAL = Symbol.for("stratal:version:neutral");
1404
+ /**
1405
+ * Default content type for request bodies and responses
1406
+ */
1407
+ const DEFAULT_CONTENT_TYPE = "application/json";
1408
+ //#endregion
1409
+ //#region src/router/router-context.ts
1410
+ /**
1411
+ * Router context wrapper with helper methods
1412
+ *
1413
+ * Provides convenient access to Hono's context and common request/response operations.
1414
+ * The native Hono context is available via the `c` property for advanced use cases.
1415
+ *
1416
+ * @example
1417
+ * ```typescript
1418
+ * async index(ctx: RouterContext): Promise<Response> {
1419
+ * // Use helper methods
1420
+ * const users = await this.service.findAll()
1421
+ * return ctx.json(users)
1422
+ * }
1423
+ *
1424
+ * async show(ctx: RouterContext): Promise<Response> {
1425
+ * // Access route params
1426
+ * const id = ctx.param('id')
1427
+ * const user = await this.service.findById(id)
1428
+ * return ctx.json(user)
1429
+ * }
1430
+ *
1431
+ * async create(ctx: RouterContext): Promise<Response> {
1432
+ * // Parse request body
1433
+ * const body = await ctx.body<CreateUserInput>()
1434
+ * const user = await this.service.create(body)
1435
+ * return ctx.json(user, 201)
1436
+ * }
1437
+ * ```
1438
+ */
1439
+ var RouterContext = class extends Macroable {
1440
+ /**
1441
+ * Native Hono context
1442
+ * Access for advanced use cases not covered by helper methods
1443
+ */
1444
+ constructor(c) {
1445
+ super();
1446
+ this.c = c;
1447
+ }
1448
+ /**
1449
+ * Get request-scoped DI container
1450
+ * Contains request-specific services and context (AuthContext)
1451
+ *
1452
+ * @throws Error if container not initialized
1453
+ */
1454
+ getContainer() {
1455
+ const container = this.c.get(ROUTER_CONTEXT_KEYS.REQUEST_CONTAINER);
1456
+ if (!container) throw new RequestContainerNotInitializedError();
1457
+ return container;
1458
+ }
1459
+ /**
1460
+ * Set locale for the current request
1461
+ *
1462
+ * @param locale - Locale code (e.g., 'en', 'fr')
1463
+ */
1464
+ setLocale(locale) {
1465
+ this.c.set(ROUTER_CONTEXT_KEYS.LOCALE, locale);
1466
+ }
1467
+ /**
1468
+ * Get locale for the current request
1469
+ *
1470
+ * @returns Current locale code
1471
+ */
1472
+ getLocale() {
1473
+ return this.c.get(ROUTER_CONTEXT_KEYS.LOCALE) || "en";
1474
+ }
1475
+ /**
1476
+ * Return JSON response
1477
+ *
1478
+ * When data is null, automatically returns 204 No Content (configurable via status param).
1479
+ *
1480
+ * @param data - Data to serialize as JSON, or null for 204
1481
+ * @param status - HTTP status code (default: 200, or 204 when data is null)
1482
+ */
1483
+ json(data, status) {
1484
+ if (data === null) return this.c.body(null, status ?? 204);
1485
+ return this.c.json(data, status);
1486
+ }
1487
+ /**
1488
+ * Get route parameter value
1489
+ *
1490
+ * @param key - Parameter name (e.g., 'id' for /users/:id)
1491
+ */
1492
+ param(key) {
1493
+ return this.c.req.valid("param")[key];
1494
+ }
1495
+ /**
1496
+ * Get query parameter value
1497
+ *
1498
+ * @param key - Query parameter name
1499
+ */
1500
+ query(key) {
1501
+ const validated = this.c.req.valid("query");
1502
+ return key ? validated[key] : validated;
1503
+ }
1504
+ /**
1505
+ * Get request header value
1506
+ *
1507
+ * @param name - Header name (case-insensitive)
1508
+ */
1509
+ header(name) {
1510
+ return this.c.req.header(name);
1511
+ }
1512
+ /**
1513
+ * Get validated request body from OpenAPI route
1514
+ * Returns pre-validated data that has passed schema validation
1515
+ *
1516
+ * @returns Validated JSON body
1517
+ */
1518
+ body() {
1519
+ return this.c.req.valid("json");
1520
+ }
1521
+ /**
1522
+ * Return text response
1523
+ *
1524
+ * @param text - Text content
1525
+ * @param status - HTTP status code (default: 200)
1526
+ */
1527
+ text(text, status) {
1528
+ return this.c.text(text, status);
1529
+ }
1530
+ /**
1531
+ * Return HTML response
1532
+ *
1533
+ * @param html - HTML content
1534
+ * @param status - HTTP status code (default: 200)
1535
+ */
1536
+ html(html, status) {
1537
+ return this.c.html(html, status);
1538
+ }
1539
+ /**
1540
+ * Generate a URL from a named route.
1541
+ *
1542
+ * Keys matching `:param` placeholders fill the path.
1543
+ * Domain params are consumed from the same object.
1544
+ * Extra keys become query string parameters.
1545
+ *
1546
+ * @param name - Named route identifier
1547
+ * @param params - Route params + domain params + extra query params
1548
+ * @param options - URL generation options (e.g., `{ absolute: true }`)
1549
+ *
1550
+ * @example
1551
+ * ```typescript
1552
+ * ctx.route('users.show', { id: '1' }) // '/v1/users/1'
1553
+ * ctx.route('users.show', { id: '1', q: 'test' }) // '/v1/users/1?q=test'
1554
+ * ```
1555
+ */
1556
+ route(name, params, options) {
1557
+ return this.resolveUri().route(name, params, options);
1558
+ }
1559
+ /**
1560
+ * Get a domain parameter value from the current request.
1561
+ * Domain params are set by the domain matching middleware.
1562
+ *
1563
+ * @param key - Domain parameter name (e.g., 'tenant' from '{tenant}.myapp.com')
1564
+ *
1565
+ * @example
1566
+ * ```typescript
1567
+ * const tenant = ctx.domain('tenant')
1568
+ * ```
1569
+ */
1570
+ domain(key) {
1571
+ return this.c.get(`domain:${key}`);
1572
+ }
1573
+ /**
1574
+ * Generate a signed URL from a named route.
1575
+ *
1576
+ * @param name - Named route identifier
1577
+ * @param params - Route params (same as route())
1578
+ * @param options - Signing options (e.g., expiresIn) and URL options
1579
+ * @returns Signed URL string with signature query param
1580
+ */
1581
+ async signedUrl(name, params, options) {
1582
+ return this.resolveUri().signedRoute(name, params, options);
1583
+ }
1584
+ /**
1585
+ * Check if the current request has a valid signature.
1586
+ *
1587
+ * @returns true if the URL signature is valid and not expired
1588
+ */
1589
+ async hasValidSignature() {
1590
+ return this.resolveUri().hasValidSignature();
1591
+ }
1592
+ /**
1593
+ * Redirect to another URL
1594
+ *
1595
+ * @param url - Target URL
1596
+ * @param status - HTTP status code (default: 302)
1597
+ */
1598
+ redirect(url, status) {
1599
+ return this.c.redirect(url, status);
1600
+ }
1601
+ /**
1602
+ * Return a streaming response (binary/generic)
1603
+ *
1604
+ * @param callback - Async function that writes to the stream
1605
+ * @param onError - Optional error handler called if an error occurs during streaming
1606
+ */
1607
+ stream(callback, onError) {
1608
+ return stream(this.c, callback, onError);
1609
+ }
1610
+ /**
1611
+ * Return a streaming text response
1612
+ *
1613
+ * Automatically sets `Content-Encoding: Identity` for Cloudflare Workers compatibility.
1614
+ *
1615
+ * @param callback - Async function that writes text to the stream
1616
+ * @param onError - Optional error handler called if an error occurs during streaming
1617
+ */
1618
+ streamText(callback, onError) {
1619
+ this.c.header("Content-Encoding", "Identity");
1620
+ return streamText(this.c, callback, onError);
1621
+ }
1622
+ /**
1623
+ * Return a Server-Sent Events (SSE) streaming response
1624
+ *
1625
+ * Automatically sets `Content-Encoding: Identity` for Cloudflare Workers compatibility.
1626
+ *
1627
+ * @param callback - Async function that writes SSE events to the stream
1628
+ * @param onError - Optional error handler called if an error occurs during streaming
1629
+ */
1630
+ streamSSE(callback, onError) {
1631
+ this.c.header("Content-Encoding", "Identity");
1632
+ return streamSSE(this.c, callback, onError);
1633
+ }
1634
+ resolveUri() {
1635
+ return this.getContainer().resolve(ROUTER_TOKENS.Uri);
1636
+ }
1637
+ };
1638
+ //#endregion
1639
+ //#region src/errors/exception-context.ts
1640
+ /**
1641
+ * Create an HTTP exception context from a Hono context.
1642
+ *
1643
+ * @param c - The raw Hono context from the request
1644
+ * @returns An {@link HttpExceptionContext} wrapping a RouterContext
1645
+ */
1646
+ function createHttpExceptionContext(c) {
1647
+ return {
1648
+ type: "http",
1649
+ ctx: new RouterContext(c)
1650
+ };
1651
+ }
1652
+ /**
1653
+ * Create a queue exception context.
1654
+ *
1655
+ * @param queueName - The name of the queue being processed
1656
+ * @returns A {@link QueueExceptionContext}
1657
+ */
1658
+ function createQueueExceptionContext(queueName) {
1659
+ return {
1660
+ type: "queue",
1661
+ queueName
1662
+ };
1663
+ }
1664
+ /**
1665
+ * Create a cron exception context.
1666
+ *
1667
+ * @returns A {@link CronExceptionContext}
1668
+ */
1669
+ function createCronExceptionContext() {
1670
+ return { type: "cron" };
1671
+ }
1672
+ /**
1673
+ * Create a CLI command exception context.
1674
+ *
1675
+ * @param commandName - The name of the command that threw
1676
+ * @returns A {@link CliExceptionContext}
1677
+ */
1678
+ function createCliExceptionContext(commandName) {
1679
+ return {
1680
+ type: "cli",
1681
+ commandName
1682
+ };
1683
+ }
1684
+ //#endregion
1685
+ //#region src/errors/request-container-not-initialized.error.ts
1686
+ /**
1687
+ * RequestContainerNotInitializedError
1688
+ *
1689
+ * Thrown when attempting to access the request-scoped container before it has been initialized.
1690
+ * This typically indicates that the RouterService middleware hasn't run yet,
1691
+ * or the router context is being accessed outside of a request lifecycle.
1692
+ */
1693
+ var RequestContainerNotInitializedError = class extends ApplicationError {
1694
+ constructor() {
1695
+ super("errors.requestContainerNotInitialized", ERROR_CODES.SYSTEM.REQUEST_CONTAINER_NOT_INITIALIZED);
1696
+ }
1697
+ };
1698
+ //#endregion
1699
+ //#region src/errors/stratal-not-initialized.error.ts
1700
+ /**
1701
+ * StratalNotInitializedError
1702
+ *
1703
+ * Thrown when attempting to resolve the Application instance before Stratal has been instantiated.
1704
+ * This typically indicates that the Stratal instance is not exported as the default export.
1705
+ */
1706
+ var StratalNotInitializedError = class extends ApplicationError {
1707
+ constructor() {
1708
+ super("errors.stratalNotInitialized", ERROR_CODES.SYSTEM.INFRASTRUCTURE_ERROR);
1709
+ }
1710
+ };
1711
+ //#endregion
1712
+ export { Scope as A, ConditionalBindingFallbackError as B, abort as C, runWithContainer as D, getContainer as E, injectable$1 as F, ApplicationError as H, instancePerContainerCachingFactory$1 as I, singleton as L, container$1 as M, delay as N, ContainerNotInitializedError as O, inject$1 as P, ConditionalBindingBuilderImpl as R, HttpException as S, containerStorage as T, ROUTER_TOKENS as V, ExceptionHandler as _, createHttpExceptionContext as a, getHttpStatus as b, DEFAULT_CONTENT_TYPE as c, ROUTER_CONTEXT_KEYS as d, ROUTE_METADATA_KEYS as f, DefaultExceptionHandler as g, isErrorResponse as h, createCronExceptionContext as i, Container as j, ERROR_CODES as k, HTTP_METHODS as l, VERSION_NEUTRAL as m, RequestContainerNotInitializedError as n, createQueueExceptionContext as o, SECURITY_SCHEMES as p, createCliExceptionContext as r, RouterContext as s, StratalNotInitializedError as t, METHOD_STATUS_CODES as u, isApplicationError as v, I18N_TOKENS as w, resolveHttpStatus as x, InternalError as y, RequestScopeOperationNotAllowedError as z };
1713
+
1714
+ //# sourceMappingURL=errors-B4pYgYON.mjs.map