multermate 1.1.0 → 1.1.1

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.
@@ -0,0 +1,211 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.ALLOWED_FILE_TYPES = void 0;
7
+ exports.uploadSingle = uploadSingle;
8
+ exports.uploadMultiple = uploadMultiple;
9
+ exports.deleteFile = deleteFile;
10
+ const promises_1 = __importDefault(require("fs/promises"));
11
+ const multer_1 = __importDefault(require("multer"));
12
+ const path_1 = __importDefault(require("path"));
13
+ const uuid_1 = require("uuid");
14
+ // Define allowed MIME types
15
+ const ALLOWED_MIME_TYPES = {
16
+ images: ["image/jpeg", "image/jpg", "image/png", "image/gif"],
17
+ videos: ["video/mp4", "video/mpeg", "video/ogg", "video/webm", "video/avi"],
18
+ pdfs: ["application/pdf"],
19
+ all: [
20
+ "image/jpeg",
21
+ "image/jpg",
22
+ "image/png",
23
+ "image/gif",
24
+ "video/mp4",
25
+ "video/mpeg",
26
+ "video/ogg",
27
+ "video/webm",
28
+ "video/avi",
29
+ "application/pdf",
30
+ ],
31
+ };
32
+ /**
33
+ * Function to configure storage for Multer.
34
+ *
35
+ * @param destination - The destination folder where files will be stored.
36
+ * @returns Multer storage configuration object.
37
+ */
38
+ const configureStorage = (destination) => {
39
+ return multer_1.default.diskStorage({
40
+ destination: (_req, _file, cb) => {
41
+ cb(null, destination || "uploads"); // Default folder is "uploads" if none is provided.
42
+ },
43
+ filename: (_req, file, cb) => {
44
+ const sanitizedFilename = file.originalname.replace(/\\/g, "/");
45
+ const extension = path_1.default.extname(sanitizedFilename);
46
+ const fieldName = file.fieldname || "file"; // Use the field name as part of the filename.
47
+ const uniqueName = (0, uuid_1.v4)(); // Generate a unique name using uuid.
48
+ let fileName = `${uniqueName}-${fieldName}${extension}`;
49
+ // Replace backslashes with forward slashes in the final filename
50
+ fileName = fileName.replace(/\\/g, "/");
51
+ cb(null, fileName); // Set the final filename.
52
+ },
53
+ });
54
+ };
55
+ /**
56
+ * Function to configure file filter for Multer.
57
+ *
58
+ * @param allowedMimeTypes - Array of allowed MIME types.
59
+ * @returns File filter function for Multer.
60
+ */
61
+ const configureFileFilter = (allowedMimeTypes) => {
62
+ return (_req, file, cb) => {
63
+ if (allowedMimeTypes.includes(file.mimetype)) {
64
+ cb(null, true); // Allow the file if its MIME type is allowed.
65
+ }
66
+ else {
67
+ const error = new Error("Invalid file type. Only specified file types are allowed.");
68
+ error.code = 'INVALID_FILE_TYPE';
69
+ cb(error); // Reject the file if its MIME type is not allowed.
70
+ }
71
+ };
72
+ };
73
+ /**
74
+ * Function to configure Multer with the provided options.
75
+ *
76
+ * @param options - Configuration options for Multer.
77
+ * @returns Multer instance configured with the provided options.
78
+ */
79
+ const configureMulter = ({ destination, filename, fileTypes = [], customMimeTypes = [], fileSizeLimit, preservePath = false, }) => {
80
+ const storage = configureStorage(destination);
81
+ // Combine allowed MIME types based on fileTypes array
82
+ let allowedMimeTypes = [];
83
+ if (customMimeTypes.length > 0) {
84
+ // Use custom MIME types if provided
85
+ allowedMimeTypes = customMimeTypes;
86
+ }
87
+ else {
88
+ // Use default MIME types for specified fileTypes
89
+ fileTypes.forEach((type) => {
90
+ if (ALLOWED_MIME_TYPES[type]) {
91
+ allowedMimeTypes = allowedMimeTypes.concat(ALLOWED_MIME_TYPES[type]);
92
+ }
93
+ });
94
+ // If no specific file types are provided, use all allowed MIME types
95
+ if (allowedMimeTypes.length === 0) {
96
+ allowedMimeTypes = ALLOWED_MIME_TYPES.all;
97
+ }
98
+ }
99
+ const fileFilter = configureFileFilter(allowedMimeTypes);
100
+ return (0, multer_1.default)({
101
+ storage,
102
+ fileFilter,
103
+ limits: { fileSize: fileSizeLimit || 1024 * 1024 * 50 }, // Default 50MB file size limit
104
+ preservePath,
105
+ });
106
+ };
107
+ /**
108
+ * Function to handle a single file upload.
109
+ *
110
+ * @param options - Configuration options for the single file upload.
111
+ * @returns Multer middleware configured for single file upload.
112
+ */
113
+ function uploadSingle(options = {}) {
114
+ // Create destination directory if it doesn't exist
115
+ const destination = options.destination || 'uploads';
116
+ const multerInstance = configureMulter(options);
117
+ const middleware = multerInstance.single(options.filename || "file");
118
+ return (req, res, next) => {
119
+ // Make sure the destination directory exists
120
+ require('fs').mkdirSync(destination, { recursive: true });
121
+ middleware(req, res, (err) => {
122
+ if (err) {
123
+ if (err.code === 'LIMIT_FILE_SIZE') {
124
+ req.fileValidationError = 'File size limit exceeded';
125
+ }
126
+ else if (err.code === 'INVALID_FILE_TYPE') {
127
+ req.fileValidationError = 'Invalid file type';
128
+ }
129
+ else {
130
+ req.fileValidationError = err.message;
131
+ }
132
+ }
133
+ next();
134
+ });
135
+ };
136
+ }
137
+ /**
138
+ * Function to handle multiple file uploads across multiple fields.
139
+ *
140
+ * @param options - Configuration options for multiple file uploads.
141
+ * @returns Multer middleware configured for multiple file uploads.
142
+ */
143
+ function uploadMultiple(options) {
144
+ const destination = options.destination || 'uploads';
145
+ // Map fields configuration to multer format
146
+ const fieldConfigs = options.fields.map(field => ({
147
+ name: field.name,
148
+ maxCount: field.maxCount || 10, // Default maxCount is 10 if not specified.
149
+ }));
150
+ let allowedFileTypes = [];
151
+ options.fields.forEach((field) => {
152
+ const types = field.fileTypes || [];
153
+ types.forEach((type) => {
154
+ if (ALLOWED_MIME_TYPES[type]) {
155
+ allowedFileTypes = allowedFileTypes.concat(ALLOWED_MIME_TYPES[type]);
156
+ }
157
+ });
158
+ });
159
+ const multerConfig = {
160
+ destination,
161
+ fileTypes: [],
162
+ customMimeTypes: options.customMimeTypes || [],
163
+ fileSizeLimit: options.fileSizeLimit,
164
+ preservePath: options.preservePath
165
+ };
166
+ const multerInstance = configureMulter(multerConfig);
167
+ const middleware = multerInstance.fields(fieldConfigs);
168
+ return (req, res, next) => {
169
+ // Make sure the destination directory exists
170
+ require('fs').mkdirSync(destination, { recursive: true });
171
+ middleware(req, res, (err) => {
172
+ if (err) {
173
+ if (err.code === 'LIMIT_FILE_SIZE') {
174
+ req.fileValidationError = 'File size limit exceeded';
175
+ }
176
+ else if (err.code === 'INVALID_FILE_TYPE') {
177
+ req.fileValidationError = 'Invalid file type';
178
+ }
179
+ else {
180
+ req.fileValidationError = err.message;
181
+ }
182
+ }
183
+ next();
184
+ });
185
+ };
186
+ }
187
+ /**
188
+ * Utility function to delete a file from the filesystem
189
+ *
190
+ * @param filePath - The path to the file that needs to be deleted
191
+ * @returns Promise that resolves to true if deletion was successful, false otherwise
192
+ */
193
+ async function deleteFile(filePath) {
194
+ try {
195
+ await promises_1.default.unlink(filePath);
196
+ return true;
197
+ }
198
+ catch (error) {
199
+ console.error(`Error deleting file: ${error.message}`);
200
+ return false;
201
+ }
202
+ }
203
+ // Export the allowed file types for reference
204
+ exports.ALLOWED_FILE_TYPES = Object.keys(ALLOWED_MIME_TYPES);
205
+ // Export your functions
206
+ exports.default = {
207
+ uploadSingle,
208
+ uploadMultiple,
209
+ deleteFile,
210
+ ALLOWED_FILE_TYPES: exports.ALLOWED_FILE_TYPES
211
+ };
@@ -0,0 +1,202 @@
1
+ import fs from 'fs/promises';
2
+ import multer from 'multer';
3
+ import path from 'path';
4
+ import { v4 as uuidv4 } from 'uuid';
5
+ // Define allowed MIME types
6
+ const ALLOWED_MIME_TYPES = {
7
+ images: ["image/jpeg", "image/jpg", "image/png", "image/gif"],
8
+ videos: ["video/mp4", "video/mpeg", "video/ogg", "video/webm", "video/avi"],
9
+ pdfs: ["application/pdf"],
10
+ all: [
11
+ "image/jpeg",
12
+ "image/jpg",
13
+ "image/png",
14
+ "image/gif",
15
+ "video/mp4",
16
+ "video/mpeg",
17
+ "video/ogg",
18
+ "video/webm",
19
+ "video/avi",
20
+ "application/pdf",
21
+ ],
22
+ };
23
+ /**
24
+ * Function to configure storage for Multer.
25
+ *
26
+ * @param destination - The destination folder where files will be stored.
27
+ * @returns Multer storage configuration object.
28
+ */
29
+ const configureStorage = (destination) => {
30
+ return multer.diskStorage({
31
+ destination: (_req, _file, cb) => {
32
+ cb(null, destination || "uploads"); // Default folder is "uploads" if none is provided.
33
+ },
34
+ filename: (_req, file, cb) => {
35
+ const sanitizedFilename = file.originalname.replace(/\\/g, "/");
36
+ const extension = path.extname(sanitizedFilename);
37
+ const fieldName = file.fieldname || "file"; // Use the field name as part of the filename.
38
+ const uniqueName = uuidv4(); // Generate a unique name using uuid.
39
+ let fileName = `${uniqueName}-${fieldName}${extension}`;
40
+ // Replace backslashes with forward slashes in the final filename
41
+ fileName = fileName.replace(/\\/g, "/");
42
+ cb(null, fileName); // Set the final filename.
43
+ },
44
+ });
45
+ };
46
+ /**
47
+ * Function to configure file filter for Multer.
48
+ *
49
+ * @param allowedMimeTypes - Array of allowed MIME types.
50
+ * @returns File filter function for Multer.
51
+ */
52
+ const configureFileFilter = (allowedMimeTypes) => {
53
+ return (_req, file, cb) => {
54
+ if (allowedMimeTypes.includes(file.mimetype)) {
55
+ cb(null, true); // Allow the file if its MIME type is allowed.
56
+ }
57
+ else {
58
+ const error = new Error("Invalid file type. Only specified file types are allowed.");
59
+ error.code = 'INVALID_FILE_TYPE';
60
+ cb(error); // Reject the file if its MIME type is not allowed.
61
+ }
62
+ };
63
+ };
64
+ /**
65
+ * Function to configure Multer with the provided options.
66
+ *
67
+ * @param options - Configuration options for Multer.
68
+ * @returns Multer instance configured with the provided options.
69
+ */
70
+ const configureMulter = ({ destination, filename, fileTypes = [], customMimeTypes = [], fileSizeLimit, preservePath = false, }) => {
71
+ const storage = configureStorage(destination);
72
+ // Combine allowed MIME types based on fileTypes array
73
+ let allowedMimeTypes = [];
74
+ if (customMimeTypes.length > 0) {
75
+ // Use custom MIME types if provided
76
+ allowedMimeTypes = customMimeTypes;
77
+ }
78
+ else {
79
+ // Use default MIME types for specified fileTypes
80
+ fileTypes.forEach((type) => {
81
+ if (ALLOWED_MIME_TYPES[type]) {
82
+ allowedMimeTypes = allowedMimeTypes.concat(ALLOWED_MIME_TYPES[type]);
83
+ }
84
+ });
85
+ // If no specific file types are provided, use all allowed MIME types
86
+ if (allowedMimeTypes.length === 0) {
87
+ allowedMimeTypes = ALLOWED_MIME_TYPES.all;
88
+ }
89
+ }
90
+ const fileFilter = configureFileFilter(allowedMimeTypes);
91
+ return multer({
92
+ storage,
93
+ fileFilter,
94
+ limits: { fileSize: fileSizeLimit || 1024 * 1024 * 50 }, // Default 50MB file size limit
95
+ preservePath,
96
+ });
97
+ };
98
+ /**
99
+ * Function to handle a single file upload.
100
+ *
101
+ * @param options - Configuration options for the single file upload.
102
+ * @returns Multer middleware configured for single file upload.
103
+ */
104
+ export function uploadSingle(options = {}) {
105
+ // Create destination directory if it doesn't exist
106
+ const destination = options.destination || 'uploads';
107
+ const multerInstance = configureMulter(options);
108
+ const middleware = multerInstance.single(options.filename || "file");
109
+ return (req, res, next) => {
110
+ // Make sure the destination directory exists
111
+ require('fs').mkdirSync(destination, { recursive: true });
112
+ middleware(req, res, (err) => {
113
+ if (err) {
114
+ if (err.code === 'LIMIT_FILE_SIZE') {
115
+ req.fileValidationError = 'File size limit exceeded';
116
+ }
117
+ else if (err.code === 'INVALID_FILE_TYPE') {
118
+ req.fileValidationError = 'Invalid file type';
119
+ }
120
+ else {
121
+ req.fileValidationError = err.message;
122
+ }
123
+ }
124
+ next();
125
+ });
126
+ };
127
+ }
128
+ /**
129
+ * Function to handle multiple file uploads across multiple fields.
130
+ *
131
+ * @param options - Configuration options for multiple file uploads.
132
+ * @returns Multer middleware configured for multiple file uploads.
133
+ */
134
+ export function uploadMultiple(options) {
135
+ const destination = options.destination || 'uploads';
136
+ // Map fields configuration to multer format
137
+ const fieldConfigs = options.fields.map(field => ({
138
+ name: field.name,
139
+ maxCount: field.maxCount || 10, // Default maxCount is 10 if not specified.
140
+ }));
141
+ let allowedFileTypes = [];
142
+ options.fields.forEach((field) => {
143
+ const types = field.fileTypes || [];
144
+ types.forEach((type) => {
145
+ if (ALLOWED_MIME_TYPES[type]) {
146
+ allowedFileTypes = allowedFileTypes.concat(ALLOWED_MIME_TYPES[type]);
147
+ }
148
+ });
149
+ });
150
+ const multerConfig = {
151
+ destination,
152
+ fileTypes: [],
153
+ customMimeTypes: options.customMimeTypes || [],
154
+ fileSizeLimit: options.fileSizeLimit,
155
+ preservePath: options.preservePath
156
+ };
157
+ const multerInstance = configureMulter(multerConfig);
158
+ const middleware = multerInstance.fields(fieldConfigs);
159
+ return (req, res, next) => {
160
+ // Make sure the destination directory exists
161
+ require('fs').mkdirSync(destination, { recursive: true });
162
+ middleware(req, res, (err) => {
163
+ if (err) {
164
+ if (err.code === 'LIMIT_FILE_SIZE') {
165
+ req.fileValidationError = 'File size limit exceeded';
166
+ }
167
+ else if (err.code === 'INVALID_FILE_TYPE') {
168
+ req.fileValidationError = 'Invalid file type';
169
+ }
170
+ else {
171
+ req.fileValidationError = err.message;
172
+ }
173
+ }
174
+ next();
175
+ });
176
+ };
177
+ }
178
+ /**
179
+ * Utility function to delete a file from the filesystem
180
+ *
181
+ * @param filePath - The path to the file that needs to be deleted
182
+ * @returns Promise that resolves to true if deletion was successful, false otherwise
183
+ */
184
+ export async function deleteFile(filePath) {
185
+ try {
186
+ await fs.unlink(filePath);
187
+ return true;
188
+ }
189
+ catch (error) {
190
+ console.error(`Error deleting file: ${error.message}`);
191
+ return false;
192
+ }
193
+ }
194
+ // Export the allowed file types for reference
195
+ export const ALLOWED_FILE_TYPES = Object.keys(ALLOWED_MIME_TYPES);
196
+ // Export your functions
197
+ export default {
198
+ uploadSingle,
199
+ uploadMultiple,
200
+ deleteFile,
201
+ ALLOWED_FILE_TYPES
202
+ };
@@ -0,0 +1 @@
1
+ {"type": "module"}
package/index.d.ts ADDED
@@ -0,0 +1 @@
1
+ export * from './types/index';
package/index.js ADDED
@@ -0,0 +1,2 @@
1
+ // CommonJS entry point
2
+ module.exports = require("./dist/cjs/index");
package/index.mjs ADDED
@@ -0,0 +1,15 @@
1
+ // ES Module entry point
2
+ export * from "./dist/esm/index.js";
3
+
4
+ // This pattern ensures better compatibility with various Node.js environments
5
+ import { createRequire } from "module";
6
+ const require = createRequire(import.meta.url);
7
+ const multermate = require("./dist/cjs/index.js");
8
+
9
+ // Re-export everything from the CJS module
10
+ export const uploadSingle = multermate.uploadSingle;
11
+ export const uploadMultiple = multermate.uploadMultiple;
12
+ export const deleteFile = multermate.deleteFile;
13
+
14
+ // Default export
15
+ export default multermate;
package/package.json CHANGED
@@ -1,23 +1,59 @@
1
1
  {
2
2
  "name": "multermate",
3
- "version": "1.1.0",
4
- "description": "A flexible and customizable npm package for configuring Multer",
5
- "main": "./dist/index.cjs.js",
6
- "module": "./dist/index.mjs",
3
+ "version": "1.1.1",
4
+ "description": "A flexible and customizable file upload utility built on top of Multer with TypeScript support",
5
+ "main": "index.js",
6
+ "module": "index.mjs",
7
+ "types": "index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "require": "./dist/cjs/index.js",
11
+ "import": "./dist/esm/index.js",
12
+ "types": "./types/index.d.ts"
13
+ }
14
+ },
15
+ "files": [
16
+ "dist/",
17
+ "types/",
18
+ "index.js",
19
+ "index.mjs",
20
+ "index.d.ts"
21
+ ],
7
22
  "scripts": {
8
- "build:cjs": "babel src --out-dir dist --extensions \".js\" --plugins=@babel/plugin-transform-modules-commonjs",
9
- "build:esm": "babel src --out-dir dist --extensions \".js\"",
10
- "build": "npm run build:cjs && npm run build:esm",
23
+ "build": "npm run clean && npm run build:cjs && npm run build:esm && npm run build:types",
24
+ "build:cjs": "tsc --module commonjs --outDir dist/cjs",
25
+ "build:esm": "tsc --module ES2020 --outDir dist/esm && echo '{\"type\": \"module\"}' > dist/esm/package.json",
26
+ "build:types": "tsc --emitDeclarationOnly",
27
+ "clean": "node -e \"const fs=require('fs'); ['dist', 'types'].forEach(dir => { try { fs.rmSync(dir, {recursive: true, force: true}); } catch(e){} })\"",
28
+ "prepublishOnly": "npm run build",
11
29
  "test": "echo \"Error: no test specified\" && exit 1"
12
30
  },
31
+ "keywords": [
32
+ "multer",
33
+ "file upload",
34
+ "middleware",
35
+ "node.js",
36
+ "configurable"
37
+ ],
38
+ "author": "Wasim Zaman",
39
+ "license": "MIT",
13
40
  "dependencies": {
14
41
  "multer": "1.4.5-lts.1",
15
42
  "uuid": "^10.0.0"
16
43
  },
17
44
  "devDependencies": {
18
- "@babel/cli": "^7.25.6",
19
- "@babel/core": "^7.25.2",
20
- "@babel/plugin-transform-modules-commonjs": "^7.24.8",
21
- "@babel/preset-env": "^7.25.4"
22
- }
45
+ "@types/multer": "^1.4.11",
46
+ "@types/node": "^20.10.5",
47
+ "@types/uuid": "^9.0.7",
48
+ "rimraf": "^5.0.5",
49
+ "typescript": "^5.3.3"
50
+ },
51
+ "repository": {
52
+ "type": "git",
53
+ "url": "git+https://github.com/Wasim-Zaman/multermate.git"
54
+ },
55
+ "bugs": {
56
+ "url": "https://github.com/Wasim-Zaman/multermate/issues"
57
+ },
58
+ "homepage": "https://github.com/Wasim-Zaman/multermate#readme"
23
59
  }