express-storage 2.0.3 → 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.
- package/README.md +366 -34
- package/dist/cjs/config/index.d.ts +10 -0
- package/dist/cjs/config/index.d.ts.map +1 -0
- package/dist/cjs/config/index.js +19 -0
- package/dist/cjs/config/index.js.map +1 -0
- package/dist/cjs/drivers/azure.driver.d.ts +27 -42
- package/dist/cjs/drivers/azure.driver.d.ts.map +1 -1
- package/dist/cjs/drivers/azure.driver.js +206 -212
- package/dist/cjs/drivers/azure.driver.js.map +1 -1
- package/dist/cjs/drivers/base.driver.d.ts +69 -103
- package/dist/cjs/drivers/base.driver.d.ts.map +1 -1
- package/dist/cjs/drivers/base.driver.js +170 -167
- package/dist/cjs/drivers/base.driver.js.map +1 -1
- package/dist/cjs/drivers/gcs.driver.d.ts +20 -38
- package/dist/cjs/drivers/gcs.driver.d.ts.map +1 -1
- package/dist/cjs/drivers/gcs.driver.js +160 -176
- package/dist/cjs/drivers/gcs.driver.js.map +1 -1
- package/dist/cjs/drivers/index.d.ts +15 -0
- package/dist/cjs/drivers/index.d.ts.map +1 -0
- package/dist/cjs/drivers/index.js +26 -0
- package/dist/cjs/drivers/index.js.map +1 -0
- package/dist/cjs/drivers/local.driver.d.ts +24 -45
- package/dist/cjs/drivers/local.driver.d.ts.map +1 -1
- package/dist/cjs/drivers/local.driver.js +266 -338
- package/dist/cjs/drivers/local.driver.js.map +1 -1
- package/dist/cjs/drivers/s3.driver.d.ts +19 -39
- package/dist/cjs/drivers/s3.driver.d.ts.map +1 -1
- package/dist/cjs/drivers/s3.driver.js +205 -197
- package/dist/cjs/drivers/s3.driver.js.map +1 -1
- package/dist/cjs/factory/driver.factory.d.ts +32 -51
- package/dist/cjs/factory/driver.factory.d.ts.map +1 -1
- package/dist/cjs/factory/driver.factory.js +75 -155
- package/dist/cjs/factory/driver.factory.js.map +1 -1
- package/dist/cjs/index.d.ts +11 -15
- package/dist/cjs/index.d.ts.map +1 -1
- package/dist/cjs/index.js +14 -47
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/storage-manager.d.ts +107 -125
- package/dist/cjs/storage-manager.d.ts.map +1 -1
- package/dist/cjs/storage-manager.js +346 -416
- package/dist/cjs/storage-manager.js.map +1 -1
- package/dist/cjs/types/storage.types.d.ts +250 -107
- package/dist/cjs/types/storage.types.d.ts.map +1 -1
- package/dist/cjs/utils/file.utils.d.ts +62 -8
- package/dist/cjs/utils/file.utils.d.ts.map +1 -1
- package/dist/cjs/utils/file.utils.js +196 -29
- package/dist/cjs/utils/file.utils.js.map +1 -1
- package/dist/cjs/utils/index.d.ts +12 -0
- package/dist/cjs/utils/index.d.ts.map +1 -0
- package/dist/cjs/utils/index.js +36 -0
- package/dist/cjs/utils/index.js.map +1 -0
- package/dist/cjs/utils/rate-limiter.d.ts +40 -0
- package/dist/cjs/utils/rate-limiter.d.ts.map +1 -0
- package/dist/cjs/utils/rate-limiter.js +87 -0
- package/dist/cjs/utils/rate-limiter.js.map +1 -0
- package/dist/esm/config/index.d.ts +10 -0
- package/dist/esm/config/index.d.ts.map +1 -0
- package/dist/esm/config/index.js +10 -0
- package/dist/esm/config/index.js.map +1 -0
- package/dist/esm/drivers/azure.driver.d.ts +27 -42
- package/dist/esm/drivers/azure.driver.d.ts.map +1 -1
- package/dist/esm/drivers/azure.driver.js +172 -210
- package/dist/esm/drivers/azure.driver.js.map +1 -1
- package/dist/esm/drivers/base.driver.d.ts +69 -103
- package/dist/esm/drivers/base.driver.d.ts.map +1 -1
- package/dist/esm/drivers/base.driver.js +171 -168
- package/dist/esm/drivers/base.driver.js.map +1 -1
- package/dist/esm/drivers/gcs.driver.d.ts +20 -38
- package/dist/esm/drivers/gcs.driver.d.ts.map +1 -1
- package/dist/esm/drivers/gcs.driver.js +126 -174
- package/dist/esm/drivers/gcs.driver.js.map +1 -1
- package/dist/esm/drivers/index.d.ts +15 -0
- package/dist/esm/drivers/index.d.ts.map +1 -0
- package/dist/esm/drivers/index.js +15 -0
- package/dist/esm/drivers/index.js.map +1 -0
- package/dist/esm/drivers/local.driver.d.ts +24 -45
- package/dist/esm/drivers/local.driver.d.ts.map +1 -1
- package/dist/esm/drivers/local.driver.js +266 -338
- package/dist/esm/drivers/local.driver.js.map +1 -1
- package/dist/esm/drivers/s3.driver.d.ts +19 -39
- package/dist/esm/drivers/s3.driver.d.ts.map +1 -1
- package/dist/esm/drivers/s3.driver.js +171 -195
- package/dist/esm/drivers/s3.driver.js.map +1 -1
- package/dist/esm/factory/driver.factory.d.ts +32 -51
- package/dist/esm/factory/driver.factory.d.ts.map +1 -1
- package/dist/esm/factory/driver.factory.js +73 -158
- package/dist/esm/factory/driver.factory.js.map +1 -1
- package/dist/esm/index.d.ts +11 -15
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/index.js +12 -19
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/storage-manager.d.ts +107 -125
- package/dist/esm/storage-manager.d.ts.map +1 -1
- package/dist/esm/storage-manager.js +348 -418
- package/dist/esm/storage-manager.js.map +1 -1
- package/dist/esm/types/storage.types.d.ts +250 -107
- package/dist/esm/types/storage.types.d.ts.map +1 -1
- package/dist/esm/utils/file.utils.d.ts +62 -8
- package/dist/esm/utils/file.utils.d.ts.map +1 -1
- package/dist/esm/utils/file.utils.js +190 -29
- package/dist/esm/utils/file.utils.js.map +1 -1
- package/dist/esm/utils/index.d.ts +12 -0
- package/dist/esm/utils/index.d.ts.map +1 -0
- package/dist/esm/utils/index.js +11 -0
- package/dist/esm/utils/index.js.map +1 -0
- package/dist/esm/utils/rate-limiter.d.ts +40 -0
- package/dist/esm/utils/rate-limiter.d.ts.map +1 -0
- package/dist/esm/utils/rate-limiter.js +82 -0
- package/dist/esm/utils/rate-limiter.js.map +1 -0
- package/package.json +83 -48
- package/src/config/index.ts +17 -0
- package/src/drivers/azure.driver.ts +434 -0
- package/src/drivers/base.driver.ts +436 -0
- package/src/drivers/gcs.driver.ts +366 -0
- package/src/drivers/index.ts +15 -0
- package/src/drivers/local.driver.ts +626 -0
- package/src/drivers/s3.driver.ts +459 -0
- package/src/factory/driver.factory.ts +101 -0
- package/src/index.ts +72 -0
- package/src/storage-manager.ts +801 -0
- package/src/types/storage.types.ts +561 -0
- package/src/utils/config.utils.ts +229 -0
- package/src/utils/file.utils.ts +536 -0
- package/src/utils/index.ts +35 -0
- package/src/utils/rate-limiter.ts +94 -0
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { StorageErrorCode } from '../types/storage.types.js';
|
|
1
2
|
/**
|
|
2
3
|
* Creates a unique filename that won't collide with existing files.
|
|
3
4
|
*
|
|
@@ -30,6 +31,17 @@ export declare function sanitizeFileName(fileName: string): string;
|
|
|
30
31
|
* Returns an error message if invalid, null if OK.
|
|
31
32
|
*/
|
|
32
33
|
export declare function validateFileName(fileName: string): string | null;
|
|
34
|
+
/**
|
|
35
|
+
* Returns true if the value contains path traversal sequences (`..`) or null bytes.
|
|
36
|
+
* Checks both the raw value and its URL-decoded form to catch encoded attacks
|
|
37
|
+
* like `%2e%2e/etc/passwd`.
|
|
38
|
+
*/
|
|
39
|
+
export declare function hasPathTraversal(value: string): boolean;
|
|
40
|
+
/**
|
|
41
|
+
* URL-encodes each segment of a `/`-separated path individually.
|
|
42
|
+
* Preserves the `/` separators while encoding special characters within segments.
|
|
43
|
+
*/
|
|
44
|
+
export declare function encodePathSegments(filePath: string): string;
|
|
33
45
|
/**
|
|
34
46
|
* Creates a date-based folder path: YYYY/MM
|
|
35
47
|
*
|
|
@@ -41,7 +53,7 @@ export declare function createMonthBasedPath(basePath: string): string;
|
|
|
41
53
|
* Creates a directory if it doesn't exist.
|
|
42
54
|
* Also creates any parent directories needed (recursive).
|
|
43
55
|
*/
|
|
44
|
-
export declare function ensureDirectoryExists(dirPath: string): void
|
|
56
|
+
export declare function ensureDirectoryExists(dirPath: string): Promise<void>;
|
|
45
57
|
/**
|
|
46
58
|
* Converts bytes to a human-readable string.
|
|
47
59
|
*
|
|
@@ -106,6 +118,50 @@ export interface RetryOptions {
|
|
|
106
118
|
* });
|
|
107
119
|
*/
|
|
108
120
|
export declare function withRetry<T>(operation: () => Promise<T>, options?: RetryOptions): Promise<T>;
|
|
121
|
+
/**
|
|
122
|
+
* Returns true if the string is a valid MIME type format (type/subtype).
|
|
123
|
+
*/
|
|
124
|
+
export declare function isValidMimeType(mimeType: string): boolean;
|
|
125
|
+
/**
|
|
126
|
+
* Validates a folder path for use in storage operations.
|
|
127
|
+
* Returns an error message if invalid, null if OK.
|
|
128
|
+
*/
|
|
129
|
+
export declare function validateFolderPath(folder: string): string | null;
|
|
130
|
+
/**
|
|
131
|
+
* Validates a file against upload constraints (size, MIME type, extension).
|
|
132
|
+
* Returns `{ error, code }` on failure, `null` if the file passes all checks.
|
|
133
|
+
*/
|
|
134
|
+
export declare function validateFileForUpload(file: Express.Multer.File, options: {
|
|
135
|
+
maxSize?: number;
|
|
136
|
+
allowedMimeTypes?: string[];
|
|
137
|
+
allowedExtensions?: string[];
|
|
138
|
+
}): {
|
|
139
|
+
error: string;
|
|
140
|
+
code: StorageErrorCode;
|
|
141
|
+
} | null;
|
|
142
|
+
/**
|
|
143
|
+
* Detects MIME type from file content by examining magic bytes.
|
|
144
|
+
*
|
|
145
|
+
* Useful in `beforeUpload` hooks to verify that a file's actual content
|
|
146
|
+
* matches its declared MIME type — particularly for cloud uploads where
|
|
147
|
+
* the driver trusts the client-provided MIME type.
|
|
148
|
+
*
|
|
149
|
+
* @param data - Buffer containing at least the first 12 bytes of the file
|
|
150
|
+
* @returns Detected MIME type, or undefined if unknown
|
|
151
|
+
*
|
|
152
|
+
* @example
|
|
153
|
+
* const storage = new StorageManager({
|
|
154
|
+
* hooks: {
|
|
155
|
+
* beforeUpload: async (file) => {
|
|
156
|
+
* const actual = detectMimeType(file.buffer);
|
|
157
|
+
* if (actual && actual !== file.mimetype) {
|
|
158
|
+
* throw new Error(`Content mismatch: declared ${file.mimetype}, detected ${actual}`);
|
|
159
|
+
* }
|
|
160
|
+
* },
|
|
161
|
+
* },
|
|
162
|
+
* });
|
|
163
|
+
*/
|
|
164
|
+
export declare function detectMimeType(data: Buffer): string | undefined;
|
|
109
165
|
/**
|
|
110
166
|
* Pauses execution for the specified number of milliseconds.
|
|
111
167
|
*/
|
|
@@ -116,6 +172,8 @@ export declare function sleep(ms: number): Promise<void>;
|
|
|
116
172
|
export interface ConcurrencyOptions {
|
|
117
173
|
/** Maximum parallel operations. Default: 10 */
|
|
118
174
|
maxConcurrent?: number;
|
|
175
|
+
/** Pass an AbortSignal to cancel remaining work mid-flight. */
|
|
176
|
+
signal?: AbortSignal | undefined;
|
|
119
177
|
}
|
|
120
178
|
/**
|
|
121
179
|
* Processes an array with a concurrency limit.
|
|
@@ -123,15 +181,11 @@ export interface ConcurrencyOptions {
|
|
|
123
181
|
* Prevents overwhelming APIs or running out of resources by limiting
|
|
124
182
|
* how many operations run at once.
|
|
125
183
|
*
|
|
126
|
-
*
|
|
127
|
-
*
|
|
128
|
-
*
|
|
129
|
-
*
|
|
130
|
-
* Note: The input array is snapshotted at the start to prevent issues
|
|
131
|
-
* if the caller modifies it during processing.
|
|
184
|
+
* Uses a shared-index work-stealing approach: workers pull the next
|
|
185
|
+
* available item as soon as they finish one, ensuring even load
|
|
186
|
+
* distribution regardless of per-item processing time.
|
|
132
187
|
*
|
|
133
188
|
* @example
|
|
134
|
-
* // Upload 100 files, but only 10 at a time
|
|
135
189
|
* const results = await withConcurrencyLimit(
|
|
136
190
|
* files,
|
|
137
191
|
* (file) => uploadFile(file),
|
|
@@ -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":"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"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import path from 'path';
|
|
2
|
-
import
|
|
2
|
+
import fsPromises from 'fs/promises';
|
|
3
3
|
import crypto from 'crypto';
|
|
4
4
|
/**
|
|
5
5
|
* Creates a unique filename that won't collide with existing files.
|
|
@@ -12,7 +12,7 @@ import crypto from 'crypto';
|
|
|
12
12
|
*/
|
|
13
13
|
export function generateUniqueFileName(originalName) {
|
|
14
14
|
const timestamp = Date.now();
|
|
15
|
-
const randomSuffix = crypto.randomBytes(
|
|
15
|
+
const randomSuffix = crypto.randomBytes(5).toString('hex');
|
|
16
16
|
// Handle dotfiles like .gitignore or .env (they have no extension)
|
|
17
17
|
let extension;
|
|
18
18
|
let baseName;
|
|
@@ -77,6 +77,30 @@ export function validateFileName(fileName) {
|
|
|
77
77
|
}
|
|
78
78
|
return null;
|
|
79
79
|
}
|
|
80
|
+
/**
|
|
81
|
+
* Returns true if the value contains path traversal sequences (`..`) or null bytes.
|
|
82
|
+
* Checks both the raw value and its URL-decoded form to catch encoded attacks
|
|
83
|
+
* like `%2e%2e/etc/passwd`.
|
|
84
|
+
*/
|
|
85
|
+
export function hasPathTraversal(value) {
|
|
86
|
+
if (value.includes('..') || value.includes('\0')) {
|
|
87
|
+
return true;
|
|
88
|
+
}
|
|
89
|
+
try {
|
|
90
|
+
const decoded = decodeURIComponent(value);
|
|
91
|
+
return decoded.includes('..') || decoded.includes('\0');
|
|
92
|
+
}
|
|
93
|
+
catch {
|
|
94
|
+
return true;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* URL-encodes each segment of a `/`-separated path individually.
|
|
99
|
+
* Preserves the `/` separators while encoding special characters within segments.
|
|
100
|
+
*/
|
|
101
|
+
export function encodePathSegments(filePath) {
|
|
102
|
+
return filePath.split('/').map(segment => encodeURIComponent(segment)).join('/');
|
|
103
|
+
}
|
|
80
104
|
/**
|
|
81
105
|
* Creates a date-based folder path: YYYY/MM
|
|
82
106
|
*
|
|
@@ -93,10 +117,8 @@ export function createMonthBasedPath(basePath) {
|
|
|
93
117
|
* Creates a directory if it doesn't exist.
|
|
94
118
|
* Also creates any parent directories needed (recursive).
|
|
95
119
|
*/
|
|
96
|
-
export function ensureDirectoryExists(dirPath) {
|
|
97
|
-
|
|
98
|
-
fs.mkdirSync(dirPath, { recursive: true });
|
|
99
|
-
}
|
|
120
|
+
export async function ensureDirectoryExists(dirPath) {
|
|
121
|
+
await fsPromises.mkdir(dirPath, { recursive: true });
|
|
100
122
|
}
|
|
101
123
|
/**
|
|
102
124
|
* Converts bytes to a human-readable string.
|
|
@@ -213,6 +235,151 @@ export async function withRetry(operation, options = {}) {
|
|
|
213
235
|
}
|
|
214
236
|
throw lastError || new Error(`Operation failed after ${maxAttempts} attempts`);
|
|
215
237
|
}
|
|
238
|
+
/**
|
|
239
|
+
* Returns true if the string is a valid MIME type format (type/subtype).
|
|
240
|
+
*/
|
|
241
|
+
export function isValidMimeType(mimeType) {
|
|
242
|
+
return /^[a-zA-Z0-9][a-zA-Z0-9!#$&\-^_.+]*\/[a-zA-Z0-9][a-zA-Z0-9!#$&\-^_.+]*$/.test(mimeType);
|
|
243
|
+
}
|
|
244
|
+
/**
|
|
245
|
+
* Validates a folder path for use in storage operations.
|
|
246
|
+
* Returns an error message if invalid, null if OK.
|
|
247
|
+
*/
|
|
248
|
+
export function validateFolderPath(folder) {
|
|
249
|
+
if (hasPathTraversal(folder)) {
|
|
250
|
+
return folder.includes('..')
|
|
251
|
+
? 'Folder path cannot contain path traversal sequences (..)'
|
|
252
|
+
: 'Folder path cannot contain null bytes';
|
|
253
|
+
}
|
|
254
|
+
if (/[<>:"|?*\\;$`']/.test(folder)) {
|
|
255
|
+
return "Folder path contains invalid characters. Avoid: < > : \" | ? * \\ ; $ ` '";
|
|
256
|
+
}
|
|
257
|
+
if (/\/{2,}/.test(folder)) {
|
|
258
|
+
return 'Folder path cannot contain consecutive slashes';
|
|
259
|
+
}
|
|
260
|
+
return null;
|
|
261
|
+
}
|
|
262
|
+
/**
|
|
263
|
+
* Validates a file against upload constraints (size, MIME type, extension).
|
|
264
|
+
* Returns `{ error, code }` on failure, `null` if the file passes all checks.
|
|
265
|
+
*/
|
|
266
|
+
export function validateFileForUpload(file, options) {
|
|
267
|
+
if (!file) {
|
|
268
|
+
return { error: 'No file provided', code: 'NO_FILE' };
|
|
269
|
+
}
|
|
270
|
+
if (options.maxSize !== undefined && file.size > options.maxSize) {
|
|
271
|
+
return { error: `File size ${file.size} exceeds maximum allowed size of ${options.maxSize} bytes`, code: 'FILE_TOO_LARGE' };
|
|
272
|
+
}
|
|
273
|
+
if (options.allowedMimeTypes) {
|
|
274
|
+
if (options.allowedMimeTypes.length === 0) {
|
|
275
|
+
return { error: 'No MIME types are allowed (allowedMimeTypes is empty). To allow all types, omit this option or use ["*/*"]', code: 'INVALID_MIME_TYPE' };
|
|
276
|
+
}
|
|
277
|
+
const allowsAll = options.allowedMimeTypes.includes('*/*') || options.allowedMimeTypes.includes('*');
|
|
278
|
+
if (!allowsAll && !options.allowedMimeTypes.includes(file.mimetype)) {
|
|
279
|
+
return { error: `File type '${file.mimetype}' is not allowed. Allowed types: ${options.allowedMimeTypes.join(', ')}`, code: 'INVALID_MIME_TYPE' };
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
if (options.allowedExtensions) {
|
|
283
|
+
if (options.allowedExtensions.length === 0) {
|
|
284
|
+
return { error: 'No file extensions are allowed (allowedExtensions is empty). To allow all extensions, use ["*"]', code: 'INVALID_EXTENSION' };
|
|
285
|
+
}
|
|
286
|
+
const ext = getFileExtension(file.originalname || '').toLowerCase();
|
|
287
|
+
const normalizedAllowed = options.allowedExtensions.map(e => e.toLowerCase());
|
|
288
|
+
const SPECIAL_VALUES = ['', '*', 'none'];
|
|
289
|
+
if (ext === '') {
|
|
290
|
+
const allowsNoExtension = normalizedAllowed.some(e => SPECIAL_VALUES.includes(e));
|
|
291
|
+
if (!allowsNoExtension) {
|
|
292
|
+
return { error: `File has no extension. Allowed extensions: ${options.allowedExtensions.join(', ')} (use '' or '*' to allow files without extensions)`, code: 'INVALID_EXTENSION' };
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
else {
|
|
296
|
+
const normalizedExtensions = normalizedAllowed
|
|
297
|
+
.filter(e => !SPECIAL_VALUES.includes(e))
|
|
298
|
+
.map(e => e.startsWith('.') ? e : `.${e}`);
|
|
299
|
+
const allowsAllExt = normalizedAllowed.includes('*');
|
|
300
|
+
if (!allowsAllExt && !normalizedExtensions.includes(ext)) {
|
|
301
|
+
return { error: `File extension '${ext}' is not allowed. Allowed extensions: ${options.allowedExtensions.join(', ')}`, code: 'INVALID_EXTENSION' };
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
return null;
|
|
306
|
+
}
|
|
307
|
+
// ---------------------------------------------------------------------------
|
|
308
|
+
// MIME type detection from file content (magic bytes)
|
|
309
|
+
// ---------------------------------------------------------------------------
|
|
310
|
+
const MAGIC_SIGNATURES = [
|
|
311
|
+
{ bytes: [0xFF, 0xD8, 0xFF], mimeType: 'image/jpeg' },
|
|
312
|
+
{ bytes: [0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A], mimeType: 'image/png' },
|
|
313
|
+
{ bytes: [0x47, 0x49, 0x46, 0x38, 0x37, 0x61], mimeType: 'image/gif' },
|
|
314
|
+
{ bytes: [0x47, 0x49, 0x46, 0x38, 0x39, 0x61], mimeType: 'image/gif' },
|
|
315
|
+
{ bytes: [0x42, 0x4D], mimeType: 'image/bmp' },
|
|
316
|
+
{ bytes: [0x25, 0x50, 0x44, 0x46], mimeType: 'application/pdf' },
|
|
317
|
+
{ bytes: [0x50, 0x4B, 0x03, 0x04], mimeType: 'application/zip' },
|
|
318
|
+
{ bytes: [0x50, 0x4B, 0x05, 0x06], mimeType: 'application/zip' },
|
|
319
|
+
{ bytes: [0x50, 0x4B, 0x07, 0x08], mimeType: 'application/zip' },
|
|
320
|
+
{ bytes: [0x1F, 0x8B], mimeType: 'application/gzip' },
|
|
321
|
+
{ bytes: [0x52, 0x61, 0x72, 0x21, 0x1A, 0x07], mimeType: 'application/vnd.rar' },
|
|
322
|
+
{ bytes: [0x37, 0x7A, 0xBC, 0xAF, 0x27, 0x1C], mimeType: 'application/x-7z-compressed' },
|
|
323
|
+
{ bytes: [0x49, 0x44, 0x33], mimeType: 'audio/mpeg' },
|
|
324
|
+
{ bytes: [0xFF, 0xFB], mimeType: 'audio/mpeg' },
|
|
325
|
+
{ bytes: [0xFF, 0xFA], mimeType: 'audio/mpeg' },
|
|
326
|
+
{ bytes: [0x4F, 0x67, 0x67, 0x53], mimeType: 'audio/ogg' },
|
|
327
|
+
{ bytes: [0x66, 0x74, 0x79, 0x70], mimeType: 'video/mp4', offset: 4 },
|
|
328
|
+
{ bytes: [0x4D, 0x5A], mimeType: 'application/x-msdownload' },
|
|
329
|
+
{ bytes: [0x7F, 0x45, 0x4C, 0x46], mimeType: 'application/x-executable' },
|
|
330
|
+
];
|
|
331
|
+
/**
|
|
332
|
+
* Detects MIME type from file content by examining magic bytes.
|
|
333
|
+
*
|
|
334
|
+
* Useful in `beforeUpload` hooks to verify that a file's actual content
|
|
335
|
+
* matches its declared MIME type — particularly for cloud uploads where
|
|
336
|
+
* the driver trusts the client-provided MIME type.
|
|
337
|
+
*
|
|
338
|
+
* @param data - Buffer containing at least the first 12 bytes of the file
|
|
339
|
+
* @returns Detected MIME type, or undefined if unknown
|
|
340
|
+
*
|
|
341
|
+
* @example
|
|
342
|
+
* const storage = new StorageManager({
|
|
343
|
+
* hooks: {
|
|
344
|
+
* beforeUpload: async (file) => {
|
|
345
|
+
* const actual = detectMimeType(file.buffer);
|
|
346
|
+
* if (actual && actual !== file.mimetype) {
|
|
347
|
+
* throw new Error(`Content mismatch: declared ${file.mimetype}, detected ${actual}`);
|
|
348
|
+
* }
|
|
349
|
+
* },
|
|
350
|
+
* },
|
|
351
|
+
* });
|
|
352
|
+
*/
|
|
353
|
+
export function detectMimeType(data) {
|
|
354
|
+
if (!data || data.length === 0)
|
|
355
|
+
return undefined;
|
|
356
|
+
for (const sig of MAGIC_SIGNATURES) {
|
|
357
|
+
const offset = sig.offset || 0;
|
|
358
|
+
if (offset + sig.bytes.length > data.length)
|
|
359
|
+
continue;
|
|
360
|
+
let match = true;
|
|
361
|
+
for (let i = 0; i < sig.bytes.length; i++) {
|
|
362
|
+
if (data[offset + i] !== sig.bytes[i]) {
|
|
363
|
+
match = false;
|
|
364
|
+
break;
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
if (match)
|
|
368
|
+
return sig.mimeType;
|
|
369
|
+
}
|
|
370
|
+
// RIFF container: bytes 0-3 = 'RIFF', bytes 8-11 = sub-format
|
|
371
|
+
if (data.length >= 12 &&
|
|
372
|
+
data[0] === 0x52 && data[1] === 0x49 &&
|
|
373
|
+
data[2] === 0x46 && data[3] === 0x46) {
|
|
374
|
+
const sub = data.subarray(8, 12).toString('ascii');
|
|
375
|
+
switch (sub) {
|
|
376
|
+
case 'WEBP': return 'image/webp';
|
|
377
|
+
case 'WAVE': return 'audio/wav';
|
|
378
|
+
case 'AVI ': return 'video/x-msvideo';
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
return undefined;
|
|
382
|
+
}
|
|
216
383
|
/**
|
|
217
384
|
* Pauses execution for the specified number of milliseconds.
|
|
218
385
|
*/
|
|
@@ -225,15 +392,11 @@ export function sleep(ms) {
|
|
|
225
392
|
* Prevents overwhelming APIs or running out of resources by limiting
|
|
226
393
|
* how many operations run at once.
|
|
227
394
|
*
|
|
228
|
-
*
|
|
229
|
-
*
|
|
230
|
-
*
|
|
231
|
-
*
|
|
232
|
-
* Note: The input array is snapshotted at the start to prevent issues
|
|
233
|
-
* if the caller modifies it during processing.
|
|
395
|
+
* Uses a shared-index work-stealing approach: workers pull the next
|
|
396
|
+
* available item as soon as they finish one, ensuring even load
|
|
397
|
+
* distribution regardless of per-item processing time.
|
|
234
398
|
*
|
|
235
399
|
* @example
|
|
236
|
-
* // Upload 100 files, but only 10 at a time
|
|
237
400
|
* const results = await withConcurrencyLimit(
|
|
238
401
|
* files,
|
|
239
402
|
* (file) => uploadFile(file),
|
|
@@ -241,36 +404,34 @@ export function sleep(ms) {
|
|
|
241
404
|
* );
|
|
242
405
|
*/
|
|
243
406
|
export async function withConcurrencyLimit(items, operation, options = {}) {
|
|
244
|
-
const { maxConcurrent = 10 } = options;
|
|
407
|
+
const { maxConcurrent = 10, signal } = options;
|
|
245
408
|
if (items.length === 0) {
|
|
246
409
|
return [];
|
|
247
410
|
}
|
|
248
|
-
|
|
411
|
+
signal?.throwIfAborted();
|
|
249
412
|
const itemsCopy = [...items];
|
|
250
413
|
const itemCount = itemsCopy.length;
|
|
251
|
-
// For small batches, just process everything at once
|
|
252
414
|
if (itemCount <= maxConcurrent) {
|
|
253
415
|
return Promise.all(itemsCopy.map((item, index) => operation(item, index)));
|
|
254
416
|
}
|
|
255
417
|
const results = new Array(itemCount);
|
|
256
418
|
const workerCount = Math.min(maxConcurrent, itemCount);
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
419
|
+
let nextIndex = 0;
|
|
420
|
+
const createWorker = async () => {
|
|
421
|
+
while (nextIndex < itemCount) {
|
|
422
|
+
signal?.throwIfAborted();
|
|
423
|
+
const index = nextIndex++;
|
|
424
|
+
if (index >= itemCount)
|
|
425
|
+
break;
|
|
426
|
+
const item = itemsCopy[index];
|
|
427
|
+
if (item !== undefined) {
|
|
428
|
+
results[index] = await operation(item, index);
|
|
267
429
|
}
|
|
268
|
-
}
|
|
430
|
+
}
|
|
269
431
|
};
|
|
270
|
-
// Start all workers with their pre-assigned index ranges
|
|
271
432
|
const workers = [];
|
|
272
433
|
for (let i = 0; i < workerCount; i++) {
|
|
273
|
-
workers.push(createWorker(
|
|
434
|
+
workers.push(createWorker());
|
|
274
435
|
}
|
|
275
436
|
await Promise.all(workers);
|
|
276
437
|
return results;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"file.utils.js","sourceRoot":"","sources":["../../../src/utils/file.utils.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"file.utils.js","sourceRoot":"","sources":["../../../src/utils/file.utils.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* express-storage/utils
|
|
3
|
+
*
|
|
4
|
+
* Standalone utility functions for file handling, retries, and concurrency.
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* import { withRetry, formatFileSize, withConcurrencyLimit } from 'express-storage/utils';
|
|
8
|
+
*/
|
|
9
|
+
export { generateUniqueFileName, sanitizeFileName, validateFileName, hasPathTraversal, encodePathSegments, isValidMimeType, validateFolderPath, validateFileForUpload, createMonthBasedPath, ensureDirectoryExists, formatFileSize, validateFileSize, validateFileType, getFileExtension, isImageFile, isDocumentFile, detectMimeType, withRetry, sleep, withConcurrencyLimit, } from './file.utils.js';
|
|
10
|
+
export type { RetryOptions, ConcurrencyOptions } from './file.utils.js';
|
|
11
|
+
export { InMemoryRateLimiter, isRateLimiterAdapter } from './rate-limiter.js';
|
|
12
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/utils/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EACL,sBAAsB,EACtB,gBAAgB,EAChB,gBAAgB,EAChB,gBAAgB,EAChB,kBAAkB,EAClB,eAAe,EACf,kBAAkB,EAClB,qBAAqB,EACrB,oBAAoB,EACpB,qBAAqB,EACrB,cAAc,EACd,gBAAgB,EAChB,gBAAgB,EAChB,gBAAgB,EAChB,WAAW,EACX,cAAc,EACd,cAAc,EACd,SAAS,EACT,KAAK,EACL,oBAAoB,GACrB,MAAM,iBAAiB,CAAC;AAEzB,YAAY,EAAE,YAAY,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAExE,OAAO,EAAE,mBAAmB,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* express-storage/utils
|
|
3
|
+
*
|
|
4
|
+
* Standalone utility functions for file handling, retries, and concurrency.
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* import { withRetry, formatFileSize, withConcurrencyLimit } from 'express-storage/utils';
|
|
8
|
+
*/
|
|
9
|
+
export { generateUniqueFileName, sanitizeFileName, validateFileName, hasPathTraversal, encodePathSegments, isValidMimeType, validateFolderPath, validateFileForUpload, createMonthBasedPath, ensureDirectoryExists, formatFileSize, validateFileSize, validateFileType, getFileExtension, isImageFile, isDocumentFile, detectMimeType, withRetry, sleep, withConcurrencyLimit, } from './file.utils.js';
|
|
10
|
+
export { InMemoryRateLimiter, isRateLimiterAdapter } from './rate-limiter.js';
|
|
11
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/utils/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EACL,sBAAsB,EACtB,gBAAgB,EAChB,gBAAgB,EAChB,gBAAgB,EAChB,kBAAkB,EAClB,eAAe,EACf,kBAAkB,EAClB,qBAAqB,EACrB,oBAAoB,EACpB,qBAAqB,EACrB,cAAc,EACd,gBAAgB,EAChB,gBAAgB,EAChB,gBAAgB,EAChB,WAAW,EACX,cAAc,EACd,cAAc,EACd,SAAS,EACT,KAAK,EACL,oBAAoB,GACrB,MAAM,iBAAiB,CAAC;AAIzB,OAAO,EAAE,mBAAmB,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { RateLimiterAdapter, RateLimitOptions } from '../types/storage.types.js';
|
|
2
|
+
/**
|
|
3
|
+
* O(1) sliding window counter rate limiter for presigned URL generation.
|
|
4
|
+
*
|
|
5
|
+
* Uses a two-bucket sliding window algorithm: tracks request counts for the
|
|
6
|
+
* current and previous windows, then estimates the effective count with a
|
|
7
|
+
* time-weighted blend. This provides smooth rate limiting without storing
|
|
8
|
+
* individual timestamps.
|
|
9
|
+
*
|
|
10
|
+
* All operations are O(1) time and O(1) space regardless of request volume.
|
|
11
|
+
*
|
|
12
|
+
* Suitable for single-process applications. For clustered/multi-process
|
|
13
|
+
* deployments, implement `RateLimiterAdapter` backed by a shared store.
|
|
14
|
+
*/
|
|
15
|
+
export declare class InMemoryRateLimiter implements RateLimiterAdapter {
|
|
16
|
+
private maxRequests;
|
|
17
|
+
private windowMs;
|
|
18
|
+
private currentCount;
|
|
19
|
+
private previousCount;
|
|
20
|
+
private windowStart;
|
|
21
|
+
constructor(options: RateLimitOptions);
|
|
22
|
+
/**
|
|
23
|
+
* Rotates the window buckets if the current window has elapsed.
|
|
24
|
+
*/
|
|
25
|
+
private slide;
|
|
26
|
+
/**
|
|
27
|
+
* Returns the estimated request count across the sliding window,
|
|
28
|
+
* blending the previous window's count proportionally with elapsed time.
|
|
29
|
+
*/
|
|
30
|
+
private getEstimatedCount;
|
|
31
|
+
tryAcquire(): boolean;
|
|
32
|
+
getRemainingRequests(): number;
|
|
33
|
+
getResetTime(): number;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Returns true if the value is a RateLimiterAdapter (has the required methods),
|
|
37
|
+
* as opposed to plain RateLimitOptions.
|
|
38
|
+
*/
|
|
39
|
+
export declare function isRateLimiterAdapter(value: unknown): value is RateLimiterAdapter;
|
|
40
|
+
//# sourceMappingURL=rate-limiter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rate-limiter.d.ts","sourceRoot":"","sources":["../../../src/utils/rate-limiter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAEjF;;;;;;;;;;;;GAYG;AACH,qBAAa,mBAAoB,YAAW,kBAAkB;IAC5D,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,YAAY,CAAa;IACjC,OAAO,CAAC,aAAa,CAAa;IAClC,OAAO,CAAC,WAAW,CAAS;gBAEhB,OAAO,EAAE,gBAAgB;IAMrC;;OAEG;IACH,OAAO,CAAC,KAAK;IAeb;;;OAGG;IACH,OAAO,CAAC,iBAAiB;IAMzB,UAAU,IAAI,OAAO;IAWrB,oBAAoB,IAAI,MAAM;IAK9B,YAAY,IAAI,MAAM;CAQvB;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,kBAAkB,CAOhF"}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* O(1) sliding window counter rate limiter for presigned URL generation.
|
|
3
|
+
*
|
|
4
|
+
* Uses a two-bucket sliding window algorithm: tracks request counts for the
|
|
5
|
+
* current and previous windows, then estimates the effective count with a
|
|
6
|
+
* time-weighted blend. This provides smooth rate limiting without storing
|
|
7
|
+
* individual timestamps.
|
|
8
|
+
*
|
|
9
|
+
* All operations are O(1) time and O(1) space regardless of request volume.
|
|
10
|
+
*
|
|
11
|
+
* Suitable for single-process applications. For clustered/multi-process
|
|
12
|
+
* deployments, implement `RateLimiterAdapter` backed by a shared store.
|
|
13
|
+
*/
|
|
14
|
+
export class InMemoryRateLimiter {
|
|
15
|
+
maxRequests;
|
|
16
|
+
windowMs;
|
|
17
|
+
currentCount = 0;
|
|
18
|
+
previousCount = 0;
|
|
19
|
+
windowStart;
|
|
20
|
+
constructor(options) {
|
|
21
|
+
this.maxRequests = options.maxRequests;
|
|
22
|
+
this.windowMs = options.windowMs || 60000;
|
|
23
|
+
this.windowStart = Date.now();
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Rotates the window buckets if the current window has elapsed.
|
|
27
|
+
*/
|
|
28
|
+
slide() {
|
|
29
|
+
const now = Date.now();
|
|
30
|
+
const elapsed = now - this.windowStart;
|
|
31
|
+
if (elapsed >= this.windowMs * 2) {
|
|
32
|
+
this.previousCount = 0;
|
|
33
|
+
this.currentCount = 0;
|
|
34
|
+
this.windowStart = now;
|
|
35
|
+
}
|
|
36
|
+
else if (elapsed >= this.windowMs) {
|
|
37
|
+
this.previousCount = this.currentCount;
|
|
38
|
+
this.currentCount = 0;
|
|
39
|
+
this.windowStart += this.windowMs;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Returns the estimated request count across the sliding window,
|
|
44
|
+
* blending the previous window's count proportionally with elapsed time.
|
|
45
|
+
*/
|
|
46
|
+
getEstimatedCount() {
|
|
47
|
+
const elapsed = Date.now() - this.windowStart;
|
|
48
|
+
const weight = Math.max(0, (this.windowMs - elapsed) / this.windowMs);
|
|
49
|
+
return this.previousCount * weight + this.currentCount;
|
|
50
|
+
}
|
|
51
|
+
tryAcquire() {
|
|
52
|
+
this.slide();
|
|
53
|
+
if (this.getEstimatedCount() >= this.maxRequests) {
|
|
54
|
+
return false;
|
|
55
|
+
}
|
|
56
|
+
this.currentCount++;
|
|
57
|
+
return true;
|
|
58
|
+
}
|
|
59
|
+
getRemainingRequests() {
|
|
60
|
+
this.slide();
|
|
61
|
+
return Math.max(0, Math.floor(this.maxRequests - this.getEstimatedCount()));
|
|
62
|
+
}
|
|
63
|
+
getResetTime() {
|
|
64
|
+
this.slide();
|
|
65
|
+
if (this.currentCount === 0 && this.previousCount === 0) {
|
|
66
|
+
return 0;
|
|
67
|
+
}
|
|
68
|
+
const elapsed = Date.now() - this.windowStart;
|
|
69
|
+
return Math.max(0, this.windowMs - elapsed);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Returns true if the value is a RateLimiterAdapter (has the required methods),
|
|
74
|
+
* as opposed to plain RateLimitOptions.
|
|
75
|
+
*/
|
|
76
|
+
export function isRateLimiterAdapter(value) {
|
|
77
|
+
return (typeof value === 'object' &&
|
|
78
|
+
value !== null &&
|
|
79
|
+
'tryAcquire' in value &&
|
|
80
|
+
typeof value.tryAcquire === 'function');
|
|
81
|
+
}
|
|
82
|
+
//# sourceMappingURL=rate-limiter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rate-limiter.js","sourceRoot":"","sources":["../../../src/utils/rate-limiter.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;GAYG;AACH,MAAM,OAAO,mBAAmB;IACtB,WAAW,CAAS;IACpB,QAAQ,CAAS;IACjB,YAAY,GAAW,CAAC,CAAC;IACzB,aAAa,GAAW,CAAC,CAAC;IAC1B,WAAW,CAAS;IAE5B,YAAY,OAAyB;QACnC,IAAI,CAAC,WAAW,GAAG,OAAO,CAAC,WAAW,CAAC;QACvC,IAAI,CAAC,QAAQ,GAAG,OAAO,CAAC,QAAQ,IAAI,KAAK,CAAC;QAC1C,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IAChC,CAAC;IAED;;OAEG;IACK,KAAK;QACX,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACvB,MAAM,OAAO,GAAG,GAAG,GAAG,IAAI,CAAC,WAAW,CAAC;QAEvC,IAAI,OAAO,IAAI,IAAI,CAAC,QAAQ,GAAG,CAAC,EAAE,CAAC;YACjC,IAAI,CAAC,aAAa,GAAG,CAAC,CAAC;YACvB,IAAI,CAAC,YAAY,GAAG,CAAC,CAAC;YACtB,IAAI,CAAC,WAAW,GAAG,GAAG,CAAC;QACzB,CAAC;aAAM,IAAI,OAAO,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YACpC,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC,YAAY,CAAC;YACvC,IAAI,CAAC,YAAY,GAAG,CAAC,CAAC;YACtB,IAAI,CAAC,WAAW,IAAI,IAAI,CAAC,QAAQ,CAAC;QACpC,CAAC;IACH,CAAC;IAED;;;OAGG;IACK,iBAAiB;QACvB,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,WAAW,CAAC;QAC9C,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,QAAQ,GAAG,OAAO,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC,CAAC;QACtE,OAAO,IAAI,CAAC,aAAa,GAAG,MAAM,GAAG,IAAI,CAAC,YAAY,CAAC;IACzD,CAAC;IAED,UAAU;QACR,IAAI,CAAC,KAAK,EAAE,CAAC;QAEb,IAAI,IAAI,CAAC,iBAAiB,EAAE,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;YACjD,OAAO,KAAK,CAAC;QACf,CAAC;QAED,IAAI,CAAC,YAAY,EAAE,CAAC;QACpB,OAAO,IAAI,CAAC;IACd,CAAC;IAED,oBAAoB;QAClB,IAAI,CAAC,KAAK,EAAE,CAAC;QACb,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC,iBAAiB,EAAE,CAAC,CAAC,CAAC;IAC9E,CAAC;IAED,YAAY;QACV,IAAI,CAAC,KAAK,EAAE,CAAC;QACb,IAAI,IAAI,CAAC,YAAY,KAAK,CAAC,IAAI,IAAI,CAAC,aAAa,KAAK,CAAC,EAAE,CAAC;YACxD,OAAO,CAAC,CAAC;QACX,CAAC;QACD,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,WAAW,CAAC;QAC9C,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,QAAQ,GAAG,OAAO,CAAC,CAAC;IAC9C,CAAC;CACF;AAED;;;GAGG;AACH,MAAM,UAAU,oBAAoB,CAAC,KAAc;IACjD,OAAO,CACL,OAAO,KAAK,KAAK,QAAQ;QACzB,KAAK,KAAK,IAAI;QACd,YAAY,IAAI,KAAK;QACrB,OAAQ,KAA4B,CAAC,UAAU,KAAK,UAAU,CAC/D,CAAC;AACJ,CAAC"}
|