@bernierllc/file-uploader 0.7.1 → 0.7.2

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bernierllc/file-uploader",
3
- "version": "0.7.1",
3
+ "version": "0.7.2",
4
4
  "description": "Multipart file upload handler with validation and progress tracking",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -28,7 +28,7 @@
28
28
  "dependencies": {
29
29
  "busboy": "^1.6.0",
30
30
  "uuid": "^11.1.1",
31
- "@bernierllc/logger": "1.7.1"
31
+ "@bernierllc/logger": "2.0.0"
32
32
  },
33
33
  "devDependencies": {
34
34
  "@types/busboy": "^1.5.0",
package/dist/errors.d.ts DELETED
@@ -1,11 +0,0 @@
1
- export type FileUploaderErrorCode = 'FILE_UPLOADER_ERROR' | 'INVALID_INPUT' | 'OPERATION_FAILED';
2
- export interface FileUploaderErrorOptions {
3
- cause?: Error;
4
- code?: FileUploaderErrorCode;
5
- context?: Record<string, unknown>;
6
- }
7
- export declare class FileUploaderError extends Error {
8
- readonly code: FileUploaderErrorCode;
9
- readonly context?: Record<string, unknown>;
10
- constructor(message: string, options?: FileUploaderErrorOptions);
11
- }
package/dist/errors.js DELETED
@@ -1,20 +0,0 @@
1
- /*
2
- Copyright (c) 2025 Bernier LLC
3
-
4
- This file is licensed to the client under a limited-use license.
5
- The client may use and modify this code only within the scope of the project it was delivered for.
6
- Redistribution or use in other products or commercial offerings is not permitted without written consent from Bernier LLC.
7
- */
8
- export class FileUploaderError extends Error {
9
- code;
10
- context;
11
- constructor(message, options) {
12
- super(message, options?.cause ? { cause: options.cause } : undefined);
13
- this.name = 'FileUploaderError';
14
- this.code = options?.code ?? 'FILE_UPLOADER_ERROR';
15
- if (options?.context !== undefined) {
16
- this.context = options.context;
17
- }
18
- Error.captureStackTrace?.(this, this.constructor);
19
- }
20
- }
@@ -1,25 +0,0 @@
1
- import type { IncomingMessage } from 'http';
2
- import type { UploadConfig, FileInfo, UploadProgress, UploadResult } from './types.js';
3
- /**
4
- * Multipart file upload handler
5
- */
6
- export declare class FileUploader {
7
- private readonly config;
8
- private readonly progressMap;
9
- constructor(config?: UploadConfig);
10
- /**
11
- * Handle multipart form upload
12
- */
13
- handleMultipart(req: IncomingMessage): Promise<UploadResult[]>;
14
- /**
15
- * Validate file
16
- */
17
- validateFile(file: FileInfo): {
18
- valid: boolean;
19
- error?: string;
20
- };
21
- /**
22
- * Get upload progress
23
- */
24
- getProgress(fileId: string): UploadProgress | null;
25
- }
@@ -1,169 +0,0 @@
1
- /*
2
- Copyright (c) 2025 Bernier LLC
3
-
4
- This file is licensed to the client under a limited-use license.
5
- The client may use and modify this code *only within the scope of the project it was delivered for*.
6
- Redistribution or use in other products or commercial offerings is not permitted without written consent from Bernier LLC.
7
- */
8
- import { createLogger } from '@bernierllc/logger';
9
- import Busboy from 'busboy';
10
- import { randomUUID } from 'crypto';
11
- const logger = createLogger();
12
- /**
13
- * Multipart file upload handler
14
- */
15
- export class FileUploader {
16
- config;
17
- progressMap = new Map();
18
- constructor(config = {}) {
19
- this.config = {
20
- maxFileSize: config.maxFileSize ?? 10 * 1024 * 1024, // 10MB default
21
- maxFiles: config.maxFiles ?? 10,
22
- allowedMimeTypes: config.allowedMimeTypes,
23
- destination: config.destination,
24
- onProgress: config.onProgress
25
- };
26
- }
27
- /**
28
- * Handle multipart form upload
29
- */
30
- async handleMultipart(req) {
31
- return new Promise((resolve, reject) => {
32
- const results = [];
33
- let fileCount = 0;
34
- const errors = [];
35
- const bb = Busboy({ headers: req.headers });
36
- bb.on('file', (fieldname, file, info) => {
37
- fileCount++;
38
- if (fileCount > this.config.maxFiles) {
39
- const error = new Error(`Maximum file count exceeded: ${this.config.maxFiles}`);
40
- errors.push(error);
41
- file.resume();
42
- return;
43
- }
44
- const fileId = randomUUID();
45
- const fileInfo = {
46
- fieldname,
47
- filename: info.filename,
48
- encoding: info.encoding,
49
- mimeType: info.mimeType,
50
- size: 0
51
- };
52
- // Validate file
53
- const validation = this.validateFile(fileInfo);
54
- if (!validation.valid) {
55
- const error = new Error(validation.error || 'File validation failed');
56
- errors.push(error);
57
- file.resume();
58
- return;
59
- }
60
- // Track progress
61
- let bytesUploaded = 0;
62
- const progress = {
63
- fileId,
64
- bytesUploaded: 0,
65
- totalBytes: 0,
66
- percentage: 0
67
- };
68
- this.progressMap.set(fileId, progress);
69
- // Collect file data
70
- const chunks = [];
71
- file.on('data', (chunk) => {
72
- chunks.push(chunk);
73
- bytesUploaded += chunk.length;
74
- fileInfo.size = bytesUploaded;
75
- // Update progress
76
- progress.bytesUploaded = bytesUploaded;
77
- progress.totalBytes = bytesUploaded; // Will be updated when complete
78
- progress.percentage = this.config.maxFileSize
79
- ? Math.min(100, (bytesUploaded / this.config.maxFileSize) * 100)
80
- : 0;
81
- if (this.config.onProgress) {
82
- this.config.onProgress(progress);
83
- }
84
- // Check size limit
85
- if (bytesUploaded > this.config.maxFileSize) {
86
- const error = new Error(`File size exceeded: ${this.config.maxFileSize} bytes`);
87
- errors.push(error);
88
- file.resume();
89
- }
90
- });
91
- file.on('end', async () => {
92
- try {
93
- const buffer = Buffer.concat(chunks);
94
- fileInfo.size = buffer.length;
95
- // Update final progress
96
- progress.totalBytes = buffer.length;
97
- progress.percentage = 100;
98
- if (this.config.onProgress) {
99
- this.config.onProgress(progress);
100
- }
101
- // Determine destination
102
- let destination;
103
- if (typeof this.config.destination === 'function') {
104
- destination = await this.config.destination(fileInfo);
105
- }
106
- else {
107
- destination = this.config.destination || `/uploads/${fileId}-${fileInfo.filename}`;
108
- }
109
- const result = {
110
- fileId,
111
- filename: fileInfo.filename,
112
- size: buffer.length,
113
- mimeType: fileInfo.mimeType,
114
- destination
115
- };
116
- results.push(result);
117
- this.progressMap.delete(fileId);
118
- logger.debug('File uploaded', { fileId, filename: fileInfo.filename, size: buffer.length });
119
- }
120
- catch (error) {
121
- const err = error instanceof Error ? error : new Error(String(error));
122
- errors.push(err);
123
- logger.error('Error processing file', err);
124
- }
125
- });
126
- });
127
- bb.on('finish', () => {
128
- if (errors.length > 0) {
129
- reject(new Error(`Upload failed: ${errors.map(e => e.message).join(', ')}`));
130
- }
131
- else {
132
- resolve(results);
133
- }
134
- });
135
- bb.on('error', (error) => {
136
- reject(error);
137
- });
138
- req.pipe(bb);
139
- });
140
- }
141
- /**
142
- * Validate file
143
- */
144
- validateFile(file) {
145
- // Check MIME type
146
- if (this.config.allowedMimeTypes && this.config.allowedMimeTypes.length > 0) {
147
- if (!this.config.allowedMimeTypes.includes(file.mimeType)) {
148
- return {
149
- valid: false,
150
- error: `MIME type ${file.mimeType} not allowed. Allowed types: ${this.config.allowedMimeTypes.join(', ')}`
151
- };
152
- }
153
- }
154
- // Check file size (if already known)
155
- if (file.size > this.config.maxFileSize) {
156
- return {
157
- valid: false,
158
- error: `File size ${file.size} exceeds maximum ${this.config.maxFileSize} bytes`
159
- };
160
- }
161
- return { valid: true };
162
- }
163
- /**
164
- * Get upload progress
165
- */
166
- getProgress(fileId) {
167
- return this.progressMap.get(fileId) || null;
168
- }
169
- }
package/dist/index.d.ts DELETED
@@ -1,3 +0,0 @@
1
- export { FileUploader } from './file-uploader.js';
2
- export type { UploadConfig, FileInfo, UploadProgress, UploadResult } from './types.js';
3
- export { FileUploaderError, type FileUploaderErrorCode, type FileUploaderErrorOptions } from './errors.js';
package/dist/index.js DELETED
@@ -1,10 +0,0 @@
1
- /*
2
- Copyright (c) 2025 Bernier LLC
3
-
4
- This file is licensed to the client under a limited-use license.
5
- The client may use and modify this code *only within the scope of the project it was delivered for*.
6
- Redistribution or use in other products or commercial offerings is not permitted without written consent from Bernier LLC.
7
- */
8
- export { FileUploader } from './file-uploader.js';
9
- // Error types
10
- export { FileUploaderError } from './errors.js';
package/dist/types.d.ts DELETED
@@ -1,60 +0,0 @@
1
- /**
2
- * File upload configuration
3
- */
4
- export interface UploadConfig {
5
- /** Maximum file size in bytes */
6
- maxFileSize?: number;
7
- /** Allowed MIME types */
8
- allowedMimeTypes?: string[];
9
- /** Maximum number of files */
10
- maxFiles?: number;
11
- /** Destination path or function */
12
- destination?: string | ((file: FileInfo) => Promise<string>);
13
- /** Progress callback */
14
- onProgress?: (progress: UploadProgress) => void;
15
- }
16
- /**
17
- * File information
18
- */
19
- export interface FileInfo {
20
- /** Form field name */
21
- fieldname: string;
22
- /** Original filename */
23
- filename: string;
24
- /** File encoding */
25
- encoding: string;
26
- /** MIME type */
27
- mimeType: string;
28
- /** File size in bytes */
29
- size: number;
30
- }
31
- /**
32
- * Upload progress information
33
- */
34
- export interface UploadProgress {
35
- /** File identifier */
36
- fileId: string;
37
- /** Bytes uploaded */
38
- bytesUploaded: number;
39
- /** Total bytes */
40
- totalBytes: number;
41
- /** Percentage complete */
42
- percentage: number;
43
- }
44
- /**
45
- * Upload result
46
- */
47
- export interface UploadResult {
48
- /** File identifier */
49
- fileId: string;
50
- /** Filename */
51
- filename: string;
52
- /** File size */
53
- size: number;
54
- /** MIME type */
55
- mimeType: string;
56
- /** Destination path */
57
- destination: string;
58
- /** Optional URL */
59
- url?: string;
60
- }
package/dist/types.js DELETED
@@ -1,8 +0,0 @@
1
- /*
2
- Copyright (c) 2025 Bernier LLC
3
-
4
- This file is licensed to the client under a limited-use license.
5
- The client may use and modify this code *only within the scope of the project it was delivered for*.
6
- Redistribution or use in other products or commercial offerings is not permitted without written consent from Bernier LLC.
7
- */
8
- export {};