express-storage 2.0.2 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (177) hide show
  1. package/README.md +366 -34
  2. package/dist/cjs/config/index.d.ts +10 -0
  3. package/dist/cjs/config/index.d.ts.map +1 -0
  4. package/dist/cjs/config/index.js +19 -0
  5. package/dist/cjs/config/index.js.map +1 -0
  6. package/dist/cjs/drivers/azure.driver.d.ts +73 -0
  7. package/dist/cjs/drivers/azure.driver.d.ts.map +1 -0
  8. package/dist/cjs/drivers/azure.driver.js +390 -0
  9. package/dist/cjs/drivers/azure.driver.js.map +1 -0
  10. package/dist/cjs/drivers/base.driver.d.ts +136 -0
  11. package/dist/cjs/drivers/base.driver.d.ts.map +1 -0
  12. package/dist/cjs/drivers/base.driver.js +357 -0
  13. package/dist/cjs/drivers/base.driver.js.map +1 -0
  14. package/dist/{drivers → cjs/drivers}/gcs.driver.d.ts +20 -38
  15. package/dist/cjs/drivers/gcs.driver.d.ts.map +1 -0
  16. package/dist/cjs/drivers/gcs.driver.js +343 -0
  17. package/dist/cjs/drivers/gcs.driver.js.map +1 -0
  18. package/dist/cjs/drivers/index.d.ts +15 -0
  19. package/dist/cjs/drivers/index.d.ts.map +1 -0
  20. package/dist/cjs/drivers/index.js +26 -0
  21. package/dist/cjs/drivers/index.js.map +1 -0
  22. package/dist/cjs/drivers/local.driver.d.ts +86 -0
  23. package/dist/cjs/drivers/local.driver.d.ts.map +1 -0
  24. package/dist/cjs/drivers/local.driver.js +556 -0
  25. package/dist/cjs/drivers/local.driver.js.map +1 -0
  26. package/dist/{drivers → cjs/drivers}/s3.driver.d.ts +19 -39
  27. package/dist/cjs/drivers/s3.driver.d.ts.map +1 -0
  28. package/dist/cjs/drivers/s3.driver.js +400 -0
  29. package/dist/cjs/drivers/s3.driver.js.map +1 -0
  30. package/dist/cjs/factory/driver.factory.d.ts +43 -0
  31. package/dist/cjs/factory/driver.factory.d.ts.map +1 -0
  32. package/dist/cjs/factory/driver.factory.js +101 -0
  33. package/dist/cjs/factory/driver.factory.js.map +1 -0
  34. package/dist/cjs/index.d.ts +26 -0
  35. package/dist/cjs/index.d.ts.map +1 -0
  36. package/dist/cjs/index.js +31 -0
  37. package/dist/cjs/index.js.map +1 -0
  38. package/dist/cjs/package.json +1 -0
  39. package/dist/cjs/storage-manager.d.ts +210 -0
  40. package/dist/cjs/storage-manager.d.ts.map +1 -0
  41. package/dist/cjs/storage-manager.js +649 -0
  42. package/dist/cjs/storage-manager.js.map +1 -0
  43. package/dist/cjs/types/storage.types.d.ts +438 -0
  44. package/dist/cjs/types/storage.types.d.ts.map +1 -0
  45. package/dist/cjs/types/storage.types.js +3 -0
  46. package/dist/cjs/types/storage.types.js.map +1 -0
  47. package/dist/cjs/utils/config.utils.d.ts.map +1 -0
  48. package/dist/cjs/utils/config.utils.js +213 -0
  49. package/dist/cjs/utils/config.utils.js.map +1 -0
  50. package/dist/{utils → cjs/utils}/file.utils.d.ts +62 -8
  51. package/dist/cjs/utils/file.utils.d.ts.map +1 -0
  52. package/dist/cjs/utils/file.utils.js +464 -0
  53. package/dist/cjs/utils/file.utils.js.map +1 -0
  54. package/dist/cjs/utils/index.d.ts +12 -0
  55. package/dist/cjs/utils/index.d.ts.map +1 -0
  56. package/dist/cjs/utils/index.js +36 -0
  57. package/dist/cjs/utils/index.js.map +1 -0
  58. package/dist/cjs/utils/rate-limiter.d.ts +40 -0
  59. package/dist/cjs/utils/rate-limiter.d.ts.map +1 -0
  60. package/dist/cjs/utils/rate-limiter.js +87 -0
  61. package/dist/cjs/utils/rate-limiter.js.map +1 -0
  62. package/dist/esm/config/index.d.ts +10 -0
  63. package/dist/esm/config/index.d.ts.map +1 -0
  64. package/dist/esm/config/index.js +10 -0
  65. package/dist/esm/config/index.js.map +1 -0
  66. package/dist/esm/drivers/azure.driver.d.ts +73 -0
  67. package/dist/esm/drivers/azure.driver.d.ts.map +1 -0
  68. package/dist/esm/drivers/azure.driver.js +353 -0
  69. package/dist/esm/drivers/azure.driver.js.map +1 -0
  70. package/dist/esm/drivers/base.driver.d.ts +136 -0
  71. package/dist/esm/drivers/base.driver.d.ts.map +1 -0
  72. package/dist/esm/drivers/base.driver.js +350 -0
  73. package/dist/esm/drivers/base.driver.js.map +1 -0
  74. package/dist/esm/drivers/gcs.driver.d.ts +68 -0
  75. package/dist/esm/drivers/gcs.driver.d.ts.map +1 -0
  76. package/dist/esm/drivers/gcs.driver.js +306 -0
  77. package/dist/esm/drivers/gcs.driver.js.map +1 -0
  78. package/dist/esm/drivers/index.d.ts +15 -0
  79. package/dist/esm/drivers/index.d.ts.map +1 -0
  80. package/dist/esm/drivers/index.js +15 -0
  81. package/dist/esm/drivers/index.js.map +1 -0
  82. package/dist/esm/drivers/local.driver.d.ts +86 -0
  83. package/dist/esm/drivers/local.driver.d.ts.map +1 -0
  84. package/dist/esm/drivers/local.driver.js +549 -0
  85. package/dist/esm/drivers/local.driver.js.map +1 -0
  86. package/dist/esm/drivers/s3.driver.d.ts +69 -0
  87. package/dist/esm/drivers/s3.driver.d.ts.map +1 -0
  88. package/dist/esm/drivers/s3.driver.js +363 -0
  89. package/dist/esm/drivers/s3.driver.js.map +1 -0
  90. package/dist/esm/factory/driver.factory.d.ts +43 -0
  91. package/dist/esm/factory/driver.factory.d.ts.map +1 -0
  92. package/dist/esm/factory/driver.factory.js +92 -0
  93. package/dist/esm/factory/driver.factory.js.map +1 -0
  94. package/dist/esm/index.d.ts +26 -0
  95. package/dist/esm/index.d.ts.map +1 -0
  96. package/dist/esm/index.js +26 -0
  97. package/dist/esm/index.js.map +1 -0
  98. package/dist/esm/package.json +1 -0
  99. package/dist/esm/storage-manager.d.ts +210 -0
  100. package/dist/esm/storage-manager.d.ts.map +1 -0
  101. package/dist/esm/storage-manager.js +645 -0
  102. package/dist/esm/storage-manager.js.map +1 -0
  103. package/dist/esm/types/storage.types.d.ts +438 -0
  104. package/dist/esm/types/storage.types.d.ts.map +1 -0
  105. package/dist/esm/types/storage.types.js.map +1 -0
  106. package/dist/esm/utils/config.utils.d.ts +45 -0
  107. package/dist/esm/utils/config.utils.d.ts.map +1 -0
  108. package/dist/esm/utils/config.utils.js.map +1 -0
  109. package/dist/esm/utils/file.utils.d.ts +196 -0
  110. package/dist/esm/utils/file.utils.d.ts.map +1 -0
  111. package/dist/esm/utils/file.utils.js +439 -0
  112. package/dist/esm/utils/file.utils.js.map +1 -0
  113. package/dist/esm/utils/index.d.ts +12 -0
  114. package/dist/esm/utils/index.d.ts.map +1 -0
  115. package/dist/esm/utils/index.js +11 -0
  116. package/dist/esm/utils/index.js.map +1 -0
  117. package/dist/esm/utils/rate-limiter.d.ts +40 -0
  118. package/dist/esm/utils/rate-limiter.d.ts.map +1 -0
  119. package/dist/esm/utils/rate-limiter.js +82 -0
  120. package/dist/esm/utils/rate-limiter.js.map +1 -0
  121. package/package.json +90 -52
  122. package/src/config/index.ts +17 -0
  123. package/src/drivers/azure.driver.ts +434 -0
  124. package/src/drivers/base.driver.ts +436 -0
  125. package/src/drivers/gcs.driver.ts +366 -0
  126. package/src/drivers/index.ts +15 -0
  127. package/src/drivers/local.driver.ts +626 -0
  128. package/src/drivers/s3.driver.ts +459 -0
  129. package/src/factory/driver.factory.ts +101 -0
  130. package/src/index.ts +72 -0
  131. package/src/storage-manager.ts +801 -0
  132. package/src/types/storage.types.ts +561 -0
  133. package/src/utils/config.utils.ts +229 -0
  134. package/src/utils/file.utils.ts +536 -0
  135. package/src/utils/index.ts +35 -0
  136. package/src/utils/rate-limiter.ts +94 -0
  137. package/dist/drivers/azure.driver.d.ts +0 -88
  138. package/dist/drivers/azure.driver.d.ts.map +0 -1
  139. package/dist/drivers/azure.driver.js +0 -391
  140. package/dist/drivers/azure.driver.js.map +0 -1
  141. package/dist/drivers/base.driver.d.ts +0 -170
  142. package/dist/drivers/base.driver.d.ts.map +0 -1
  143. package/dist/drivers/base.driver.js +0 -347
  144. package/dist/drivers/base.driver.js.map +0 -1
  145. package/dist/drivers/gcs.driver.d.ts.map +0 -1
  146. package/dist/drivers/gcs.driver.js +0 -354
  147. package/dist/drivers/gcs.driver.js.map +0 -1
  148. package/dist/drivers/local.driver.d.ts +0 -107
  149. package/dist/drivers/local.driver.d.ts.map +0 -1
  150. package/dist/drivers/local.driver.js +0 -621
  151. package/dist/drivers/local.driver.js.map +0 -1
  152. package/dist/drivers/s3.driver.d.ts.map +0 -1
  153. package/dist/drivers/s3.driver.js +0 -387
  154. package/dist/drivers/s3.driver.js.map +0 -1
  155. package/dist/factory/driver.factory.d.ts +0 -62
  156. package/dist/factory/driver.factory.d.ts.map +0 -1
  157. package/dist/factory/driver.factory.js +0 -177
  158. package/dist/factory/driver.factory.js.map +0 -1
  159. package/dist/index.d.ts +0 -30
  160. package/dist/index.d.ts.map +0 -1
  161. package/dist/index.js +0 -33
  162. package/dist/index.js.map +0 -1
  163. package/dist/storage-manager.d.ts +0 -228
  164. package/dist/storage-manager.d.ts.map +0 -1
  165. package/dist/storage-manager.js +0 -715
  166. package/dist/storage-manager.js.map +0 -1
  167. package/dist/types/storage.types.d.ts +0 -295
  168. package/dist/types/storage.types.d.ts.map +0 -1
  169. package/dist/types/storage.types.js.map +0 -1
  170. package/dist/utils/config.utils.d.ts.map +0 -1
  171. package/dist/utils/config.utils.js.map +0 -1
  172. package/dist/utils/file.utils.d.ts.map +0 -1
  173. package/dist/utils/file.utils.js +0 -278
  174. package/dist/utils/file.utils.js.map +0 -1
  175. /package/dist/{utils → cjs/utils}/config.utils.d.ts +0 -0
  176. /package/dist/{types → esm/types}/storage.types.js +0 -0
  177. /package/dist/{utils → esm/utils}/config.utils.js +0 -0
@@ -0,0 +1,561 @@
1
+ /**
2
+ * The storage drivers you can use.
3
+ *
4
+ * Direct drivers upload through your server.
5
+ * Presigned drivers give you URLs for client-side uploads.
6
+ */
7
+ export type StorageDriver =
8
+ | "s3"
9
+ | "s3-presigned"
10
+ | "gcs"
11
+ | "gcs-presigned"
12
+ | "azure"
13
+ | "azure-presigned"
14
+ | "local";
15
+
16
+ // ---------------------------------------------------------------------------
17
+ // Error codes
18
+ // ---------------------------------------------------------------------------
19
+
20
+ /**
21
+ * Programmatic error codes for every failure case.
22
+ *
23
+ * Use these to branch on specific error conditions without parsing strings:
24
+ * ```typescript
25
+ * if (!result.success) {
26
+ * switch (result.code) {
27
+ * case 'FILE_TOO_LARGE': showSizeError(); break;
28
+ * case 'INVALID_MIME_TYPE': showTypeError(); break;
29
+ * case 'RATE_LIMITED': retryLater(); break;
30
+ * }
31
+ * }
32
+ * ```
33
+ */
34
+ export type StorageErrorCode =
35
+ | "NO_FILE"
36
+ | "FILE_EMPTY"
37
+ | "FILE_TOO_LARGE"
38
+ | "INVALID_MIME_TYPE"
39
+ | "INVALID_EXTENSION"
40
+ | "INVALID_FILENAME"
41
+ | "INVALID_INPUT"
42
+ | "PATH_TRAVERSAL"
43
+ | "FILE_NOT_FOUND"
44
+ | "VALIDATION_FAILED"
45
+ | "RATE_LIMITED"
46
+ | "HOOK_ABORTED"
47
+ | "PRESIGNED_NOT_SUPPORTED"
48
+ | "PROVIDER_ERROR";
49
+
50
+ // ---------------------------------------------------------------------------
51
+ // Result types — discriminated unions for type-safe narrowing
52
+ // ---------------------------------------------------------------------------
53
+
54
+ /**
55
+ * What you get back after uploading a file.
56
+ *
57
+ * Use `result.success` to narrow the type:
58
+ * ```typescript
59
+ * const result = await storage.uploadFile(file);
60
+ * if (result.success) {
61
+ * console.log(result.reference); // TypeScript knows this exists
62
+ * } else {
63
+ * console.log(result.error); // TypeScript knows this exists
64
+ * console.log(result.code); // e.g., 'FILE_TOO_LARGE'
65
+ * }
66
+ * ```
67
+ */
68
+ export type FileUploadResult = FileUploadSuccess | FileUploadError;
69
+
70
+ export interface FileUploadSuccess {
71
+ success: true;
72
+ /** The stored file path — pass this to deleteFile(), getMetadata(), generateViewUrl(), etc. */
73
+ reference: string;
74
+ /** URL to access the file */
75
+ fileUrl: string;
76
+ }
77
+
78
+ export interface FileUploadError {
79
+ success: false;
80
+ /** What went wrong */
81
+ error: string;
82
+ /** Programmatic error code */
83
+ code: StorageErrorCode;
84
+ }
85
+
86
+ /**
87
+ * What you get back after deleting a file.
88
+ */
89
+ export type DeleteResult = DeleteSuccess | DeleteError;
90
+
91
+ export interface DeleteSuccess {
92
+ success: true;
93
+ /** The file reference that was deleted */
94
+ reference: string;
95
+ }
96
+
97
+ export interface DeleteError {
98
+ success: false;
99
+ /** The file reference that failed to delete */
100
+ reference: string;
101
+ /** What went wrong */
102
+ error: string;
103
+ /** Programmatic error code */
104
+ code: StorageErrorCode;
105
+ }
106
+
107
+ /**
108
+ * What you get back when generating presigned URLs.
109
+ */
110
+ /**
111
+ * Driver-level presigned URL result. Drivers only set uploadUrl or viewUrl.
112
+ * StorageManager enriches this into PresignedUploadUrlResult / PresignedViewUrlResult
113
+ * with guaranteed fields like fileName, reference, and expiresIn.
114
+ */
115
+ export type PresignedUrlResult = PresignedUrlSuccess | PresignedUrlError;
116
+
117
+ export interface PresignedUrlSuccess {
118
+ success: true;
119
+ /** URL for uploading (set by generateUploadUrl) */
120
+ uploadUrl?: string;
121
+ /** URL for viewing/downloading (set by generateViewUrl) */
122
+ viewUrl?: string;
123
+ }
124
+
125
+ export interface PresignedUrlError {
126
+ success: false;
127
+ /** What went wrong */
128
+ error: string;
129
+ /** Programmatic error code */
130
+ code: StorageErrorCode;
131
+ }
132
+
133
+ /**
134
+ * Stricter result from StorageManager.generateUploadUrl().
135
+ * On success, fileName, reference, uploadUrl, and expiresIn are guaranteed.
136
+ */
137
+ export type PresignedUploadUrlResult =
138
+ | PresignedUploadUrlSuccess
139
+ | PresignedUrlError;
140
+
141
+ export interface PresignedUploadUrlSuccess extends PresignedUrlSuccess {
142
+ fileName: string;
143
+ reference: string;
144
+ uploadUrl: string;
145
+ expiresIn: number;
146
+ /** Folder path if any (e.g., "users/123/uploads") */
147
+ filePath?: string;
148
+ /** The content type this URL is restricted to */
149
+ contentType?: string;
150
+ /** The file size this URL is restricted to (S3/GCS enforce this) */
151
+ fileSize?: number;
152
+ /** True for Azure — you must call validateAndConfirmUpload after */
153
+ requiresValidation?: boolean;
154
+ }
155
+
156
+ /**
157
+ * Stricter result from StorageManager.generateViewUrl().
158
+ * On success, reference, viewUrl, and expiresIn are guaranteed.
159
+ */
160
+ export type PresignedViewUrlResult =
161
+ | PresignedViewUrlSuccess
162
+ | PresignedUrlError;
163
+
164
+ export interface PresignedViewUrlSuccess extends PresignedUrlSuccess {
165
+ reference: string;
166
+ viewUrl: string;
167
+ expiresIn: number;
168
+ }
169
+
170
+ /**
171
+ * What you get back after validating an upload.
172
+ */
173
+ export type BlobValidationResult = BlobValidationSuccess | BlobValidationError;
174
+
175
+ export interface BlobValidationSuccess {
176
+ success: true;
177
+ /** The file reference that was validated */
178
+ reference: string;
179
+ /** URL to view the file */
180
+ viewUrl?: string;
181
+ /** What the file's content type actually is */
182
+ actualContentType?: string;
183
+ /** What the file's size actually is */
184
+ actualFileSize?: number;
185
+ /** How long the view URL is valid */
186
+ expiresIn?: number;
187
+ }
188
+
189
+ export interface BlobValidationError {
190
+ success: false;
191
+ /** What went wrong */
192
+ error: string;
193
+ /** Programmatic error code */
194
+ code: StorageErrorCode;
195
+ /** Actual content type (for diagnostic purposes) */
196
+ actualContentType?: string;
197
+ /** Actual file size (for diagnostic purposes) */
198
+ actualFileSize?: number;
199
+ }
200
+
201
+ /**
202
+ * What you get back when listing files.
203
+ */
204
+ export type ListFilesResult = ListFilesSuccess | ListFilesError;
205
+
206
+ export interface ListFilesSuccess {
207
+ success: true;
208
+ /** The files found */
209
+ files: FileInfo[];
210
+ /** Token for getting the next page of results */
211
+ nextToken?: string;
212
+ }
213
+
214
+ export interface ListFilesError {
215
+ success: false;
216
+ /** What went wrong */
217
+ error: string;
218
+ /** Programmatic error code */
219
+ code: StorageErrorCode;
220
+ }
221
+
222
+ // ---------------------------------------------------------------------------
223
+ // Validation & upload options
224
+ // ---------------------------------------------------------------------------
225
+
226
+ /**
227
+ * Options for validating uploads (especially important for Azure).
228
+ */
229
+ export interface BlobValidationOptions {
230
+ /** The content type you're expecting */
231
+ expectedContentType?: string;
232
+ /** The file size you're expecting (in bytes) */
233
+ expectedFileSize?: number;
234
+ /** Delete the file if validation fails (default: true) */
235
+ deleteOnFailure?: boolean;
236
+ }
237
+
238
+ /**
239
+ * Options for validating files before upload.
240
+ */
241
+ export interface FileValidationOptions {
242
+ /** Maximum file size in bytes */
243
+ maxSize?: number;
244
+ /** Allowed MIME types (e.g., ['image/jpeg', 'image/png']) */
245
+ allowedMimeTypes?: string[];
246
+ /** Allowed extensions (e.g., ['.jpg', '.png'] or ['jpg', 'png']) */
247
+ allowedExtensions?: string[];
248
+ }
249
+
250
+ /**
251
+ * Options for customizing how files are uploaded.
252
+ */
253
+ export interface UploadOptions {
254
+ /** Override the detected content type */
255
+ contentType?: string;
256
+ /** Custom metadata (key-value pairs stored with the file) */
257
+ metadata?: Record<string, string>;
258
+ /** Cache-Control header (e.g., 'max-age=31536000') */
259
+ cacheControl?: string;
260
+ /** Content-Disposition header (e.g., 'attachment; filename="file.pdf"') */
261
+ contentDisposition?: string;
262
+ /** AbortSignal to cancel an in-flight upload */
263
+ signal?: AbortSignal | undefined;
264
+ }
265
+
266
+ /**
267
+ * File metadata for generating multiple presigned URLs at once.
268
+ */
269
+ export interface FileMetadata {
270
+ /** The filename */
271
+ fileName: string;
272
+ /** MIME type (e.g., 'image/jpeg') */
273
+ contentType?: string;
274
+ /** File size in bytes */
275
+ fileSize?: number;
276
+ }
277
+
278
+ // ---------------------------------------------------------------------------
279
+ // Lifecycle hooks
280
+ // ---------------------------------------------------------------------------
281
+
282
+ /**
283
+ * Context passed to the onError hook.
284
+ */
285
+ export interface HookErrorContext {
286
+ operation:
287
+ | "upload"
288
+ | "uploadMultiple"
289
+ | "delete"
290
+ | "deleteMultiple"
291
+ | "generateUploadUrl"
292
+ | "generateViewUrl"
293
+ | "validateUpload"
294
+ | "listFiles";
295
+ file?: Express.Multer.File;
296
+ reference?: string;
297
+ }
298
+
299
+ /**
300
+ * Hooks let you tap into the upload/delete lifecycle without modifying drivers.
301
+ *
302
+ * All hooks are optional and async-safe. If a "before" hook throws, the
303
+ * operation is aborted and an error result is returned.
304
+ *
305
+ * @example
306
+ * const storage = new StorageManager({
307
+ * driver: 's3',
308
+ * hooks: {
309
+ * beforeUpload: async (file) => {
310
+ * await virusScan(file.buffer);
311
+ * },
312
+ * afterUpload: (result) => {
313
+ * auditLog('file_uploaded', result);
314
+ * },
315
+ * onError: (error, ctx) => {
316
+ * metrics.increment('storage.error', { operation: ctx.operation });
317
+ * },
318
+ * },
319
+ * });
320
+ */
321
+ export interface StorageHooks {
322
+ /** Called before each file upload. Throw to abort. */
323
+ beforeUpload?: (
324
+ file: Express.Multer.File,
325
+ options?: UploadOptions,
326
+ ) => void | Promise<void>;
327
+ /** Called after each file upload (success or failure). */
328
+ afterUpload?: (
329
+ result: FileUploadResult,
330
+ file: Express.Multer.File,
331
+ ) => void | Promise<void>;
332
+ /** Called before each file deletion. Throw to abort. */
333
+ beforeDelete?: (reference: string) => void | Promise<void>;
334
+ /** Called after each file deletion. */
335
+ afterDelete?: (reference: string, success: boolean) => void | Promise<void>;
336
+ /** Called when any operation encounters an error. */
337
+ onError?: (error: Error, context: HookErrorContext) => void | Promise<void>;
338
+ }
339
+
340
+ // ---------------------------------------------------------------------------
341
+ // Rate limiting
342
+ // ---------------------------------------------------------------------------
343
+
344
+ /**
345
+ * Adapter interface for rate limiting presigned URL generation.
346
+ *
347
+ * The built-in InMemoryRateLimiter works for single-process apps.
348
+ * For multi-process/clustered deployments, implement this interface
349
+ * backed by Redis, Memcached, or another shared store.
350
+ *
351
+ * @example
352
+ * // Custom Redis-backed rate limiter
353
+ * class RedisRateLimiter implements RateLimiterAdapter {
354
+ * async tryAcquire() { ... }
355
+ * async getRemainingRequests() { ... }
356
+ * async getResetTime() { ... }
357
+ * }
358
+ * const storage = new StorageManager({
359
+ * driver: 's3',
360
+ * rateLimiter: new RedisRateLimiter(redis),
361
+ * });
362
+ */
363
+ export interface RateLimiterAdapter {
364
+ /** Check if a request is allowed and record it if so. */
365
+ tryAcquire(): boolean | Promise<boolean>;
366
+ /** Get the number of remaining requests in the current window. */
367
+ getRemainingRequests(): number | Promise<number>;
368
+ /** Get the time until the rate limit resets (in ms). */
369
+ getResetTime(): number | Promise<number>;
370
+ }
371
+
372
+ /**
373
+ * Shorthand options for creating the built-in InMemoryRateLimiter.
374
+ */
375
+ export interface RateLimitOptions {
376
+ /** Maximum number of presigned URLs that can be generated per window */
377
+ maxRequests: number;
378
+ /** Time window in milliseconds (default: 60000 = 1 minute) */
379
+ windowMs?: number;
380
+ }
381
+
382
+ // ---------------------------------------------------------------------------
383
+ // Configuration
384
+ // ---------------------------------------------------------------------------
385
+
386
+ /**
387
+ * Logging interface — pass your own logger if you want debug output.
388
+ */
389
+ export interface Logger {
390
+ debug: (message: string, ...args: unknown[]) => void;
391
+ info: (message: string, ...args: unknown[]) => void;
392
+ warn: (message: string, ...args: unknown[]) => void;
393
+ error: (message: string, ...args: unknown[]) => void;
394
+ }
395
+
396
+ /**
397
+ * Credentials and settings for storage configuration.
398
+ *
399
+ * These can be passed programmatically to override environment variables.
400
+ */
401
+ export interface StorageCredentials {
402
+ /** Bucket or container name (S3/GCS bucket, Azure container) */
403
+ bucketName?: string;
404
+ /** Default folder path (e.g., 'uploads/files') */
405
+ bucketPath?: string;
406
+ /** Local storage directory */
407
+ localPath?: string;
408
+ /** How long presigned URLs stay valid (seconds, default: 600) */
409
+ presignedUrlExpiry?: number;
410
+ /** Maximum file size (bytes, default: 5GB) */
411
+ maxFileSize?: number;
412
+
413
+ // AWS S3
414
+ awsRegion?: string;
415
+ awsAccessKey?: string;
416
+ awsSecretKey?: string;
417
+
418
+ // Google Cloud Storage
419
+ gcsProjectId?: string;
420
+ gcsCredentials?: string;
421
+
422
+ // Azure Blob Storage
423
+ azureConnectionString?: string;
424
+ azureAccountName?: string;
425
+ azureAccountKey?: string;
426
+ /** Azure container name (overrides bucketName for Azure driver) */
427
+ azureContainerName?: string;
428
+ }
429
+
430
+ /**
431
+ * Options for initializing StorageManager.
432
+ */
433
+ export interface StorageOptions {
434
+ /** Which storage driver to use */
435
+ driver: StorageDriver;
436
+ /** Credentials and settings (optional — can come from env vars) */
437
+ credentials?: StorageCredentials;
438
+ /** Your logger (optional — silent by default) */
439
+ logger?: Logger;
440
+ /**
441
+ * Rate limiting for presigned URL generation.
442
+ * Pass RateLimitOptions for the built-in in-memory limiter,
443
+ * or a RateLimiterAdapter for a custom implementation (e.g., Redis).
444
+ */
445
+ rateLimiter?: RateLimitOptions | RateLimiterAdapter;
446
+ /** Lifecycle hooks (optional) */
447
+ hooks?: StorageHooks;
448
+ /** Maximum parallel operations for batch methods (default: 10) */
449
+ concurrency?: number;
450
+ }
451
+
452
+ /**
453
+ * Options for batch operations (uploadFiles, deleteFiles, etc.).
454
+ */
455
+ export interface BatchOptions {
456
+ /** AbortSignal to cancel the batch operation mid-flight. */
457
+ signal?: AbortSignal | undefined;
458
+ }
459
+
460
+ /**
461
+ * Public configuration — safe to expose via getConfig().
462
+ * Contains non-sensitive settings only.
463
+ */
464
+ export interface PublicStorageConfig {
465
+ driver: StorageDriver;
466
+ bucketName?: string | undefined;
467
+ bucketPath?: string | undefined;
468
+ localPath?: string | undefined;
469
+ presignedUrlExpiry?: number | undefined;
470
+ maxFileSize?: number | undefined;
471
+ awsRegion?: string | undefined;
472
+ gcsProjectId?: string | undefined;
473
+ azureAccountName?: string | undefined;
474
+ azureContainerName?: string | undefined;
475
+ }
476
+
477
+ /**
478
+ * Internal configuration format (used by drivers). Extends the public config
479
+ * with sensitive credential fields that should never be exposed to consumers.
480
+ */
481
+ export interface StorageConfig extends PublicStorageConfig {
482
+ awsAccessKey?: string | undefined;
483
+ awsSecretKey?: string | undefined;
484
+ gcsCredentials?: string | undefined;
485
+ azureConnectionString?: string | undefined;
486
+ azureAccountKey?: string | undefined;
487
+ }
488
+
489
+ /**
490
+ * Information about a single file.
491
+ */
492
+ export interface FileInfo {
493
+ /** File path/name */
494
+ name: string;
495
+ /** File size in bytes */
496
+ size?: number;
497
+ /** MIME type */
498
+ contentType?: string;
499
+ /** When the file was last modified */
500
+ lastModified?: Date;
501
+ }
502
+
503
+ /**
504
+ * The interface all storage drivers implement.
505
+ */
506
+ export interface IStorageDriver {
507
+ upload(
508
+ file: Express.Multer.File,
509
+ options?: UploadOptions,
510
+ ): Promise<FileUploadResult>;
511
+ generateUploadUrl(
512
+ fileName: string,
513
+ contentType?: string,
514
+ fileSize?: number,
515
+ ): Promise<PresignedUrlResult>;
516
+ generateViewUrl(fileName: string): Promise<PresignedUrlResult>;
517
+ delete(fileName: string): Promise<DeleteResult>;
518
+ validateAndConfirmUpload(
519
+ reference: string,
520
+ options?: BlobValidationOptions,
521
+ ): Promise<BlobValidationResult>;
522
+ listFiles(
523
+ prefix?: string,
524
+ maxResults?: number,
525
+ continuationToken?: string,
526
+ ): Promise<ListFilesResult>;
527
+ getMetadata(reference: string): Promise<FileInfo | null>;
528
+ /** Releases SDK client connections and internal resources. */
529
+ destroy(): void;
530
+ }
531
+
532
+ /**
533
+ * Result of configuration validation.
534
+ */
535
+ export interface ValidationResult {
536
+ isValid: boolean;
537
+ errors: string[];
538
+ }
539
+
540
+ /**
541
+ * Environment variables we look for.
542
+ */
543
+ export interface EnvironmentConfig {
544
+ FILE_DRIVER: string;
545
+ BUCKET_NAME?: string | undefined;
546
+ BUCKET_PATH?: string | undefined;
547
+ LOCAL_PATH?: string | undefined;
548
+ PRESIGNED_URL_EXPIRY?: string | undefined;
549
+ MAX_FILE_SIZE?: string | undefined;
550
+
551
+ AWS_REGION?: string | undefined;
552
+ AWS_ACCESS_KEY?: string | undefined;
553
+ AWS_SECRET_KEY?: string | undefined;
554
+
555
+ GCS_PROJECT_ID?: string | undefined;
556
+ GCS_CREDENTIALS?: string | undefined;
557
+
558
+ AZURE_CONNECTION_STRING?: string | undefined;
559
+ AZURE_ACCOUNT_NAME?: string | undefined;
560
+ AZURE_ACCOUNT_KEY?: string | undefined;
561
+ }