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.
- package/README.md +519 -348
- package/dist/drivers/azure.driver.d.ts +88 -0
- package/dist/drivers/azure.driver.d.ts.map +1 -0
- package/dist/drivers/azure.driver.js +367 -0
- package/dist/drivers/azure.driver.js.map +1 -0
- package/dist/drivers/base.driver.d.ts +125 -24
- package/dist/drivers/base.driver.d.ts.map +1 -1
- package/dist/drivers/base.driver.js +248 -62
- package/dist/drivers/base.driver.js.map +1 -1
- package/dist/drivers/gcs.driver.d.ts +60 -13
- package/dist/drivers/gcs.driver.d.ts.map +1 -1
- package/dist/drivers/gcs.driver.js +242 -41
- package/dist/drivers/gcs.driver.js.map +1 -1
- package/dist/drivers/local.driver.d.ts +89 -12
- package/dist/drivers/local.driver.d.ts.map +1 -1
- package/dist/drivers/local.driver.js +533 -45
- package/dist/drivers/local.driver.js.map +1 -1
- package/dist/drivers/s3.driver.d.ts +64 -13
- package/dist/drivers/s3.driver.d.ts.map +1 -1
- package/dist/drivers/s3.driver.js +269 -41
- package/dist/drivers/s3.driver.js.map +1 -1
- package/dist/factory/driver.factory.d.ts +35 -29
- package/dist/factory/driver.factory.d.ts.map +1 -1
- package/dist/factory/driver.factory.js +119 -59
- package/dist/factory/driver.factory.js.map +1 -1
- package/dist/index.d.ts +23 -22
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +26 -46
- package/dist/index.js.map +1 -1
- package/dist/storage-manager.d.ts +205 -52
- package/dist/storage-manager.d.ts.map +1 -1
- package/dist/storage-manager.js +644 -73
- package/dist/storage-manager.js.map +1 -1
- package/dist/types/storage.types.d.ts +243 -18
- package/dist/types/storage.types.d.ts.map +1 -1
- package/dist/utils/config.utils.d.ts +28 -4
- package/dist/utils/config.utils.d.ts.map +1 -1
- package/dist/utils/config.utils.js +121 -47
- package/dist/utils/config.utils.js.map +1 -1
- package/dist/utils/file.utils.d.ts +111 -14
- package/dist/utils/file.utils.d.ts.map +1 -1
- package/dist/utils/file.utils.js +215 -32
- package/dist/utils/file.utils.js.map +1 -1
- package/package.json +51 -27
- package/dist/drivers/oci.driver.d.ts +0 -37
- package/dist/drivers/oci.driver.d.ts.map +0 -1
- package/dist/drivers/oci.driver.js +0 -84
- package/dist/drivers/oci.driver.js.map +0 -1
|
@@ -1,115 +1,176 @@
|
|
|
1
1
|
import dotenv from 'dotenv';
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
let dotenvInitialized = false;
|
|
3
|
+
/**
|
|
4
|
+
* Loads your .env file if it hasn't been loaded already.
|
|
5
|
+
* Safe to call multiple times — it only runs once.
|
|
6
|
+
*/
|
|
7
|
+
export function initializeDotenv() {
|
|
8
|
+
if (!dotenvInitialized) {
|
|
9
|
+
dotenv.config();
|
|
10
|
+
dotenvInitialized = true;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
5
13
|
const ENV_KEYS = {
|
|
6
14
|
FILE_DRIVER: 'FILE_DRIVER',
|
|
7
15
|
BUCKET_NAME: 'BUCKET_NAME',
|
|
16
|
+
BUCKET_PATH: 'BUCKET_PATH',
|
|
8
17
|
LOCAL_PATH: 'LOCAL_PATH',
|
|
9
18
|
PRESIGNED_URL_EXPIRY: 'PRESIGNED_URL_EXPIRY',
|
|
10
|
-
|
|
19
|
+
MAX_FILE_SIZE: 'MAX_FILE_SIZE',
|
|
11
20
|
AWS_REGION: 'AWS_REGION',
|
|
12
21
|
AWS_ACCESS_KEY: 'AWS_ACCESS_KEY',
|
|
13
22
|
AWS_SECRET_KEY: 'AWS_SECRET_KEY',
|
|
14
|
-
// Google Cloud Storage
|
|
15
23
|
GCS_PROJECT_ID: 'GCS_PROJECT_ID',
|
|
16
24
|
GCS_CREDENTIALS: 'GCS_CREDENTIALS',
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
25
|
+
AZURE_CONNECTION_STRING: 'AZURE_CONNECTION_STRING',
|
|
26
|
+
AZURE_ACCOUNT_NAME: 'AZURE_ACCOUNT_NAME',
|
|
27
|
+
AZURE_ACCOUNT_KEY: 'AZURE_ACCOUNT_KEY',
|
|
20
28
|
};
|
|
21
|
-
// Default configuration
|
|
22
29
|
const DEFAULT_CONFIG = {
|
|
23
|
-
presignedUrlExpiry: 600,
|
|
30
|
+
presignedUrlExpiry: 600,
|
|
24
31
|
localPath: 'public/express-storage',
|
|
25
32
|
};
|
|
26
33
|
/**
|
|
27
|
-
*
|
|
34
|
+
* Reads storage configuration from environment variables.
|
|
35
|
+
* Automatically loads .env on first call.
|
|
28
36
|
*/
|
|
29
37
|
export function loadEnvironmentConfig() {
|
|
38
|
+
initializeDotenv();
|
|
30
39
|
return {
|
|
31
40
|
FILE_DRIVER: process.env[ENV_KEYS.FILE_DRIVER] || '',
|
|
32
41
|
BUCKET_NAME: process.env[ENV_KEYS.BUCKET_NAME] || undefined,
|
|
42
|
+
BUCKET_PATH: process.env[ENV_KEYS.BUCKET_PATH] || undefined,
|
|
33
43
|
LOCAL_PATH: process.env[ENV_KEYS.LOCAL_PATH] || undefined,
|
|
34
44
|
PRESIGNED_URL_EXPIRY: process.env[ENV_KEYS.PRESIGNED_URL_EXPIRY] || undefined,
|
|
35
|
-
|
|
45
|
+
MAX_FILE_SIZE: process.env[ENV_KEYS.MAX_FILE_SIZE] || undefined,
|
|
36
46
|
AWS_REGION: process.env[ENV_KEYS.AWS_REGION] || undefined,
|
|
37
47
|
AWS_ACCESS_KEY: process.env[ENV_KEYS.AWS_ACCESS_KEY] || undefined,
|
|
38
48
|
AWS_SECRET_KEY: process.env[ENV_KEYS.AWS_SECRET_KEY] || undefined,
|
|
39
|
-
// Google Cloud Storage
|
|
40
49
|
GCS_PROJECT_ID: process.env[ENV_KEYS.GCS_PROJECT_ID] || undefined,
|
|
41
50
|
GCS_CREDENTIALS: process.env[ENV_KEYS.GCS_CREDENTIALS] || undefined,
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
51
|
+
AZURE_CONNECTION_STRING: process.env[ENV_KEYS.AZURE_CONNECTION_STRING] || undefined,
|
|
52
|
+
AZURE_ACCOUNT_NAME: process.env[ENV_KEYS.AZURE_ACCOUNT_NAME] || undefined,
|
|
53
|
+
AZURE_ACCOUNT_KEY: process.env[ENV_KEYS.AZURE_ACCOUNT_KEY] || undefined,
|
|
45
54
|
};
|
|
46
55
|
}
|
|
47
56
|
/**
|
|
48
|
-
*
|
|
57
|
+
* Safely parses a string to an integer, returning a default for invalid values.
|
|
58
|
+
*
|
|
59
|
+
* Unlike parseInt(), this function rejects strings with trailing non-numeric characters.
|
|
60
|
+
* For example, "100abc" returns the default value, not 100.
|
|
61
|
+
*/
|
|
62
|
+
function parseIntSafe(value, defaultValue) {
|
|
63
|
+
if (!value)
|
|
64
|
+
return defaultValue;
|
|
65
|
+
// Trim whitespace and check if the entire string is a valid integer
|
|
66
|
+
const trimmed = value.trim();
|
|
67
|
+
// Check if the string matches a valid integer pattern (optional sign followed by digits)
|
|
68
|
+
if (!/^-?\d+$/.test(trimmed)) {
|
|
69
|
+
return defaultValue;
|
|
70
|
+
}
|
|
71
|
+
const parsed = parseInt(trimmed, 10);
|
|
72
|
+
return Number.isNaN(parsed) ? defaultValue : parsed;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Converts environment variables into a StorageConfig object.
|
|
49
76
|
*/
|
|
50
77
|
export function environmentToStorageConfig(envConfig) {
|
|
51
78
|
const config = {
|
|
52
79
|
driver: envConfig.FILE_DRIVER,
|
|
53
80
|
bucketName: envConfig.BUCKET_NAME,
|
|
81
|
+
bucketPath: envConfig.BUCKET_PATH || '',
|
|
54
82
|
localPath: envConfig.LOCAL_PATH || DEFAULT_CONFIG.localPath,
|
|
55
|
-
presignedUrlExpiry: envConfig.PRESIGNED_URL_EXPIRY
|
|
56
|
-
|
|
57
|
-
: DEFAULT_CONFIG.presignedUrlExpiry,
|
|
58
|
-
// AWS S3
|
|
83
|
+
presignedUrlExpiry: parseIntSafe(envConfig.PRESIGNED_URL_EXPIRY, DEFAULT_CONFIG.presignedUrlExpiry),
|
|
84
|
+
maxFileSize: parseIntSafe(envConfig.MAX_FILE_SIZE, undefined),
|
|
59
85
|
awsRegion: envConfig.AWS_REGION,
|
|
60
86
|
awsAccessKey: envConfig.AWS_ACCESS_KEY,
|
|
61
87
|
awsSecretKey: envConfig.AWS_SECRET_KEY,
|
|
62
|
-
// Google Cloud Storage
|
|
63
88
|
gcsProjectId: envConfig.GCS_PROJECT_ID,
|
|
64
89
|
gcsCredentials: envConfig.GCS_CREDENTIALS,
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
90
|
+
azureConnectionString: envConfig.AZURE_CONNECTION_STRING,
|
|
91
|
+
azureAccountName: envConfig.AZURE_ACCOUNT_NAME,
|
|
92
|
+
azureAccountKey: envConfig.AZURE_ACCOUNT_KEY,
|
|
93
|
+
azureContainerName: envConfig.BUCKET_NAME, // Use BUCKET_NAME for Azure container
|
|
68
94
|
};
|
|
69
95
|
return config;
|
|
70
96
|
}
|
|
71
97
|
/**
|
|
72
|
-
*
|
|
98
|
+
* Validates a storage configuration.
|
|
99
|
+
*
|
|
100
|
+
* Checks that:
|
|
101
|
+
* - A valid driver is specified
|
|
102
|
+
* - Required credentials are present for the chosen driver
|
|
103
|
+
* - Numeric values are within acceptable ranges
|
|
104
|
+
*
|
|
105
|
+
* Returns an object with isValid and an array of error messages.
|
|
73
106
|
*/
|
|
74
107
|
export function validateStorageConfig(config) {
|
|
75
108
|
const errors = [];
|
|
76
|
-
//
|
|
109
|
+
// Check driver
|
|
77
110
|
if (!config.driver) {
|
|
78
111
|
errors.push('FILE_DRIVER is required');
|
|
79
112
|
}
|
|
80
|
-
else if (!['s3', 's3-presigned', 'gcs', 'gcs-presigned', '
|
|
81
|
-
errors.push(`Invalid FILE_DRIVER: ${config.driver}. Must be one of: s3, s3-presigned, gcs, gcs-presigned,
|
|
113
|
+
else if (!['s3', 's3-presigned', 'gcs', 'gcs-presigned', 'azure', 'azure-presigned', 'local'].includes(config.driver)) {
|
|
114
|
+
errors.push(`Invalid FILE_DRIVER: ${config.driver}. Must be one of: s3, s3-presigned, gcs, gcs-presigned, azure, azure-presigned, local`);
|
|
82
115
|
}
|
|
83
|
-
//
|
|
116
|
+
// S3 requirements
|
|
84
117
|
if (config.driver?.includes('s3')) {
|
|
85
118
|
if (!config.bucketName)
|
|
86
119
|
errors.push('BUCKET_NAME is required for S3');
|
|
87
120
|
if (!config.awsRegion)
|
|
88
121
|
errors.push('AWS_REGION is required for S3');
|
|
89
|
-
|
|
90
|
-
errors.push('AWS_ACCESS_KEY is required for S3');
|
|
91
|
-
if (!config.awsSecretKey)
|
|
92
|
-
errors.push('AWS_SECRET_KEY is required for S3');
|
|
122
|
+
// Access keys are optional — IAM roles work when running on AWS
|
|
93
123
|
}
|
|
124
|
+
// GCS requirements
|
|
94
125
|
if (config.driver?.includes('gcs')) {
|
|
95
126
|
if (!config.bucketName)
|
|
96
127
|
errors.push('BUCKET_NAME is required for GCS');
|
|
97
128
|
if (!config.gcsProjectId)
|
|
98
129
|
errors.push('GCS_PROJECT_ID is required for GCS');
|
|
99
|
-
|
|
100
|
-
errors.push('GCS_CREDENTIALS is required for GCS');
|
|
130
|
+
// Credentials are optional — ADC works when running on GCP
|
|
101
131
|
}
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
if (
|
|
108
|
-
|
|
132
|
+
// Azure requirements
|
|
133
|
+
if (config.driver?.includes('azure')) {
|
|
134
|
+
const hasConnectionString = !!config.azureConnectionString;
|
|
135
|
+
const hasAccountKey = config.azureAccountName && config.azureAccountKey;
|
|
136
|
+
const hasManagedIdentity = config.azureAccountName && !config.azureAccountKey;
|
|
137
|
+
if (config.driver === 'azure-presigned') {
|
|
138
|
+
// Presigned mode needs account key for SAS URL generation
|
|
139
|
+
if (!hasConnectionString && !hasAccountKey) {
|
|
140
|
+
errors.push('Azure presigned driver requires either AZURE_CONNECTION_STRING or both AZURE_ACCOUNT_NAME and AZURE_ACCOUNT_KEY (Managed Identity cannot generate SAS URLs)');
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
else {
|
|
144
|
+
// Direct mode supports any authentication method
|
|
145
|
+
if (!hasConnectionString && !hasAccountKey && !hasManagedIdentity) {
|
|
146
|
+
errors.push('Azure requires AZURE_CONNECTION_STRING, AZURE_ACCOUNT_NAME + AZURE_ACCOUNT_KEY, or AZURE_ACCOUNT_NAME only (for Managed Identity)');
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
if (!config.azureContainerName) {
|
|
150
|
+
errors.push('BUCKET_NAME is required for Azure');
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
// Validate URL expiry time
|
|
154
|
+
if (config.presignedUrlExpiry !== undefined) {
|
|
155
|
+
if (Number.isNaN(config.presignedUrlExpiry) || config.presignedUrlExpiry <= 0) {
|
|
156
|
+
errors.push('PRESIGNED_URL_EXPIRY must be a positive number greater than 0');
|
|
157
|
+
}
|
|
158
|
+
// Max 7 days — that's the cloud provider limit
|
|
159
|
+
const MAX_EXPIRY = 604800;
|
|
160
|
+
if (!Number.isNaN(config.presignedUrlExpiry) && config.presignedUrlExpiry > MAX_EXPIRY) {
|
|
161
|
+
errors.push(`PRESIGNED_URL_EXPIRY cannot exceed ${MAX_EXPIRY} seconds (7 days). Cloud providers enforce this limit.`);
|
|
162
|
+
}
|
|
109
163
|
}
|
|
110
|
-
// Validate
|
|
111
|
-
if (config.
|
|
112
|
-
|
|
164
|
+
// Validate max file size
|
|
165
|
+
if (config.maxFileSize !== undefined) {
|
|
166
|
+
if (Number.isNaN(config.maxFileSize) || config.maxFileSize <= 0) {
|
|
167
|
+
errors.push('MAX_FILE_SIZE must be a positive number greater than 0');
|
|
168
|
+
}
|
|
169
|
+
// Max 5TB — reasonable limit for single uploads
|
|
170
|
+
const MAX_FILE_SIZE_LIMIT = 5 * 1024 * 1024 * 1024 * 1024;
|
|
171
|
+
if (!Number.isNaN(config.maxFileSize) && config.maxFileSize > MAX_FILE_SIZE_LIMIT) {
|
|
172
|
+
errors.push(`MAX_FILE_SIZE cannot exceed ${MAX_FILE_SIZE_LIMIT} bytes (5TB). Consider using multipart uploads for larger files.`);
|
|
173
|
+
}
|
|
113
174
|
}
|
|
114
175
|
return {
|
|
115
176
|
isValid: errors.length === 0,
|
|
@@ -117,7 +178,8 @@ export function validateStorageConfig(config) {
|
|
|
117
178
|
};
|
|
118
179
|
}
|
|
119
180
|
/**
|
|
120
|
-
*
|
|
181
|
+
* Convenience function that loads and validates config in one call.
|
|
182
|
+
* Returns both the config and validation result.
|
|
121
183
|
*/
|
|
122
184
|
export function loadAndValidateConfig() {
|
|
123
185
|
const envConfig = loadEnvironmentConfig();
|
|
@@ -125,4 +187,16 @@ export function loadAndValidateConfig() {
|
|
|
125
187
|
const validation = validateStorageConfig(config);
|
|
126
188
|
return { config, validation };
|
|
127
189
|
}
|
|
190
|
+
/**
|
|
191
|
+
* Resets the dotenv initialization flag.
|
|
192
|
+
*
|
|
193
|
+
* This is primarily useful for testing scenarios where you need to
|
|
194
|
+
* reinitialize dotenv with different environment variables.
|
|
195
|
+
*
|
|
196
|
+
* WARNING: This does not clear previously loaded environment variables.
|
|
197
|
+
* It only allows initializeDotenv() to run again.
|
|
198
|
+
*/
|
|
199
|
+
export function resetDotenvInitialization() {
|
|
200
|
+
dotenvInitialized = false;
|
|
201
|
+
}
|
|
128
202
|
//# sourceMappingURL=config.utils.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.utils.js","sourceRoot":"","sources":["../../src/utils/config.utils.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,QAAQ,CAAC;AAG5B,
|
|
1
|
+
{"version":3,"file":"config.utils.js","sourceRoot":"","sources":["../../src/utils/config.utils.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,45 +1,142 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Creates a unique filename that won't collide with existing files.
|
|
3
|
+
*
|
|
4
|
+
* Format: {timestamp}_{random}_{sanitized_name}.{extension}
|
|
5
|
+
* Example: 1769104576000_a1b2c3d4e5_my_image.jpeg
|
|
6
|
+
*
|
|
7
|
+
* The random part uses crypto.randomBytes() for extra collision resistance
|
|
8
|
+
* in high-throughput scenarios.
|
|
3
9
|
*/
|
|
4
10
|
export declare function generateUniqueFileName(originalName: string): string;
|
|
5
11
|
/**
|
|
6
|
-
*
|
|
12
|
+
* Makes a filename safe for storage by removing problematic characters.
|
|
13
|
+
*
|
|
14
|
+
* Replaces anything that isn't alphanumeric, a dot, or a hyphen with underscores.
|
|
15
|
+
* This ensures compatibility with all filesystems and cloud storage providers.
|
|
16
|
+
*
|
|
17
|
+
* Note: Unicode characters like Chinese or emojis become underscores.
|
|
18
|
+
* If you need to preserve these, consider using your own sanitization function.
|
|
7
19
|
*/
|
|
8
20
|
export declare function sanitizeFileName(fileName: string): string;
|
|
9
21
|
/**
|
|
10
|
-
*
|
|
22
|
+
* Checks if a filename is safe to use.
|
|
23
|
+
*
|
|
24
|
+
* Rejects:
|
|
25
|
+
* - Empty filenames
|
|
26
|
+
* - Filenames over 255 characters
|
|
27
|
+
* - Path traversal attempts (../, /, \)
|
|
28
|
+
* - Null bytes
|
|
29
|
+
*
|
|
30
|
+
* Returns an error message if invalid, null if OK.
|
|
31
|
+
*/
|
|
32
|
+
export declare function validateFileName(fileName: string): string | null;
|
|
33
|
+
/**
|
|
34
|
+
* Creates a date-based folder path: YYYY/MM
|
|
35
|
+
*
|
|
36
|
+
* Uses UTC to keep things consistent across timezones.
|
|
37
|
+
* Example: For January 2026 -> 'uploads/2026/01'
|
|
11
38
|
*/
|
|
12
39
|
export declare function createMonthBasedPath(basePath: string): string;
|
|
13
40
|
/**
|
|
14
|
-
*
|
|
41
|
+
* Creates a directory if it doesn't exist.
|
|
42
|
+
* Also creates any parent directories needed (recursive).
|
|
15
43
|
*/
|
|
16
44
|
export declare function ensureDirectoryExists(dirPath: string): void;
|
|
17
45
|
/**
|
|
18
|
-
*
|
|
46
|
+
* Converts bytes to a human-readable string.
|
|
47
|
+
*
|
|
48
|
+
* Examples:
|
|
49
|
+
* - 1024 -> "1 KB"
|
|
50
|
+
* - 1048576 -> "1 MB"
|
|
51
|
+
* - 0 -> "0 Bytes"
|
|
19
52
|
*/
|
|
20
53
|
export declare function formatFileSize(bytes: number): string;
|
|
21
54
|
/**
|
|
22
|
-
*
|
|
55
|
+
* Checks if a file size is within the allowed limit.
|
|
23
56
|
*/
|
|
24
57
|
export declare function validateFileSize(fileSize: number, maxSize: number): boolean;
|
|
25
58
|
/**
|
|
26
|
-
*
|
|
59
|
+
* Checks if a MIME type is in the allowed list.
|
|
27
60
|
*/
|
|
28
61
|
export declare function validateFileType(mimeType: string, allowedTypes: string[]): boolean;
|
|
29
62
|
/**
|
|
30
|
-
*
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
*
|
|
63
|
+
* Extracts the file extension (lowercase, includes the dot).
|
|
64
|
+
*
|
|
65
|
+
* Examples:
|
|
66
|
+
* - 'photo.jpg' -> '.jpg'
|
|
67
|
+
* - '.gitignore' -> '' (dotfiles have no extension)
|
|
68
|
+
* - 'archive.tar.gz' -> '.gz' (only the last extension)
|
|
35
69
|
*/
|
|
36
70
|
export declare function getFileExtension(fileName: string): string;
|
|
37
71
|
/**
|
|
38
|
-
*
|
|
72
|
+
* Checks if a MIME type indicates an image.
|
|
39
73
|
*/
|
|
40
74
|
export declare function isImageFile(mimeType: string): boolean;
|
|
41
75
|
/**
|
|
42
|
-
*
|
|
76
|
+
* Checks if a MIME type indicates a document (PDF, Word, Excel, etc.).
|
|
43
77
|
*/
|
|
44
78
|
export declare function isDocumentFile(mimeType: string): boolean;
|
|
79
|
+
/**
|
|
80
|
+
* Configuration for retry behavior.
|
|
81
|
+
*/
|
|
82
|
+
export interface RetryOptions {
|
|
83
|
+
/** Total attempts including the first one. Default: 3 */
|
|
84
|
+
maxAttempts?: number;
|
|
85
|
+
/** Starting delay between retries in ms. Default: 1000 */
|
|
86
|
+
baseDelay?: number;
|
|
87
|
+
/** Maximum delay between retries in ms. Default: 10000 */
|
|
88
|
+
maxDelay?: number;
|
|
89
|
+
/** Use exponential backoff. Default: true */
|
|
90
|
+
exponentialBackoff?: boolean;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Retries an async operation with exponential backoff.
|
|
94
|
+
*
|
|
95
|
+
* Great for cloud operations that might fail due to network blips
|
|
96
|
+
* or rate limiting.
|
|
97
|
+
*
|
|
98
|
+
* @example
|
|
99
|
+
* // Retry up to 3 times with increasing delays
|
|
100
|
+
* const result = await withRetry(() => storage.uploadFile(file));
|
|
101
|
+
*
|
|
102
|
+
* // More aggressive retry strategy
|
|
103
|
+
* const result = await withRetry(() => fetchData(), {
|
|
104
|
+
* maxAttempts: 5,
|
|
105
|
+
* baseDelay: 500
|
|
106
|
+
* });
|
|
107
|
+
*/
|
|
108
|
+
export declare function withRetry<T>(operation: () => Promise<T>, options?: RetryOptions): Promise<T>;
|
|
109
|
+
/**
|
|
110
|
+
* Pauses execution for the specified number of milliseconds.
|
|
111
|
+
*/
|
|
112
|
+
export declare function sleep(ms: number): Promise<void>;
|
|
113
|
+
/**
|
|
114
|
+
* Configuration for concurrent execution.
|
|
115
|
+
*/
|
|
116
|
+
export interface ConcurrencyOptions {
|
|
117
|
+
/** Maximum parallel operations. Default: 10 */
|
|
118
|
+
maxConcurrent?: number;
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Processes an array with a concurrency limit.
|
|
122
|
+
*
|
|
123
|
+
* Prevents overwhelming APIs or running out of resources by limiting
|
|
124
|
+
* how many operations run at once.
|
|
125
|
+
*
|
|
126
|
+
* Implementation uses pre-assigned chunk-based processing to avoid any
|
|
127
|
+
* potential race conditions with shared index counters. Each worker gets
|
|
128
|
+
* its own set of indices to process.
|
|
129
|
+
*
|
|
130
|
+
* Note: The input array is snapshotted at the start to prevent issues
|
|
131
|
+
* if the caller modifies it during processing.
|
|
132
|
+
*
|
|
133
|
+
* @example
|
|
134
|
+
* // Upload 100 files, but only 10 at a time
|
|
135
|
+
* const results = await withConcurrencyLimit(
|
|
136
|
+
* files,
|
|
137
|
+
* (file) => uploadFile(file),
|
|
138
|
+
* { maxConcurrent: 10 }
|
|
139
|
+
* );
|
|
140
|
+
*/
|
|
141
|
+
export declare function withConcurrencyLimit<T, R>(items: T[], operation: (item: T, index: number) => Promise<R>, options?: ConcurrencyOptions): Promise<R[]>;
|
|
45
142
|
//# sourceMappingURL=file.utils.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"file.utils.d.ts","sourceRoot":"","sources":["../../src/utils/file.utils.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"file.utils.d.ts","sourceRoot":"","sources":["../../src/utils/file.utils.ts"],"names":[],"mappings":"AAIA;;;;;;;;GAQG;AACH,wBAAgB,sBAAsB,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,CAsBnE;AAED;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAQzD;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAuBhE;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAM7D;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAI3D;AAED;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAwBpD;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAE3E;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,GAAG,OAAO,CAElF;AAED;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CASzD;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAErD;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAWxD;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,yDAAyD;IACzD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,0DAA0D;IAC1D,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,0DAA0D;IAC1D,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,6CAA6C;IAC7C,kBAAkB,CAAC,EAAE,OAAO,CAAC;CAC9B;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAsB,SAAS,CAAC,CAAC,EAC/B,SAAS,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EAC3B,OAAO,GAAE,YAAiB,GACzB,OAAO,CAAC,CAAC,CAAC,CA2BZ;AAED;;GAEG;AACH,wBAAgB,KAAK,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAE/C;AAED;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,+CAA+C;IAC/C,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAsB,oBAAoB,CAAC,CAAC,EAAE,CAAC,EAC7C,KAAK,EAAE,CAAC,EAAE,EACV,SAAS,EAAE,CAAC,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,CAAC,CAAC,EACjD,OAAO,GAAE,kBAAuB,GAC/B,OAAO,CAAC,CAAC,EAAE,CAAC,CAyCd"}
|