multermate 2.2.1 → 2.3.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/dist/cjs/index.js CHANGED
@@ -7,8 +7,8 @@ exports.MIME_TYPES = exports.ALLOWED_FILE_TYPES = exports.MultermateError = void
7
7
  exports.uploadSingle = uploadSingle;
8
8
  exports.uploadMultiple = uploadMultiple;
9
9
  exports.deleteFile = deleteFile;
10
- const promises_1 = __importDefault(require("fs/promises"));
11
10
  const fs_1 = require("fs");
11
+ const promises_1 = __importDefault(require("fs/promises"));
12
12
  const multer_1 = __importDefault(require("multer"));
13
13
  const path_1 = __importDefault(require("path"));
14
14
  const uuid_1 = require("uuid");
@@ -16,7 +16,7 @@ const uuid_1 = require("uuid");
16
16
  class MultermateError extends Error {
17
17
  constructor(message, code, field) {
18
18
  super(message);
19
- this.name = 'MultermateError';
19
+ this.name = "MultermateError";
20
20
  this.code = code;
21
21
  this.field = field;
22
22
  // Maintains proper stack trace for where our error was thrown (only available on V8)
@@ -30,131 +30,310 @@ exports.MultermateError = MultermateError;
30
30
  const ALLOWED_MIME_TYPES = {
31
31
  // Images (all common image formats)
32
32
  images: [
33
- "image/jpeg", "image/jpg", "image/png", "image/gif", "image/webp",
34
- "image/svg+xml", "image/bmp", "image/tiff", "image/ico", "image/avif",
35
- "image/heic", "image/heif", "image/x-icon", "image/vnd.microsoft.icon"
33
+ "image/jpeg",
34
+ "image/jpg",
35
+ "image/png",
36
+ "image/gif",
37
+ "image/webp",
38
+ "image/svg+xml",
39
+ "image/bmp",
40
+ "image/tiff",
41
+ "image/ico",
42
+ "image/avif",
43
+ "image/heic",
44
+ "image/heif",
45
+ "image/x-icon",
46
+ "image/vnd.microsoft.icon",
36
47
  ],
37
48
  // Videos (all common video formats)
38
49
  videos: [
39
- "video/mp4", "video/mpeg", "video/ogg", "video/webm", "video/avi",
40
- "video/mov", "video/wmv", "video/flv", "video/mkv", "video/m4v", "video/3gp",
41
- "video/quicktime", "video/x-msvideo", "video/x-ms-wmv", "video/x-flv"
50
+ "video/mp4",
51
+ "video/mpeg",
52
+ "video/ogg",
53
+ "video/webm",
54
+ "video/avi",
55
+ "video/mov",
56
+ "video/wmv",
57
+ "video/flv",
58
+ "video/mkv",
59
+ "video/m4v",
60
+ "video/3gp",
61
+ "video/quicktime",
62
+ "video/x-msvideo",
63
+ "video/x-ms-wmv",
64
+ "video/x-flv",
42
65
  ],
43
66
  // Audio (all common audio formats)
44
67
  audio: [
45
- "audio/mpeg", "audio/wav", "audio/ogg", "audio/aac", "audio/flac",
46
- "audio/m4a", "audio/wma", "audio/mp3", "audio/webm", "audio/x-wav",
47
- "audio/x-m4a", "audio/x-aac", "audio/opus", "audio/amr"
68
+ "audio/mpeg",
69
+ "audio/wav",
70
+ "audio/ogg",
71
+ "audio/aac",
72
+ "audio/flac",
73
+ "audio/m4a",
74
+ "audio/wma",
75
+ "audio/mp3",
76
+ "audio/webm",
77
+ "audio/x-wav",
78
+ "audio/x-m4a",
79
+ "audio/x-aac",
80
+ "audio/opus",
81
+ "audio/amr",
48
82
  ],
49
83
  // Documents (all common document formats)
50
84
  documents: [
51
- "application/pdf", "application/msword",
85
+ "application/pdf",
86
+ "application/msword",
52
87
  "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
53
88
  "application/vnd.ms-excel",
54
89
  "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
55
90
  "application/vnd.ms-powerpoint",
56
91
  "application/vnd.openxmlformats-officedocument.presentationml.presentation",
57
- "application/rtf", "application/vnd.oasis.opendocument.text",
58
- "application/vnd.oasis.opendocument.spreadsheet", "application/vnd.oasis.opendocument.presentation",
59
- "application/vnd.apple.pages", "application/vnd.apple.numbers", "application/vnd.apple.keynote"
92
+ "application/rtf",
93
+ "application/vnd.oasis.opendocument.text",
94
+ "application/vnd.oasis.opendocument.spreadsheet",
95
+ "application/vnd.oasis.opendocument.presentation",
96
+ "application/vnd.apple.pages",
97
+ "application/vnd.apple.numbers",
98
+ "application/vnd.apple.keynote",
60
99
  ],
61
100
  // Text files (all common text formats)
62
101
  text: [
63
- "text/plain", "text/csv", "text/html", "text/css", "text/javascript",
64
- "text/xml", "text/markdown", "text/x-python", "text/x-java-source",
65
- "text/x-c", "text/x-c++", "text/x-php", "text/x-ruby", "text/x-go",
66
- "text/x-rust", "text/x-typescript", "text/x-swift", "text/x-kotlin",
67
- "text/x-scala", "text/x-perl", "text/x-shell", "text/x-sh", "text/x-bash",
68
- "text/x-yaml", "text/yaml", "text/x-toml", "text/x-ini", "text/x-log"
102
+ "text/plain",
103
+ "text/csv",
104
+ "text/html",
105
+ "text/css",
106
+ "text/javascript",
107
+ "text/xml",
108
+ "text/markdown",
109
+ "text/x-python",
110
+ "text/x-java-source",
111
+ "text/x-c",
112
+ "text/x-c++",
113
+ "text/x-php",
114
+ "text/x-ruby",
115
+ "text/x-go",
116
+ "text/x-rust",
117
+ "text/x-typescript",
118
+ "text/x-swift",
119
+ "text/x-kotlin",
120
+ "text/x-scala",
121
+ "text/x-perl",
122
+ "text/x-shell",
123
+ "text/x-sh",
124
+ "text/x-bash",
125
+ "text/x-yaml",
126
+ "text/yaml",
127
+ "text/x-toml",
128
+ "text/x-ini",
129
+ "text/x-log",
69
130
  ],
70
131
  // Archives (all common archive formats)
71
132
  archives: [
72
- "application/zip", "application/x-rar-compressed", "application/x-tar",
73
- "application/gzip", "application/x-7z-compressed", "application/x-bzip2",
74
- "application/x-xz", "application/x-compress", "application/x-lz4",
75
- "application/x-lzma", "application/vnd.rar"
133
+ "application/zip",
134
+ "application/x-rar-compressed",
135
+ "application/x-tar",
136
+ "application/gzip",
137
+ "application/x-7z-compressed",
138
+ "application/x-bzip2",
139
+ "application/x-xz",
140
+ "application/x-compress",
141
+ "application/x-lz4",
142
+ "application/x-lzma",
143
+ "application/vnd.rar",
76
144
  ],
77
145
  // Code files (programming language files)
78
146
  code: [
79
- "application/json", "application/xml", "application/javascript",
80
- "application/typescript", "text/x-python", "text/x-java-source",
81
- "text/x-c", "text/x-c++", "text/x-php", "text/x-ruby", "text/x-go",
82
- "text/x-rust", "text/x-swift", "text/x-kotlin", "text/x-scala",
83
- "text/x-csharp", "text/x-vb", "text/x-sql", "application/sql"
147
+ "application/json",
148
+ "application/xml",
149
+ "application/javascript",
150
+ "application/typescript",
151
+ "text/x-python",
152
+ "text/x-java-source",
153
+ "text/x-c",
154
+ "text/x-c++",
155
+ "text/x-php",
156
+ "text/x-ruby",
157
+ "text/x-go",
158
+ "text/x-rust",
159
+ "text/x-swift",
160
+ "text/x-kotlin",
161
+ "text/x-scala",
162
+ "text/x-csharp",
163
+ "text/x-vb",
164
+ "text/x-sql",
165
+ "application/sql",
84
166
  ],
85
167
  // Spreadsheets (separate category)
86
168
  spreadsheets: [
87
169
  "application/vnd.ms-excel",
88
170
  "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
89
- "text/csv", "application/csv"
171
+ "text/csv",
172
+ "application/csv",
90
173
  ],
91
174
  // Presentations (separate category)
92
175
  presentations: [
93
176
  "application/vnd.ms-powerpoint",
94
177
  "application/vnd.openxmlformats-officedocument.presentationml.presentation",
95
- "application/vnd.oasis.opendocument.presentation"
178
+ "application/vnd.oasis.opendocument.presentation",
96
179
  ],
97
180
  // Fonts (font files)
98
181
  fonts: [
99
- "font/woff", "font/woff2", "font/ttf", "font/otf", "font/eot",
100
- "application/font-woff", "application/font-woff2", "application/x-font-ttf",
101
- "application/x-font-otf", "application/vnd.ms-fontobject"
182
+ "font/woff",
183
+ "font/woff2",
184
+ "font/ttf",
185
+ "font/otf",
186
+ "font/eot",
187
+ "application/font-woff",
188
+ "application/font-woff2",
189
+ "application/x-font-ttf",
190
+ "application/x-font-otf",
191
+ "application/vnd.ms-fontobject",
102
192
  ],
103
193
  // CAD files
104
194
  cad: [
105
- "application/dwg", "application/dxf", "model/vnd.dwf",
106
- "application/acad", "image/vnd.dwg"
195
+ "application/dwg",
196
+ "application/dxf",
197
+ "model/vnd.dwf",
198
+ "application/acad",
199
+ "image/vnd.dwg",
107
200
  ],
108
201
  // 3D models
109
202
  models: [
110
- "model/obj", "model/gltf+json", "model/gltf-binary", "model/x3d+xml",
111
- "model/stl", "model/ply", "application/x-blender"
203
+ "model/obj",
204
+ "model/gltf+json",
205
+ "model/gltf-binary",
206
+ "model/x3d+xml",
207
+ "model/stl",
208
+ "model/ply",
209
+ "application/x-blender",
112
210
  ],
113
211
  // PDFs (separate category for backward compatibility)
114
212
  pdfs: ["application/pdf"],
115
213
  // All allowed types - comprehensive list (this is for backward compatibility)
116
214
  all: [
117
215
  // Images
118
- "image/jpeg", "image/jpg", "image/png", "image/gif", "image/webp",
119
- "image/svg+xml", "image/bmp", "image/tiff", "image/ico", "image/avif",
120
- "image/heic", "image/heif", "image/x-icon", "image/vnd.microsoft.icon",
216
+ "image/jpeg",
217
+ "image/jpg",
218
+ "image/png",
219
+ "image/gif",
220
+ "image/webp",
221
+ "image/svg+xml",
222
+ "image/bmp",
223
+ "image/tiff",
224
+ "image/ico",
225
+ "image/avif",
226
+ "image/heic",
227
+ "image/heif",
228
+ "image/x-icon",
229
+ "image/vnd.microsoft.icon",
121
230
  // Videos
122
- "video/mp4", "video/mpeg", "video/ogg", "video/webm", "video/avi",
123
- "video/mov", "video/wmv", "video/flv", "video/mkv", "video/m4v", "video/3gp",
124
- "video/quicktime", "video/x-msvideo", "video/x-ms-wmv", "video/x-flv",
231
+ "video/mp4",
232
+ "video/mpeg",
233
+ "video/ogg",
234
+ "video/webm",
235
+ "video/avi",
236
+ "video/mov",
237
+ "video/wmv",
238
+ "video/flv",
239
+ "video/mkv",
240
+ "video/m4v",
241
+ "video/3gp",
242
+ "video/quicktime",
243
+ "video/x-msvideo",
244
+ "video/x-ms-wmv",
245
+ "video/x-flv",
125
246
  // Audio
126
- "audio/mpeg", "audio/wav", "audio/ogg", "audio/aac", "audio/flac",
127
- "audio/m4a", "audio/wma", "audio/mp3", "audio/webm", "audio/x-wav",
128
- "audio/x-m4a", "audio/x-aac", "audio/opus", "audio/amr",
247
+ "audio/mpeg",
248
+ "audio/wav",
249
+ "audio/ogg",
250
+ "audio/aac",
251
+ "audio/flac",
252
+ "audio/m4a",
253
+ "audio/wma",
254
+ "audio/mp3",
255
+ "audio/webm",
256
+ "audio/x-wav",
257
+ "audio/x-m4a",
258
+ "audio/x-aac",
259
+ "audio/opus",
260
+ "audio/amr",
129
261
  // Documents
130
- "application/pdf", "application/msword",
262
+ "application/pdf",
263
+ "application/msword",
131
264
  "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
132
265
  "application/vnd.ms-excel",
133
266
  "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
134
267
  "application/vnd.ms-powerpoint",
135
268
  "application/vnd.openxmlformats-officedocument.presentationml.presentation",
136
- "application/rtf", "application/vnd.oasis.opendocument.text",
137
- "application/vnd.oasis.opendocument.spreadsheet", "application/vnd.oasis.opendocument.presentation",
138
- "application/vnd.apple.pages", "application/vnd.apple.numbers", "application/vnd.apple.keynote",
269
+ "application/rtf",
270
+ "application/vnd.oasis.opendocument.text",
271
+ "application/vnd.oasis.opendocument.spreadsheet",
272
+ "application/vnd.oasis.opendocument.presentation",
273
+ "application/vnd.apple.pages",
274
+ "application/vnd.apple.numbers",
275
+ "application/vnd.apple.keynote",
139
276
  // Text files
140
- "text/plain", "text/csv", "text/html", "text/css", "text/javascript",
141
- "text/xml", "text/markdown", "text/x-python", "text/x-java-source",
142
- "text/x-c", "text/x-c++", "text/x-php", "text/x-ruby", "text/x-go",
143
- "text/x-rust", "text/x-typescript", "text/x-swift", "text/x-kotlin",
144
- "text/x-scala", "text/x-perl", "text/x-shell", "text/x-sh", "text/x-bash",
145
- "text/x-yaml", "text/yaml", "text/x-toml", "text/x-ini", "text/x-log",
277
+ "text/plain",
278
+ "text/csv",
279
+ "text/html",
280
+ "text/css",
281
+ "text/javascript",
282
+ "text/xml",
283
+ "text/markdown",
284
+ "text/x-python",
285
+ "text/x-java-source",
286
+ "text/x-c",
287
+ "text/x-c++",
288
+ "text/x-php",
289
+ "text/x-ruby",
290
+ "text/x-go",
291
+ "text/x-rust",
292
+ "text/x-typescript",
293
+ "text/x-swift",
294
+ "text/x-kotlin",
295
+ "text/x-scala",
296
+ "text/x-perl",
297
+ "text/x-shell",
298
+ "text/x-sh",
299
+ "text/x-bash",
300
+ "text/x-yaml",
301
+ "text/yaml",
302
+ "text/x-toml",
303
+ "text/x-ini",
304
+ "text/x-log",
146
305
  // Archives
147
- "application/zip", "application/x-rar-compressed", "application/x-tar",
148
- "application/gzip", "application/x-7z-compressed", "application/x-bzip2",
149
- "application/x-xz", "application/x-compress", "application/x-lz4",
150
- "application/x-lzma", "application/vnd.rar",
306
+ "application/zip",
307
+ "application/x-rar-compressed",
308
+ "application/x-tar",
309
+ "application/gzip",
310
+ "application/x-7z-compressed",
311
+ "application/x-bzip2",
312
+ "application/x-xz",
313
+ "application/x-compress",
314
+ "application/x-lz4",
315
+ "application/x-lzma",
316
+ "application/vnd.rar",
151
317
  // Code/Data
152
- "application/json", "application/xml", "application/javascript",
153
- "application/typescript", "text/x-csharp", "text/x-vb", "text/x-sql", "application/sql",
318
+ "application/json",
319
+ "application/xml",
320
+ "application/javascript",
321
+ "application/typescript",
322
+ "text/x-csharp",
323
+ "text/x-vb",
324
+ "text/x-sql",
325
+ "application/sql",
154
326
  // Fonts
155
- "font/woff", "font/woff2", "font/ttf", "font/otf", "font/eot",
156
- "application/font-woff", "application/font-woff2", "application/x-font-ttf",
157
- "application/x-font-otf", "application/vnd.ms-fontobject"
327
+ "font/woff",
328
+ "font/woff2",
329
+ "font/ttf",
330
+ "font/otf",
331
+ "font/eot",
332
+ "application/font-woff",
333
+ "application/font-woff2",
334
+ "application/x-font-ttf",
335
+ "application/x-font-otf",
336
+ "application/vnd.ms-fontobject",
158
337
  ],
159
338
  };
160
339
  /**
@@ -174,11 +353,11 @@ const configureStorage = (destination) => {
174
353
  }
175
354
  catch (error) {
176
355
  // Directory might already exist, that's okay
177
- if (error.code === 'EEXIST') {
356
+ if (error.code === "EEXIST") {
178
357
  cb(null, dir);
179
358
  }
180
359
  else {
181
- cb(new MultermateError(`Failed to create destination directory: ${dir}. Error: ${error.message}`, 'DESTINATION_ERROR'), '');
360
+ cb(new MultermateError(`Failed to create destination directory: ${dir}. Error: ${error.message}`, "DESTINATION_ERROR"), "");
182
361
  }
183
362
  }
184
363
  },
@@ -194,26 +373,93 @@ const configureStorage = (destination) => {
194
373
  cb(null, fileName);
195
374
  }
196
375
  catch (error) {
197
- cb(new MultermateError('Failed to generate filename', 'FILENAME_ERROR'), '');
376
+ cb(new MultermateError("Failed to generate filename", "FILENAME_ERROR"), "");
198
377
  }
199
378
  },
200
379
  });
201
380
  };
202
- const normalizePathForDb = (value) => value.replace(/\\/g, '/');
381
+ const normalizePathForDb = (value) => value.replace(/\\/g, "/");
382
+ const FILE_KIND_TO_CATEGORIES = {
383
+ image: ["images"],
384
+ video: ["videos"],
385
+ audio: ["audio"],
386
+ document: ["documents"],
387
+ text: ["text"],
388
+ archive: ["archives"],
389
+ code: ["code"],
390
+ spreadsheet: ["spreadsheets"],
391
+ presentation: ["presentations"],
392
+ font: ["fonts"],
393
+ cad: ["cad"],
394
+ model: ["models"],
395
+ mix: ["images", "videos", "audio", "documents", "text", "archives"],
396
+ any: ["all"],
397
+ };
398
+ const FILE_TYPE_ALIASES = {
399
+ image: ["images"],
400
+ images: ["images"],
401
+ video: ["videos"],
402
+ videos: ["videos"],
403
+ document: ["documents"],
404
+ documents: ["documents"],
405
+ archive: ["archives"],
406
+ archives: ["archives"],
407
+ spreadsheet: ["spreadsheets"],
408
+ spreadsheets: ["spreadsheets"],
409
+ presentation: ["presentations"],
410
+ presentations: ["presentations"],
411
+ font: ["fonts"],
412
+ fonts: ["fonts"],
413
+ model: ["models"],
414
+ models: ["models"],
415
+ mixed: ["images", "videos", "audio", "documents", "text", "archives"],
416
+ mix: ["images", "videos", "audio", "documents", "text", "archives"],
417
+ any: ["all"],
418
+ };
419
+ const resolveAllowedMimeTypes = (fileTypes = [], fileKinds = []) => {
420
+ const selectedCategories = new Set();
421
+ const invalidSelectors = [];
422
+ fileKinds.forEach((kind) => {
423
+ const categories = FILE_KIND_TO_CATEGORIES[kind];
424
+ categories.forEach((category) => selectedCategories.add(category));
425
+ });
426
+ fileTypes.forEach((rawType) => {
427
+ const normalized = String(rawType).toLowerCase();
428
+ const aliasCategories = FILE_TYPE_ALIASES[normalized];
429
+ if (aliasCategories) {
430
+ aliasCategories.forEach((category) => selectedCategories.add(category));
431
+ return;
432
+ }
433
+ if (ALLOWED_MIME_TYPES[normalized]) {
434
+ selectedCategories.add(normalized);
435
+ return;
436
+ }
437
+ invalidSelectors.push(rawType);
438
+ });
439
+ let allowedMimeTypes = [];
440
+ selectedCategories.forEach((category) => {
441
+ allowedMimeTypes = allowedMimeTypes.concat(ALLOWED_MIME_TYPES[category] || []);
442
+ });
443
+ return {
444
+ allowedMimeTypes: [...new Set(allowedMimeTypes)],
445
+ invalidSelectors,
446
+ };
447
+ };
203
448
  const getPhysicalDestination = (destination, absoluteDestination) => {
204
449
  if (!absoluteDestination) {
205
- return destination || 'uploads';
450
+ return destination || "uploads";
206
451
  }
207
452
  if (!path_1.default.isAbsolute(absoluteDestination)) {
208
- throw new MultermateError(`absoluteDestination must be an absolute path. Received: ${absoluteDestination}`, 'INVALID_ABSOLUTE_DESTINATION');
453
+ throw new MultermateError(`absoluteDestination must be an absolute path. Received: ${absoluteDestination}`, "INVALID_ABSOLUTE_DESTINATION");
209
454
  }
210
- return absoluteDestination;
455
+ const destinationSegment = (destination || "uploads").replace(/^[\\/]+/, "");
456
+ return path_1.default.join(absoluteDestination, destinationSegment);
211
457
  };
212
458
  const sanitizeStoredPathsForDb = (req, destination, absoluteDestination) => {
213
459
  if (!absoluteDestination) {
214
460
  return;
215
461
  }
216
- const dbDestination = normalizePathForDb(destination || 'uploads');
462
+ const dbDestination = normalizePathForDb(destination || "uploads");
217
463
  if (req.file) {
218
464
  req.file.destination = dbDestination;
219
465
  req.file.path = normalizePathForDb(path_1.default.join(dbDestination, req.file.filename));
@@ -246,12 +492,12 @@ const configureFileFilter = (allowedMimeTypes) => {
246
492
  cb(null, true);
247
493
  }
248
494
  else {
249
- const error = new MultermateError(`Invalid file type: ${file.mimetype}. Allowed types: ${allowedMimeTypes.join(', ')}`, 'INVALID_FILE_TYPE', file.fieldname);
495
+ const error = new MultermateError(`Invalid file type: ${file.mimetype}. Allowed types: ${allowedMimeTypes.join(", ")}`, "INVALID_FILE_TYPE", file.fieldname);
250
496
  cb(error);
251
497
  }
252
498
  }
253
499
  catch (error) {
254
- cb(new MultermateError('File filter error', 'FILTER_ERROR'));
500
+ cb(new MultermateError("File filter error", "FILTER_ERROR"));
255
501
  }
256
502
  };
257
503
  };
@@ -261,7 +507,7 @@ const configureFileFilter = (allowedMimeTypes) => {
261
507
  * @param options - Configuration options for Multer.
262
508
  * @returns Multer instance configured with the provided options.
263
509
  */
264
- const configureMulter = ({ destination, absoluteDestination, filename, fileTypes = [], customMimeTypes = [], fileSizeLimit, preservePath = false, }) => {
510
+ const configureMulter = ({ destination, absoluteDestination, filename, fileKinds = [], fileTypes = [], customMimeTypes = [], fileSizeLimit, preservePath = false, }) => {
265
511
  try {
266
512
  const storage = configureStorage(getPhysicalDestination(destination, absoluteDestination));
267
513
  // Combine allowed MIME types based on fileTypes array
@@ -270,13 +516,13 @@ const configureMulter = ({ destination, absoluteDestination, filename, fileTypes
270
516
  // Use custom MIME types if provided
271
517
  allowedMimeTypes = customMimeTypes;
272
518
  }
273
- else if (fileTypes.length > 0) {
274
- // Use default MIME types for specified fileTypes
275
- fileTypes.forEach((type) => {
276
- if (ALLOWED_MIME_TYPES[type]) {
277
- allowedMimeTypes = allowedMimeTypes.concat(ALLOWED_MIME_TYPES[type]);
278
- }
279
- });
519
+ else if (fileTypes.length > 0 || fileKinds.length > 0) {
520
+ const { allowedMimeTypes: resolvedMimeTypes, invalidSelectors } = resolveAllowedMimeTypes(fileTypes, fileKinds);
521
+ if (invalidSelectors.length > 0) {
522
+ throw new MultermateError(`Unknown file type selectors: ${invalidSelectors.join(", ")}. ` +
523
+ `Use supported categories from ALLOWED_FILE_TYPES or fileKinds (image, document, video, mix, etc.).`, "INVALID_FILE_TYPE_SELECTOR");
524
+ }
525
+ allowedMimeTypes = resolvedMimeTypes;
280
526
  }
281
527
  // If neither customMimeTypes nor fileTypes are provided, allowedMimeTypes remains empty
282
528
  // This means ALL file types are allowed (no restrictions)
@@ -291,7 +537,7 @@ const configureMulter = ({ destination, absoluteDestination, filename, fileTypes
291
537
  });
292
538
  }
293
539
  catch (error) {
294
- throw new MultermateError('Failed to configure multer', 'CONFIGURATION_ERROR');
540
+ throw new MultermateError("Failed to configure multer", "CONFIGURATION_ERROR");
295
541
  }
296
542
  };
297
543
  /**
@@ -315,27 +561,28 @@ function uploadSingle(options = {}) {
315
561
  }
316
562
  middleware(req, res, (err) => {
317
563
  if (err) {
318
- let errorMessage = 'Unknown upload error';
319
- let errorCode = 'UPLOAD_ERROR';
564
+ let errorMessage = "Unknown upload error";
565
+ let errorCode = "UPLOAD_ERROR";
320
566
  if (err instanceof MultermateError) {
321
567
  // Our custom error
322
568
  req.fileValidationError = err.message;
323
569
  return next(err);
324
570
  }
325
- else if (err.code === 'LIMIT_FILE_SIZE') {
326
- errorMessage = `File size limit exceeded. Maximum allowed size: ${options.fileSizeLimit || '50MB'}`;
327
- errorCode = 'FILE_SIZE_LIMIT_EXCEEDED';
571
+ else if (err.code === "LIMIT_FILE_SIZE") {
572
+ errorMessage = `File size limit exceeded. Maximum allowed size: ${options.fileSizeLimit || "50MB"}`;
573
+ errorCode = "FILE_SIZE_LIMIT_EXCEEDED";
328
574
  }
329
- else if (err.code === 'INVALID_FILE_TYPE') {
330
- errorMessage = 'Invalid file type. Please check allowed file types.';
331
- errorCode = 'INVALID_FILE_TYPE';
575
+ else if (err.code === "INVALID_FILE_TYPE") {
576
+ errorMessage =
577
+ "Invalid file type. Please check allowed file types.";
578
+ errorCode = "INVALID_FILE_TYPE";
332
579
  }
333
- else if (err.code === 'LIMIT_UNEXPECTED_FILE') {
334
- errorMessage = 'Unexpected field';
335
- errorCode = 'UNEXPECTED_FIELD';
580
+ else if (err.code === "LIMIT_UNEXPECTED_FILE") {
581
+ errorMessage = "Unexpected field";
582
+ errorCode = "UNEXPECTED_FIELD";
336
583
  }
337
584
  else {
338
- errorMessage = err.message || 'Upload failed';
585
+ errorMessage = err.message || "Upload failed";
339
586
  }
340
587
  const multermateError = new MultermateError(errorMessage, errorCode);
341
588
  req.fileValidationError = errorMessage;
@@ -347,7 +594,7 @@ function uploadSingle(options = {}) {
347
594
  };
348
595
  }
349
596
  catch (error) {
350
- throw new MultermateError('Failed to create upload middleware', 'MIDDLEWARE_CREATION_ERROR');
597
+ throw new MultermateError("Failed to create upload middleware", "MIDDLEWARE_CREATION_ERROR");
351
598
  }
352
599
  }
353
600
  /**
@@ -360,7 +607,7 @@ function uploadMultiple(options) {
360
607
  try {
361
608
  const storageDestination = getPhysicalDestination(options.destination, options.absoluteDestination);
362
609
  // Map fields configuration to multer format
363
- const fieldConfigs = options.fields.map(field => ({
610
+ const fieldConfigs = options.fields.map((field) => ({
364
611
  name: field.name,
365
612
  maxCount: field.maxCount || 10, // Default maxCount is 10 if not specified.
366
613
  }));
@@ -373,12 +620,11 @@ function uploadMultiple(options) {
373
620
  else {
374
621
  // Collect file types from individual fields
375
622
  options.fields.forEach((field) => {
376
- const types = field.fileTypes || [];
377
- types.forEach((type) => {
378
- if (ALLOWED_MIME_TYPES[type]) {
379
- allowedFileTypes = allowedFileTypes.concat(ALLOWED_MIME_TYPES[type]);
380
- }
381
- });
623
+ const { allowedMimeTypes, invalidSelectors } = resolveAllowedMimeTypes(field.fileTypes || [], field.fileKinds || []);
624
+ if (invalidSelectors.length > 0) {
625
+ throw new MultermateError(`Unknown file type selectors in field "${field.name}": ${invalidSelectors.join(", ")}`, "INVALID_FILE_TYPE_SELECTOR", field.name);
626
+ }
627
+ allowedFileTypes = allowedFileTypes.concat(allowedMimeTypes);
382
628
  });
383
629
  }
384
630
  const multerConfig = {
@@ -387,7 +633,7 @@ function uploadMultiple(options) {
387
633
  fileTypes: [],
388
634
  customMimeTypes: allowedFileTypes.length > 0 ? allowedFileTypes : [],
389
635
  fileSizeLimit: options.fileSizeLimit,
390
- preservePath: options.preservePath
636
+ preservePath: options.preservePath,
391
637
  };
392
638
  const multerInstance = configureMulter(multerConfig);
393
639
  const middleware = multerInstance.fields(fieldConfigs);
@@ -401,31 +647,32 @@ function uploadMultiple(options) {
401
647
  }
402
648
  middleware(req, res, (err) => {
403
649
  if (err) {
404
- let errorMessage = 'Unknown upload error';
405
- let errorCode = 'UPLOAD_ERROR';
650
+ let errorMessage = "Unknown upload error";
651
+ let errorCode = "UPLOAD_ERROR";
406
652
  if (err instanceof MultermateError) {
407
653
  // Our custom error
408
654
  req.fileValidationError = err.message;
409
655
  return next(err);
410
656
  }
411
- else if (err.code === 'LIMIT_FILE_SIZE') {
412
- errorMessage = `File size limit exceeded. Maximum allowed size: ${options.fileSizeLimit || '50MB'}`;
413
- errorCode = 'FILE_SIZE_LIMIT_EXCEEDED';
657
+ else if (err.code === "LIMIT_FILE_SIZE") {
658
+ errorMessage = `File size limit exceeded. Maximum allowed size: ${options.fileSizeLimit || "50MB"}`;
659
+ errorCode = "FILE_SIZE_LIMIT_EXCEEDED";
414
660
  }
415
- else if (err.code === 'INVALID_FILE_TYPE') {
416
- errorMessage = 'Invalid file type. Please check allowed file types.';
417
- errorCode = 'INVALID_FILE_TYPE';
661
+ else if (err.code === "INVALID_FILE_TYPE") {
662
+ errorMessage =
663
+ "Invalid file type. Please check allowed file types.";
664
+ errorCode = "INVALID_FILE_TYPE";
418
665
  }
419
- else if (err.code === 'LIMIT_UNEXPECTED_FILE') {
420
- errorMessage = 'Unexpected field';
421
- errorCode = 'UNEXPECTED_FIELD';
666
+ else if (err.code === "LIMIT_UNEXPECTED_FILE") {
667
+ errorMessage = "Unexpected field";
668
+ errorCode = "UNEXPECTED_FIELD";
422
669
  }
423
- else if (err.code === 'LIMIT_FILE_COUNT') {
424
- errorMessage = 'Too many files';
425
- errorCode = 'FILE_COUNT_LIMIT_EXCEEDED';
670
+ else if (err.code === "LIMIT_FILE_COUNT") {
671
+ errorMessage = "Too many files";
672
+ errorCode = "FILE_COUNT_LIMIT_EXCEEDED";
426
673
  }
427
674
  else {
428
- errorMessage = err.message || 'Upload failed';
675
+ errorMessage = err.message || "Upload failed";
429
676
  }
430
677
  const multermateError = new MultermateError(errorMessage, errorCode);
431
678
  req.fileValidationError = errorMessage;
@@ -437,7 +684,7 @@ function uploadMultiple(options) {
437
684
  };
438
685
  }
439
686
  catch (error) {
440
- throw new MultermateError('Failed to create multiple upload middleware', 'MIDDLEWARE_CREATION_ERROR');
687
+ throw new MultermateError("Failed to create multiple upload middleware", "MIDDLEWARE_CREATION_ERROR");
441
688
  }
442
689
  }
443
690
  /**
@@ -448,8 +695,8 @@ function uploadMultiple(options) {
448
695
  */
449
696
  async function deleteFile(filePath) {
450
697
  try {
451
- if (!filePath || typeof filePath !== 'string') {
452
- throw new MultermateError('Invalid file path provided', 'INVALID_PATH');
698
+ if (!filePath || typeof filePath !== "string") {
699
+ throw new MultermateError("Invalid file path provided", "INVALID_PATH");
453
700
  }
454
701
  await promises_1.default.unlink(filePath);
455
702
  return true;
@@ -458,14 +705,14 @@ async function deleteFile(filePath) {
458
705
  if (error instanceof MultermateError) {
459
706
  throw error;
460
707
  }
461
- if (error.code === 'ENOENT') {
462
- throw new MultermateError(`File not found: ${filePath}`, 'FILE_NOT_FOUND');
708
+ if (error.code === "ENOENT") {
709
+ throw new MultermateError(`File not found: ${filePath}`, "FILE_NOT_FOUND");
463
710
  }
464
- else if (error.code === 'EACCES') {
465
- throw new MultermateError(`Permission denied: ${filePath}`, 'PERMISSION_DENIED');
711
+ else if (error.code === "EACCES") {
712
+ throw new MultermateError(`Permission denied: ${filePath}`, "PERMISSION_DENIED");
466
713
  }
467
714
  else {
468
- throw new MultermateError(`Failed to delete file: ${error.message}`, 'DELETE_ERROR');
715
+ throw new MultermateError(`Failed to delete file: ${error.message}`, "DELETE_ERROR");
469
716
  }
470
717
  }
471
718
  }
@@ -480,5 +727,5 @@ exports.default = {
480
727
  deleteFile,
481
728
  MultermateError,
482
729
  ALLOWED_FILE_TYPES: exports.ALLOWED_FILE_TYPES,
483
- MIME_TYPES: exports.MIME_TYPES
730
+ MIME_TYPES: exports.MIME_TYPES,
484
731
  };