multermate 2.1.0 → 2.2.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/LICENSE CHANGED
@@ -1,21 +1,21 @@
1
- MIT License
2
-
3
- Copyright (c) 2024 Wasim Zaman
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Wasim Zaman
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/dist/cjs/index.js CHANGED
@@ -8,6 +8,7 @@ exports.uploadSingle = uploadSingle;
8
8
  exports.uploadMultiple = uploadMultiple;
9
9
  exports.deleteFile = deleteFile;
10
10
  const promises_1 = __importDefault(require("fs/promises"));
11
+ const fs_1 = require("fs");
11
12
  const multer_1 = __importDefault(require("multer"));
12
13
  const path_1 = __importDefault(require("path"));
13
14
  const uuid_1 = require("uuid");
@@ -166,9 +167,9 @@ const configureStorage = (destination) => {
166
167
  return multer_1.default.diskStorage({
167
168
  destination: (req, file, cb) => {
168
169
  const dir = destination || "uploads";
169
- // Create directory synchronously to avoid callback issues
170
+ // Create directory synchronously - multer destination callback doesn't support async
170
171
  try {
171
- require('fs').mkdirSync(dir, { recursive: true });
172
+ (0, fs_1.mkdirSync)(dir, { recursive: true });
172
173
  cb(null, dir);
173
174
  }
174
175
  catch (error) {
@@ -177,7 +178,7 @@ const configureStorage = (destination) => {
177
178
  cb(null, dir);
178
179
  }
179
180
  else {
180
- cb(new MultermateError(`Failed to create destination directory: ${dir}`, 'DESTINATION_ERROR'), '');
181
+ cb(new MultermateError(`Failed to create destination directory: ${dir}. Error: ${error.message}`, 'DESTINATION_ERROR'), '');
181
182
  }
182
183
  }
183
184
  },
@@ -198,6 +199,34 @@ const configureStorage = (destination) => {
198
199
  },
199
200
  });
200
201
  };
202
+ const normalizePathForDb = (value) => value.replace(/\\/g, '/');
203
+ const getPhysicalDestination = (destination, absoluteDestination) => {
204
+ if (!absoluteDestination) {
205
+ return destination || 'uploads';
206
+ }
207
+ if (!path_1.default.isAbsolute(absoluteDestination)) {
208
+ throw new MultermateError(`absoluteDestination must be an absolute path. Received: ${absoluteDestination}`, 'INVALID_ABSOLUTE_DESTINATION');
209
+ }
210
+ return absoluteDestination;
211
+ };
212
+ const sanitizeStoredPathsForDb = (req, destination, absoluteDestination) => {
213
+ if (!absoluteDestination) {
214
+ return;
215
+ }
216
+ const dbDestination = normalizePathForDb(destination || 'uploads');
217
+ if (req.file) {
218
+ req.file.destination = dbDestination;
219
+ req.file.path = normalizePathForDb(path_1.default.join(dbDestination, req.file.filename));
220
+ }
221
+ if (req.files && !Array.isArray(req.files)) {
222
+ Object.values(req.files).forEach((files) => {
223
+ files.forEach((file) => {
224
+ file.destination = dbDestination;
225
+ file.path = normalizePathForDb(path_1.default.join(dbDestination, file.filename));
226
+ });
227
+ });
228
+ }
229
+ };
201
230
  /**
202
231
  * Function to configure file filter for Multer.
203
232
  *
@@ -232,9 +261,9 @@ const configureFileFilter = (allowedMimeTypes) => {
232
261
  * @param options - Configuration options for Multer.
233
262
  * @returns Multer instance configured with the provided options.
234
263
  */
235
- const configureMulter = ({ destination, filename, fileTypes = [], customMimeTypes = [], fileSizeLimit, preservePath = false, }) => {
264
+ const configureMulter = ({ destination, absoluteDestination, filename, fileTypes = [], customMimeTypes = [], fileSizeLimit, preservePath = false, }) => {
236
265
  try {
237
- const storage = configureStorage(destination);
266
+ const storage = configureStorage(getPhysicalDestination(destination, absoluteDestination));
238
267
  // Combine allowed MIME types based on fileTypes array
239
268
  let allowedMimeTypes = [];
240
269
  if (customMimeTypes.length > 0) {
@@ -273,13 +302,13 @@ const configureMulter = ({ destination, filename, fileTypes = [], customMimeType
273
302
  */
274
303
  function uploadSingle(options = {}) {
275
304
  try {
276
- const destination = options.destination || 'uploads';
305
+ const storageDestination = getPhysicalDestination(options.destination, options.absoluteDestination);
277
306
  const multerInstance = configureMulter(options);
278
307
  const middleware = multerInstance.single(options.filename || "file");
279
308
  return (req, res, next) => {
280
309
  // Make sure the destination directory exists
281
310
  try {
282
- require('fs').mkdirSync(destination, { recursive: true });
311
+ (0, fs_1.mkdirSync)(storageDestination, { recursive: true });
283
312
  }
284
313
  catch (error) {
285
314
  // Directory might already exist, ignore error
@@ -312,6 +341,7 @@ function uploadSingle(options = {}) {
312
341
  req.fileValidationError = errorMessage;
313
342
  return next(multermateError);
314
343
  }
344
+ sanitizeStoredPathsForDb(req, options.destination, options.absoluteDestination);
315
345
  next();
316
346
  });
317
347
  };
@@ -328,7 +358,7 @@ function uploadSingle(options = {}) {
328
358
  */
329
359
  function uploadMultiple(options) {
330
360
  try {
331
- const destination = options.destination || 'uploads';
361
+ const storageDestination = getPhysicalDestination(options.destination, options.absoluteDestination);
332
362
  // Map fields configuration to multer format
333
363
  const fieldConfigs = options.fields.map(field => ({
334
364
  name: field.name,
@@ -352,7 +382,8 @@ function uploadMultiple(options) {
352
382
  });
353
383
  }
354
384
  const multerConfig = {
355
- destination,
385
+ destination: options.destination,
386
+ absoluteDestination: options.absoluteDestination,
356
387
  fileTypes: [],
357
388
  customMimeTypes: allowedFileTypes.length > 0 ? allowedFileTypes : [],
358
389
  fileSizeLimit: options.fileSizeLimit,
@@ -363,7 +394,7 @@ function uploadMultiple(options) {
363
394
  return (req, res, next) => {
364
395
  // Make sure the destination directory exists
365
396
  try {
366
- require('fs').mkdirSync(destination, { recursive: true });
397
+ (0, fs_1.mkdirSync)(storageDestination, { recursive: true });
367
398
  }
368
399
  catch (error) {
369
400
  // Directory might already exist, ignore error
@@ -400,6 +431,7 @@ function uploadMultiple(options) {
400
431
  req.fileValidationError = errorMessage;
401
432
  return next(multermateError);
402
433
  }
434
+ sanitizeStoredPathsForDb(req, options.destination, options.absoluteDestination);
403
435
  next();
404
436
  });
405
437
  };
package/dist/esm/index.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import fs from 'fs/promises';
2
+ import { mkdirSync } from 'fs';
2
3
  import multer from 'multer';
3
4
  import path from 'path';
4
5
  import { v4 as uuidv4 } from 'uuid';
@@ -156,9 +157,9 @@ const configureStorage = (destination) => {
156
157
  return multer.diskStorage({
157
158
  destination: (req, file, cb) => {
158
159
  const dir = destination || "uploads";
159
- // Create directory synchronously to avoid callback issues
160
+ // Create directory synchronously - multer destination callback doesn't support async
160
161
  try {
161
- require('fs').mkdirSync(dir, { recursive: true });
162
+ mkdirSync(dir, { recursive: true });
162
163
  cb(null, dir);
163
164
  }
164
165
  catch (error) {
@@ -167,7 +168,7 @@ const configureStorage = (destination) => {
167
168
  cb(null, dir);
168
169
  }
169
170
  else {
170
- cb(new MultermateError(`Failed to create destination directory: ${dir}`, 'DESTINATION_ERROR'), '');
171
+ cb(new MultermateError(`Failed to create destination directory: ${dir}. Error: ${error.message}`, 'DESTINATION_ERROR'), '');
171
172
  }
172
173
  }
173
174
  },
@@ -188,6 +189,34 @@ const configureStorage = (destination) => {
188
189
  },
189
190
  });
190
191
  };
192
+ const normalizePathForDb = (value) => value.replace(/\\/g, '/');
193
+ const getPhysicalDestination = (destination, absoluteDestination) => {
194
+ if (!absoluteDestination) {
195
+ return destination || 'uploads';
196
+ }
197
+ if (!path.isAbsolute(absoluteDestination)) {
198
+ throw new MultermateError(`absoluteDestination must be an absolute path. Received: ${absoluteDestination}`, 'INVALID_ABSOLUTE_DESTINATION');
199
+ }
200
+ return absoluteDestination;
201
+ };
202
+ const sanitizeStoredPathsForDb = (req, destination, absoluteDestination) => {
203
+ if (!absoluteDestination) {
204
+ return;
205
+ }
206
+ const dbDestination = normalizePathForDb(destination || 'uploads');
207
+ if (req.file) {
208
+ req.file.destination = dbDestination;
209
+ req.file.path = normalizePathForDb(path.join(dbDestination, req.file.filename));
210
+ }
211
+ if (req.files && !Array.isArray(req.files)) {
212
+ Object.values(req.files).forEach((files) => {
213
+ files.forEach((file) => {
214
+ file.destination = dbDestination;
215
+ file.path = normalizePathForDb(path.join(dbDestination, file.filename));
216
+ });
217
+ });
218
+ }
219
+ };
191
220
  /**
192
221
  * Function to configure file filter for Multer.
193
222
  *
@@ -222,9 +251,9 @@ const configureFileFilter = (allowedMimeTypes) => {
222
251
  * @param options - Configuration options for Multer.
223
252
  * @returns Multer instance configured with the provided options.
224
253
  */
225
- const configureMulter = ({ destination, filename, fileTypes = [], customMimeTypes = [], fileSizeLimit, preservePath = false, }) => {
254
+ const configureMulter = ({ destination, absoluteDestination, filename, fileTypes = [], customMimeTypes = [], fileSizeLimit, preservePath = false, }) => {
226
255
  try {
227
- const storage = configureStorage(destination);
256
+ const storage = configureStorage(getPhysicalDestination(destination, absoluteDestination));
228
257
  // Combine allowed MIME types based on fileTypes array
229
258
  let allowedMimeTypes = [];
230
259
  if (customMimeTypes.length > 0) {
@@ -263,13 +292,13 @@ const configureMulter = ({ destination, filename, fileTypes = [], customMimeType
263
292
  */
264
293
  export function uploadSingle(options = {}) {
265
294
  try {
266
- const destination = options.destination || 'uploads';
295
+ const storageDestination = getPhysicalDestination(options.destination, options.absoluteDestination);
267
296
  const multerInstance = configureMulter(options);
268
297
  const middleware = multerInstance.single(options.filename || "file");
269
298
  return (req, res, next) => {
270
299
  // Make sure the destination directory exists
271
300
  try {
272
- require('fs').mkdirSync(destination, { recursive: true });
301
+ mkdirSync(storageDestination, { recursive: true });
273
302
  }
274
303
  catch (error) {
275
304
  // Directory might already exist, ignore error
@@ -302,6 +331,7 @@ export function uploadSingle(options = {}) {
302
331
  req.fileValidationError = errorMessage;
303
332
  return next(multermateError);
304
333
  }
334
+ sanitizeStoredPathsForDb(req, options.destination, options.absoluteDestination);
305
335
  next();
306
336
  });
307
337
  };
@@ -318,7 +348,7 @@ export function uploadSingle(options = {}) {
318
348
  */
319
349
  export function uploadMultiple(options) {
320
350
  try {
321
- const destination = options.destination || 'uploads';
351
+ const storageDestination = getPhysicalDestination(options.destination, options.absoluteDestination);
322
352
  // Map fields configuration to multer format
323
353
  const fieldConfigs = options.fields.map(field => ({
324
354
  name: field.name,
@@ -342,7 +372,8 @@ export function uploadMultiple(options) {
342
372
  });
343
373
  }
344
374
  const multerConfig = {
345
- destination,
375
+ destination: options.destination,
376
+ absoluteDestination: options.absoluteDestination,
346
377
  fileTypes: [],
347
378
  customMimeTypes: allowedFileTypes.length > 0 ? allowedFileTypes : [],
348
379
  fileSizeLimit: options.fileSizeLimit,
@@ -353,7 +384,7 @@ export function uploadMultiple(options) {
353
384
  return (req, res, next) => {
354
385
  // Make sure the destination directory exists
355
386
  try {
356
- require('fs').mkdirSync(destination, { recursive: true });
387
+ mkdirSync(storageDestination, { recursive: true });
357
388
  }
358
389
  catch (error) {
359
390
  // Directory might already exist, ignore error
@@ -390,6 +421,7 @@ export function uploadMultiple(options) {
390
421
  req.fileValidationError = errorMessage;
391
422
  return next(multermateError);
392
423
  }
424
+ sanitizeStoredPathsForDb(req, options.destination, options.absoluteDestination);
393
425
  next();
394
426
  });
395
427
  };
@@ -1 +1 @@
1
- {"type": "module"}
1
+ '{"type": "module"}'
package/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
- // Main type definitions - re-export everything from types directory
2
- export * from './types/index';
3
-
4
- // Default export for CommonJS compatibility
5
- import multermateDefault from './types/index';
6
- export default multermateDefault;
1
+ // Main type definitions - re-export everything from types directory
2
+ export * from './types/index';
3
+
4
+ // Default export for CommonJS compatibility
5
+ import multermateDefault from './types/index';
6
+ export default multermateDefault;
package/index.js CHANGED
@@ -1,9 +1,9 @@
1
- // CommonJS entry point
2
- try {
3
- module.exports = require("./dist/cjs/index.js");
4
- } catch (error) {
5
- // Fallback if dist is not built yet
6
- throw new Error(
7
- 'MulterMate: Please run "npm run build" to build the package before using it.'
8
- );
9
- }
1
+ // CommonJS entry point
2
+ try {
3
+ module.exports = require("./dist/cjs/index.js");
4
+ } catch (error) {
5
+ // Fallback if dist is not built yet
6
+ throw new Error(
7
+ 'MulterMate: Please run "npm run build" to build the package before using it.'
8
+ );
9
+ }
package/index.mjs CHANGED
@@ -1,6 +1,6 @@
1
- // ES Module entry point
2
- export * from "./dist/esm/index.js";
3
-
4
- // Default export for better compatibility
5
- import multermateDefault from "./dist/esm/index.js";
6
- export default multermateDefault;
1
+ // ES Module entry point
2
+ export * from "./dist/esm/index.js";
3
+
4
+ // Default export for better compatibility
5
+ import multermateDefault from "./dist/esm/index.js";
6
+ export default multermateDefault;
package/package.json CHANGED
@@ -1,73 +1,73 @@
1
- {
2
- "name": "multermate",
3
- "version": "2.1.0",
4
- "description": "A powerful and flexible file upload utility built on top of Multer with TypeScript support, comprehensive file type handling, custom error classes, and universal JavaScript module compatibility",
5
- "main": "index.js",
6
- "module": "index.mjs",
7
- "types": "index.d.ts",
8
- "exports": {
9
- ".": {
10
- "types": "./types/index.d.ts",
11
- "import": "./dist/esm/index.js",
12
- "require": "./dist/cjs/index.js",
13
- "default": "./dist/cjs/index.js"
14
- },
15
- "./package.json": "./package.json"
16
- },
17
- "files": [
18
- "dist/",
19
- "types/",
20
- "index.js",
21
- "index.mjs",
22
- "index.d.ts",
23
- "LICENSE",
24
- "readme.md"
25
- ],
26
- "scripts": {
27
- "build": "npm run clean && npm run build:cjs && npm run build:esm && npm run build:types && npm run build:entries",
28
- "build:cjs": "tsc --module commonjs --outDir dist/cjs --target ES2018",
29
- "build:esm": "tsc --module ES2020 --outDir dist/esm --target ES2020 && echo '{\"type\": \"module\"}' > dist/esm/package.json",
30
- "build:types": "tsc --emitDeclarationOnly --outDir types",
31
- "build:entries": "node -e \"console.log('Build entries completed')\"",
32
- "clean": "node -e \"const fs=require('fs'); ['dist', 'types'].forEach(dir => { try { fs.rmSync(dir, {recursive: true, force: true}); } catch(e){} })\"",
33
- "prepublishOnly": "npm run build",
34
- "test": "echo \"Error: no test specified\" && exit 1"
35
- },
36
- "keywords": [
37
- "multer",
38
- "file upload",
39
- "middleware",
40
- "node.js",
41
- "typescript",
42
- "express",
43
- "file handling",
44
- "form data",
45
- "multipart",
46
- "configurable",
47
- "file validation",
48
- "error handling"
49
- ],
50
- "author": "Wasim Zaman",
51
- "license": "MIT",
52
- "dependencies": {
53
- "multer": "1.4.5-lts.1",
54
- "uuid": "^10.0.0"
55
- },
56
- "devDependencies": {
57
- "@types/express": "^5.0.3",
58
- "@types/multer": "^1.4.11",
59
- "@types/node": "^20.10.5",
60
- "@types/uuid": "^9.0.7",
61
- "express": "^5.1.0",
62
- "rimraf": "^5.0.5",
63
- "typescript": "^5.3.3"
64
- },
65
- "repository": {
66
- "type": "git",
67
- "url": "git+https://github.com/Wasim-Zaman/multermate.git"
68
- },
69
- "bugs": {
70
- "url": "https://github.com/Wasim-Zaman/multermate/issues"
71
- },
72
- "homepage": "https://github.com/Wasim-Zaman/multermate#readme"
73
- }
1
+ {
2
+ "name": "multermate",
3
+ "version": "2.2.0",
4
+ "description": "A powerful and flexible file upload utility built on top of Multer with TypeScript support, comprehensive file type handling, custom error classes, and universal JavaScript module compatibility",
5
+ "main": "index.js",
6
+ "module": "index.mjs",
7
+ "types": "index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./types/index.d.ts",
11
+ "import": "./dist/esm/index.js",
12
+ "require": "./dist/cjs/index.js",
13
+ "default": "./dist/cjs/index.js"
14
+ },
15
+ "./package.json": "./package.json"
16
+ },
17
+ "files": [
18
+ "dist/",
19
+ "types/",
20
+ "index.js",
21
+ "index.mjs",
22
+ "index.d.ts",
23
+ "LICENSE",
24
+ "readme.md"
25
+ ],
26
+ "scripts": {
27
+ "build": "npm run clean && npm run build:cjs && npm run build:esm && npm run build:types && npm run build:entries",
28
+ "build:cjs": "tsc --module commonjs --outDir dist/cjs --target ES2018",
29
+ "build:esm": "tsc --module ES2020 --outDir dist/esm --target ES2020 && echo '{\"type\": \"module\"}' > dist/esm/package.json",
30
+ "build:types": "tsc --emitDeclarationOnly --outDir types",
31
+ "build:entries": "node -e \"console.log('Build entries completed')\"",
32
+ "clean": "node -e \"const fs=require('fs'); ['dist', 'types'].forEach(dir => { try { fs.rmSync(dir, {recursive: true, force: true}); } catch(e){} })\"",
33
+ "prepublishOnly": "npm run build",
34
+ "test": "echo \"Error: no test specified\" && exit 1"
35
+ },
36
+ "keywords": [
37
+ "multer",
38
+ "file upload",
39
+ "middleware",
40
+ "node.js",
41
+ "typescript",
42
+ "express",
43
+ "file handling",
44
+ "form data",
45
+ "multipart",
46
+ "configurable",
47
+ "file validation",
48
+ "error handling"
49
+ ],
50
+ "author": "Wasim Zaman",
51
+ "license": "MIT",
52
+ "dependencies": {
53
+ "multer": "1.4.5-lts.1",
54
+ "uuid": "^10.0.0"
55
+ },
56
+ "devDependencies": {
57
+ "@types/express": "^5.0.3",
58
+ "@types/multer": "^1.4.11",
59
+ "@types/node": "^20.10.5",
60
+ "@types/uuid": "^9.0.7",
61
+ "express": "^5.1.0",
62
+ "rimraf": "^5.0.5",
63
+ "typescript": "^5.9.3"
64
+ },
65
+ "repository": {
66
+ "type": "git",
67
+ "url": "git+https://github.com/Wasim-Zaman/multermate.git"
68
+ },
69
+ "bugs": {
70
+ "url": "https://github.com/Wasim-Zaman/multermate/issues"
71
+ },
72
+ "homepage": "https://github.com/Wasim-Zaman/multermate#readme"
73
+ }