express-storage 1.0.0 → 1.1.2

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 (48) hide show
  1. package/README.md +519 -348
  2. package/dist/drivers/azure.driver.d.ts +88 -0
  3. package/dist/drivers/azure.driver.d.ts.map +1 -0
  4. package/dist/drivers/azure.driver.js +367 -0
  5. package/dist/drivers/azure.driver.js.map +1 -0
  6. package/dist/drivers/base.driver.d.ts +125 -24
  7. package/dist/drivers/base.driver.d.ts.map +1 -1
  8. package/dist/drivers/base.driver.js +248 -62
  9. package/dist/drivers/base.driver.js.map +1 -1
  10. package/dist/drivers/gcs.driver.d.ts +60 -13
  11. package/dist/drivers/gcs.driver.d.ts.map +1 -1
  12. package/dist/drivers/gcs.driver.js +242 -41
  13. package/dist/drivers/gcs.driver.js.map +1 -1
  14. package/dist/drivers/local.driver.d.ts +89 -12
  15. package/dist/drivers/local.driver.d.ts.map +1 -1
  16. package/dist/drivers/local.driver.js +533 -45
  17. package/dist/drivers/local.driver.js.map +1 -1
  18. package/dist/drivers/s3.driver.d.ts +64 -13
  19. package/dist/drivers/s3.driver.d.ts.map +1 -1
  20. package/dist/drivers/s3.driver.js +269 -41
  21. package/dist/drivers/s3.driver.js.map +1 -1
  22. package/dist/factory/driver.factory.d.ts +35 -29
  23. package/dist/factory/driver.factory.d.ts.map +1 -1
  24. package/dist/factory/driver.factory.js +119 -59
  25. package/dist/factory/driver.factory.js.map +1 -1
  26. package/dist/index.d.ts +23 -22
  27. package/dist/index.d.ts.map +1 -1
  28. package/dist/index.js +26 -46
  29. package/dist/index.js.map +1 -1
  30. package/dist/storage-manager.d.ts +205 -52
  31. package/dist/storage-manager.d.ts.map +1 -1
  32. package/dist/storage-manager.js +644 -73
  33. package/dist/storage-manager.js.map +1 -1
  34. package/dist/types/storage.types.d.ts +243 -18
  35. package/dist/types/storage.types.d.ts.map +1 -1
  36. package/dist/utils/config.utils.d.ts +28 -4
  37. package/dist/utils/config.utils.d.ts.map +1 -1
  38. package/dist/utils/config.utils.js +121 -47
  39. package/dist/utils/config.utils.js.map +1 -1
  40. package/dist/utils/file.utils.d.ts +111 -14
  41. package/dist/utils/file.utils.d.ts.map +1 -1
  42. package/dist/utils/file.utils.js +215 -32
  43. package/dist/utils/file.utils.js.map +1 -1
  44. package/package.json +51 -27
  45. package/dist/drivers/oci.driver.d.ts +0 -37
  46. package/dist/drivers/oci.driver.d.ts.map +0 -1
  47. package/dist/drivers/oci.driver.js +0 -84
  48. package/dist/drivers/oci.driver.js.map +0 -1
@@ -1,69 +1,170 @@
1
- import { IStorageDriver, FileUploadResult, PresignedUrlResult, StorageConfig } from '../types/storage.types.js';
1
+ import { Readable } from 'stream';
2
+ import { IStorageDriver, FileUploadResult, PresignedUrlResult, StorageConfig, BlobValidationOptions, BlobValidationResult, ListFilesResult, UploadOptions, DeleteResult } from '../types/storage.types.js';
2
3
  /**
3
- * Abstract base class for all storage drivers
4
+ * BaseStorageDriver - The foundation that all storage drivers build upon.
5
+ *
6
+ * This abstract class provides common functionality that every driver needs:
7
+ * filename generation, file validation, content reading, and result formatting.
8
+ *
9
+ * If you're building a custom driver, extend this class and implement the
10
+ * abstract methods. You'll get all the helper methods for free.
4
11
  */
5
12
  export declare abstract class BaseStorageDriver implements IStorageDriver {
6
13
  protected config: StorageConfig;
7
14
  constructor(config: StorageConfig);
8
15
  /**
9
- * Upload a single file
16
+ * Builds the full storage path by combining the bucket path with the filename.
17
+ * For example: 'uploads' + 'photo.jpg' = 'uploads/photo.jpg'
10
18
  */
11
- abstract upload(file: Express.Multer.File): Promise<FileUploadResult>;
19
+ protected buildFilePath(fileName: string): string;
12
20
  /**
13
- * Upload multiple files
21
+ * Returns the configured bucket path, cleaned up and ready to use.
14
22
  */
15
- uploadMultiple(files: Express.Multer.File[]): Promise<FileUploadResult[]>;
23
+ protected getBucketPath(): string;
16
24
  /**
17
- * Generate upload URL (for presigned drivers)
25
+ * Uploads a single file. Each driver implements this differently.
18
26
  */
19
- abstract generateUploadUrl(fileName: string): Promise<PresignedUrlResult>;
27
+ abstract upload(file: Express.Multer.File, options?: UploadOptions): Promise<FileUploadResult>;
20
28
  /**
21
- * Generate view URL (for presigned drivers)
29
+ * Uploads multiple files with smart concurrency control.
30
+ * Processes up to 10 files at a time to balance speed and system resources.
22
31
  */
23
- abstract generateViewUrl(fileName: string): Promise<PresignedUrlResult>;
32
+ uploadMultiple(files: Express.Multer.File[], options?: UploadOptions): Promise<FileUploadResult[]>;
24
33
  /**
25
- * Generate multiple upload URLs
34
+ * Creates a presigned URL for uploading.
35
+ * Each cloud provider has its own way of doing this.
26
36
  */
27
- generateMultipleUploadUrls(fileNames: string[]): Promise<PresignedUrlResult[]>;
37
+ abstract generateUploadUrl(fileName: string, contentType?: string, fileSize?: number): Promise<PresignedUrlResult>;
28
38
  /**
29
- * Generate multiple view URLs
39
+ * Creates a presigned URL for viewing/downloading.
30
40
  */
31
- generateMultipleViewUrls(fileNames: string[]): Promise<PresignedUrlResult[]>;
41
+ abstract generateViewUrl(fileName: string): Promise<PresignedUrlResult>;
32
42
  /**
33
- * Delete a single file
43
+ * Deletes a file from storage.
34
44
  */
35
45
  abstract delete(fileName: string): Promise<boolean>;
36
46
  /**
37
- * Delete multiple files
47
+ * Lists files with optional filtering and pagination.
48
+ */
49
+ abstract listFiles(prefix?: string, maxResults?: number, continuationToken?: string): Promise<ListFilesResult>;
50
+ /**
51
+ * Deletes multiple files with smart concurrency control.
52
+ * Returns detailed results so you know exactly what happened with each file.
38
53
  */
39
- deleteMultiple(fileNames: string[]): Promise<boolean[]>;
54
+ deleteMultiple(fileNames: string[]): Promise<DeleteResult[]>;
40
55
  /**
41
- * Generate unique filename with timestamp
56
+ * Creates a unique filename that won't collide with existing files.
57
+ * Format: {timestamp}_{random}_{original_name}.{ext}
42
58
  */
43
59
  protected generateFileName(originalName: string): string;
44
60
  /**
45
- * Create success result
61
+ * Builds a success response for upload operations.
46
62
  */
47
63
  protected createSuccessResult(fileName: string, fileUrl?: string): FileUploadResult;
48
64
  /**
49
- * Create error result
65
+ * Builds an error response for upload operations.
50
66
  */
51
67
  protected createErrorResult(error: string): FileUploadResult;
52
68
  /**
53
- * Create presigned success result
69
+ * Builds a success response for presigned URL operations.
54
70
  */
55
71
  protected createPresignedSuccessResult(uploadUrl?: string, viewUrl?: string): PresignedUrlResult;
56
72
  /**
57
- * Create presigned error result
73
+ * Builds an error response for presigned URL operations.
58
74
  */
59
75
  protected createPresignedErrorResult(error: string): PresignedUrlResult;
60
76
  /**
61
- * Validate file before upload
77
+ * Validates a file before upload.
78
+ *
79
+ * Checks for common issues:
80
+ * - Missing file
81
+ * - No original name
82
+ * - No MIME type
83
+ * - Empty content
84
+ *
85
+ * Works with both Multer memory storage (file.buffer) and disk storage (file.path).
62
86
  */
63
87
  protected validateFile(file: Express.Multer.File): string[];
64
88
  /**
65
- * Get presigned URL expiry time
89
+ * Reads the file content, whether it's in memory or on disk.
90
+ *
91
+ * Note: For disk storage, this reads the file but doesn't delete it.
92
+ * Call cleanupTempFile() afterward if you need to remove the temp file.
93
+ *
94
+ * @warning **MEMORY IMPLICATIONS**: This method loads the ENTIRE file into memory.
95
+ * For large files, this can cause memory exhaustion and application crashes.
96
+ *
97
+ * **ALWAYS call `shouldUseStreaming(file)` first** and use `getFileStream()`
98
+ * for files larger than 100MB. Example:
99
+ *
100
+ * ```typescript
101
+ * if (this.shouldUseStreaming(file)) {
102
+ * return this.uploadWithStream(file);
103
+ * }
104
+ * const content = this.getFileContent(file); // Safe for smaller files
105
+ * ```
106
+ *
107
+ * Memory usage: A 1GB file will allocate ~1GB of heap memory.
108
+ * Node.js default heap limit is ~1.5GB, so large files WILL crash your app.
109
+ *
110
+ * @param file - The Multer file object
111
+ * @returns Buffer containing the entire file contents
112
+ * @throws Error if file has neither buffer nor path
113
+ */
114
+ protected getFileContent(file: Express.Multer.File): Buffer;
115
+ /**
116
+ * Returns a readable stream for the file content.
117
+ *
118
+ * Use this instead of getFileContent() for large files to avoid
119
+ * loading the entire file into memory. Particularly useful for
120
+ * files larger than 100MB.
121
+ *
122
+ * @param file - The Multer file object
123
+ * @returns A readable stream of the file content
124
+ */
125
+ protected getFileStream(file: Express.Multer.File): Readable;
126
+ /**
127
+ * Determines if a file should use streaming based on its size.
128
+ *
129
+ * Files larger than 100MB benefit from streaming to reduce memory usage.
130
+ *
131
+ * @param file - The Multer file object
132
+ * @returns true if the file should use streaming
133
+ */
134
+ protected shouldUseStreaming(file: Express.Multer.File): boolean;
135
+ /**
136
+ * Gets the file size, reading from disk if necessary.
137
+ *
138
+ * @param file - The Multer file object
139
+ * @returns The file size in bytes
140
+ */
141
+ protected getFileSize(file: Express.Multer.File): number;
142
+ /**
143
+ * Removes a temporary file created by Multer disk storage.
144
+ *
145
+ * Call this after a successful upload if you're using disk storage
146
+ * and want to clean up. Memory storage doesn't need this — the
147
+ * garbage collector handles cleanup automatically.
148
+ *
149
+ * @returns true if the file was deleted, false otherwise
150
+ */
151
+ cleanupTempFile(file: Express.Multer.File): boolean;
152
+ /**
153
+ * Returns how long presigned URLs should be valid (in seconds).
154
+ *
155
+ * Clamps the value to stay within cloud provider limits:
156
+ * - Minimum: 1 second
157
+ * - Maximum: 7 days (604800 seconds)
158
+ * - Default: 10 minutes (600 seconds)
66
159
  */
67
160
  protected getPresignedUrlExpiry(): number;
161
+ /**
162
+ * Confirms that an upload completed successfully.
163
+ *
164
+ * The default implementation just checks if the file exists.
165
+ * Azure overrides this to validate file properties since Azure
166
+ * doesn't enforce constraints at the presigned URL level.
167
+ */
168
+ validateAndConfirmUpload(reference: string, _options?: BlobValidationOptions): Promise<BlobValidationResult>;
68
169
  }
69
170
  //# sourceMappingURL=base.driver.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"base.driver.d.ts","sourceRoot":"","sources":["../../src/drivers/base.driver.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAGhH;;GAEG;AACH,8BAAsB,iBAAkB,YAAW,cAAc;IAC/D,SAAS,CAAC,MAAM,EAAE,aAAa,CAAC;gBAEpB,MAAM,EAAE,aAAa;IAIjC;;OAEG;IACH,QAAQ,CAAC,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,MAAM,CAAC,IAAI,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAErE;;OAEG;IACG,cAAc,CAAC,KAAK,EAAE,OAAO,CAAC,MAAM,CAAC,IAAI,EAAE,GAAG,OAAO,CAAC,gBAAgB,EAAE,CAAC;IAkB/E;;OAEG;IACH,QAAQ,CAAC,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,CAAC;IAEzE;;OAEG;IACH,QAAQ,CAAC,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,CAAC;IAEvE;;OAEG;IACG,0BAA0B,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,kBAAkB,EAAE,CAAC;IAkBpF;;OAEG;IACG,wBAAwB,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,kBAAkB,EAAE,CAAC;IAkBlF;;OAEG;IACH,QAAQ,CAAC,MAAM,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAEnD;;OAEG;IACG,cAAc,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC;IAe7D;;OAEG;IACH,SAAS,CAAC,gBAAgB,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM;IAIxD;;OAEG;IACH,SAAS,CAAC,mBAAmB,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,gBAAgB;IAWnF;;OAEG;IACH,SAAS,CAAC,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,gBAAgB;IAO5D;;OAEG;IACH,SAAS,CAAC,4BAA4B,CAAC,SAAS,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,kBAAkB;IAahG;;OAEG;IACH,SAAS,CAAC,0BAA0B,CAAC,KAAK,EAAE,MAAM,GAAG,kBAAkB;IAOvE;;OAEG;IACH,SAAS,CAAC,YAAY,CAAC,IAAI,EAAE,OAAO,CAAC,MAAM,CAAC,IAAI,GAAG,MAAM,EAAE;IAuB3D;;OAEG;IACH,SAAS,CAAC,qBAAqB,IAAI,MAAM;CAG1C"}
1
+ {"version":3,"file":"base.driver.d.ts","sourceRoot":"","sources":["../../src/drivers/base.driver.ts"],"names":[],"mappings":"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"}
@@ -1,92 +1,94 @@
1
- import { generateUniqueFileName } from '../utils/file.utils.js';
1
+ import fs from 'fs';
2
+ import { Readable } from 'stream';
3
+ import { generateUniqueFileName, withConcurrencyLimit } from '../utils/file.utils.js';
4
+ /** Threshold for using streaming uploads (100MB) */
5
+ const STREAM_THRESHOLD = 100 * 1024 * 1024;
2
6
  /**
3
- * Abstract base class for all storage drivers
7
+ * BaseStorageDriver - The foundation that all storage drivers build upon.
8
+ *
9
+ * This abstract class provides common functionality that every driver needs:
10
+ * filename generation, file validation, content reading, and result formatting.
11
+ *
12
+ * If you're building a custom driver, extend this class and implement the
13
+ * abstract methods. You'll get all the helper methods for free.
4
14
  */
5
15
  export class BaseStorageDriver {
6
16
  constructor(config) {
7
17
  this.config = config;
8
18
  }
9
19
  /**
10
- * Upload multiple files
20
+ * Builds the full storage path by combining the bucket path with the filename.
21
+ * For example: 'uploads' + 'photo.jpg' = 'uploads/photo.jpg'
11
22
  */
12
- async uploadMultiple(files) {
13
- const results = [];
14
- for (const file of files) {
15
- try {
16
- const result = await this.upload(file);
17
- results.push(result);
18
- }
19
- catch (error) {
20
- results.push({
21
- success: false,
22
- error: error instanceof Error ? error.message : 'Upload failed',
23
- });
24
- }
23
+ buildFilePath(fileName) {
24
+ const bucketPath = this.config.bucketPath?.trim();
25
+ if (!bucketPath || bucketPath === '' || bucketPath === '/') {
26
+ return fileName;
25
27
  }
26
- return results;
28
+ const normalizedPath = bucketPath.replace(/^\/+|\/+$/g, '');
29
+ return `${normalizedPath}/${fileName}`;
27
30
  }
28
31
  /**
29
- * Generate multiple upload URLs
32
+ * Returns the configured bucket path, cleaned up and ready to use.
30
33
  */
31
- async generateMultipleUploadUrls(fileNames) {
32
- const results = [];
33
- for (const fileName of fileNames) {
34
- try {
35
- const result = await this.generateUploadUrl(fileName);
36
- results.push(result);
37
- }
38
- catch (error) {
39
- results.push({
40
- success: false,
41
- error: error instanceof Error ? error.message : 'Failed to generate upload URL',
42
- });
43
- }
44
- }
45
- return results;
34
+ getBucketPath() {
35
+ return this.config.bucketPath?.trim()?.replace(/^\/+|\/+$/g, '') || '';
46
36
  }
47
37
  /**
48
- * Generate multiple view URLs
38
+ * Uploads multiple files with smart concurrency control.
39
+ * Processes up to 10 files at a time to balance speed and system resources.
49
40
  */
50
- async generateMultipleViewUrls(fileNames) {
51
- const results = [];
52
- for (const fileName of fileNames) {
41
+ async uploadMultiple(files, options) {
42
+ if (!files || files.length === 0) {
43
+ return [];
44
+ }
45
+ return withConcurrencyLimit(files, async (file) => {
53
46
  try {
54
- const result = await this.generateViewUrl(fileName);
55
- results.push(result);
47
+ return await this.upload(file, options);
56
48
  }
57
49
  catch (error) {
58
- results.push({
50
+ return {
59
51
  success: false,
60
- error: error instanceof Error ? error.message : 'Failed to generate view URL',
61
- });
52
+ error: error instanceof Error ? error.message : 'Failed to upload file',
53
+ };
62
54
  }
63
- }
64
- return results;
55
+ }, { maxConcurrent: 10 });
65
56
  }
66
57
  /**
67
- * Delete multiple files
58
+ * Deletes multiple files with smart concurrency control.
59
+ * Returns detailed results so you know exactly what happened with each file.
68
60
  */
69
61
  async deleteMultiple(fileNames) {
70
- const results = [];
71
- for (const fileName of fileNames) {
62
+ if (!fileNames || fileNames.length === 0) {
63
+ return [];
64
+ }
65
+ return withConcurrencyLimit(fileNames, async (fileName) => {
72
66
  try {
73
- const result = await this.delete(fileName);
74
- results.push(result);
67
+ const success = await this.delete(fileName);
68
+ const result = { success, fileName };
69
+ if (!success) {
70
+ result.error = 'File not found or already deleted';
71
+ }
72
+ return result;
75
73
  }
76
74
  catch (error) {
77
- results.push(false);
75
+ return {
76
+ success: false,
77
+ fileName,
78
+ error: error instanceof Error ? error.message : 'Failed to delete file',
79
+ };
78
80
  }
79
- }
80
- return results;
81
+ }, { maxConcurrent: 10 });
81
82
  }
82
83
  /**
83
- * Generate unique filename with timestamp
84
+ * Creates a unique filename that won't collide with existing files.
85
+ * Format: {timestamp}_{random}_{original_name}.{ext}
84
86
  */
85
87
  generateFileName(originalName) {
86
88
  return generateUniqueFileName(originalName);
87
89
  }
88
90
  /**
89
- * Create success result
91
+ * Builds a success response for upload operations.
90
92
  */
91
93
  createSuccessResult(fileName, fileUrl) {
92
94
  const result = {
@@ -99,7 +101,7 @@ export class BaseStorageDriver {
99
101
  return result;
100
102
  }
101
103
  /**
102
- * Create error result
104
+ * Builds an error response for upload operations.
103
105
  */
104
106
  createErrorResult(error) {
105
107
  return {
@@ -108,7 +110,7 @@ export class BaseStorageDriver {
108
110
  };
109
111
  }
110
112
  /**
111
- * Create presigned success result
113
+ * Builds a success response for presigned URL operations.
112
114
  */
113
115
  createPresignedSuccessResult(uploadUrl, viewUrl) {
114
116
  const result = {
@@ -123,7 +125,7 @@ export class BaseStorageDriver {
123
125
  return result;
124
126
  }
125
127
  /**
126
- * Create presigned error result
128
+ * Builds an error response for presigned URL operations.
127
129
  */
128
130
  createPresignedErrorResult(error) {
129
131
  return {
@@ -132,7 +134,15 @@ export class BaseStorageDriver {
132
134
  };
133
135
  }
134
136
  /**
135
- * Validate file before upload
137
+ * Validates a file before upload.
138
+ *
139
+ * Checks for common issues:
140
+ * - Missing file
141
+ * - No original name
142
+ * - No MIME type
143
+ * - Empty content
144
+ *
145
+ * Works with both Multer memory storage (file.buffer) and disk storage (file.path).
136
146
  */
137
147
  validateFile(file) {
138
148
  const errors = [];
@@ -146,16 +156,192 @@ export class BaseStorageDriver {
146
156
  if (!file.mimetype) {
147
157
  errors.push('File must have a MIME type');
148
158
  }
149
- if (!file.buffer || file.buffer.length === 0) {
150
- errors.push('File buffer is empty');
159
+ // Check for file content (could be in memory or on disk)
160
+ const hasBuffer = (file.buffer?.length ?? 0) > 0;
161
+ const hasPath = typeof file.path === 'string' && file.path.length > 0;
162
+ const hasEmptyBuffer = file.buffer !== null && file.buffer !== undefined && file.buffer.length === 0;
163
+ if (hasEmptyBuffer && !hasPath) {
164
+ errors.push('File is empty (0 bytes)');
165
+ }
166
+ else if (!hasBuffer && !hasPath) {
167
+ errors.push('File must have either buffer (memory storage) or path (disk storage)');
168
+ }
169
+ // For disk storage, verify the file exists and isn't empty
170
+ if (hasPath && !hasBuffer) {
171
+ try {
172
+ const stats = fs.statSync(file.path);
173
+ if (stats.size === 0) {
174
+ errors.push('File is empty (0 bytes)');
175
+ }
176
+ if (!file.size || file.size === 0) {
177
+ file.size = stats.size;
178
+ }
179
+ }
180
+ catch {
181
+ errors.push('Cannot read file from disk storage path');
182
+ }
151
183
  }
152
184
  return errors;
153
185
  }
154
186
  /**
155
- * Get presigned URL expiry time
187
+ * Reads the file content, whether it's in memory or on disk.
188
+ *
189
+ * Note: For disk storage, this reads the file but doesn't delete it.
190
+ * Call cleanupTempFile() afterward if you need to remove the temp file.
191
+ *
192
+ * @warning **MEMORY IMPLICATIONS**: This method loads the ENTIRE file into memory.
193
+ * For large files, this can cause memory exhaustion and application crashes.
194
+ *
195
+ * **ALWAYS call `shouldUseStreaming(file)` first** and use `getFileStream()`
196
+ * for files larger than 100MB. Example:
197
+ *
198
+ * ```typescript
199
+ * if (this.shouldUseStreaming(file)) {
200
+ * return this.uploadWithStream(file);
201
+ * }
202
+ * const content = this.getFileContent(file); // Safe for smaller files
203
+ * ```
204
+ *
205
+ * Memory usage: A 1GB file will allocate ~1GB of heap memory.
206
+ * Node.js default heap limit is ~1.5GB, so large files WILL crash your app.
207
+ *
208
+ * @param file - The Multer file object
209
+ * @returns Buffer containing the entire file contents
210
+ * @throws Error if file has neither buffer nor path
211
+ */
212
+ getFileContent(file) {
213
+ if ((file.buffer?.length ?? 0) > 0) {
214
+ return file.buffer;
215
+ }
216
+ if (file.path) {
217
+ return fs.readFileSync(file.path);
218
+ }
219
+ throw new Error('File has neither buffer nor path - cannot read content');
220
+ }
221
+ /**
222
+ * Returns a readable stream for the file content.
223
+ *
224
+ * Use this instead of getFileContent() for large files to avoid
225
+ * loading the entire file into memory. Particularly useful for
226
+ * files larger than 100MB.
227
+ *
228
+ * @param file - The Multer file object
229
+ * @returns A readable stream of the file content
230
+ */
231
+ getFileStream(file) {
232
+ if ((file.buffer?.length ?? 0) > 0) {
233
+ return Readable.from(file.buffer);
234
+ }
235
+ if (file.path) {
236
+ return fs.createReadStream(file.path);
237
+ }
238
+ throw new Error('File has neither buffer nor path - cannot create stream');
239
+ }
240
+ /**
241
+ * Determines if a file should use streaming based on its size.
242
+ *
243
+ * Files larger than 100MB benefit from streaming to reduce memory usage.
244
+ *
245
+ * @param file - The Multer file object
246
+ * @returns true if the file should use streaming
247
+ */
248
+ shouldUseStreaming(file) {
249
+ const size = file.size || 0;
250
+ return size > STREAM_THRESHOLD;
251
+ }
252
+ /**
253
+ * Gets the file size, reading from disk if necessary.
254
+ *
255
+ * @param file - The Multer file object
256
+ * @returns The file size in bytes
257
+ */
258
+ getFileSize(file) {
259
+ if (file.size && file.size > 0) {
260
+ return file.size;
261
+ }
262
+ if (file.buffer?.length) {
263
+ return file.buffer.length;
264
+ }
265
+ if (file.path) {
266
+ try {
267
+ const stats = fs.statSync(file.path);
268
+ return stats.size;
269
+ }
270
+ catch {
271
+ return 0;
272
+ }
273
+ }
274
+ return 0;
275
+ }
276
+ /**
277
+ * Removes a temporary file created by Multer disk storage.
278
+ *
279
+ * Call this after a successful upload if you're using disk storage
280
+ * and want to clean up. Memory storage doesn't need this — the
281
+ * garbage collector handles cleanup automatically.
282
+ *
283
+ * @returns true if the file was deleted, false otherwise
284
+ */
285
+ cleanupTempFile(file) {
286
+ if (file.path && fs.existsSync(file.path)) {
287
+ try {
288
+ fs.unlinkSync(file.path);
289
+ return true;
290
+ }
291
+ catch {
292
+ return false;
293
+ }
294
+ }
295
+ return false;
296
+ }
297
+ /**
298
+ * Returns how long presigned URLs should be valid (in seconds).
299
+ *
300
+ * Clamps the value to stay within cloud provider limits:
301
+ * - Minimum: 1 second
302
+ * - Maximum: 7 days (604800 seconds)
303
+ * - Default: 10 minutes (600 seconds)
156
304
  */
157
305
  getPresignedUrlExpiry() {
158
- return this.config.presignedUrlExpiry || 600; // Default 10 minutes
306
+ const MAX_EXPIRY = 604800;
307
+ const MIN_EXPIRY = 1;
308
+ const DEFAULT_EXPIRY = 600;
309
+ const expiry = this.config.presignedUrlExpiry;
310
+ if (expiry === undefined || expiry === null || Number.isNaN(expiry)) {
311
+ return DEFAULT_EXPIRY;
312
+ }
313
+ if (expiry < MIN_EXPIRY) {
314
+ return MIN_EXPIRY;
315
+ }
316
+ if (expiry > MAX_EXPIRY) {
317
+ return MAX_EXPIRY;
318
+ }
319
+ return expiry;
320
+ }
321
+ /**
322
+ * Confirms that an upload completed successfully.
323
+ *
324
+ * The default implementation just checks if the file exists.
325
+ * Azure overrides this to validate file properties since Azure
326
+ * doesn't enforce constraints at the presigned URL level.
327
+ */
328
+ async validateAndConfirmUpload(reference, _options) {
329
+ const viewResult = await this.generateViewUrl(reference);
330
+ if (viewResult.success) {
331
+ const result = {
332
+ success: true,
333
+ reference,
334
+ expiresIn: this.getPresignedUrlExpiry(),
335
+ };
336
+ if (viewResult.viewUrl) {
337
+ result.viewUrl = viewResult.viewUrl;
338
+ }
339
+ return result;
340
+ }
341
+ return {
342
+ success: false,
343
+ error: viewResult.error || 'File not found',
344
+ };
159
345
  }
160
346
  }
161
347
  //# sourceMappingURL=base.driver.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"base.driver.js","sourceRoot":"","sources":["../../src/drivers/base.driver.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,sBAAsB,EAAE,MAAM,wBAAwB,CAAC;AAEhE;;GAEG;AACH,MAAM,OAAgB,iBAAiB;IAGrC,YAAY,MAAqB;QAC/B,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IACvB,CAAC;IAOD;;OAEG;IACH,KAAK,CAAC,cAAc,CAAC,KAA4B;QAC/C,MAAM,OAAO,GAAuB,EAAE,CAAC;QAEvC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,IAAI,CAAC;gBACH,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;gBACvC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YACvB,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,OAAO,CAAC,IAAI,CAAC;oBACX,OAAO,EAAE,KAAK;oBACd,KAAK,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,eAAe;iBAChE,CAAC,CAAC;YACL,CAAC;QACH,CAAC;QAED,OAAO,OAAO,CAAC;IACjB,CAAC;IAYD;;OAEG;IACH,KAAK,CAAC,0BAA0B,CAAC,SAAmB;QAClD,MAAM,OAAO,GAAyB,EAAE,CAAC;QAEzC,KAAK,MAAM,QAAQ,IAAI,SAAS,EAAE,CAAC;YACjC,IAAI,CAAC;gBACH,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,iBAAiB,CAAC,QAAQ,CAAC,CAAC;gBACtD,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YACvB,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,OAAO,CAAC,IAAI,CAAC;oBACX,OAAO,EAAE,KAAK;oBACd,KAAK,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,+BAA+B;iBAChF,CAAC,CAAC;YACL,CAAC;QACH,CAAC;QAED,OAAO,OAAO,CAAC;IACjB,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,wBAAwB,CAAC,SAAmB;QAChD,MAAM,OAAO,GAAyB,EAAE,CAAC;QAEzC,KAAK,MAAM,QAAQ,IAAI,SAAS,EAAE,CAAC;YACjC,IAAI,CAAC;gBACH,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,eAAe,CAAC,QAAQ,CAAC,CAAC;gBACpD,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YACvB,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,OAAO,CAAC,IAAI,CAAC;oBACX,OAAO,EAAE,KAAK;oBACd,KAAK,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,6BAA6B;iBAC9E,CAAC,CAAC;YACL,CAAC;QACH,CAAC;QAED,OAAO,OAAO,CAAC;IACjB,CAAC;IAOD;;OAEG;IACH,KAAK,CAAC,cAAc,CAAC,SAAmB;QACtC,MAAM,OAAO,GAAc,EAAE,CAAC;QAE9B,KAAK,MAAM,QAAQ,IAAI,SAAS,EAAE,CAAC;YACjC,IAAI,CAAC;gBACH,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;gBAC3C,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YACvB,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACtB,CAAC;QACH,CAAC;QAED,OAAO,OAAO,CAAC;IACjB,CAAC;IAED;;OAEG;IACO,gBAAgB,CAAC,YAAoB;QAC7C,OAAO,sBAAsB,CAAC,YAAY,CAAC,CAAC;IAC9C,CAAC;IAED;;OAEG;IACO,mBAAmB,CAAC,QAAgB,EAAE,OAAgB;QAC9D,MAAM,MAAM,GAAqB;YAC/B,OAAO,EAAE,IAAI;YACb,QAAQ;SACT,CAAC;QACF,IAAI,OAAO,EAAE,CAAC;YACZ,MAAM,CAAC,OAAO,GAAG,OAAO,CAAC;QAC3B,CAAC;QACD,OAAO,MAAM,CAAC;IAChB,CAAC;IAED;;OAEG;IACO,iBAAiB,CAAC,KAAa;QACvC,OAAO;YACL,OAAO,EAAE,KAAK;YACd,KAAK;SACN,CAAC;IACJ,CAAC;IAED;;OAEG;IACO,4BAA4B,CAAC,SAAkB,EAAE,OAAgB;QACzE,MAAM,MAAM,GAAuB;YACjC,OAAO,EAAE,IAAI;SACd,CAAC;QACF,IAAI,SAAS,EAAE,CAAC;YACd,MAAM,CAAC,SAAS,GAAG,SAAS,CAAC;QAC/B,CAAC;QACD,IAAI,OAAO,EAAE,CAAC;YACZ,MAAM,CAAC,OAAO,GAAG,OAAO,CAAC;QAC3B,CAAC;QACD,OAAO,MAAM,CAAC;IAChB,CAAC;IAED;;OAEG;IACO,0BAA0B,CAAC,KAAa;QAChD,OAAO;YACL,OAAO,EAAE,KAAK;YACd,KAAK;SACN,CAAC;IACJ,CAAC;IAED;;OAEG;IACO,YAAY,CAAC,IAAyB;QAC9C,MAAM,MAAM,GAAa,EAAE,CAAC;QAE5B,IAAI,CAAC,IAAI,EAAE,CAAC;YACV,MAAM,CAAC,IAAI,CAAC,kBAAkB,CAAC,CAAC;YAChC,OAAO,MAAM,CAAC;QAChB,CAAC;QAED,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE,CAAC;YACvB,MAAM,CAAC,IAAI,CAAC,iCAAiC,CAAC,CAAC;QACjD,CAAC;QAED,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC;YACnB,MAAM,CAAC,IAAI,CAAC,4BAA4B,CAAC,CAAC;QAC5C,CAAC;QAED,IAAI,CAAC,IAAI,CAAC,MAAM,IAAI,IAAI,CAAC,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC7C,MAAM,CAAC,IAAI,CAAC,sBAAsB,CAAC,CAAC;QACtC,CAAC;QAED,OAAO,MAAM,CAAC;IAChB,CAAC;IAED;;OAEG;IACO,qBAAqB;QAC7B,OAAO,IAAI,CAAC,MAAM,CAAC,kBAAkB,IAAI,GAAG,CAAC,CAAC,qBAAqB;IACrE,CAAC;CACF"}
1
+ {"version":3,"file":"base.driver.js","sourceRoot":"","sources":["../../src/drivers/base.driver.ts"],"names":[],"mappings":"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"}