stratal 0.0.17 → 0.0.18

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 (178) hide show
  1. package/dist/{base-email.provider-DypUAfWm.mjs → base-email.provider-Cuw4OAB0.mjs} +1 -1
  2. package/dist/{base-email.provider-DypUAfWm.mjs.map → base-email.provider-Cuw4OAB0.mjs.map} +1 -1
  3. package/dist/bin/cloudflare-workers-loader.mjs +33 -1
  4. package/dist/bin/cloudflare-workers-loader.mjs.map +1 -1
  5. package/dist/bin/quarry.mjs +182 -5
  6. package/dist/bin/quarry.mjs.map +1 -1
  7. package/dist/cache/index.d.mts +2 -2
  8. package/dist/cache/index.d.mts.map +1 -1
  9. package/dist/cache/index.mjs +3 -10
  10. package/dist/cache/index.mjs.map +1 -1
  11. package/dist/{colors-Y7WIFXs7.mjs → colors-BTAnQRGU.mjs} +1 -1
  12. package/dist/{colors-Y7WIFXs7.mjs.map → colors-BTAnQRGU.mjs.map} +1 -1
  13. package/dist/{command-TnkPYWta.d.mts → command-B1YuV-UZ.d.mts} +2 -2
  14. package/dist/{command-TnkPYWta.d.mts.map → command-B1YuV-UZ.d.mts.map} +1 -1
  15. package/dist/{command-B1CPgsrU.mjs → command-DjGqCYHv.mjs} +3 -3
  16. package/dist/command-DjGqCYHv.mjs.map +1 -0
  17. package/dist/config/index.d.mts +2 -2
  18. package/dist/config/index.mjs +12 -19
  19. package/dist/config/index.mjs.map +1 -1
  20. package/dist/{consumer-registry-Bymm6ff4.d.mts → consumer-registry-BkuHXR_u.d.mts} +1 -1
  21. package/dist/{consumer-registry-Bymm6ff4.d.mts.map → consumer-registry-BkuHXR_u.d.mts.map} +1 -1
  22. package/dist/cron/index.d.mts +2 -2
  23. package/dist/cron/index.d.mts.map +1 -1
  24. package/dist/cron/index.mjs +1 -3
  25. package/dist/{cron-manager-CFBamKKk.mjs → cron-manager-1KnZvojs.mjs} +3 -3
  26. package/dist/{cron-manager-CFBamKKk.mjs.map → cron-manager-1KnZvojs.mjs.map} +1 -1
  27. package/dist/{cron-manager-D7imGwUT.d.mts → cron-manager-BnEZquBL.d.mts} +2 -2
  28. package/dist/{cron-manager-D7imGwUT.d.mts.map → cron-manager-BnEZquBL.d.mts.map} +1 -1
  29. package/dist/di/index.d.mts +2 -2
  30. package/dist/di/index.mjs +3 -3
  31. package/dist/email/index.d.mts +3 -3
  32. package/dist/email/index.mjs +8 -16
  33. package/dist/email/index.mjs.map +1 -1
  34. package/dist/{en-DaewN8hc.mjs → en-3QnZwP-u.mjs} +10 -1
  35. package/dist/en-3QnZwP-u.mjs.map +1 -0
  36. package/dist/errors/index.d.mts +2 -2
  37. package/dist/errors/index.mjs +2 -3
  38. package/dist/errors--RBIvDXr.mjs +1560 -0
  39. package/dist/errors--RBIvDXr.mjs.map +1 -0
  40. package/dist/{errors-DuAR5Wke.mjs → errors-B7hCnXgB.mjs} +2 -2
  41. package/dist/{errors-DuAR5Wke.mjs.map → errors-B7hCnXgB.mjs.map} +1 -1
  42. package/dist/events/index.d.mts +2 -2
  43. package/dist/events/index.mjs +1 -2
  44. package/dist/{events-CvUSgEuN.mjs → events-UTJliZhl.mjs} +2 -2
  45. package/dist/{events-CvUSgEuN.mjs.map → events-UTJliZhl.mjs.map} +1 -1
  46. package/dist/{gateway-context-CNOLkLUC.mjs → gateway-context-BdBFoQd8.mjs} +66 -10
  47. package/dist/gateway-context-BdBFoQd8.mjs.map +1 -0
  48. package/dist/guards/index.d.mts +3 -3
  49. package/dist/guards/index.d.mts.map +1 -1
  50. package/dist/guards/index.mjs +1 -1
  51. package/dist/{guards-DUk_Kzst.mjs → guards-MtDgcHnF.mjs} +1 -1
  52. package/dist/{guards-DUk_Kzst.mjs.map → guards-MtDgcHnF.mjs.map} +1 -1
  53. package/dist/i18n/index.d.mts +3 -3
  54. package/dist/i18n/index.mjs +3 -16
  55. package/dist/i18n/messages/en/index.d.mts +1 -1
  56. package/dist/i18n/messages/en/index.mjs +1 -1
  57. package/dist/i18n/utils/index.d.mts +30 -0
  58. package/dist/i18n/utils/index.d.mts.map +1 -0
  59. package/dist/i18n/utils/index.mjs +2 -0
  60. package/dist/i18n/validation/index.d.mts +1 -1
  61. package/dist/i18n/validation/index.mjs +1 -1
  62. package/dist/i18n.module-BpLLLCTg.mjs +2462 -0
  63. package/dist/i18n.module-BpLLLCTg.mjs.map +1 -0
  64. package/dist/{index-D_w_Rmtd.d.mts → index-BDh9J2KD.d.mts} +10 -1
  65. package/dist/{index-D_w_Rmtd.d.mts.map → index-BDh9J2KD.d.mts.map} +1 -1
  66. package/dist/{index-Dp6A5ywM.d.mts → index-BR23zDMy.d.mts} +1 -1
  67. package/dist/{index-Dp6A5ywM.d.mts.map → index-BR23zDMy.d.mts.map} +1 -1
  68. package/dist/index-BrmS34sa.d.mts +4287 -0
  69. package/dist/index-BrmS34sa.d.mts.map +1 -0
  70. package/dist/{index-DGRe6Yoa.d.mts → index-DPxmo6AY.d.mts} +4 -4
  71. package/dist/{index-DGRe6Yoa.d.mts.map → index-DPxmo6AY.d.mts.map} +1 -1
  72. package/dist/{index-NGxg-KP_.d.mts → index-Dfpd_ypO.d.mts} +36 -7
  73. package/dist/index-Dfpd_ypO.d.mts.map +1 -0
  74. package/dist/index.d.mts +4 -3
  75. package/dist/index.d.mts.map +1 -1
  76. package/dist/index.mjs +1 -20
  77. package/dist/{is-command-DJVI6wEJ.mjs → is-command-PvULqiTa.mjs} +2 -2
  78. package/dist/{is-command-DJVI6wEJ.mjs.map → is-command-PvULqiTa.mjs.map} +1 -1
  79. package/dist/{is-seeder-D5MIEcdz.mjs → is-seeder-BN9Ej1r7.mjs} +1 -1
  80. package/dist/{is-seeder-D5MIEcdz.mjs.map → is-seeder-BN9Ej1r7.mjs.map} +1 -1
  81. package/dist/logger/index.d.mts +1 -1
  82. package/dist/logger/index.mjs +1 -1
  83. package/dist/{logger-CGT91VY6.mjs → logger-c0ftIK4G.mjs} +29 -29
  84. package/dist/logger-c0ftIK4G.mjs.map +1 -0
  85. package/dist/module/index.d.mts +3 -4
  86. package/dist/module/index.d.mts.map +1 -1
  87. package/dist/module/index.mjs +1 -10
  88. package/dist/module-C3YZ-kZN.mjs +719 -0
  89. package/dist/module-C3YZ-kZN.mjs.map +1 -0
  90. package/dist/openapi/index.d.mts +3 -3
  91. package/dist/openapi/index.mjs +2 -15
  92. package/dist/{openapi-tools.service-B3TxYKoQ.mjs → openapi-tools.service-B77QXD56.mjs} +1 -1
  93. package/dist/{openapi-tools.service-B3TxYKoQ.mjs.map → openapi-tools.service-B77QXD56.mjs.map} +1 -1
  94. package/dist/{openapi.service-DGnX3Fc4.d.mts → openapi.service-6yj0BUY4.d.mts} +9 -17
  95. package/dist/openapi.service-6yj0BUY4.d.mts.map +1 -0
  96. package/dist/quarry/index.d.mts +25 -11
  97. package/dist/quarry/index.d.mts.map +1 -1
  98. package/dist/quarry/index.mjs +4 -8
  99. package/dist/{quarry-registry-B2rkO-JS.mjs → quarry-registry-CQCIlYTO.mjs} +37 -34
  100. package/dist/quarry-registry-CQCIlYTO.mjs.map +1 -0
  101. package/dist/queue/index.d.mts +2 -2
  102. package/dist/queue/index.mjs +3 -13
  103. package/dist/queue/index.mjs.map +1 -1
  104. package/dist/{queue.module-BtI8f4Jo.mjs → queue.module-DIjD6nr-.mjs} +39 -42
  105. package/dist/queue.module-DIjD6nr-.mjs.map +1 -0
  106. package/dist/{resend.provider-bXMEkdRJ.mjs → resend.provider-Bvw36rQy.mjs} +2 -4
  107. package/dist/{resend.provider-bXMEkdRJ.mjs.map → resend.provider-Bvw36rQy.mjs.map} +1 -1
  108. package/dist/router/index.d.mts +2 -2
  109. package/dist/router/index.mjs +5 -16
  110. package/dist/{s3-storage.provider-CttzNnDR.mjs → s3-storage.provider-BAhHDMI3.mjs} +13 -5
  111. package/dist/s3-storage.provider-BAhHDMI3.mjs.map +1 -0
  112. package/dist/seeder/index.d.mts +3 -4
  113. package/dist/seeder/index.d.mts.map +1 -1
  114. package/dist/seeder/index.mjs +2 -6
  115. package/dist/{seeder-R7RXJC35.mjs → seeder-D7VXULXB.mjs} +5 -5
  116. package/dist/{seeder-R7RXJC35.mjs.map → seeder-D7VXULXB.mjs.map} +1 -1
  117. package/dist/setup-BRIN-iYT.mjs +37 -0
  118. package/dist/setup-BRIN-iYT.mjs.map +1 -0
  119. package/dist/{smtp.provider-DrbHQztF.mjs → smtp.provider-CAwpvzvD.mjs} +2 -4
  120. package/dist/{smtp.provider-DrbHQztF.mjs.map → smtp.provider-CAwpvzvD.mjs.map} +1 -1
  121. package/dist/storage/index.d.mts +2 -2
  122. package/dist/storage/index.d.mts.map +1 -1
  123. package/dist/storage/index.mjs +2 -13
  124. package/dist/storage/providers/index.d.mts +2 -1
  125. package/dist/storage/providers/index.d.mts.map +1 -1
  126. package/dist/storage/providers/index.mjs +1 -4
  127. package/dist/{storage-CZKHOhci.mjs → storage-CJ-QOwNv.mjs} +8 -9
  128. package/dist/storage-CJ-QOwNv.mjs.map +1 -0
  129. package/dist/{storage-provider.interface-0IqcdhBf.d.mts → storage-provider.interface-YRtyYBxV.d.mts} +8 -2
  130. package/dist/storage-provider.interface-YRtyYBxV.d.mts.map +1 -0
  131. package/dist/stratal-B7G4i9-N.mjs +502 -0
  132. package/dist/stratal-B7G4i9-N.mjs.map +1 -0
  133. package/dist/{types-DahElfUw.d.mts → types-CN0zONAZ.d.mts} +2 -2
  134. package/dist/types-CN0zONAZ.d.mts.map +1 -0
  135. package/dist/{usage-generator-CVIsENuE.mjs → usage-generator-Cl1HPlUp.mjs} +2 -2
  136. package/dist/{usage-generator-CVIsENuE.mjs.map → usage-generator-Cl1HPlUp.mjs.map} +1 -1
  137. package/dist/{validation-DQTC259A.mjs → validation-B4bePOa_.mjs} +2 -2
  138. package/dist/{validation-DQTC259A.mjs.map → validation-B4bePOa_.mjs.map} +1 -1
  139. package/dist/websocket/index.d.mts +2 -2
  140. package/dist/websocket/index.d.mts.map +1 -1
  141. package/dist/websocket/index.mjs +1 -4
  142. package/dist/workers/index.d.mts +1 -1
  143. package/dist/workers/index.d.mts.map +1 -1
  144. package/dist/workers/index.mjs +2 -20
  145. package/dist/workers/index.mjs.map +1 -1
  146. package/package.json +34 -31
  147. package/dist/application-DfPtIzxF.d.mts +0 -177
  148. package/dist/application-DfPtIzxF.d.mts.map +0 -1
  149. package/dist/command-B1CPgsrU.mjs.map +0 -1
  150. package/dist/en-DaewN8hc.mjs.map +0 -1
  151. package/dist/errors-DSKapqD8.mjs +0 -707
  152. package/dist/errors-DSKapqD8.mjs.map +0 -1
  153. package/dist/gateway-context-CNOLkLUC.mjs.map +0 -1
  154. package/dist/i18n.module-Dn9SrFdS.mjs +0 -1841
  155. package/dist/i18n.module-Dn9SrFdS.mjs.map +0 -1
  156. package/dist/index-BFCxSp_f.d.mts +0 -2625
  157. package/dist/index-BFCxSp_f.d.mts.map +0 -1
  158. package/dist/index-NGxg-KP_.d.mts.map +0 -1
  159. package/dist/logger-CGT91VY6.mjs.map +0 -1
  160. package/dist/middleware/index.d.mts +0 -2
  161. package/dist/middleware/index.mjs +0 -5
  162. package/dist/middleware-Bl-b5pkt.mjs +0 -362
  163. package/dist/middleware-Bl-b5pkt.mjs.map +0 -1
  164. package/dist/module-registry-CmjBX6ol.d.mts +0 -121
  165. package/dist/module-registry-CmjBX6ol.d.mts.map +0 -1
  166. package/dist/module-tUtyVJ5E.mjs +0 -371
  167. package/dist/module-tUtyVJ5E.mjs.map +0 -1
  168. package/dist/openapi.service-DGnX3Fc4.d.mts.map +0 -1
  169. package/dist/quarry-registry-B2rkO-JS.mjs.map +0 -1
  170. package/dist/queue.module-BtI8f4Jo.mjs.map +0 -1
  171. package/dist/router-context-D9R1v2Ac.mjs +0 -267
  172. package/dist/router-context-D9R1v2Ac.mjs.map +0 -1
  173. package/dist/s3-storage.provider-CttzNnDR.mjs.map +0 -1
  174. package/dist/storage-CZKHOhci.mjs.map +0 -1
  175. package/dist/storage-provider.interface-0IqcdhBf.d.mts.map +0 -1
  176. package/dist/stratal-D5smIU1y.mjs +0 -315
  177. package/dist/stratal-D5smIU1y.mjs.map +0 -1
  178. package/dist/types-DahElfUw.d.mts.map +0 -1
@@ -1,267 +0,0 @@
1
- import { n as RequestContainerNotInitializedError } from "./errors-DSKapqD8.mjs";
2
- import { stream, streamSSE, streamText } from "hono/streaming";
3
- //#region src/router/constants.ts
4
- /**
5
- * Type-safe context keys for Hono router variables
6
- * Using symbols to avoid string collisions
7
- */
8
- const ROUTER_CONTEXT_KEYS = {
9
- REQUEST_CONTAINER: "requestContainer",
10
- LOCALE: "locale"
11
- };
12
- /**
13
- * Metadata keys for storing route and controller configuration
14
- * Using symbols to avoid collisions with other decorators
15
- */
16
- const ROUTE_METADATA_KEYS = {
17
- CONTROLLER_ROUTE: Symbol.for("stratal:controller:route"),
18
- CONTROLLER_OPTIONS: Symbol.for("stratal:controller:options"),
19
- CONTROLLER_MIDDLEWARES: Symbol.for("stratal:controller:middlewares"),
20
- ROUTE_CONFIG: Symbol.for("stratal:route:config"),
21
- DECORATED_METHODS: Symbol.for("stratal:decorated:methods"),
22
- HTTP_ROUTE_CONFIG: Symbol.for("stratal:http-route:config"),
23
- HTTP_DECORATED_METHODS: Symbol.for("stratal:http-decorated:methods"),
24
- AUTH_GUARD: Symbol.for("stratal:auth:guard"),
25
- GATEWAY_MARKER: Symbol.for("stratal:gateway:marker"),
26
- WS_ON_MESSAGE: Symbol.for("stratal:ws:on-message"),
27
- WS_ON_CLOSE: Symbol.for("stratal:ws:on-close"),
28
- WS_ON_ERROR: Symbol.for("stratal:ws:on-error")
29
- };
30
- /**
31
- * Security scheme identifiers for OpenAPI
32
- * These reference the security scheme definitions in security.schemas.ts
33
- */
34
- const SECURITY_SCHEMES = {
35
- BEARER_AUTH: "bearerAuth",
36
- API_KEY: "apiKey",
37
- SESSION_COOKIE: "sessionCookie"
38
- };
39
- /**
40
- * HTTP method mapping for RESTful controller methods
41
- * Maps controller method names to HTTP verbs and path patterns
42
- */
43
- const HTTP_METHODS = {
44
- index: {
45
- method: "get",
46
- path: ""
47
- },
48
- show: {
49
- method: "get",
50
- path: "/:id"
51
- },
52
- create: {
53
- method: "post",
54
- path: ""
55
- },
56
- update: {
57
- method: "put",
58
- path: "/:id"
59
- },
60
- patch: {
61
- method: "patch",
62
- path: "/:id"
63
- },
64
- destroy: {
65
- method: "delete",
66
- path: "/:id"
67
- }
68
- };
69
- /**
70
- * Default success status codes for RESTful controller methods
71
- * Used by @Route() decorator to auto-derive response status
72
- */
73
- const METHOD_STATUS_CODES = {
74
- index: 200,
75
- show: 200,
76
- create: 201,
77
- update: 200,
78
- patch: 200,
79
- destroy: 200
80
- };
81
- /**
82
- * Sentinel symbol to opt a controller out of versioning.
83
- * When used as the version, no prefix is applied even when defaultVersion is set.
84
- */
85
- const VERSION_NEUTRAL = Symbol.for("stratal:version:neutral");
86
- /**
87
- * Default content type for request bodies and responses
88
- */
89
- const DEFAULT_CONTENT_TYPE = "application/json";
90
- //#endregion
91
- //#region src/router/router-context.ts
92
- /**
93
- * Router context wrapper with helper methods
94
- *
95
- * Provides convenient access to Hono's context and common request/response operations.
96
- * The native Hono context is available via the `c` property for advanced use cases.
97
- *
98
- * @example
99
- * ```typescript
100
- * async index(ctx: RouterContext): Promise<Response> {
101
- * // Use helper methods
102
- * const users = await this.service.findAll()
103
- * return ctx.json(users)
104
- * }
105
- *
106
- * async show(ctx: RouterContext): Promise<Response> {
107
- * // Access route params
108
- * const id = ctx.param('id')
109
- * const user = await this.service.findById(id)
110
- * return ctx.json(user)
111
- * }
112
- *
113
- * async create(ctx: RouterContext): Promise<Response> {
114
- * // Parse request body
115
- * const body = await ctx.body<CreateUserInput>()
116
- * const user = await this.service.create(body)
117
- * return ctx.json(user, 201)
118
- * }
119
- * ```
120
- */
121
- var RouterContext = class {
122
- /**
123
- * Native Hono context
124
- * Access for advanced use cases not covered by helper methods
125
- */
126
- constructor(c) {
127
- this.c = c;
128
- }
129
- /**
130
- * Get request-scoped DI container
131
- * Contains request-specific services and context (AuthContext)
132
- *
133
- * @throws Error if container not initialized
134
- */
135
- getContainer() {
136
- const container = this.c.get(ROUTER_CONTEXT_KEYS.REQUEST_CONTAINER);
137
- if (!container) throw new RequestContainerNotInitializedError();
138
- return container;
139
- }
140
- /**
141
- * Set locale for the current request
142
- * Locale is determined by X-Locale header or defaults to config
143
- *
144
- * @param locale - Locale code (e.g., 'en', 'fr')
145
- */
146
- setLocale(locale) {
147
- this.c.set(ROUTER_CONTEXT_KEYS.LOCALE, locale);
148
- }
149
- /**
150
- * Get locale for the current request
151
- *
152
- * @returns Current locale code
153
- */
154
- getLocale() {
155
- return this.c.get(ROUTER_CONTEXT_KEYS.LOCALE) || "en";
156
- }
157
- /**
158
- * Return JSON response
159
- *
160
- * When data is null, automatically returns 204 No Content (configurable via status param).
161
- *
162
- * @param data - Data to serialize as JSON, or null for 204
163
- * @param status - HTTP status code (default: 200, or 204 when data is null)
164
- */
165
- json(data, status) {
166
- if (data === null) return this.c.body(null, status ?? 204);
167
- return this.c.json(data, status);
168
- }
169
- /**
170
- * Get route parameter value
171
- *
172
- * @param key - Parameter name (e.g., 'id' for /users/:id)
173
- */
174
- param(key) {
175
- return this.c.req.valid("param")[key];
176
- }
177
- /**
178
- * Get query parameter value
179
- *
180
- * @param key - Query parameter name
181
- */
182
- query(key) {
183
- const validated = this.c.req.valid("query");
184
- return key ? validated[key] : validated;
185
- }
186
- /**
187
- * Get request header value
188
- *
189
- * @param name - Header name (case-insensitive)
190
- */
191
- header(name) {
192
- return this.c.req.header(name);
193
- }
194
- /**
195
- * Get validated request body from OpenAPI route
196
- * Returns pre-validated data that has passed schema validation
197
- *
198
- * @returns Validated JSON body
199
- */
200
- body() {
201
- return this.c.req.valid("json");
202
- }
203
- /**
204
- * Return text response
205
- *
206
- * @param text - Text content
207
- * @param status - HTTP status code (default: 200)
208
- */
209
- text(text, status) {
210
- return this.c.text(text, status);
211
- }
212
- /**
213
- * Return HTML response
214
- *
215
- * @param html - HTML content
216
- * @param status - HTTP status code (default: 200)
217
- */
218
- html(html, status) {
219
- return this.c.html(html, status);
220
- }
221
- /**
222
- * Redirect to another URL
223
- *
224
- * @param url - Target URL
225
- * @param status - HTTP status code (default: 302)
226
- */
227
- redirect(url, status) {
228
- return this.c.redirect(url, status);
229
- }
230
- /**
231
- * Return a streaming response (binary/generic)
232
- *
233
- * @param callback - Async function that writes to the stream
234
- * @param onError - Optional error handler called if an error occurs during streaming
235
- */
236
- stream(callback, onError) {
237
- return stream(this.c, callback, onError);
238
- }
239
- /**
240
- * Return a streaming text response
241
- *
242
- * Automatically sets `Content-Encoding: Identity` for Cloudflare Workers compatibility.
243
- *
244
- * @param callback - Async function that writes text to the stream
245
- * @param onError - Optional error handler called if an error occurs during streaming
246
- */
247
- streamText(callback, onError) {
248
- this.c.header("Content-Encoding", "Identity");
249
- return streamText(this.c, callback, onError);
250
- }
251
- /**
252
- * Return a Server-Sent Events (SSE) streaming response
253
- *
254
- * Automatically sets `Content-Encoding: Identity` for Cloudflare Workers compatibility.
255
- *
256
- * @param callback - Async function that writes SSE events to the stream
257
- * @param onError - Optional error handler called if an error occurs during streaming
258
- */
259
- streamSSE(callback, onError) {
260
- this.c.header("Content-Encoding", "Identity");
261
- return streamSSE(this.c, callback, onError);
262
- }
263
- };
264
- //#endregion
265
- export { ROUTER_CONTEXT_KEYS as a, VERSION_NEUTRAL as c, METHOD_STATUS_CODES as i, DEFAULT_CONTENT_TYPE as n, ROUTE_METADATA_KEYS as o, HTTP_METHODS as r, SECURITY_SCHEMES as s, RouterContext as t };
266
-
267
- //# sourceMappingURL=router-context-D9R1v2Ac.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"router-context-D9R1v2Ac.mjs","names":["honoStream","honoStreamText","honoStreamSSE"],"sources":["../src/router/constants.ts","../src/router/router-context.ts"],"sourcesContent":["/**\n * Type-safe context keys for Hono router variables\n * Using symbols to avoid string collisions\n */\nexport const ROUTER_CONTEXT_KEYS = {\n REQUEST_CONTAINER: 'requestContainer',\n LOCALE: 'locale'\n} as const satisfies Record<string, string>\n\n/**\n * Metadata keys for storing route and controller configuration\n * Using symbols to avoid collisions with other decorators\n */\nexport const ROUTE_METADATA_KEYS = {\n CONTROLLER_ROUTE: Symbol.for('stratal:controller:route'),\n CONTROLLER_OPTIONS: Symbol.for('stratal:controller:options'),\n CONTROLLER_MIDDLEWARES: Symbol.for('stratal:controller:middlewares'),\n ROUTE_CONFIG: Symbol.for('stratal:route:config'),\n DECORATED_METHODS: Symbol.for('stratal:decorated:methods'),\n HTTP_ROUTE_CONFIG: Symbol.for('stratal:http-route:config'),\n HTTP_DECORATED_METHODS: Symbol.for('stratal:http-decorated:methods'),\n AUTH_GUARD: Symbol.for('stratal:auth:guard'),\n GATEWAY_MARKER: Symbol.for('stratal:gateway:marker'),\n WS_ON_MESSAGE: Symbol.for('stratal:ws:on-message'),\n WS_ON_CLOSE: Symbol.for('stratal:ws:on-close'),\n WS_ON_ERROR: Symbol.for('stratal:ws:on-error'),\n} as const\n\n/**\n * Security scheme identifiers for OpenAPI\n * These reference the security scheme definitions in security.schemas.ts\n */\nexport const SECURITY_SCHEMES = {\n BEARER_AUTH: 'bearerAuth',\n API_KEY: 'apiKey',\n SESSION_COOKIE: 'sessionCookie'\n} as const\n\n/**\n * HTTP method mapping for RESTful controller methods\n * Maps controller method names to HTTP verbs and path patterns\n */\nexport const HTTP_METHODS = {\n index: { method: 'get', path: '' } as const,\n show: { method: 'get', path: '/:id' } as const,\n create: { method: 'post', path: '' } as const,\n update: { method: 'put', path: '/:id' } as const,\n patch: { method: 'patch', path: '/:id' } as const,\n destroy: { method: 'delete', path: '/:id' } as const\n} as const\n\n/**\n * Default success status codes for RESTful controller methods\n * Used by @Route() decorator to auto-derive response status\n */\nexport const METHOD_STATUS_CODES = {\n index: 200,\n show: 200,\n create: 201,\n update: 200,\n patch: 200,\n destroy: 200\n} as const\n\n/**\n * Sentinel symbol to opt a controller out of versioning.\n * When used as the version, no prefix is applied even when defaultVersion is set.\n */\nexport const VERSION_NEUTRAL = Symbol.for('stratal:version:neutral')\n\n/**\n * Default content type for request bodies and responses\n */\nexport const DEFAULT_CONTENT_TYPE = 'application/json'\n","import type { Context } from 'hono'\nimport type { SSEStreamingApi } from 'hono/streaming'\nimport { stream as honoStream, streamSSE as honoStreamSSE, streamText as honoStreamText } from 'hono/streaming'\nimport type { ContentfulStatusCode, RedirectStatusCode } from 'hono/utils/http-status'\nimport type { StreamingApi } from 'hono/utils/stream'\nimport type { Container } from '../di/container'\nimport { RequestContainerNotInitializedError } from '../errors'\nimport { ROUTER_CONTEXT_KEYS } from './constants'\nimport type { RouterEnv } from './types'\n\nexport type ContextQueryResult<R extends Record<string, unknown> | undefined, K extends string | undefined> = K extends string ? string : R extends undefined ? Record<string, unknown> : R\n\n/**\n * Router context wrapper with helper methods\n *\n * Provides convenient access to Hono's context and common request/response operations.\n * The native Hono context is available via the `c` property for advanced use cases.\n *\n * @example\n * ```typescript\n * async index(ctx: RouterContext): Promise<Response> {\n * // Use helper methods\n * const users = await this.service.findAll()\n * return ctx.json(users)\n * }\n *\n * async show(ctx: RouterContext): Promise<Response> {\n * // Access route params\n * const id = ctx.param('id')\n * const user = await this.service.findById(id)\n * return ctx.json(user)\n * }\n *\n * async create(ctx: RouterContext): Promise<Response> {\n * // Parse request body\n * const body = await ctx.body<CreateUserInput>()\n * const user = await this.service.create(body)\n * return ctx.json(user, 201)\n * }\n * ```\n */\nexport class RouterContext<T extends RouterEnv = RouterEnv> {\n /**\n * Native Hono context\n * Access for advanced use cases not covered by helper methods\n */\n constructor(\n public readonly c: Context<T>\n ) { }\n\n /**\n * Get request-scoped DI container\n * Contains request-specific services and context (AuthContext)\n *\n * @throws Error if container not initialized\n */\n getContainer(): Container {\n const container = this.c.get(ROUTER_CONTEXT_KEYS.REQUEST_CONTAINER)\n if (!container) {\n throw new RequestContainerNotInitializedError()\n }\n return container as Container\n }\n\n /**\n * Set locale for the current request\n * Locale is determined by X-Locale header or defaults to config\n *\n * @param locale - Locale code (e.g., 'en', 'fr')\n */\n setLocale(locale: string): void {\n this.c.set(ROUTER_CONTEXT_KEYS.LOCALE, locale)\n }\n\n /**\n * Get locale for the current request\n *\n * @returns Current locale code\n */\n getLocale(): string {\n const locale = this.c.get(ROUTER_CONTEXT_KEYS.LOCALE)\n return (locale as string) || 'en'\n }\n\n /**\n * Return JSON response\n *\n * When data is null, automatically returns 204 No Content (configurable via status param).\n *\n * @param data - Data to serialize as JSON, or null for 204\n * @param status - HTTP status code (default: 200, or 204 when data is null)\n */\n json(data: object | null, status?: ContentfulStatusCode): Response {\n if (data === null) {\n return this.c.body(null, status ?? 204)\n }\n return this.c.json(data, status)\n }\n\n /**\n * Get route parameter value\n *\n * @param key - Parameter name (e.g., 'id' for /users/:id)\n */\n param(key: string): string {\n return (this.c.req as unknown as { valid(target: 'param'): Record<string, string> }).valid('param')[key]\n }\n\n /**\n * Get query parameter value\n *\n * @param key - Query parameter name\n */\n query<R extends Record<string, unknown> | undefined = undefined, K extends string | undefined = undefined>(key?: K): ContextQueryResult<R, K> {\n const validated = (this.c.req as unknown as { valid(target: 'query'): Record<string, unknown> }).valid('query')\n return key ? validated[key] as ContextQueryResult<R, K> : validated as ContextQueryResult<R, K>\n }\n\n /**\n * Get request header value\n *\n * @param name - Header name (case-insensitive)\n */\n header(name: string): string | undefined {\n return this.c.req.header(name)\n }\n\n /**\n * Get validated request body from OpenAPI route\n * Returns pre-validated data that has passed schema validation\n *\n * @returns Validated JSON body\n */\n body<T>(): Promise<T> {\n // Type assertion needed because req.valid() is type-safe per route\n // but this is a generic helper method that works across all routes\n return (this.c.req as unknown as { valid(target: 'json'): Promise<T> }).valid('json')\n }\n\n /**\n * Return text response\n *\n * @param text - Text content\n * @param status - HTTP status code (default: 200)\n */\n text(text: string, status?: ContentfulStatusCode): Response {\n return this.c.text(text, status)\n }\n\n /**\n * Return HTML response\n *\n * @param html - HTML content\n * @param status - HTTP status code (default: 200)\n */\n html(html: string, status?: ContentfulStatusCode): Response {\n return this.c.html(html, status)\n }\n\n /**\n * Redirect to another URL\n *\n * @param url - Target URL\n * @param status - HTTP status code (default: 302)\n */\n redirect(url: string, status?: RedirectStatusCode): Response {\n return this.c.redirect(url, status)\n }\n\n /**\n * Return a streaming response (binary/generic)\n *\n * @param callback - Async function that writes to the stream\n * @param onError - Optional error handler called if an error occurs during streaming\n */\n stream(callback: (stream: StreamingApi) => Promise<void>, onError?: (err: Error, stream: StreamingApi) => Promise<void>): Response {\n return honoStream(this.c, callback, onError)\n }\n\n /**\n * Return a streaming text response\n *\n * Automatically sets `Content-Encoding: Identity` for Cloudflare Workers compatibility.\n *\n * @param callback - Async function that writes text to the stream\n * @param onError - Optional error handler called if an error occurs during streaming\n */\n streamText(callback: (stream: StreamingApi) => Promise<void>, onError?: (err: Error, stream: StreamingApi) => Promise<void>): Response {\n this.c.header('Content-Encoding', 'Identity')\n return honoStreamText(this.c, callback, onError)\n }\n\n /**\n * Return a Server-Sent Events (SSE) streaming response\n *\n * Automatically sets `Content-Encoding: Identity` for Cloudflare Workers compatibility.\n *\n * @param callback - Async function that writes SSE events to the stream\n * @param onError - Optional error handler called if an error occurs during streaming\n */\n streamSSE(callback: (stream: SSEStreamingApi) => Promise<void>, onError?: (err: Error, stream: SSEStreamingApi) => Promise<void>): Response {\n this.c.header('Content-Encoding', 'Identity')\n return honoStreamSSE(this.c, callback, onError)\n }\n}\n"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"s3-storage.provider-CttzNnDR.mjs","names":[],"sources":["../src/storage/dom.polyfill.ts","../src/storage/providers/s3-storage.provider.ts"],"sourcesContent":["/**\n * DOM polyfills for Cloudflare Workers\n *\n * AWS SDK v3 uses DOMParser and Node for XML parsing, which are not available in Workers.\n * This module must be imported BEFORE any AWS SDK imports (including transitive).\n *\n * @see https://github.com/aws/aws-sdk-js-v3/issues/7375\n */\nimport { DOMParser } from '@xmldom/xmldom'\n\n// DOMParser polyfill for XML response parsing\nglobalThis.DOMParser = DOMParser\n\n// Node interface polyfill with DOM node type constants\n// Required by AWS SDK for XML response parsing\nif (typeof globalThis.Node === 'undefined') {\n globalThis.Node = {\n ELEMENT_NODE: 1,\n ATTRIBUTE_NODE: 2,\n TEXT_NODE: 3,\n CDATA_SECTION_NODE: 4,\n ENTITY_REFERENCE_NODE: 5,\n ENTITY_NODE: 6,\n PROCESSING_INSTRUCTION_NODE: 7,\n COMMENT_NODE: 8,\n DOCUMENT_NODE: 9,\n DOCUMENT_TYPE_NODE: 10,\n DOCUMENT_FRAGMENT_NODE: 11,\n NOTATION_NODE: 12,\n } as unknown as typeof Node\n}\n","import '../dom.polyfill'\n\nimport {\n AbortMultipartUploadCommand,\n CompleteMultipartUploadCommand,\n CreateMultipartUploadCommand,\n DeleteObjectCommand,\n DeleteObjectsCommand,\n GetObjectCommand,\n HeadObjectCommand,\n ListMultipartUploadsCommand,\n ListPartsCommand,\n PutObjectCommand,\n S3Client,\n UploadPartCommand\n} from '@aws-sdk/client-s3'\nimport { Upload } from '@aws-sdk/lib-storage'\nimport { getSignedUrl } from '@aws-sdk/s3-request-presigner'\nimport type { StorageEntry } from '../types'\nimport type { DownloadResult, PresignedUrlResult, UploadOptions, UploadResult } from '../contracts'\nimport { StorageResponseBodyMissingError } from '../errors'\nimport type {\n CompletedPart,\n CompleteMultipartResult,\n CreateMultipartOptions,\n CreateMultipartResult,\n DeleteObjectsResult,\n HeadObjectResult,\n IS3MultipartProvider,\n ListMultipartUploadsResult,\n ListPartsResult,\n UploadPartResult,\n} from './s3-multipart-provider.interface'\nimport type { StreamingBlobPayloadInputTypes } from './storage-provider.interface'\n\n/**\n * S3 Storage Provider\n * Implements storage operations using AWS SDK for S3-compatible storage\n * Works with AWS S3, Cloudflare R2, MinIO, and other S3-compatible services\n *\n * Implements IS3MultipartProvider for multipart upload support needed by TUS\n */\nexport class S3StorageProvider implements IS3MultipartProvider {\n private readonly client: S3Client\n private readonly bucket: string\n private readonly disk: string\n\n constructor(config: StorageEntry) {\n // Configure S3Client for S3-compatible storage\n this.client = new S3Client({\n region: config.region || 'auto',\n endpoint: config.endpoint,\n credentials: {\n accessKeyId: config.accessKeyId,\n secretAccessKey: config.secretAccessKey,\n },\n forcePathStyle: true,\n })\n\n this.bucket = config.bucket\n this.disk = config.disk\n }\n\n async upload(\n body: StreamingBlobPayloadInputTypes,\n path: string,\n options: UploadOptions\n ): Promise<UploadResult> {\n const command = new PutObjectCommand({\n Bucket: this.bucket,\n Key: path,\n Body: body,\n ContentType: options.mimeType,\n ContentLength: options.size,\n Metadata: options.metadata,\n Tagging: options.tagging,\n })\n\n await this.client.send(command)\n\n return {\n path,\n disk: this.disk,\n fullPath: `${this.bucket}/${path}`,\n size: options.size,\n mimeType: options.mimeType ?? 'application/octet-stream',\n uploadedAt: new Date(),\n }\n }\n\n async download(path: string): Promise<DownloadResult> {\n const command = new GetObjectCommand({\n Bucket: this.bucket,\n Key: path,\n })\n\n const response = await this.client.send(command)\n\n if (!response.Body) {\n throw new StorageResponseBodyMissingError(path)\n }\n\n return {\n toStream: () => response.Body?.transformToWebStream(),\n contentType: response.ContentType ?? 'application/octet-stream',\n size: response.ContentLength ?? 0,\n metadata: response.Metadata,\n toString: () => response.Body?.transformToString(),\n toArrayBuffer: () => response.Body?.transformToByteArray(),\n }\n }\n\n async delete(path: string): Promise<void> {\n const command = new DeleteObjectCommand({\n Bucket: this.bucket,\n Key: path,\n })\n\n await this.client.send(command)\n }\n\n async exists(path: string): Promise<boolean> {\n try {\n const command = new HeadObjectCommand({\n Bucket: this.bucket,\n Key: path,\n })\n\n await this.client.send(command)\n return true\n } catch {\n // HeadObject throws error if file doesn't exist\n return false\n }\n }\n\n async getPresignedUrl(\n path: string,\n method: 'GET' | 'PUT' | 'DELETE' | 'HEAD',\n expiresIn: number\n ): Promise<PresignedUrlResult> {\n // Select appropriate command based on method\n let command\n switch (method) {\n case 'GET':\n command = new GetObjectCommand({ Bucket: this.bucket, Key: path })\n break\n case 'PUT':\n command = new PutObjectCommand({ Bucket: this.bucket, Key: path })\n break\n case 'DELETE':\n command = new DeleteObjectCommand({ Bucket: this.bucket, Key: path })\n break\n case 'HEAD':\n command = new HeadObjectCommand({ Bucket: this.bucket, Key: path })\n break\n }\n\n const url = await getSignedUrl(this.client, command as GetObjectCommand, { expiresIn })\n\n return {\n url,\n expiresIn,\n expiresAt: new Date(Date.now() + expiresIn * 1000),\n method,\n }\n }\n\n /**\n * Chunked upload for streaming data without known size\n * Uses @aws-sdk/lib-storage for multipart upload handling\n *\n * Benefits:\n * - Handles unknown Content-Length automatically\n * - Automatic retry on transient failures\n * - Cleanup of partial uploads on error\n * - Works with S3-compatible storage (R2, MinIO, RustFS)\n *\n * @param body - Content to upload (stream or buffer)\n * @param path - Full path including disk root\n * @param options - Upload options (mimeType required, size optional)\n * @returns Upload result with metadata\n */\n async chunkedUpload(\n body: StreamingBlobPayloadInputTypes,\n path: string,\n options: Omit<UploadOptions, 'size'> & { size?: number }\n ): Promise<UploadResult> {\n const upload = new Upload({\n client: this.client,\n params: {\n Bucket: this.bucket,\n Key: path,\n Body: body,\n ContentType: options.mimeType,\n },\n // Concurrency configuration\n queueSize: 4,\n // Part size: 5MB minimum for S3\n partSize: 5 * 1024 * 1024,\n // Don't leave orphaned parts on error\n leavePartsOnError: false,\n })\n\n await upload.done()\n\n // Get the actual uploaded size via HeadObject\n const headResponse = await this.client.send(\n new HeadObjectCommand({\n Bucket: this.bucket,\n Key: path,\n })\n )\n\n return {\n path,\n disk: this.disk,\n fullPath: `${this.bucket}/${path}`,\n size: headResponse.ContentLength ?? options.size ?? 0,\n mimeType: options.mimeType ?? 'application/octet-stream',\n uploadedAt: new Date(),\n }\n }\n\n // ============================================\n // IS3MultipartProvider - Multipart upload methods\n // ============================================\n\n /**\n * Get the bucket name\n */\n getBucket(): string {\n return this.bucket\n }\n\n /**\n * Get object metadata without downloading the body\n */\n async headObject(key: string): Promise<HeadObjectResult | null> {\n const response = await this.client.send(\n new HeadObjectCommand({\n Bucket: this.bucket,\n Key: key,\n })\n )\n return {\n size: response.ContentLength ?? 0,\n contentType: response.ContentType,\n metadata: response.Metadata,\n }\n }\n\n /**\n * Delete multiple objects in a single request\n */\n async deleteObjects(keys: string[]): Promise<DeleteObjectsResult> {\n if (keys.length === 0) {\n return { deleted: 0, errors: [] }\n }\n\n const response = await this.client.send(\n new DeleteObjectsCommand({\n Bucket: this.bucket,\n Delete: {\n Objects: keys.map((key) => ({ Key: key })),\n },\n })\n )\n\n return {\n deleted: response.Deleted?.length ?? 0,\n errors: (response.Errors ?? []).map((e) => ({\n key: e.Key ?? '',\n code: e.Code ?? 'Unknown',\n message: e.Message ?? 'Unknown error',\n })),\n }\n }\n\n /**\n * Create a multipart upload\n */\n async createMultipartUpload(\n key: string,\n options?: CreateMultipartOptions\n ): Promise<CreateMultipartResult> {\n const response = await this.client.send(\n new CreateMultipartUploadCommand({\n Bucket: this.bucket,\n Key: key,\n ContentType: options?.contentType,\n CacheControl: options?.cacheControl,\n Metadata: options?.metadata,\n Tagging: options?.tagging,\n })\n )\n\n return {\n uploadId: response.UploadId ?? '',\n key: response.Key ?? '',\n }\n }\n\n /**\n * Upload a part to an existing multipart upload\n */\n async uploadPart(\n key: string,\n uploadId: string,\n partNumber: number,\n body: Uint8Array\n ): Promise<UploadPartResult> {\n const response = await this.client.send(\n new UploadPartCommand({\n Bucket: this.bucket,\n Key: key,\n UploadId: uploadId,\n PartNumber: partNumber,\n Body: body,\n ContentLength: body.length,\n })\n )\n\n return {\n etag: response.ETag ?? '',\n partNumber,\n }\n }\n\n /**\n * Complete a multipart upload\n */\n async completeMultipartUpload(\n key: string,\n uploadId: string,\n parts: CompletedPart[]\n ): Promise<CompleteMultipartResult> {\n const response = await this.client.send(\n new CompleteMultipartUploadCommand({\n Bucket: this.bucket,\n Key: key,\n UploadId: uploadId,\n MultipartUpload: {\n Parts: parts.map((p) => ({\n ETag: p.etag,\n PartNumber: p.partNumber,\n })),\n },\n })\n )\n\n return {\n location: response.Location,\n key: response.Key ?? '',\n }\n }\n\n /**\n * Abort a multipart upload\n */\n async abortMultipartUpload(key: string, uploadId: string): Promise<void> {\n await this.client.send(\n new AbortMultipartUploadCommand({\n Bucket: this.bucket,\n Key: key,\n UploadId: uploadId,\n })\n )\n }\n\n /**\n * List parts of a multipart upload\n */\n async listParts(\n key: string,\n uploadId: string,\n partNumberMarker?: string\n ): Promise<ListPartsResult> {\n const response = await this.client.send(\n new ListPartsCommand({\n Bucket: this.bucket,\n Key: key,\n UploadId: uploadId,\n PartNumberMarker: partNumberMarker,\n })\n )\n\n return {\n parts: (response.Parts ?? []).map((p) => ({\n partNumber: p.PartNumber ?? 0,\n etag: p.ETag ?? '',\n size: p.Size ?? 0,\n })),\n isTruncated: response.IsTruncated ?? false,\n nextPartNumberMarker: response.NextPartNumberMarker?.toString(),\n }\n }\n\n /**\n * List all in-progress multipart uploads\n */\n async listMultipartUploads(\n keyMarker?: string,\n uploadIdMarker?: string\n ): Promise<ListMultipartUploadsResult> {\n const response = await this.client.send(\n new ListMultipartUploadsCommand({\n Bucket: this.bucket,\n KeyMarker: keyMarker,\n UploadIdMarker: uploadIdMarker,\n })\n )\n\n return {\n uploads: (response.Uploads ?? []).map((u) => ({\n key: u.Key ?? '',\n uploadId: u.UploadId ?? '',\n initiated: u.Initiated,\n })),\n isTruncated: response.IsTruncated ?? false,\n nextKeyMarker: response.NextKeyMarker,\n nextUploadIdMarker: response.NextUploadIdMarker,\n }\n }\n}\n"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"storage-CZKHOhci.mjs","names":[],"sources":["../src/storage/storage.tokens.ts","../src/storage/services/storage-manager.service.ts","../src/storage/services/storage.service.ts","../src/storage/storage.module.ts","../src/storage/contracts/delete-file.input.ts","../src/storage/contracts/file-exists.input.ts","../src/storage/contracts/get-presigned-url.input.ts","../src/storage/contracts/upload-file.input.ts"],"sourcesContent":["/**\n * Dependency injection tokens for the Storage module\n * Using Symbol-based tokens to avoid magic strings\n */\nexport const STORAGE_TOKENS = {\n Options: Symbol.for('stratal:storage:options'),\n StorageService: Symbol.for('stratal:storage:service'),\n StorageManager: Symbol.for('stratal:storage:manager'),\n} as const\n","import { inject } from 'tsyringe'\nimport { Transient } from '../../di/decorators'\nimport { DiskNotConfiguredError, StorageProviderNotSupportedError } from '../errors'\nimport type { IStorageProvider } from '../providers/storage-provider.interface'\nimport { STORAGE_TOKENS } from '../storage.tokens'\nimport type { StorageConfig, StorageEntry } from '../types'\n\n/**\n * Storage Manager Service\n * Manages multiple storage providers (one per disk)\n * Handles lazy initialization and caching of S3Clients\n */\n@Transient(STORAGE_TOKENS.StorageManager)\nexport class StorageManagerService {\n private readonly providers = new Map<string, IStorageProvider>()\n private readonly creationPromises = new Map<string, Promise<IStorageProvider>>()\n private readonly diskConfigs = new Map<string, StorageEntry>()\n\n constructor(\n @inject(STORAGE_TOKENS.Options)\n private readonly options: StorageConfig\n ) {\n this.initializeDiskConfigs()\n }\n\n /**\n * Initialize disk configurations from options\n */\n private initializeDiskConfigs(): void {\n for (const entry of this.options.storage) {\n this.diskConfigs.set(entry.disk, entry)\n }\n }\n\n /**\n * Get provider for a specific disk\n * Lazily initializes provider on first access\n * @param diskName - Name of the disk\n * @returns Storage provider instance\n */\n async getProvider(diskName: string): Promise<IStorageProvider> {\n // Return cached provider if exists\n const cached = this.providers.get(diskName)\n if (cached) {\n return cached\n }\n\n // Return in-flight creation promise to deduplicate concurrent calls\n const inflight = this.creationPromises.get(diskName)\n if (inflight) {\n return inflight\n }\n\n // Get disk configuration\n const diskConfig = this.diskConfigs.get(diskName)\n if (!diskConfig) {\n throw new DiskNotConfiguredError(diskName)\n }\n\n // Create provider and deduplicate concurrent calls\n const promise = this.createProvider(diskConfig).then((provider) => {\n this.providers.set(diskName, provider)\n this.creationPromises.delete(diskName)\n return provider\n }).catch((error: unknown) => {\n this.creationPromises.delete(diskName)\n throw error\n })\n\n this.creationPromises.set(diskName, promise)\n\n return promise\n }\n\n /**\n * Create a provider instance based on configuration\n * Dynamically imports S3StorageProvider to avoid loading AWS SDK at module evaluation time\n * @param config - Storage entry configuration\n * @returns Storage provider instance\n */\n private async createProvider(config: StorageEntry): Promise<IStorageProvider> {\n switch (config.provider) {\n case 's3': {\n const { S3StorageProvider } = await import('../providers/s3-storage.provider')\n return new S3StorageProvider(config)\n }\n case 'gcs':\n throw new StorageProviderNotSupportedError(config.provider)\n default:\n throw new StorageProviderNotSupportedError(config.provider)\n }\n }\n\n /**\n * Get disk configuration\n * @param diskName - Name of the disk\n * @returns Storage entry configuration\n */\n getDiskConfig(diskName: string): StorageEntry {\n const config = this.diskConfigs.get(diskName)\n if (!config) {\n throw new DiskNotConfiguredError(diskName)\n }\n return config\n }\n\n /**\n * Check if a disk exists\n * @param diskName - Name of the disk\n * @returns True if disk exists, false otherwise\n */\n hasDisk(diskName: string): boolean {\n return this.diskConfigs.has(diskName)\n }\n\n /**\n * Get all available disk names\n * @returns Array of disk names\n */\n getAvailableDisks(): string[] {\n return Array.from(this.diskConfigs.keys())\n }\n}\n","import { inject } from 'tsyringe'\nimport { Transient } from '../../di/decorators'\nimport type { DownloadResult, PresignedUrlResult, UploadOptions, UploadResult } from '../contracts'\nimport {\n InvalidDiskError,\n PresignedUrlInvalidExpiryError,\n} from '../errors'\nimport type { StreamingBlobPayloadInputTypes } from '../providers/storage-provider.interface'\nimport { STORAGE_TOKENS } from '../storage.tokens'\nimport type { StorageConfig } from '../types'\nimport { StorageManagerService } from './storage-manager.service'\n\n/**\n * Storage Service\n *\n * Main facade for storage operations.\n * Request-scoped for proper isolation.\n *\n * @example\n * ```typescript\n * @inject(STORAGE_TOKENS.StorageService)\n * private readonly storage: StorageService\n *\n * await this.storage.upload(file, 'documents/report.pdf')\n * ```\n */\n@Transient(STORAGE_TOKENS.StorageService)\nexport class StorageService {\n constructor(\n @inject(STORAGE_TOKENS.StorageManager)\n protected readonly storageManager: StorageManagerService,\n @inject(STORAGE_TOKENS.Options)\n protected readonly options: StorageConfig\n ) { }\n\n /**\n * Upload content to storage\n * @param body - Content to upload (stream, buffer, or string)\n * @param relativePath - Relative path within the disk\n * @param options - Upload options including size and mime type\n * @param disk - Optional disk name (uses default if not provided)\n * @returns Upload result with metadata\n */\n async upload(\n body: StreamingBlobPayloadInputTypes,\n relativePath: string,\n options: UploadOptions,\n disk?: string\n ): Promise<UploadResult> {\n const diskName = this.resolveDisk(disk)\n const provider = await this.storageManager.getProvider(diskName)\n const fullPath = this.buildFullPath(relativePath, diskName)\n\n return provider.upload(body, fullPath, options)\n }\n\n /**\n * Download a file from storage\n * @param relativePath - Relative path within the disk\n * @param disk - Optional disk name (uses default if not provided)\n * @returns Download result with stream and metadata\n */\n async download(relativePath: string, disk?: string): Promise<DownloadResult> {\n const diskName = this.resolveDisk(disk)\n const provider = await this.storageManager.getProvider(diskName)\n const fullPath = this.buildFullPath(relativePath, diskName)\n\n return provider.download(fullPath)\n }\n\n /**\n * Delete a file from storage\n * @param relativePath - Relative path within the disk\n * @param disk - Optional disk name (uses default if not provided)\n */\n async delete(relativePath: string, disk?: string): Promise<void> {\n const diskName = this.resolveDisk(disk)\n const provider = await this.storageManager.getProvider(diskName)\n const fullPath = this.buildFullPath(relativePath, diskName)\n\n await provider.delete(fullPath)\n }\n\n /**\n * Check if a file exists in storage\n * @param relativePath - Relative path within the disk\n * @param disk - Optional disk name (uses default if not provided)\n * @returns True if file exists, false otherwise\n */\n async exists(relativePath: string, disk?: string): Promise<boolean> {\n const diskName = this.resolveDisk(disk)\n const provider = await this.storageManager.getProvider(diskName)\n const fullPath = this.buildFullPath(relativePath, diskName)\n\n return provider.exists(fullPath)\n }\n\n /**\n * Generate a presigned download URL\n * @param relativePath - Relative path within the disk\n * @param expiresIn - Optional expiry time in seconds (uses default if not provided)\n * @param disk - Optional disk name (uses default if not provided)\n * @returns Presigned URL result\n */\n async getPresignedDownloadUrl(\n relativePath: string,\n expiresIn?: number,\n disk?: string\n ): Promise<PresignedUrlResult> {\n return this.getPresignedUrl(relativePath, 'GET', expiresIn, disk)\n }\n\n /**\n * Generate a presigned upload URL\n * @param relativePath - Relative path within the disk\n * @param expiresIn - Optional expiry time in seconds (uses default if not provided)\n * @param disk - Optional disk name (uses default if not provided)\n * @returns Presigned URL result\n */\n async getPresignedUploadUrl(\n relativePath: string,\n expiresIn?: number,\n disk?: string\n ): Promise<PresignedUrlResult> {\n return this.getPresignedUrl(relativePath, 'PUT', expiresIn, disk)\n }\n\n /**\n * Generate a presigned delete URL\n * @param relativePath - Relative path within the disk\n * @param expiresIn - Optional expiry time in seconds (uses default if not provided)\n * @param disk - Optional disk name (uses default if not provided)\n * @returns Presigned URL result\n */\n async getPresignedDeleteUrl(\n relativePath: string,\n expiresIn?: number,\n disk?: string\n ): Promise<PresignedUrlResult> {\n return this.getPresignedUrl(relativePath, 'DELETE', expiresIn, disk)\n }\n\n /**\n * Generate a presigned URL for any method\n * @param relativePath - Relative path within the disk\n * @param method - HTTP method (GET, PUT, DELETE, HEAD)\n * @param expiresIn - Optional expiry time in seconds (uses default if not provided)\n * @param disk - Optional disk name (uses default if not provided)\n * @returns Presigned URL result\n */\n protected async getPresignedUrl(\n relativePath: string,\n method: 'GET' | 'PUT' | 'DELETE' | 'HEAD',\n expiresIn?: number,\n disk?: string\n ): Promise<PresignedUrlResult> {\n const diskName = this.resolveDisk(disk)\n const provider = await this.storageManager.getProvider(diskName)\n const fullPath = this.buildFullPath(relativePath, diskName)\n const validatedExpiresIn = this.validateExpiresIn(expiresIn)\n\n return provider.getPresignedUrl(fullPath, method, validatedExpiresIn)\n }\n\n /**\n * Resolve disk name (use default if not provided)\n * @param disk - Optional disk name\n * @returns Resolved disk name\n */\n protected resolveDisk(disk?: string): string {\n const diskName = disk ?? this.options.defaultStorageDisk\n\n if (!this.storageManager.hasDisk(diskName)) {\n throw new InvalidDiskError(diskName)\n }\n\n return diskName\n }\n\n /**\n * Build full path with disk root and path template substitution\n * @param relativePath - Relative path within the disk\n * @param diskName - Name of the disk\n * @returns Full path including disk root\n */\n protected buildFullPath(relativePath: string, diskName: string): string {\n const diskConfig = this.storageManager.getDiskConfig(diskName)\n let root = diskConfig.root || ''\n\n // Substitute template variables\n root = this.substituteTemplateVariables(root)\n\n // Combine root and relative path\n const fullPath = `${root}/${relativePath}`.replace(/\\/+/g, '/').replace(/^\\//, '')\n\n return fullPath\n }\n\n /**\n * Substitute template variables in path\n * Override this method in subclasses to add custom substitutions\n *\n * @param path - Path with template variables\n * @returns Path with substituted variables\n */\n protected substituteTemplateVariables(path: string): string {\n let result = path\n\n // Substitute {date}, {year}, {month}\n const now = new Date()\n result = result.replace(/{date}/g, now.toISOString().split('T')[0])\n result = result.replace(/{year}/g, now.getFullYear().toString())\n result = result.replace(/{month}/g, (now.getMonth() + 1).toString().padStart(2, '0'))\n\n return result\n }\n\n /**\n * Validate expiry time for presigned URLs\n * @param expiresIn - Optional expiry time in seconds\n * @returns Validated expiry time\n */\n protected validateExpiresIn(expiresIn?: number): number {\n const presignedUrlConfig = this.options.presignedUrl\n const validatedExpiresIn = expiresIn ?? presignedUrlConfig.defaultExpiry\n\n // S3 presigned URL limits: 1 second to 7 days (604800 seconds)\n const minExpiry = 1\n const maxExpiry = presignedUrlConfig.maxExpiry\n\n if (validatedExpiresIn < minExpiry || validatedExpiresIn > maxExpiry) {\n throw new PresignedUrlInvalidExpiryError(validatedExpiresIn, minExpiry, maxExpiry)\n }\n\n return validatedExpiresIn\n }\n\n /**\n * Get all available disk names\n * @returns Array of disk names\n */\n getAvailableDisks(): string[] {\n return this.storageManager.getAvailableDisks()\n }\n\n /**\n * Chunked upload for streaming data without known size\n * Uses multipart upload under the hood - handles retries and large files\n *\n * Use this method when:\n * - Content-Length is unknown or unreliable\n * - Uploading from streams that can't be rewound\n * - Need automatic retry handling for transient failures\n *\n * @param body - Content to upload (stream or buffer)\n * @param relativePath - Relative path within the disk\n * @param options - Upload options (mimeType required, size optional)\n * @param disk - Optional disk name (uses default if not provided)\n * @returns Upload result with metadata\n */\n async chunkedUpload(\n body: StreamingBlobPayloadInputTypes,\n relativePath: string,\n options: Omit<UploadOptions, 'size'> & { size?: number },\n disk?: string\n ): Promise<UploadResult> {\n const diskName = this.resolveDisk(disk)\n const provider = await this.storageManager.getProvider(diskName)\n const fullPath = this.buildFullPath(relativePath, diskName)\n\n return provider.chunkedUpload(body, fullPath, options)\n }\n}\n","/**\n * Storage Module\n * Provides file storage capabilities using AWS S3\n * Supports multiple disk configurations with dynamic path templates\n */\n\nimport { Scope } from '../di/types'\nimport { Module } from '../module'\nimport type { AsyncModuleOptions, DynamicModule } from '../module/types'\nimport { StorageManagerService } from './services/storage-manager.service'\nimport { StorageService } from './services/storage.service'\nimport { STORAGE_TOKENS } from './storage.tokens'\nimport type { StorageConfig } from './types'\n\n/**\n * Storage module options\n * Same as StorageConfig from types.ts\n */\nexport type StorageModuleOptions = StorageConfig\n\n@Module({\n providers: [{ provide: STORAGE_TOKENS.StorageManager, useClass: StorageManagerService, scope: Scope.Singleton },\n { provide: STORAGE_TOKENS.StorageService, useClass: StorageService },],\n})\nexport class StorageModule {\n /**\n * Configure StorageModule with static options\n *\n * @param options - Storage configuration options\n * @returns Dynamic module with storage infrastructure\n *\n * @example\n * ```typescript\n * StorageModule.forRoot({\n * storage: [{ disk: 'uploads', provider: 's3', ... }],\n * defaultStorageDisk: 'uploads',\n * presignedUrl: { defaultExpiry: 3600, maxExpiry: 86400 }\n * })\n * ```\n */\n static forRoot(options: StorageModuleOptions): DynamicModule {\n return {\n module: StorageModule,\n providers: [\n { provide: STORAGE_TOKENS.Options, useValue: options },\n ],\n }\n }\n\n /**\n * Configure StorageModule with async factory\n *\n * Use when configuration depends on other services.\n *\n * @param options - Async configuration with factory and inject tokens\n * @returns Dynamic module with storage infrastructure\n *\n * @example\n * ```typescript\n * StorageModule.forRootAsync({\n * inject: [storageConfig.KEY],\n * useFactory: (storage) => ({\n * storage: storage.storage,\n * defaultStorageDisk: storage.defaultStorageDisk,\n * presignedUrl: storage.presignedUrl\n * })\n * })\n * ```\n */\n static forRootAsync(options: AsyncModuleOptions<StorageModuleOptions>): DynamicModule {\n return {\n module: StorageModule,\n providers: [\n {\n provide: STORAGE_TOKENS.Options,\n useFactory: options.useFactory,\n inject: options.inject,\n },\n ],\n }\n }\n}\n","import { z, withI18n } from '../../i18n/validation'\n\nexport const deleteFileInputSchema = z.object({\n path: z.string().min(1, withI18n('zodI18n.errors.custom.filePathRequired')),\n disk: z.string().optional(),\n})\n\nexport type DeleteFileInput = z.infer<typeof deleteFileInputSchema>\n","import { z, withI18n } from '../../i18n/validation'\n\nexport const fileExistsInputSchema = z.object({\n path: z.string().min(1, withI18n('zodI18n.errors.custom.filePathRequired')),\n disk: z.string().optional(),\n})\n\nexport type FileExistsInput = z.infer<typeof fileExistsInputSchema>\n","import { z, withI18n } from '../../i18n/validation'\n\nexport const getPresignedUrlInputSchema = z.object({\n path: z.string().min(1, withI18n('zodI18n.errors.custom.filePathRequired')),\n method: z.enum(['GET', 'PUT', 'DELETE', 'HEAD']).default('GET'),\n expiresIn: z.number().int().min(1).max(604800).optional(),\n disk: z.string().optional(),\n})\n\nexport type GetPresignedUrlInput = z.infer<typeof getPresignedUrlInputSchema>\n\nexport const presignedUrlResultSchema = z.object({\n url: z.string().url(),\n expiresIn: z.number(),\n expiresAt: z.date(),\n method: z.enum(['GET', 'PUT', 'DELETE', 'HEAD']),\n})\n\nexport type PresignedUrlResult = z.infer<typeof presignedUrlResultSchema>\n","import { z } from '../../i18n/validation'\n\n/**\n * Upload options for streaming uploads\n */\nexport interface UploadOptions {\n /**\n * Size of the content in bytes\n */\n size: number\n /**\n * MIME type of the content\n */\n mimeType?: string\n /**\n * Custom metadata to store with the object (S3-specific)\n * Stored as S3 object metadata headers\n */\n metadata?: Record<string, string>\n /**\n * Object tagging for lifecycle policies (S3-specific)\n * Format: key=value (e.g., \"Tus-Completed=true\")\n */\n tagging?: string\n}\n\nexport const uploadResultSchema = z.object({\n path: z.string(),\n disk: z.string(),\n fullPath: z.string(),\n size: z.number(),\n mimeType: z.string(),\n uploadedAt: z.date(),\n})\n\nexport type UploadResult = z.infer<typeof uploadResultSchema>\n"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"storage-provider.interface-0IqcdhBf.d.mts","names":[],"sources":["../src/storage/types.ts","../src/storage/contracts/delete-file.input.ts","../src/storage/contracts/download-result.ts","../src/storage/contracts/file-exists.input.ts","../src/storage/contracts/get-presigned-url.input.ts","../src/storage/contracts/upload-file.input.ts","../src/storage/providers/storage-provider.interface.ts"],"mappings":";;;;;;;;UAIiB,YAAA;EAChB,IAAA;EACA,QAAA;EACA,QAAA;EACA,MAAA;EACA,MAAA;EACA,WAAA;EACA,eAAA;EACA,IAAA;EACA,UAAA;AAAA;;;;UAMgB,kBAAA;EAChB,aAAA;EACA,SAAA;AAAA;;;AAMD;UAAiB,aAAA;EAChB,OAAA,EAAS,YAAA;EACT,kBAAA;EACA,YAAA,EAAc,kBAAA;AAAA;;;cC5BF,qBAAA,EAAqB,CAAA,CAAA,SAAA;;;;KAKtB,eAAA,GAAkB,CAAA,CAAE,KAAA,QAAa,qBAAA;;;;;;;ADH7C;;UEEiB,cAAA;EFFY;;;EEM3B,QAAA,gBAAwB,cAAA,CAAe,UAAA;EAEvC,QAAA,gBAAwB,OAAA;EAExB,aAAA,gBAA6B,OAAA,CAAQ,UAAA;EFJtC;;;EESC,WAAA;EFNS;;AAMX;EEKE,IAAA;;;;AFGF;EEGE,QAAA,GAAW,MAAA;AAAA;;;cC5BA,qBAAA,EAAqB,CAAA,CAAA,SAAA;;;;KAKtB,eAAA,GAAkB,CAAA,CAAE,KAAA,QAAa,qBAAA;;;cCLhC,0BAAA,EAA0B,CAAA,CAAA,SAAA;;;;;;;;;;;KAO3B,oBAAA,GAAuB,CAAA,CAAE,KAAA,QAAa,0BAAA;AAAA,cAErC,wBAAA,EAAwB,CAAA,CAAA,SAAA;;;;;;;;;;;KAOzB,kBAAA,GAAqB,CAAA,CAAE,KAAA,QAAa,wBAAA;;;;;;UCb/B,aAAA;ELDY;;;EKK3B,IAAA;ELHD;;;EKOC,QAAA;ELHD;;;;EKQC,QAAA,GAAW,MAAA;ELLF;AAMX;;;EKIE,OAAA;AAAA;AAAA,cAGW,kBAAA,EAAkB,CAAA,CAAA,SAAA;;;;;;;;KASnB,YAAA,GAAe,CAAA,CAAE,KAAA,QAAa,kBAAA;;;;;AL/B1C;;KMGY,8BAAA,GAAiC,qBAAA;;;;;UAM5B,gBAAA;ENJhB;;;;;;;EMYC,MAAA,CAAO,IAAA,EAAM,8BAAA,EAAgC,IAAA,UAAc,OAAA,EAAS,aAAA,GAAgB,OAAA,CAAQ,YAAA;ENF3D;;;;AAQnC;EMCE,QAAA,CAAS,IAAA,WAAe,OAAA,CAAQ,cAAA;;;;;EAMhC,MAAA,CAAO,IAAA,WAAe,OAAA;ENJvB;;;;;EMWC,MAAA,CAAO,IAAA,WAAe,OAAA;;ALvCxB;;;;;;EKgDE,eAAA,CACE,IAAA,UACA,MAAA,qCACA,SAAA,WACC,OAAA,CAAQ,kBAAA;ELpDqB;;;;;;;;EK8DhC,aAAA,CACE,IAAA,EAAM,8BAAA,EACN,IAAA,UACA,OAAA,EAAS,IAAA,CAAK,aAAA;IAA2B,IAAA;EAAA,IACxC,OAAA,CAAQ,YAAA;AAAA"}