stratal 0.0.18 → 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 (172) hide show
  1. package/README.md +8 -8
  2. package/dist/{base-email.provider-Cuw4OAB0.mjs → base-email.provider-mjynzewK.mjs} +1 -1
  3. package/dist/{base-email.provider-Cuw4OAB0.mjs.map → base-email.provider-mjynzewK.mjs.map} +1 -1
  4. package/dist/bin/cloudflare-workers-loader.mjs.map +1 -1
  5. package/dist/bin/quarry.mjs +21 -36
  6. package/dist/bin/quarry.mjs.map +1 -1
  7. package/dist/cache/index.d.mts +3 -2
  8. package/dist/cache/index.d.mts.map +1 -1
  9. package/dist/cache/index.mjs +3 -3
  10. package/dist/{colors-BTAnQRGU.mjs → colors-DJaRDXoS.mjs} +1 -1
  11. package/dist/{colors-BTAnQRGU.mjs.map → colors-DJaRDXoS.mjs.map} +1 -1
  12. package/dist/{command-DjGqCYHv.mjs → command-BgSlsS4M.mjs} +2 -2
  13. package/dist/{command-DjGqCYHv.mjs.map → command-BgSlsS4M.mjs.map} +1 -1
  14. package/dist/{command-B1YuV-UZ.d.mts → command-DsQq56Lp.d.mts} +2 -2
  15. package/dist/{command-B1YuV-UZ.d.mts.map → command-DsQq56Lp.d.mts.map} +1 -1
  16. package/dist/config/index.d.mts +81 -37
  17. package/dist/config/index.d.mts.map +1 -1
  18. package/dist/config/index.mjs +126 -45
  19. package/dist/config/index.mjs.map +1 -1
  20. package/dist/{consumer-registry-BkuHXR_u.d.mts → consumer-registry-Doom7BEh.d.mts} +1 -1
  21. package/dist/{consumer-registry-BkuHXR_u.d.mts.map → consumer-registry-Doom7BEh.d.mts.map} +1 -1
  22. package/dist/controller.decorator-LZY9aHYG.mjs +66 -0
  23. package/dist/controller.decorator-LZY9aHYG.mjs.map +1 -0
  24. package/dist/cron/index.d.mts +4 -3
  25. package/dist/cron/index.d.mts.map +1 -1
  26. package/dist/cron/index.mjs +1 -1
  27. package/dist/{cron-manager-1KnZvojs.mjs → cron-manager-C30t9UZM.mjs} +29 -19
  28. package/dist/cron-manager-C30t9UZM.mjs.map +1 -0
  29. package/dist/{cron-manager-BnEZquBL.d.mts → cron-manager-RuPtFVLy.d.mts} +27 -13
  30. package/dist/cron-manager-RuPtFVLy.d.mts.map +1 -0
  31. package/dist/di/index.d.mts +1 -1
  32. package/dist/di/index.mjs +2 -2
  33. package/dist/email/index.d.mts +3 -3
  34. package/dist/email/index.mjs +87 -10
  35. package/dist/email/index.mjs.map +1 -1
  36. package/dist/{en-3QnZwP-u.mjs → en-rHmW6vD9.mjs} +5 -31
  37. package/dist/en-rHmW6vD9.mjs.map +1 -0
  38. package/dist/env-CamWD-U1.d.mts +25 -0
  39. package/dist/env-CamWD-U1.d.mts.map +1 -0
  40. package/dist/errors/index.d.mts +1 -1
  41. package/dist/errors/index.mjs +1 -1
  42. package/dist/{errors--RBIvDXr.mjs → errors-B4pYgYON.mjs} +161 -7
  43. package/dist/errors-B4pYgYON.mjs.map +1 -0
  44. package/dist/{errors-B7hCnXgB.mjs → errors-BUyUfr2Z.mjs} +14 -7
  45. package/dist/errors-BUyUfr2Z.mjs.map +1 -0
  46. package/dist/events/index.d.mts +2 -2
  47. package/dist/events/index.mjs +1 -1
  48. package/dist/{events-UTJliZhl.mjs → events-COKixqnG.mjs} +2 -2
  49. package/dist/{events-UTJliZhl.mjs.map → events-COKixqnG.mjs.map} +1 -1
  50. package/dist/{gateway-context-BdBFoQd8.mjs → gateway-context-cqZ8wMoi.mjs} +4 -65
  51. package/dist/gateway-context-cqZ8wMoi.mjs.map +1 -0
  52. package/dist/guards/index.d.mts +14 -5
  53. package/dist/guards/index.d.mts.map +1 -1
  54. package/dist/guards/index.mjs +1 -1
  55. package/dist/{guards-MtDgcHnF.mjs → guards-DMbsAxSX.mjs} +1 -1
  56. package/dist/guards-DMbsAxSX.mjs.map +1 -0
  57. package/dist/http-method.decorator-BT3ufnz8.mjs +96 -0
  58. package/dist/http-method.decorator-BT3ufnz8.mjs.map +1 -0
  59. package/dist/i18n/index.d.mts +3 -3
  60. package/dist/i18n/index.mjs +2 -2
  61. package/dist/i18n/messages/en/index.d.mts +1 -1
  62. package/dist/i18n/messages/en/index.mjs +1 -1
  63. package/dist/i18n/utils/index.mjs +1 -1
  64. package/dist/i18n/validation/index.d.mts +1 -1
  65. package/dist/i18n/validation/index.mjs +1 -1
  66. package/dist/{i18n.module-BpLLLCTg.mjs → i18n.module-CI_prYFD.mjs} +74 -196
  67. package/dist/i18n.module-CI_prYFD.mjs.map +1 -0
  68. package/dist/{index-Dfpd_ypO.d.mts → index-B437eK7p.d.mts} +26 -12
  69. package/dist/index-B437eK7p.d.mts.map +1 -0
  70. package/dist/{index-BR23zDMy.d.mts → index-CWRS7Ri3.d.mts} +1 -1
  71. package/dist/{index-BR23zDMy.d.mts.map → index-CWRS7Ri3.d.mts.map} +1 -1
  72. package/dist/{index-BDh9J2KD.d.mts → index-DFhEeFfC.d.mts} +4 -30
  73. package/dist/{index-BDh9J2KD.d.mts.map → index-DFhEeFfC.d.mts.map} +1 -1
  74. package/dist/{index-BrmS34sa.d.mts → index-DPFqRs8L.d.mts} +70 -39
  75. package/dist/index-DPFqRs8L.d.mts.map +1 -0
  76. package/dist/{index-DPxmo6AY.d.mts → index-Dnqm9ZB6.d.mts} +5 -4
  77. package/dist/index-Dnqm9ZB6.d.mts.map +1 -0
  78. package/dist/index-SHx31sBJ.d.mts +101 -0
  79. package/dist/index-SHx31sBJ.d.mts.map +1 -0
  80. package/dist/index.d.mts +3 -2
  81. package/dist/index.d.mts.map +1 -1
  82. package/dist/index.mjs +1 -1
  83. package/dist/{is-command-PvULqiTa.mjs → is-command-C6a7WTPw.mjs} +2 -2
  84. package/dist/{is-command-PvULqiTa.mjs.map → is-command-C6a7WTPw.mjs.map} +1 -1
  85. package/dist/{is-seeder-BN9Ej1r7.mjs → is-seeder-CebjZCDn.mjs} +1 -1
  86. package/dist/{is-seeder-BN9Ej1r7.mjs.map → is-seeder-CebjZCDn.mjs.map} +1 -1
  87. package/dist/logger/index.d.mts +1 -1
  88. package/dist/logger/index.mjs +1 -1
  89. package/dist/{logger-c0ftIK4G.mjs → logger-V6Ms3QnQ.mjs} +38 -20
  90. package/dist/{logger-c0ftIK4G.mjs.map → logger-V6Ms3QnQ.mjs.map} +1 -1
  91. package/dist/macroable/index.d.mts +2 -0
  92. package/dist/macroable/index.mjs +2 -0
  93. package/dist/macroable-BmufBshB.mjs +122 -0
  94. package/dist/macroable-BmufBshB.mjs.map +1 -0
  95. package/dist/module/index.d.mts +2 -2
  96. package/dist/module/index.mjs +1 -1
  97. package/dist/{module-C3YZ-kZN.mjs → module-qGE_1duv.mjs} +31 -18
  98. package/dist/module-qGE_1duv.mjs.map +1 -0
  99. package/dist/openapi/index.d.mts +3 -3
  100. package/dist/openapi/index.mjs +2 -2
  101. package/dist/{openapi-tools.service-B77QXD56.mjs → openapi-tools.service-CYWGuhue.mjs} +4 -1
  102. package/dist/{openapi-tools.service-B77QXD56.mjs.map → openapi-tools.service-CYWGuhue.mjs.map} +1 -1
  103. package/dist/{openapi.service-6yj0BUY4.d.mts → openapi.service-Bv_NioM9.d.mts} +3 -3
  104. package/dist/{openapi.service-6yj0BUY4.d.mts.map → openapi.service-Bv_NioM9.d.mts.map} +1 -1
  105. package/dist/quarry/index.d.mts +7 -7
  106. package/dist/quarry/index.d.mts.map +1 -1
  107. package/dist/quarry/index.mjs +4 -4
  108. package/dist/{quarry-registry-CQCIlYTO.mjs → quarry-registry-DFfRRkA7.mjs} +17 -15
  109. package/dist/quarry-registry-DFfRRkA7.mjs.map +1 -0
  110. package/dist/queue/index.d.mts +2 -2
  111. package/dist/queue/index.mjs +2 -2
  112. package/dist/{queue.module-DIjD6nr-.mjs → queue.module-P-G-nCYz.mjs} +4 -4
  113. package/dist/{queue.module-DIjD6nr-.mjs.map → queue.module-P-G-nCYz.mjs.map} +1 -1
  114. package/dist/r2-storage.provider-LdzK9tfG.mjs +244 -0
  115. package/dist/r2-storage.provider-LdzK9tfG.mjs.map +1 -0
  116. package/dist/{resend.provider-Bvw36rQy.mjs → resend.provider-bwILp0WI.mjs} +2 -2
  117. package/dist/{resend.provider-Bvw36rQy.mjs.map → resend.provider-bwILp0WI.mjs.map} +1 -1
  118. package/dist/router/index.d.mts +2 -2
  119. package/dist/router/index.mjs +7 -5
  120. package/dist/seeder/index.d.mts +3 -3
  121. package/dist/seeder/index.mjs +2 -2
  122. package/dist/{seeder-D7VXULXB.mjs → seeder-BcqIFa2X.mjs} +5 -5
  123. package/dist/{seeder-D7VXULXB.mjs.map → seeder-BcqIFa2X.mjs.map} +1 -1
  124. package/dist/{setup-BRIN-iYT.mjs → setup-CtekcwuO.mjs} +1 -1
  125. package/dist/{setup-BRIN-iYT.mjs.map → setup-CtekcwuO.mjs.map} +1 -1
  126. package/dist/signed-url-COX7cCWR.mjs +74 -0
  127. package/dist/signed-url-COX7cCWR.mjs.map +1 -0
  128. package/dist/{smtp.provider-CAwpvzvD.mjs → smtp.provider-B07yuARi.mjs} +2 -2
  129. package/dist/{smtp.provider-CAwpvzvD.mjs.map → smtp.provider-B07yuARi.mjs.map} +1 -1
  130. package/dist/storage/index.d.mts +39 -17
  131. package/dist/storage/index.d.mts.map +1 -1
  132. package/dist/storage/index.mjs +3 -3
  133. package/dist/storage/providers/index.d.mts +30 -70
  134. package/dist/storage/providers/index.d.mts.map +1 -1
  135. package/dist/storage/providers/index.mjs +2 -2
  136. package/dist/{storage-CJ-QOwNv.mjs → storage-P6X4h9So.mjs} +101 -27
  137. package/dist/storage-P6X4h9So.mjs.map +1 -0
  138. package/dist/{storage-provider.interface-YRtyYBxV.d.mts → storage-provider.interface-CC1nniHk.d.mts} +20 -21
  139. package/dist/storage-provider.interface-CC1nniHk.d.mts.map +1 -0
  140. package/dist/{stratal-B7G4i9-N.mjs → stratal-BCiwCFN9.mjs} +57 -26
  141. package/dist/stratal-BCiwCFN9.mjs.map +1 -0
  142. package/dist/{types-CN0zONAZ.d.mts → types-DIWemRad.d.mts} +1 -1
  143. package/dist/types-DIWemRad.d.mts.map +1 -0
  144. package/dist/{usage-generator-Cl1HPlUp.mjs → usage-generator-MBcRo0Q2.mjs} +2 -2
  145. package/dist/{usage-generator-Cl1HPlUp.mjs.map → usage-generator-MBcRo0Q2.mjs.map} +1 -1
  146. package/dist/{validation-B4bePOa_.mjs → validation-Dbg3ehdP.mjs} +1 -1
  147. package/dist/{validation-B4bePOa_.mjs.map → validation-Dbg3ehdP.mjs.map} +1 -1
  148. package/dist/websocket/index.d.mts +3 -3
  149. package/dist/websocket/index.mjs +1 -1
  150. package/dist/workers/index.d.mts +2 -1
  151. package/dist/workers/index.d.mts.map +1 -1
  152. package/dist/workers/index.mjs +2 -2
  153. package/package.json +27 -39
  154. package/dist/cron-manager-1KnZvojs.mjs.map +0 -1
  155. package/dist/cron-manager-BnEZquBL.d.mts.map +0 -1
  156. package/dist/en-3QnZwP-u.mjs.map +0 -1
  157. package/dist/errors--RBIvDXr.mjs.map +0 -1
  158. package/dist/errors-B7hCnXgB.mjs.map +0 -1
  159. package/dist/gateway-context-BdBFoQd8.mjs.map +0 -1
  160. package/dist/guards-MtDgcHnF.mjs.map +0 -1
  161. package/dist/i18n.module-BpLLLCTg.mjs.map +0 -1
  162. package/dist/index-BrmS34sa.d.mts.map +0 -1
  163. package/dist/index-DPxmo6AY.d.mts.map +0 -1
  164. package/dist/index-Dfpd_ypO.d.mts.map +0 -1
  165. package/dist/module-C3YZ-kZN.mjs.map +0 -1
  166. package/dist/quarry-registry-CQCIlYTO.mjs.map +0 -1
  167. package/dist/s3-storage.provider-BAhHDMI3.mjs +0 -343
  168. package/dist/s3-storage.provider-BAhHDMI3.mjs.map +0 -1
  169. package/dist/storage-CJ-QOwNv.mjs.map +0 -1
  170. package/dist/storage-provider.interface-YRtyYBxV.d.mts.map +0 -1
  171. package/dist/stratal-B7G4i9-N.mjs.map +0 -1
  172. package/dist/types-CN0zONAZ.d.mts.map +0 -1
@@ -1,5 +1,6 @@
1
- import { d as ApplicationError, fn as AsyncModuleOptions, mn as DynamicModule } from "../index-BrmS34sa.mjs";
2
- import { _ as StorageEntry, a as uploadResultSchema, c as getPresignedUrlInputSchema, d as fileExistsInputSchema, f as DownloadResult, g as StorageConfig, h as PresignedUrlConfig, i as UploadResult, l as presignedUrlResultSchema, m as deleteFileInputSchema, n as StreamingBlobPayloadInputTypes, o as GetPresignedUrlInput, p as DeleteFileInput, r as UploadOptions, s as PresignedUrlResult, t as IStorageProvider, u as FileExistsInput } from "../storage-provider.interface-YRtyYBxV.mjs";
1
+ import { V as RouterContext, d as ApplicationError, hn as DynamicModule, pn as AsyncModuleOptions } from "../index-DPFqRs8L.mjs";
2
+ import { t as StratalEnv } from "../env-CamWD-U1.mjs";
3
+ import { _ as StorageEntry, a as uploadResultSchema, c as getPresignedUrlInputSchema, d as fileExistsInputSchema, f as DownloadResult, g as StorageConfig, h as PresignedUrlConfig, i as UploadResult, l as presignedUrlResultSchema, m as deleteFileInputSchema, n as StreamingBlobPayloadInputTypes, o as GetPresignedUrlInput, p as DeleteFileInput, r as UploadOptions, s as PresignedUrlResult, t as IStorageProvider, u as FileExistsInput, v as StorageRouteConfig } from "../storage-provider.interface-CC1nniHk.mjs";
3
4
 
4
5
  //#region src/storage/storage.module.d.ts
5
6
  /**
@@ -11,13 +12,10 @@ declare class StorageModule {
11
12
  /**
12
13
  * Configure StorageModule with static options
13
14
  *
14
- * @param options - Storage configuration options
15
- * @returns Dynamic module with storage infrastructure
16
- *
17
15
  * @example
18
16
  * ```typescript
19
17
  * StorageModule.forRoot({
20
- * storage: [{ disk: 'uploads', provider: 's3', ... }],
18
+ * storage: [{ disk: 'uploads', binding: 'MY_BUCKET', root: 'uploads' }],
21
19
  * defaultStorageDisk: 'uploads',
22
20
  * presignedUrl: { defaultExpiry: 3600, maxExpiry: 86400 }
23
21
  * })
@@ -29,9 +27,6 @@ declare class StorageModule {
29
27
  *
30
28
  * Use when configuration depends on other services.
31
29
  *
32
- * @param options - Async configuration with factory and inject tokens
33
- * @returns Dynamic module with storage infrastructure
34
- *
35
30
  * @example
36
31
  * ```typescript
37
32
  * StorageModule.forRootAsync({
@@ -62,14 +57,15 @@ declare const STORAGE_TOKENS: {
62
57
  /**
63
58
  * Storage Manager Service
64
59
  * Manages multiple storage providers (one per disk)
65
- * Handles lazy initialization and caching of S3Clients
60
+ * Handles lazy initialization and caching of R2 providers
66
61
  */
67
62
  declare class StorageManagerService {
68
63
  private readonly options;
64
+ private readonly env;
69
65
  private readonly providers;
70
66
  private readonly creationPromises;
71
67
  private readonly diskConfigs;
72
- constructor(options: StorageConfig);
68
+ constructor(options: StorageConfig, env: StratalEnv);
73
69
  /**
74
70
  * Initialize disk configurations from options
75
71
  */
@@ -82,8 +78,8 @@ declare class StorageManagerService {
82
78
  */
83
79
  getProvider(diskName: string): Promise<IStorageProvider>;
84
80
  /**
85
- * Create a provider instance based on configuration
86
- * Dynamically imports S3StorageProvider to avoid loading AWS SDK at module evaluation time
81
+ * Create an R2 provider instance
82
+ * Dynamically imports R2StorageProvider to support code splitting
87
83
  * @param config - Storage entry configuration
88
84
  * @returns Storage provider instance
89
85
  */
@@ -240,6 +236,27 @@ declare class StorageService {
240
236
  }, disk?: string): Promise<UploadResult>;
241
237
  }
242
238
  //#endregion
239
+ //#region src/storage/controllers/storage.controller.d.ts
240
+ /**
241
+ * Storage Controller
242
+ *
243
+ * Auto-registered controller that proxies R2 operations behind signed URLs.
244
+ * Signature verification is applied via VerifySignatureMiddleware on the module's
245
+ * configureRoutes() method.
246
+ *
247
+ * Routes:
248
+ * - GET /storage/:disk/* → download file
249
+ * - PUT /storage/:disk/* → upload file
250
+ * - DELETE /storage/:disk/* → delete file
251
+ */
252
+ declare class StorageController {
253
+ private readonly storage;
254
+ constructor(storage: StorageService);
255
+ download(ctx: RouterContext): Promise<Response>;
256
+ upload(ctx: RouterContext): Promise<Response>;
257
+ destroy(ctx: RouterContext): Promise<Response>;
258
+ }
259
+ //#endregion
243
260
  //#region src/storage/errors/disk-not-configured.error.d.ts
244
261
  declare class DiskNotConfiguredError extends ApplicationError {
245
262
  constructor(disk: string);
@@ -270,9 +287,14 @@ declare class PresignedUrlInvalidExpiryError extends ApplicationError {
270
287
  constructor(expiresIn: number, min: number, max: number);
271
288
  }
272
289
  //#endregion
273
- //#region src/storage/errors/storage-provider-not-supported.error.d.ts
274
- declare class StorageProviderNotSupportedError extends ApplicationError {
275
- constructor(provider: string);
290
+ //#region src/storage/errors/r2-binding-not-found.error.d.ts
291
+ declare class R2BindingNotFoundError extends ApplicationError {
292
+ constructor(binding: string);
293
+ }
294
+ //#endregion
295
+ //#region src/storage/errors/r2-presigned-url-secret-missing.error.d.ts
296
+ declare class R2PresignedUrlSecretMissingError extends ApplicationError {
297
+ constructor();
276
298
  }
277
299
  //#endregion
278
300
  //#region src/storage/errors/storage-response-body-missing.error.d.ts
@@ -280,5 +302,5 @@ declare class StorageResponseBodyMissingError extends ApplicationError {
280
302
  constructor(path: string);
281
303
  }
282
304
  //#endregion
283
- export { DeleteFileInput, DiskNotConfiguredError, DownloadResult, FileExistsInput, FileNotFoundError, FileTooLargeError, GetPresignedUrlInput, type IStorageProvider, InvalidDiskError, InvalidFileTypeError, type PresignedUrlConfig, PresignedUrlInvalidExpiryError, PresignedUrlResult, STORAGE_TOKENS, type StorageConfig, type StorageEntry, StorageManagerService, StorageModule, StorageProviderNotSupportedError, StorageResponseBodyMissingError, StorageService, type StreamingBlobPayloadInputTypes, UploadOptions, UploadResult, deleteFileInputSchema, fileExistsInputSchema, getPresignedUrlInputSchema, presignedUrlResultSchema, uploadResultSchema };
305
+ export { DeleteFileInput, DiskNotConfiguredError, DownloadResult, FileExistsInput, FileNotFoundError, FileTooLargeError, GetPresignedUrlInput, type IStorageProvider, InvalidDiskError, InvalidFileTypeError, type PresignedUrlConfig, PresignedUrlInvalidExpiryError, PresignedUrlResult, R2BindingNotFoundError, R2PresignedUrlSecretMissingError, STORAGE_TOKENS, type StorageConfig, StorageController, type StorageEntry, StorageManagerService, StorageModule, StorageResponseBodyMissingError, type StorageRouteConfig, StorageService, type StreamingBlobPayloadInputTypes, UploadOptions, UploadResult, deleteFileInputSchema, fileExistsInputSchema, getPresignedUrlInputSchema, presignedUrlResultSchema, uploadResultSchema };
284
306
  //# sourceMappingURL=index.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","names":[],"sources":["../../src/storage/storage.module.ts","../../src/storage/storage.tokens.ts","../../src/storage/services/storage-manager.service.ts","../../src/storage/services/storage.service.ts","../../src/storage/errors/disk-not-configured.error.ts","../../src/storage/errors/file-not-found.error.ts","../../src/storage/errors/file-too-large.error.ts","../../src/storage/errors/invalid-disk.error.ts","../../src/storage/errors/invalid-file-type.error.ts","../../src/storage/errors/presigned-url-invalid-expiry.error.ts","../../src/storage/errors/storage-provider-not-supported.error.ts","../../src/storage/errors/storage-response-body-missing.error.ts"],"mappings":";;;;;;AAoBA;;KAFY,oBAAA,GAAuB,aAAA;AAAA,cAMtB,aAAA;EAgBoC;;;;;;;;;;;;;;;EAAA,OAAxC,OAAA,CAAQ,OAAA,EAAS,oBAAA,GAAuB,aAAA;EA6BsC;;;;ACjEvF;;;;;;;;;;;;ACQA;;;;EFyDuF,OAA9E,YAAA,CAAa,OAAA,EAAS,kBAAA,CAAmB,oBAAA,IAAwB,aAAA;AAAA;;;;;;;cCjE7D,cAAA;EAAA;;;;;;;;ADcb;;;cELa,qBAAA;EAAA,iBAOQ,OAAA;EAAA,iBANF,SAAA;EAAA,iBACA,gBAAA;EAAA,iBACA,WAAA;cAIE,OAAA,EAAS,aAAA;EFoBmB;;;EAAA,QEZvC,qBAAA;EFyC6E;;;;;;EE7B/E,WAAA,CAAY,QAAA,WAAmB,OAAA,CAAQ,gBAAA;EF6BhB;;;;;;EAAA,QEWf,cAAA;;;AD5EhB;;;EC8FE,aAAA,CAAc,QAAA,WAAmB,YAAA;;;;;;EAajC,OAAA,CAAQ,QAAA;;;AAnGV;;EA2GE,iBAAA,CAAA;AAAA;;;AFrGF;;;;;AAEA;;;;;;;;;AAFA,cGSa,cAAA;EAAA,mBAGU,cAAA,EAAgB,qBAAA;EAAA,mBAEhB,OAAA,EAAS,aAAA;cAFT,cAAA,EAAgB,qBAAA,EAEhB,OAAA,EAAS,aAAA;EHQiB;;;;;;;;EGGzC,MAAA,CACJ,IAAA,EAAM,8BAAA,EACN,YAAA,UACA,OAAA,EAAS,aAAA,EACT,IAAA,YACC,OAAA,CAAQ,YAAA;;;AF5Cb;;;;EE0DQ,QAAA,CAAS,YAAA,UAAsB,IAAA,YAAgB,OAAA,CAAQ,cAAA;;;;;;EAavD,MAAA,CAAO,YAAA,UAAsB,IAAA,YAAgB,OAAA;;AD/DrD;;;;;EC6EQ,MAAA,CAAO,YAAA,UAAsB,IAAA,YAAgB,OAAA;EDSlB;;;;;;;ECM3B,uBAAA,CACJ,YAAA,UACA,SAAA,WACA,IAAA,YACC,OAAA,CAAQ,kBAAA;EDxFiB;;;;;;;ECmGtB,qBAAA,CACJ,YAAA,UACA,SAAA,WACA,IAAA,YACC,OAAA,CAAQ,kBAAA;EDzBX;;;;;;;ECoCM,qBAAA,CACJ,YAAA,UACA,SAAA,WACA,IAAA,YACC,OAAA,CAAQ,kBAAA;;;;AAhHb;;;;;YA4HkB,eAAA,CACd,YAAA,UACA,MAAA,qCACA,SAAA,WACA,IAAA,YACC,OAAA,CAAQ,kBAAA;EA3HmB;;;;;EAAA,UAyIpB,WAAA,CAAY,IAAA;EA3G+B;;;;;;EAAA,UA2H3C,aAAA,CAAc,YAAA,UAAsB,QAAA;EA/CnC;;;;;;;EAAA,UAmED,2BAAA,CAA4B,IAAA;EA4DnC;;;;;EAAA,UA3CO,iBAAA,CAAkB,SAAA;EA9LE;;;;EAiN9B,iBAAA,CAAA;EAjNqB;;;;;;;;;;;;;;;EAoOf,aAAA,CACJ,IAAA,EAAM,8BAAA,EACN,YAAA,UACA,OAAA,EAAS,IAAA,CAAK,aAAA;IAA2B,IAAA;EAAA,GACzC,IAAA,YACC,OAAA,CAAQ,YAAA;AAAA;;;cCvQA,sBAAA,SAA+B,gBAAA;cAC9B,IAAA;AAAA;;;cCAD,iBAAA,SAA0B,gBAAA;cACzB,IAAA;AAAA;;;cCDD,iBAAA,SAA0B,gBAAA;cACzB,IAAA,UAAc,OAAA;AAAA;;;cCDf,gBAAA,SAAyB,gBAAA;cACxB,IAAA;AAAA;;;cCDD,oBAAA,SAA6B,gBAAA;cAC5B,QAAA;AAAA;;;cCDD,8BAAA,SAAuC,gBAAA;cACtC,SAAA,UAAmB,GAAA,UAAa,GAAA;AAAA;;;cCDjC,gCAAA,SAAyC,gBAAA;cACxC,QAAA;AAAA;;;cCDD,+BAAA,SAAwC,gBAAA;cACvC,IAAA;AAAA"}
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../../src/storage/storage.module.ts","../../src/storage/storage.tokens.ts","../../src/storage/services/storage-manager.service.ts","../../src/storage/services/storage.service.ts","../../src/storage/controllers/storage.controller.ts","../../src/storage/errors/disk-not-configured.error.ts","../../src/storage/errors/file-not-found.error.ts","../../src/storage/errors/file-too-large.error.ts","../../src/storage/errors/invalid-disk.error.ts","../../src/storage/errors/invalid-file-type.error.ts","../../src/storage/errors/presigned-url-invalid-expiry.error.ts","../../src/storage/errors/r2-binding-not-found.error.ts","../../src/storage/errors/r2-presigned-url-secret-missing.error.ts","../../src/storage/errors/storage-response-body-missing.error.ts"],"mappings":";;;;;;;;AAoBA;KAFY,oBAAA,GAAuB,aAAA;AAAA,cAQtB,aAAA;EAaa;;;;;;;;;;;;EAAA,OAAjB,OAAA,CAAQ,OAAA,EAAS,oBAAA,GAAuB,aAAA;EA0BC;;;;;;;;AC7DlD;;;;;;;;;ED6DkD,OAAzC,YAAA,CAAa,OAAA,EAAS,kBAAA,CAAmB,oBAAA,IAAwB,aAAA;AAAA;;;;;;;cC7D7D,cAAA;EAAA;;;;;;;;ADcb;;;cEHa,qBAAA;EAAA,iBAOQ,OAAA;EAAA,iBAEA,GAAA;EAAA,iBARF,SAAA;EAAA,iBACA,gBAAA;EAAA,iBACA,WAAA;cAIE,OAAA,EAAS,aAAA,EAET,GAAA,EAAK,UAAA;EFyCwB;;;EAAA,QEjCxC,qBAAA;EFiC6E;;;;;;EErB/E,WAAA,CAAY,QAAA,WAAmB,OAAA,CAAQ,gBAAA;EFqBG;;;;;;EAAA,QEmBlC,cAAA;;ADhFhB;;;;EC8FE,aAAA,CAAc,QAAA,WAAmB,YAAA;;;;;;EAajC,OAAA,CAAQ,QAAA;;AAjGV;;;EAyGE,iBAAA,CAAA;AAAA;;;;AFrGF;;;;;AAEA;;;;;;;;cGOa,cAAA;EAAA,mBAGU,cAAA,EAAgB,qBAAA;EAAA,mBAEhB,OAAA,EAAS,aAAA;cAFT,cAAA,EAAgB,qBAAA,EAEhB,OAAA,EAAS,aAAA;EHOf;;;;;;;;EGIT,MAAA,CACJ,IAAA,EAAM,8BAAA,EACN,YAAA,UACA,OAAA,EAAS,aAAA,EACT,IAAA,YACC,OAAA,CAAQ,YAAA;;;;AF5Cb;;;EE0DQ,QAAA,CAAS,YAAA,UAAsB,IAAA,YAAgB,OAAA,CAAQ,cAAA;;;;;;EAavD,MAAA,CAAO,YAAA,UAAsB,IAAA,YAAgB,OAAA;;;AD7DrD;;;;EC2EQ,MAAA,CAAO,YAAA,UAAsB,IAAA,YAAgB,OAAA;ED7CN;;;;;;;EC4DvC,uBAAA,CACJ,YAAA,UACA,SAAA,WACA,IAAA,YACC,OAAA,CAAQ,kBAAA;ED3FM;;;;;;;ECsGX,qBAAA,CACJ,YAAA,UACA,SAAA,WACA,IAAA,YACC,OAAA,CAAQ,kBAAA;ED/EL;;;;;;;EC0FA,qBAAA,CACJ,YAAA,UACA,SAAA,WACA,IAAA,YACC,OAAA,CAAQ,kBAAA;ED3BX;;;;;;;;EAAA,UCuCgB,eAAA,CACd,YAAA,UACA,MAAA,qCACA,SAAA,WACA,IAAA,YACC,OAAA,CAAQ,kBAAA;EAhIc;;;;;EAAA,UA8If,WAAA,CAAY,IAAA;EA7Hd;;;;;;EAAA,UA6IE,aAAA,CAAc,YAAA,UAAsB,QAAA;EAhGK;;;;;;;EAAA,UAoHzC,2BAAA,CAA4B,IAAA;EAlDnC;;;;;EAAA,UAmEO,iBAAA,CAAkB,SAAA;EA0ClB;;;;EAxBV,iBAAA,CAAA;EAhN8B;;;;;;;;;;;;;;;EAmOxB,aAAA,CACJ,IAAA,EAAM,8BAAA,EACN,YAAA,UACA,OAAA,EAAS,IAAA,CAAK,aAAA;IAA2B,IAAA;EAAA,GACzC,IAAA,YACC,OAAA,CAAQ,YAAA;AAAA;;;;;;AHtPb;;;;;AAEA;;;;cIMa,iBAAA;EAAA,iBAGQ,OAAA;cAAA,OAAA,EAAS,cAAA;EAItB,QAAA,CAAS,GAAA,EAAK,aAAA,GAAgB,OAAA,CAAQ,QAAA;EAoBtC,MAAA,CAAO,GAAA,EAAK,aAAA,GAAgB,OAAA,CAAQ,QAAA;EAiBpC,OAAA,CAAQ,GAAA,EAAK,aAAA,GAAgB,OAAA,CAAQ,QAAA;AAAA;;;cCpEhC,sBAAA,SAA+B,gBAAA;cAC9B,IAAA;AAAA;;;cCAD,iBAAA,SAA0B,gBAAA;cACzB,IAAA;AAAA;;;cCDD,iBAAA,SAA0B,gBAAA;cACzB,IAAA,UAAc,OAAA;AAAA;;;cCDf,gBAAA,SAAyB,gBAAA;cACxB,IAAA;AAAA;;;cCDD,oBAAA,SAA6B,gBAAA;cAC5B,QAAA;AAAA;;;cCDD,8BAAA,SAAuC,gBAAA;cACtC,SAAA,UAAmB,GAAA,UAAa,GAAA;AAAA;;;cCFjC,sBAAA,SAA+B,gBAAA;cAC9B,OAAA;AAAA;;;cCDD,gCAAA,SAAyC,gBAAA;EAAA,WAAA,CAAA;AAAA;;;cCCzC,+BAAA,SAAwC,gBAAA;cACvC,IAAA;AAAA"}
@@ -1,3 +1,3 @@
1
- import { a as InvalidDiskError, c as DiskNotConfiguredError, i as InvalidFileTypeError, n as StorageProviderNotSupportedError, o as FileTooLargeError, r as PresignedUrlInvalidExpiryError, s as FileNotFoundError, t as StorageResponseBodyMissingError } from "../errors-B7hCnXgB.mjs";
2
- import { a as deleteFileInputSchema, c as StorageManagerService, i as fileExistsInputSchema, l as STORAGE_TOKENS, n as getPresignedUrlInputSchema, o as StorageModule, r as presignedUrlResultSchema, s as StorageService, t as uploadResultSchema } from "../storage-CJ-QOwNv.mjs";
3
- export { DiskNotConfiguredError, FileNotFoundError, FileTooLargeError, InvalidDiskError, InvalidFileTypeError, PresignedUrlInvalidExpiryError, STORAGE_TOKENS, StorageManagerService, StorageModule, StorageProviderNotSupportedError, StorageResponseBodyMissingError, StorageService, deleteFileInputSchema, fileExistsInputSchema, getPresignedUrlInputSchema, presignedUrlResultSchema, uploadResultSchema };
1
+ import { a as InvalidFileTypeError, c as FileNotFoundError, i as PresignedUrlInvalidExpiryError, l as DiskNotConfiguredError, n as R2PresignedUrlSecretMissingError, o as InvalidDiskError, r as R2BindingNotFoundError, s as FileTooLargeError, t as StorageResponseBodyMissingError } from "../errors-BUyUfr2Z.mjs";
2
+ import { a as deleteFileInputSchema, c as StorageService, i as fileExistsInputSchema, l as StorageManagerService, n as getPresignedUrlInputSchema, o as StorageController, r as presignedUrlResultSchema, s as StorageModule, t as uploadResultSchema, u as STORAGE_TOKENS } from "../storage-P6X4h9So.mjs";
3
+ export { DiskNotConfiguredError, FileNotFoundError, FileTooLargeError, InvalidDiskError, InvalidFileTypeError, PresignedUrlInvalidExpiryError, R2BindingNotFoundError, R2PresignedUrlSecretMissingError, STORAGE_TOKENS, StorageController, StorageManagerService, StorageModule, StorageResponseBodyMissingError, StorageService, deleteFileInputSchema, fileExistsInputSchema, getPresignedUrlInputSchema, presignedUrlResultSchema, uploadResultSchema };
@@ -1,6 +1,7 @@
1
- import { _ as StorageEntry, f as DownloadResult, i as UploadResult, n as StreamingBlobPayloadInputTypes, r as UploadOptions, s as PresignedUrlResult, t as IStorageProvider } from "../../storage-provider.interface-YRtyYBxV.mjs";
1
+ import { t as StratalEnv } from "../../env-CamWD-U1.mjs";
2
+ import { _ as StorageEntry, f as DownloadResult, i as UploadResult, n as StreamingBlobPayloadInputTypes, r as UploadOptions, s as PresignedUrlResult, t as IStorageProvider, v as StorageRouteConfig } from "../../storage-provider.interface-CC1nniHk.mjs";
2
3
 
3
- //#region src/storage/providers/s3-multipart-provider.interface.d.ts
4
+ //#region src/storage/providers/multipart-provider.interface.d.ts
4
5
  /**
5
6
  * Options for creating a multipart upload
6
7
  */
@@ -9,16 +10,14 @@ interface CreateMultipartOptions {
9
10
  contentType?: string;
10
11
  /** Cache control header */
11
12
  cacheControl?: string;
12
- /** Custom S3 metadata */
13
+ /** Custom metadata */
13
14
  metadata?: Record<string, string>;
14
- /** S3 object tagging (key=value format) */
15
- tagging?: string;
16
15
  }
17
16
  /**
18
17
  * Result of creating a multipart upload
19
18
  */
20
19
  interface CreateMultipartResult {
21
- /** S3 upload ID for subsequent part uploads */
20
+ /** Upload ID for subsequent part uploads */
22
21
  uploadId: string;
23
22
  /** Object key */
24
23
  key: string;
@@ -27,7 +26,7 @@ interface CreateMultipartResult {
27
26
  * Result of uploading a part
28
27
  */
29
28
  interface UploadPartResult {
30
- /** ETag returned by S3 */
29
+ /** ETag returned by the storage provider */
31
30
  etag: string;
32
31
  /** Part number */
33
32
  partNumber: number;
@@ -45,7 +44,7 @@ interface CompletedPart {
45
44
  * Result of completing a multipart upload
46
45
  */
47
46
  interface CompleteMultipartResult {
48
- /** S3 location URL */
47
+ /** Location URL */
49
48
  location?: string;
50
49
  /** Object key */
51
50
  key: string;
@@ -121,13 +120,12 @@ interface DeleteObjectsResult {
121
120
  }[];
122
121
  }
123
122
  /**
124
- * S3-specific storage provider interface with multipart upload support
123
+ * Storage provider interface with multipart upload support
125
124
  *
126
- * Extends the generic IStorageProvider with S3 multipart upload operations.
127
- * This interface is S3-specific - other providers (GCS, local) would have
128
- * their own resumable upload interfaces if needed.
125
+ * Extends the generic IStorageProvider with multipart upload operations
126
+ * needed by resumable upload protocols like TUS.
129
127
  */
130
- interface IS3MultipartProvider extends IStorageProvider {
128
+ interface IMultipartProvider extends IStorageProvider {
131
129
  /**
132
130
  * Create a multipart upload
133
131
  * @param key - Object key
@@ -160,6 +158,7 @@ interface IS3MultipartProvider extends IStorageProvider {
160
158
  abortMultipartUpload(key: string, uploadId: string): Promise<void>;
161
159
  /**
162
160
  * List parts of a multipart upload
161
+ * Requires s3Compat configuration on the storage entry
163
162
  * @param key - Object key
164
163
  * @param uploadId - Upload ID
165
164
  * @param partNumberMarker - Optional marker for pagination
@@ -168,6 +167,7 @@ interface IS3MultipartProvider extends IStorageProvider {
168
167
  listParts(key: string, uploadId: string, partNumberMarker?: string): Promise<ListPartsResult>;
169
168
  /**
170
169
  * List all in-progress multipart uploads
170
+ * Requires s3Compat configuration on the storage entry
171
171
  * @param keyMarker - Optional key marker for pagination
172
172
  * @param uploadIdMarker - Optional upload ID marker for pagination
173
173
  * @returns List of uploads
@@ -188,86 +188,46 @@ interface IS3MultipartProvider extends IStorageProvider {
188
188
  */
189
189
  deleteObjects(keys: string[]): Promise<DeleteObjectsResult>;
190
190
  /**
191
- * Get the bucket name
192
- * @returns Bucket name from configuration
191
+ * Get the bucket/binding name
192
+ * @returns Bucket or binding name from configuration
193
193
  */
194
194
  getBucket(): string;
195
195
  }
196
196
  //#endregion
197
- //#region src/storage/providers/s3-storage.provider.d.ts
197
+ //#region src/storage/providers/r2-storage.provider.d.ts
198
198
  /**
199
- * S3 Storage Provider
200
- * Implements storage operations using AWS SDK for S3-compatible storage
201
- * Works with AWS S3, Cloudflare R2, MinIO, and other S3-compatible services
199
+ * R2 Storage Provider
200
+ * Implements storage operations using native Cloudflare R2 bindings
202
201
  *
203
- * Implements IS3MultipartProvider for multipart upload support needed by TUS
202
+ * Implements IMultipartProvider for multipart upload support needed by TUS.
203
+ * Tracks multipart upload metadata using companion R2 objects so that
204
+ * listParts/listMultipartUploads work in all environments (local, test, prod).
204
205
  */
205
- declare class S3StorageProvider implements IS3MultipartProvider {
206
- private readonly client;
207
- private readonly presigningClient;
206
+ declare class R2StorageProvider implements IMultipartProvider {
207
+ private readonly config;
208
208
  private readonly bucket;
209
- private readonly disk;
210
- constructor(config: StorageEntry);
209
+ private readonly env;
210
+ private readonly routeBasePath;
211
+ constructor(config: StorageEntry, bucket: R2Bucket, env: StratalEnv, routeConfig?: StorageRouteConfig);
211
212
  upload(body: StreamingBlobPayloadInputTypes, path: string, options: UploadOptions): Promise<UploadResult>;
212
213
  download(path: string): Promise<DownloadResult>;
213
214
  delete(path: string): Promise<void>;
214
215
  exists(path: string): Promise<boolean>;
215
216
  getPresignedUrl(path: string, method: 'GET' | 'PUT' | 'DELETE' | 'HEAD', expiresIn: number): Promise<PresignedUrlResult>;
216
- /**
217
- * Chunked upload for streaming data without known size
218
- * Uses @aws-sdk/lib-storage for multipart upload handling
219
- *
220
- * Benefits:
221
- * - Handles unknown Content-Length automatically
222
- * - Automatic retry on transient failures
223
- * - Cleanup of partial uploads on error
224
- * - Works with S3-compatible storage (R2, MinIO, RustFS)
225
- *
226
- * @param body - Content to upload (stream or buffer)
227
- * @param path - Full path including disk root
228
- * @param options - Upload options (mimeType required, size optional)
229
- * @returns Upload result with metadata
230
- */
231
217
  chunkedUpload(body: StreamingBlobPayloadInputTypes, path: string, options: Omit<UploadOptions, 'size'> & {
232
218
  size?: number;
233
219
  }): Promise<UploadResult>;
234
- /**
235
- * Get the bucket name
236
- */
237
220
  getBucket(): string;
238
- /**
239
- * Get object metadata without downloading the body
240
- */
241
221
  headObject(key: string): Promise<HeadObjectResult | null>;
242
- /**
243
- * Delete multiple objects in a single request
244
- */
245
222
  deleteObjects(keys: string[]): Promise<DeleteObjectsResult>;
246
- /**
247
- * Create a multipart upload
248
- */
249
223
  createMultipartUpload(key: string, options?: CreateMultipartOptions): Promise<CreateMultipartResult>;
250
- /**
251
- * Upload a part to an existing multipart upload
252
- */
253
224
  uploadPart(key: string, uploadId: string, partNumber: number, body: Uint8Array): Promise<UploadPartResult>;
254
- /**
255
- * Complete a multipart upload
256
- */
257
225
  completeMultipartUpload(key: string, uploadId: string, parts: CompletedPart[]): Promise<CompleteMultipartResult>;
258
- /**
259
- * Abort a multipart upload
260
- */
261
226
  abortMultipartUpload(key: string, uploadId: string): Promise<void>;
262
- /**
263
- * List parts of a multipart upload
264
- */
265
- listParts(key: string, uploadId: string, partNumberMarker?: string): Promise<ListPartsResult>;
266
- /**
267
- * List all in-progress multipart uploads
268
- */
269
- listMultipartUploads(keyMarker?: string, uploadIdMarker?: string): Promise<ListMultipartUploadsResult>;
227
+ listParts(_key: string, uploadId: string, partNumberMarker?: string): Promise<ListPartsResult>;
228
+ listMultipartUploads(keyMarker?: string, _uploadIdMarker?: string): Promise<ListMultipartUploadsResult>;
229
+ private cleanupTrackingObjects;
270
230
  }
271
231
  //#endregion
272
- export { CompleteMultipartResult, CompletedPart, CreateMultipartOptions, CreateMultipartResult, DeleteObjectsResult, HeadObjectResult, IS3MultipartProvider, IStorageProvider, ListMultipartUploadsResult, ListPartsResult, MultipartUploadInfo, PartInfo, S3StorageProvider, StreamingBlobPayloadInputTypes, UploadPartResult };
232
+ export { CompleteMultipartResult, CompletedPart, CreateMultipartOptions, CreateMultipartResult, DeleteObjectsResult, HeadObjectResult, IMultipartProvider, IStorageProvider, ListMultipartUploadsResult, ListPartsResult, MultipartUploadInfo, PartInfo, R2StorageProvider, StreamingBlobPayloadInputTypes, UploadPartResult };
273
233
  //# sourceMappingURL=index.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","names":[],"sources":["../../../src/storage/providers/s3-multipart-provider.interface.ts","../../../src/storage/providers/s3-storage.provider.ts"],"mappings":"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"}
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../../../src/storage/providers/multipart-provider.interface.ts","../../../src/storage/providers/r2-storage.provider.ts"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- import { t as S3StorageProvider } from "../../s3-storage.provider-BAhHDMI3.mjs";
2
- export { S3StorageProvider };
1
+ import { t as R2StorageProvider } from "../../r2-storage.provider-LdzK9tfG.mjs";
2
+ export { R2StorageProvider };
@@ -1,8 +1,10 @@
1
- import { A as Scope } from "./errors--RBIvDXr.mjs";
2
- import { a as __decorate, o as __decorateParam, p as Transient, s as __decorateMetadata } from "./logger-c0ftIK4G.mjs";
3
- import { S as Module } from "./module-C3YZ-kZN.mjs";
4
- import { a as withI18n, i as z } from "./validation-B4bePOa_.mjs";
5
- import { a as InvalidDiskError, c as DiskNotConfiguredError, n as StorageProviderNotSupportedError, r as PresignedUrlInvalidExpiryError } from "./errors-B7hCnXgB.mjs";
1
+ import { A as Scope } from "./errors-B4pYgYON.mjs";
2
+ import { a as __decorate, f as DI_TOKENS, o as __decorateParam, p as Transient, s as __decorateMetadata } from "./logger-V6Ms3QnQ.mjs";
3
+ import { C as Module } from "./module-qGE_1duv.mjs";
4
+ import { a as withI18n, i as z } from "./validation-Dbg3ehdP.mjs";
5
+ import { t as Controller } from "./controller.decorator-LZY9aHYG.mjs";
6
+ import { n as Delete, o as Put, r as Get } from "./http-method.decorator-BT3ufnz8.mjs";
7
+ import { c as FileNotFoundError, i as PresignedUrlInvalidExpiryError, l as DiskNotConfiguredError, o as InvalidDiskError, r as R2BindingNotFoundError } from "./errors-BUyUfr2Z.mjs";
6
8
  import { inject } from "tsyringe";
7
9
  //#region src/storage/storage.tokens.ts
8
10
  /**
@@ -20,8 +22,9 @@ let StorageManagerService = class StorageManagerService {
20
22
  providers = /* @__PURE__ */ new Map();
21
23
  creationPromises = /* @__PURE__ */ new Map();
22
24
  diskConfigs = /* @__PURE__ */ new Map();
23
- constructor(options) {
25
+ constructor(options, env) {
24
26
  this.options = options;
27
+ this.env = env;
25
28
  this.initializeDiskConfigs();
26
29
  }
27
30
  /**
@@ -55,20 +58,16 @@ let StorageManagerService = class StorageManagerService {
55
58
  return promise;
56
59
  }
57
60
  /**
58
- * Create a provider instance based on configuration
59
- * Dynamically imports S3StorageProvider to avoid loading AWS SDK at module evaluation time
61
+ * Create an R2 provider instance
62
+ * Dynamically imports R2StorageProvider to support code splitting
60
63
  * @param config - Storage entry configuration
61
64
  * @returns Storage provider instance
62
65
  */
63
66
  async createProvider(config) {
64
- switch (config.provider) {
65
- case "s3": {
66
- const { S3StorageProvider } = await import("./s3-storage.provider-BAhHDMI3.mjs").then((n) => n.n);
67
- return new S3StorageProvider(config);
68
- }
69
- case "gcs": throw new StorageProviderNotSupportedError(config.provider);
70
- default: throw new StorageProviderNotSupportedError(config.provider);
71
- }
67
+ const { R2StorageProvider } = await import("./r2-storage.provider-LdzK9tfG.mjs").then((n) => n.n);
68
+ const bucket = this.env[config.binding];
69
+ if (!bucket) throw new R2BindingNotFoundError(config.binding);
70
+ return new R2StorageProvider(config, bucket, this.env, this.options.route);
72
71
  }
73
72
  /**
74
73
  * Get disk configuration
@@ -99,7 +98,8 @@ let StorageManagerService = class StorageManagerService {
99
98
  StorageManagerService = __decorate([
100
99
  Transient(STORAGE_TOKENS.StorageManager),
101
100
  __decorateParam(0, inject(STORAGE_TOKENS.Options)),
102
- __decorateMetadata("design:paramtypes", [Object])
101
+ __decorateParam(1, inject(DI_TOKENS.CloudflareEnv)),
102
+ __decorateMetadata("design:paramtypes", [Object, Object])
103
103
  ], StorageManagerService);
104
104
  //#endregion
105
105
  //#region src/storage/services/storage.service.ts
@@ -290,7 +290,7 @@ StorageService = __decorate([
290
290
  //#region src/storage/storage.module.ts
291
291
  /**
292
292
  * Storage Module
293
- * Provides file storage capabilities using AWS S3
293
+ * Provides file storage capabilities using Cloudflare R2
294
294
  * Supports multiple disk configurations with dynamic path templates
295
295
  */
296
296
  var _StorageModule;
@@ -298,13 +298,10 @@ let StorageModule = _StorageModule = class StorageModule {
298
298
  /**
299
299
  * Configure StorageModule with static options
300
300
  *
301
- * @param options - Storage configuration options
302
- * @returns Dynamic module with storage infrastructure
303
- *
304
301
  * @example
305
302
  * ```typescript
306
303
  * StorageModule.forRoot({
307
- * storage: [{ disk: 'uploads', provider: 's3', ... }],
304
+ * storage: [{ disk: 'uploads', binding: 'MY_BUCKET', root: 'uploads' }],
308
305
  * defaultStorageDisk: 'uploads',
309
306
  * presignedUrl: { defaultExpiry: 3600, maxExpiry: 86400 }
310
307
  * })
@@ -324,9 +321,6 @@ let StorageModule = _StorageModule = class StorageModule {
324
321
  *
325
322
  * Use when configuration depends on other services.
326
323
  *
327
- * @param options - Async configuration with factory and inject tokens
328
- * @returns Dynamic module with storage infrastructure
329
- *
330
324
  * @example
331
325
  * ```typescript
332
326
  * StorageModule.forRootAsync({
@@ -359,6 +353,86 @@ StorageModule = _StorageModule = __decorate([Module({ providers: [{
359
353
  useClass: StorageService
360
354
  }] })], StorageModule);
361
355
  //#endregion
356
+ //#region src/storage/controllers/storage.controller.ts
357
+ const diskParam = z.object({ disk: z.string() });
358
+ let StorageController = class StorageController {
359
+ constructor(storage) {
360
+ this.storage = storage;
361
+ }
362
+ async download(ctx) {
363
+ const disk = ctx.param("disk");
364
+ const path = extractWildcardPath(ctx);
365
+ const result = await this.storage.download(path, disk);
366
+ const stream = result.toStream();
367
+ if (!stream) throw new FileNotFoundError(path);
368
+ return new Response(stream, { headers: {
369
+ "Content-Type": result.contentType,
370
+ "Content-Length": String(result.size),
371
+ "Content-Disposition": "inline"
372
+ } });
373
+ }
374
+ async upload(ctx) {
375
+ const disk = ctx.param("disk");
376
+ const path = extractWildcardPath(ctx);
377
+ const body = ctx.c.req.raw.body;
378
+ const contentType = ctx.header("content-type") ?? "application/octet-stream";
379
+ const contentLength = ctx.header("content-length");
380
+ await this.storage.upload(body, path, {
381
+ mimeType: contentType,
382
+ size: contentLength ? parseInt(contentLength, 10) : 0
383
+ }, disk);
384
+ return ctx.json({
385
+ path,
386
+ disk
387
+ }, 200);
388
+ }
389
+ async destroy(ctx) {
390
+ const disk = ctx.param("disk");
391
+ const path = extractWildcardPath(ctx);
392
+ await this.storage.delete(path, disk);
393
+ return ctx.c.body(null, 204);
394
+ }
395
+ };
396
+ __decorate([
397
+ Get("/:disk/*", {
398
+ hideFromDocs: true,
399
+ params: diskParam
400
+ }),
401
+ __decorateMetadata("design:type", Function),
402
+ __decorateMetadata("design:paramtypes", [Object]),
403
+ __decorateMetadata("design:returntype", Promise)
404
+ ], StorageController.prototype, "download", null);
405
+ __decorate([
406
+ Put("/:disk/*", {
407
+ hideFromDocs: true,
408
+ params: diskParam
409
+ }),
410
+ __decorateMetadata("design:type", Function),
411
+ __decorateMetadata("design:paramtypes", [Object]),
412
+ __decorateMetadata("design:returntype", Promise)
413
+ ], StorageController.prototype, "upload", null);
414
+ __decorate([
415
+ Delete("/:disk/*", {
416
+ hideFromDocs: true,
417
+ params: diskParam
418
+ }),
419
+ __decorateMetadata("design:type", Function),
420
+ __decorateMetadata("design:paramtypes", [Object]),
421
+ __decorateMetadata("design:returntype", Promise)
422
+ ], StorageController.prototype, "destroy", null);
423
+ StorageController = __decorate([
424
+ Controller("/storage", { hideFromDocs: true }),
425
+ __decorateParam(0, inject(STORAGE_TOKENS.StorageService)),
426
+ __decorateMetadata("design:paramtypes", [Object])
427
+ ], StorageController);
428
+ /**
429
+ * Extract the wildcard path from the Hono context.
430
+ * Hono stores wildcard params under the key matching the path pattern.
431
+ */
432
+ function extractWildcardPath(ctx) {
433
+ return new URL(ctx.c.req.url).pathname.split("/").slice(3).join("/");
434
+ }
435
+ //#endregion
362
436
  //#region src/storage/contracts/delete-file.input.ts
363
437
  const deleteFileInputSchema = z.object({
364
438
  path: z.string().min(1, withI18n("zodI18n.errors.custom.filePathRequired")),
@@ -405,6 +479,6 @@ const uploadResultSchema = z.object({
405
479
  uploadedAt: z.date()
406
480
  });
407
481
  //#endregion
408
- export { deleteFileInputSchema as a, StorageManagerService as c, fileExistsInputSchema as i, STORAGE_TOKENS as l, getPresignedUrlInputSchema as n, StorageModule as o, presignedUrlResultSchema as r, StorageService as s, uploadResultSchema as t };
482
+ export { deleteFileInputSchema as a, StorageService as c, fileExistsInputSchema as i, StorageManagerService as l, getPresignedUrlInputSchema as n, StorageController as o, presignedUrlResultSchema as r, StorageModule as s, uploadResultSchema as t, STORAGE_TOKENS as u };
409
483
 
410
- //# sourceMappingURL=storage-CJ-QOwNv.mjs.map
484
+ //# sourceMappingURL=storage-P6X4h9So.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"storage-P6X4h9So.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/controllers/storage.controller.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 { DI_TOKENS } from '../../di/tokens'\nimport { type StratalEnv } from '../../env'\nimport { DiskNotConfiguredError, R2BindingNotFoundError } 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 R2 providers\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 @inject(DI_TOKENS.CloudflareEnv)\n private readonly env: StratalEnv\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 an R2 provider instance\n * Dynamically imports R2StorageProvider to support code splitting\n * @param config - Storage entry configuration\n * @returns Storage provider instance\n */\n private async createProvider(config: StorageEntry): Promise<IStorageProvider> {\n const { R2StorageProvider } = await import('../providers/r2-storage.provider')\n const bucket = this.env[config.binding as keyof StratalEnv] as unknown as R2Bucket | undefined\n if (!bucket) {\n throw new R2BindingNotFoundError(config.binding)\n }\n return new R2StorageProvider(config, bucket, this.env, this.options.route)\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 { type 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 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 Cloudflare R2\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: [\n { provide: STORAGE_TOKENS.StorageManager, useClass: StorageManagerService, scope: Scope.Singleton },\n { provide: STORAGE_TOKENS.StorageService, useClass: StorageService },\n ],\n})\nexport class StorageModule {\n /**\n * Configure StorageModule with static options\n *\n * @example\n * ```typescript\n * StorageModule.forRoot({\n * storage: [{ disk: 'uploads', binding: 'MY_BUCKET', root: 'uploads' }],\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 * @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 { inject } from 'tsyringe'\nimport { z } from '../../i18n/validation'\nimport { Controller } from '../../router/decorators/controller.decorator'\nimport { Delete, Get, Put } from '../../router/decorators/http-method.decorator'\nimport { type RouterContext } from '../../router/router-context'\nimport { FileNotFoundError } from '../errors'\nimport type { StorageService } from '../services/storage.service'\nimport { STORAGE_TOKENS } from '../storage.tokens'\n\nconst diskParam = z.object({\n disk: z.string(),\n})\n\n/**\n * Storage Controller\n *\n * Auto-registered controller that proxies R2 operations behind signed URLs.\n * Signature verification is applied via VerifySignatureMiddleware on the module's\n * configureRoutes() method.\n *\n * Routes:\n * - GET /storage/:disk/* → download file\n * - PUT /storage/:disk/* → upload file\n * - DELETE /storage/:disk/* → delete file\n */\n@Controller('/storage', { hideFromDocs: true })\nexport class StorageController {\n constructor(\n @inject(STORAGE_TOKENS.StorageService)\n private readonly storage: StorageService\n ) {}\n\n @Get('/:disk/*', { hideFromDocs: true, params: diskParam })\n async download(ctx: RouterContext): Promise<Response> {\n const disk = ctx.param('disk')\n const path = extractWildcardPath(ctx)\n const result = await this.storage.download(path, disk)\n\n const stream = result.toStream()\n if (!stream) {\n throw new FileNotFoundError(path)\n }\n\n return new Response(stream, {\n headers: {\n 'Content-Type': result.contentType,\n 'Content-Length': String(result.size),\n 'Content-Disposition': 'inline',\n },\n })\n }\n\n @Put('/:disk/*', { hideFromDocs: true, params: diskParam })\n async upload(ctx: RouterContext): Promise<Response> {\n const disk = ctx.param('disk')\n const path = extractWildcardPath(ctx)\n\n const body = ctx.c.req.raw.body\n const contentType = ctx.header('content-type') ?? 'application/octet-stream'\n const contentLength = ctx.header('content-length')\n\n await this.storage.upload(body, path, {\n mimeType: contentType,\n size: contentLength ? parseInt(contentLength, 10) : 0,\n }, disk)\n\n return ctx.json({ path, disk }, 200)\n }\n\n @Delete('/:disk/*', { hideFromDocs: true, params: diskParam })\n async destroy(ctx: RouterContext): Promise<Response> {\n const disk = ctx.param('disk')\n const path = extractWildcardPath(ctx)\n\n await this.storage.delete(path, disk)\n\n return ctx.c.body(null, 204)\n }\n}\n\n/**\n * Extract the wildcard path from the Hono context.\n * Hono stores wildcard params under the key matching the path pattern.\n */\nfunction extractWildcardPath(ctx: RouterContext): string {\n // Hono exposes wildcard capture as the raw path after the matched prefix\n const url = new URL(ctx.c.req.url)\n const fullPath = url.pathname\n // Remove /storage/:disk/ prefix to get the file path\n const parts = fullPath.split('/')\n // ['', 'storage', 'disk', ...rest]\n return parts.slice(3).join('/')\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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"}