mastercontroller 1.3.1 → 1.3.3

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/MasterTools.js CHANGED
@@ -101,7 +101,24 @@ class MasterTools{
101
101
  return sha.digest('hex');
102
102
  }
103
103
 
104
+ /**
105
+ * @deprecated This custom base64 implementation ONLY works for TEXT strings, NOT binary files.
106
+ * For binary files (images, PDFs, videos), use Node.js Buffer API or the new file conversion methods below.
107
+ * This method will be removed in v2.0.
108
+ *
109
+ * @example
110
+ * // ❌ WRONG - Corrupts binary files
111
+ * const base64 = tools.base64().encode(binaryData);
112
+ *
113
+ * // ✅ CORRECT - Use Node.js Buffer
114
+ * const base64 = Buffer.from(binaryData).toString('base64');
115
+ *
116
+ * // ✅ CORRECT - Use new helper methods
117
+ * const base64 = tools.fileToBase64('/path/to/file.jpg');
118
+ */
104
119
  base64(){
120
+ console.warn('[DEPRECATED] MasterTools.base64() only works for TEXT strings, not binary files. Use Buffer.toString("base64") or tools.fileToBase64() instead. This method will be removed in v2.0.');
121
+
105
122
  var $that = this;
106
123
  return {
107
124
  encode: function(string){
@@ -159,6 +176,377 @@ class MasterTools{
159
176
  }
160
177
  };
161
178
 
179
+ // ============================================================================
180
+ // FILE CONVERSION UTILITIES (Production-Grade)
181
+ // ============================================================================
182
+
183
+ /**
184
+ * Convert file to base64 string (binary-safe)
185
+ *
186
+ * @param {String|Object} filePathOrFile - File path or formidable file object
187
+ * @param {Object} options - Conversion options
188
+ * @param {Number} options.maxSize - Maximum file size in bytes (default: 10MB)
189
+ * @param {Boolean} options.includeDataURI - Include data URI prefix (default: false)
190
+ * @returns {String} Base64 encoded string
191
+ *
192
+ * @example
193
+ * // Convert uploaded file to base64
194
+ * const file = obj.params.formData.files.image[0];
195
+ * const base64 = master.tools.fileToBase64(file);
196
+ *
197
+ * @example
198
+ * // Convert file by path with data URI
199
+ * const base64 = master.tools.fileToBase64('/path/to/image.jpg', {
200
+ * includeDataURI: true,
201
+ * maxSize: 5 * 1024 * 1024 // 5MB limit
202
+ * });
203
+ * // Returns: "data:image/jpeg;base64,/9j/4AAQSkZJRg..."
204
+ */
205
+ fileToBase64(filePathOrFile, options = {}) {
206
+ const fs = require('fs');
207
+ const path = require('path');
208
+
209
+ // Extract file path from formidable file object or use as-is
210
+ const filepath = typeof filePathOrFile === 'object' && filePathOrFile.filepath
211
+ ? filePathOrFile.filepath
212
+ : filePathOrFile;
213
+
214
+ // Validate file path
215
+ if (!filepath || typeof filepath !== 'string') {
216
+ throw new Error('Invalid file path provided');
217
+ }
218
+
219
+ if (!fs.existsSync(filepath)) {
220
+ throw new Error(`File not found: ${filepath}`);
221
+ }
222
+
223
+ // Get file stats
224
+ const stats = fs.statSync(filepath);
225
+
226
+ // Check file size
227
+ const maxSize = options.maxSize || 10 * 1024 * 1024; // 10MB default
228
+ if (stats.size > maxSize) {
229
+ throw new Error(`File size (${stats.size} bytes) exceeds maximum (${maxSize} bytes). Use streaming for large files.`);
230
+ }
231
+
232
+ // Security: Check if it's actually a file
233
+ if (!stats.isFile()) {
234
+ throw new Error('Path is not a file');
235
+ }
236
+
237
+ try {
238
+ // Read file as binary buffer
239
+ const buffer = fs.readFileSync(filepath);
240
+
241
+ // Convert to base64
242
+ const base64 = buffer.toString('base64');
243
+
244
+ // Include data URI if requested
245
+ if (options.includeDataURI) {
246
+ const mimetype = filePathOrFile.mimetype || this._getMimeTypeFromPath(filepath);
247
+ return `data:${mimetype};base64,${base64}`;
248
+ }
249
+
250
+ return base64;
251
+ } catch (error) {
252
+ throw new Error(`Failed to convert file to base64: ${error.message}`);
253
+ }
254
+ }
255
+
256
+ /**
257
+ * Convert base64 string to file (binary-safe)
258
+ *
259
+ * @param {String} base64String - Base64 encoded string (with or without data URI)
260
+ * @param {String} outputPath - Output file path
261
+ * @param {Object} options - Conversion options
262
+ * @param {Boolean} options.overwrite - Overwrite existing file (default: false)
263
+ * @returns {Object} File information {path, size, mimetype}
264
+ *
265
+ * @example
266
+ * const fileInfo = master.tools.base64ToFile(
267
+ * 'data:image/jpeg;base64,/9j/4AAQSkZJRg...',
268
+ * '/path/to/output.jpg',
269
+ * { overwrite: true }
270
+ * );
271
+ * console.log(fileInfo);
272
+ * // { path: '/path/to/output.jpg', size: 51234, mimetype: 'image/jpeg' }
273
+ */
274
+ base64ToFile(base64String, outputPath, options = {}) {
275
+ const fs = require('fs');
276
+ const path = require('path');
277
+
278
+ // Validate inputs
279
+ if (!base64String || typeof base64String !== 'string') {
280
+ throw new Error('Invalid base64 string provided');
281
+ }
282
+
283
+ if (!outputPath || typeof outputPath !== 'string') {
284
+ throw new Error('Invalid output path provided');
285
+ }
286
+
287
+ // Check if file exists
288
+ if (fs.existsSync(outputPath) && !options.overwrite) {
289
+ throw new Error(`File already exists: ${outputPath}. Set overwrite: true to replace.`);
290
+ }
291
+
292
+ // Extract mimetype and base64 data from data URI if present
293
+ let mimetype = null;
294
+ let base64Data = base64String;
295
+
296
+ const dataURIMatch = base64String.match(/^data:([^;]+);base64,(.+)$/);
297
+ if (dataURIMatch) {
298
+ mimetype = dataURIMatch[1];
299
+ base64Data = dataURIMatch[2];
300
+ }
301
+
302
+ // Validate base64 format
303
+ if (!/^[A-Za-z0-9+/]*={0,2}$/.test(base64Data)) {
304
+ throw new Error('Invalid base64 string format');
305
+ }
306
+
307
+ try {
308
+ // Convert base64 to buffer
309
+ const buffer = Buffer.from(base64Data, 'base64');
310
+
311
+ // Ensure output directory exists
312
+ const dir = path.dirname(outputPath);
313
+ if (!fs.existsSync(dir)) {
314
+ fs.mkdirSync(dir, { recursive: true });
315
+ }
316
+
317
+ // Write buffer to file
318
+ fs.writeFileSync(outputPath, buffer);
319
+
320
+ // Get file stats
321
+ const stats = fs.statSync(outputPath);
322
+
323
+ return {
324
+ path: outputPath,
325
+ size: stats.size,
326
+ mimetype: mimetype || this._getMimeTypeFromPath(outputPath)
327
+ };
328
+ } catch (error) {
329
+ throw new Error(`Failed to convert base64 to file: ${error.message}`);
330
+ }
331
+ }
332
+
333
+ /**
334
+ * Convert file to Node.js Buffer (binary-safe)
335
+ *
336
+ * @param {String|Object} filePathOrFile - File path or formidable file object
337
+ * @param {Object} options - Conversion options
338
+ * @param {Number} options.maxSize - Maximum file size in bytes (default: 10MB)
339
+ * @returns {Buffer} Node.js Buffer containing file data
340
+ *
341
+ * @example
342
+ * const file = obj.params.formData.files.document[0];
343
+ * const buffer = master.tools.fileToBuffer(file);
344
+ * console.log(buffer.length); // File size in bytes
345
+ */
346
+ fileToBuffer(filePathOrFile, options = {}) {
347
+ const fs = require('fs');
348
+
349
+ const filepath = typeof filePathOrFile === 'object' && filePathOrFile.filepath
350
+ ? filePathOrFile.filepath
351
+ : filePathOrFile;
352
+
353
+ if (!filepath || typeof filepath !== 'string') {
354
+ throw new Error('Invalid file path provided');
355
+ }
356
+
357
+ if (!fs.existsSync(filepath)) {
358
+ throw new Error(`File not found: ${filepath}`);
359
+ }
360
+
361
+ const stats = fs.statSync(filepath);
362
+ const maxSize = options.maxSize || 10 * 1024 * 1024; // 10MB default
363
+
364
+ if (stats.size > maxSize) {
365
+ throw new Error(`File size (${stats.size} bytes) exceeds maximum (${maxSize} bytes)`);
366
+ }
367
+
368
+ if (!stats.isFile()) {
369
+ throw new Error('Path is not a file');
370
+ }
371
+
372
+ try {
373
+ return fs.readFileSync(filepath);
374
+ } catch (error) {
375
+ throw new Error(`Failed to read file to buffer: ${error.message}`);
376
+ }
377
+ }
378
+
379
+ /**
380
+ * Convert file to Uint8Array byte array (binary-safe)
381
+ *
382
+ * @param {String|Object} filePathOrFile - File path or formidable file object
383
+ * @param {Object} options - Conversion options
384
+ * @param {Number} options.maxSize - Maximum file size in bytes (default: 10MB)
385
+ * @returns {Uint8Array} Byte array
386
+ *
387
+ * @example
388
+ * const file = obj.params.formData.files.data[0];
389
+ * const bytes = master.tools.fileToBytes(file);
390
+ * console.log(bytes[0]); // First byte
391
+ * console.log(bytes.length); // Total bytes
392
+ */
393
+ fileToBytes(filePathOrFile, options = {}) {
394
+ const buffer = this.fileToBuffer(filePathOrFile, options);
395
+ return new Uint8Array(buffer);
396
+ }
397
+
398
+ /**
399
+ * Convert Buffer or Uint8Array to base64 string
400
+ *
401
+ * @param {Buffer|Uint8Array} bytes - Binary data
402
+ * @returns {String} Base64 encoded string
403
+ *
404
+ * @example
405
+ * const buffer = fs.readFileSync('/path/to/file.pdf');
406
+ * const base64 = master.tools.bytesToBase64(buffer);
407
+ */
408
+ bytesToBase64(bytes) {
409
+ if (!bytes) {
410
+ throw new Error('Invalid bytes provided');
411
+ }
412
+
413
+ try {
414
+ // Convert Uint8Array to Buffer if needed
415
+ const buffer = Buffer.isBuffer(bytes) ? bytes : Buffer.from(bytes);
416
+ return buffer.toString('base64');
417
+ } catch (error) {
418
+ throw new Error(`Failed to convert bytes to base64: ${error.message}`);
419
+ }
420
+ }
421
+
422
+ /**
423
+ * Convert base64 string to Buffer
424
+ *
425
+ * @param {String} base64String - Base64 encoded string (with or without data URI)
426
+ * @returns {Buffer} Node.js Buffer
427
+ *
428
+ * @example
429
+ * const base64 = 'SGVsbG8gV29ybGQ=';
430
+ * const buffer = master.tools.base64ToBytes(base64);
431
+ * console.log(buffer.toString('utf8')); // "Hello World"
432
+ */
433
+ base64ToBytes(base64String) {
434
+ if (!base64String || typeof base64String !== 'string') {
435
+ throw new Error('Invalid base64 string provided');
436
+ }
437
+
438
+ // Strip data URI prefix if present
439
+ let base64Data = base64String;
440
+ const dataURIMatch = base64String.match(/^data:[^;]+;base64,(.+)$/);
441
+ if (dataURIMatch) {
442
+ base64Data = dataURIMatch[1];
443
+ }
444
+
445
+ // Validate base64 format
446
+ if (!/^[A-Za-z0-9+/]*={0,2}$/.test(base64Data)) {
447
+ throw new Error('Invalid base64 string format');
448
+ }
449
+
450
+ try {
451
+ return Buffer.from(base64Data, 'base64');
452
+ } catch (error) {
453
+ throw new Error(`Failed to convert base64 to bytes: ${error.message}`);
454
+ }
455
+ }
456
+
457
+ /**
458
+ * Stream large file to base64 (for files > 10MB)
459
+ * Returns a Promise that resolves with base64 string
460
+ *
461
+ * @param {String|Object} filePathOrFile - File path or formidable file object
462
+ * @param {Object} options - Streaming options
463
+ * @param {Function} options.onProgress - Progress callback (percent: number) => void
464
+ * @returns {Promise<String>} Base64 encoded string
465
+ *
466
+ * @example
467
+ * // Stream 500MB video file
468
+ * const base64 = await master.tools.streamFileToBase64('/path/to/video.mp4', {
469
+ * onProgress: (percent) => console.log(`${percent}% complete`)
470
+ * });
471
+ */
472
+ async streamFileToBase64(filePathOrFile, options = {}) {
473
+ const fs = require('fs');
474
+ const { Transform } = require('stream');
475
+
476
+ const filepath = typeof filePathOrFile === 'object' && filePathOrFile.filepath
477
+ ? filePathOrFile.filepath
478
+ : filePathOrFile;
479
+
480
+ if (!filepath || !fs.existsSync(filepath)) {
481
+ throw new Error(`File not found: ${filepath}`);
482
+ }
483
+
484
+ const stats = fs.statSync(filepath);
485
+ if (!stats.isFile()) {
486
+ throw new Error('Path is not a file');
487
+ }
488
+
489
+ return new Promise((resolve, reject) => {
490
+ const chunks = [];
491
+ let bytesRead = 0;
492
+
493
+ const readStream = fs.createReadStream(filepath);
494
+
495
+ readStream.on('data', (chunk) => {
496
+ chunks.push(chunk);
497
+ bytesRead += chunk.length;
498
+
499
+ // Progress callback
500
+ if (options.onProgress && typeof options.onProgress === 'function') {
501
+ const percent = Math.round((bytesRead / stats.size) * 100);
502
+ options.onProgress(percent);
503
+ }
504
+ });
505
+
506
+ readStream.on('end', () => {
507
+ try {
508
+ const buffer = Buffer.concat(chunks);
509
+ const base64 = buffer.toString('base64');
510
+ resolve(base64);
511
+ } catch (error) {
512
+ reject(new Error(`Failed to convert stream to base64: ${error.message}`));
513
+ }
514
+ });
515
+
516
+ readStream.on('error', (error) => {
517
+ reject(new Error(`Stream error: ${error.message}`));
518
+ });
519
+ });
520
+ }
521
+
522
+ /**
523
+ * Get MIME type from file path
524
+ * @private
525
+ */
526
+ _getMimeTypeFromPath(filepath) {
527
+ const path = require('path');
528
+ const ext = path.extname(filepath).toLowerCase();
529
+
530
+ const mimeTypes = {
531
+ '.jpg': 'image/jpeg',
532
+ '.jpeg': 'image/jpeg',
533
+ '.png': 'image/png',
534
+ '.gif': 'image/gif',
535
+ '.webp': 'image/webp',
536
+ '.svg': 'image/svg+xml',
537
+ '.pdf': 'application/pdf',
538
+ '.txt': 'text/plain',
539
+ '.json': 'application/json',
540
+ '.xml': 'application/xml',
541
+ '.zip': 'application/zip',
542
+ '.mp4': 'video/mp4',
543
+ '.mp3': 'audio/mpeg',
544
+ '.wav': 'audio/wav'
545
+ };
546
+
547
+ return mimeTypes[ext] || 'application/octet-stream';
548
+ }
549
+
162
550
  combineObjandArray(data, objParams){
163
551
 
164
552
  if(Array.isArray(data) === false){