@arela/uploader 0.2.4 → 0.2.5
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 +2 -1
- package/src/commands/UploadCommand.js +388 -0
- package/src/config/config.js +173 -0
- package/src/errors/ErrorHandler.js +271 -0
- package/src/errors/ErrorTypes.js +104 -0
- package/src/index-old.js +2650 -0
- package/src/index.js +248 -2594
- package/src/services/DatabaseService.js +953 -0
- package/src/services/LoggingService.js +194 -0
- package/src/services/upload/ApiUploadService.js +147 -0
- package/src/services/upload/BaseUploadService.js +36 -0
- package/src/services/upload/SupabaseUploadService.js +107 -0
- package/src/services/upload/UploadServiceFactory.js +68 -0
- package/src/utils/FileOperations.js +148 -0
- package/src/utils/FileSanitizer.js +99 -0
- package/src/utils/PathDetector.js +196 -0
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ApiError,
|
|
3
|
+
ArelaError,
|
|
4
|
+
ConfigurationError,
|
|
5
|
+
DatabaseError,
|
|
6
|
+
DetectionError,
|
|
7
|
+
FileOperationError,
|
|
8
|
+
NetworkError,
|
|
9
|
+
UploadServiceError,
|
|
10
|
+
ValidationError,
|
|
11
|
+
} from './ErrorTypes.js';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Error Handler Service
|
|
15
|
+
* Provides centralized error handling, logging, and user-friendly error messages
|
|
16
|
+
*/
|
|
17
|
+
export class ErrorHandler {
|
|
18
|
+
constructor(logger) {
|
|
19
|
+
this.logger = logger;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Handle and format error for user display
|
|
24
|
+
* @param {Error} error - The error to handle
|
|
25
|
+
* @param {Object} context - Additional context about where the error occurred
|
|
26
|
+
* @returns {Object} Formatted error information
|
|
27
|
+
*/
|
|
28
|
+
handleError(error, context = {}) {
|
|
29
|
+
const errorInfo = this.#analyzeError(error, context);
|
|
30
|
+
|
|
31
|
+
// Log the error with appropriate level
|
|
32
|
+
this.#logError(errorInfo);
|
|
33
|
+
|
|
34
|
+
return errorInfo;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Handle fatal errors that should terminate the application
|
|
39
|
+
* @param {Error} error - The fatal error
|
|
40
|
+
* @param {Object} context - Additional context
|
|
41
|
+
*/
|
|
42
|
+
handleFatalError(error, context = {}) {
|
|
43
|
+
const errorInfo = this.handleError(error, context);
|
|
44
|
+
|
|
45
|
+
console.error('\n❌ Fatal Error - Application will exit');
|
|
46
|
+
console.error(` ${errorInfo.userMessage}`);
|
|
47
|
+
|
|
48
|
+
if (this.logger.isVerboseEnabled() && errorInfo.technicalDetails) {
|
|
49
|
+
console.error(` Technical Details: ${errorInfo.technicalDetails}`);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
this.logger.flush();
|
|
53
|
+
process.exit(1);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Create a user-friendly error from a technical error
|
|
58
|
+
* @param {string} userMessage - User-friendly error message
|
|
59
|
+
* @param {Error} originalError - The original technical error
|
|
60
|
+
* @param {string} code - Error code
|
|
61
|
+
* @returns {ArelaError} User-friendly error
|
|
62
|
+
*/
|
|
63
|
+
createUserFriendlyError(userMessage, originalError, code = 'USER_ERROR') {
|
|
64
|
+
const error = new ArelaError(userMessage, code, {
|
|
65
|
+
originalError: originalError.message,
|
|
66
|
+
originalStack: originalError.stack,
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
return error;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Analyze error and determine appropriate handling
|
|
74
|
+
* @private
|
|
75
|
+
* @param {Error} error - Error to analyze
|
|
76
|
+
* @param {Object} context - Error context
|
|
77
|
+
* @returns {Object} Error analysis result
|
|
78
|
+
*/
|
|
79
|
+
#analyzeError(error, context) {
|
|
80
|
+
const analysis = {
|
|
81
|
+
type: error.constructor.name,
|
|
82
|
+
code: error.code || 'UNKNOWN_ERROR',
|
|
83
|
+
message: error.message,
|
|
84
|
+
userMessage: this.#getUserFriendlyMessage(error),
|
|
85
|
+
technicalDetails: error.details || null,
|
|
86
|
+
context,
|
|
87
|
+
timestamp: error.timestamp || new Date().toISOString(),
|
|
88
|
+
severity: this.#determineSeverity(error),
|
|
89
|
+
recoverable: this.#isRecoverable(error),
|
|
90
|
+
stack: error.stack,
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
return analysis;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Generate user-friendly error message
|
|
98
|
+
* @private
|
|
99
|
+
* @param {Error} error - Error to process
|
|
100
|
+
* @returns {string} User-friendly message
|
|
101
|
+
*/
|
|
102
|
+
#getUserFriendlyMessage(error) {
|
|
103
|
+
if (error instanceof ConfigurationError) {
|
|
104
|
+
return `Configuration issue: ${error.message}`;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
if (error instanceof FileOperationError) {
|
|
108
|
+
const filePath = error.details?.filePath;
|
|
109
|
+
return `File operation failed${filePath ? ` for ${filePath}` : ''}: ${error.message}`;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if (error instanceof UploadServiceError) {
|
|
113
|
+
const serviceName = error.details?.serviceName;
|
|
114
|
+
return `Upload failed${serviceName ? ` (${serviceName})` : ''}: ${error.message}`;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
if (error instanceof ApiError) {
|
|
118
|
+
const statusCode = error.details?.statusCode;
|
|
119
|
+
return `API request failed${statusCode ? ` (HTTP ${statusCode})` : ''}: ${error.message}`;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
if (error instanceof DatabaseError) {
|
|
123
|
+
const operation = error.details?.operation;
|
|
124
|
+
return `Database operation failed${operation ? ` (${operation})` : ''}: ${error.message}`;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
if (error instanceof DetectionError) {
|
|
128
|
+
const filePath = error.details?.filePath;
|
|
129
|
+
return `File detection failed${filePath ? ` for ${filePath}` : ''}: ${error.message}`;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
if (error instanceof ValidationError) {
|
|
133
|
+
const field = error.details?.field;
|
|
134
|
+
return `Validation failed${field ? ` for ${field}` : ''}: ${error.message}`;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
if (error instanceof NetworkError) {
|
|
138
|
+
return `Network error: ${error.message}`;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
if (error instanceof ArelaError) {
|
|
142
|
+
return error.message;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Generic error handling
|
|
146
|
+
return `An unexpected error occurred: ${error.message}`;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Determine error severity
|
|
151
|
+
* @private
|
|
152
|
+
* @param {Error} error - Error to analyze
|
|
153
|
+
* @returns {string} Severity level
|
|
154
|
+
*/
|
|
155
|
+
#determineSeverity(error) {
|
|
156
|
+
if (error instanceof ConfigurationError) {
|
|
157
|
+
return 'fatal';
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
if (error instanceof NetworkError || error instanceof ApiError) {
|
|
161
|
+
return 'high';
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
if (error instanceof DatabaseError || error instanceof UploadServiceError) {
|
|
165
|
+
return 'high';
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
if (error instanceof FileOperationError || error instanceof DetectionError) {
|
|
169
|
+
return 'medium';
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
if (error instanceof ValidationError) {
|
|
173
|
+
return 'low';
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
return 'medium';
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Determine if error is recoverable
|
|
181
|
+
* @private
|
|
182
|
+
* @param {Error} error - Error to analyze
|
|
183
|
+
* @returns {boolean} True if recoverable
|
|
184
|
+
*/
|
|
185
|
+
#isRecoverable(error) {
|
|
186
|
+
if (error instanceof ConfigurationError) {
|
|
187
|
+
return false;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
if (error instanceof NetworkError || error instanceof ApiError) {
|
|
191
|
+
// Network errors might be temporary
|
|
192
|
+
return true;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
if (error instanceof FileOperationError) {
|
|
196
|
+
// File errors might be recoverable depending on the specific error
|
|
197
|
+
return true;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
if (error instanceof DetectionError || error instanceof ValidationError) {
|
|
201
|
+
return true;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
return false;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Log error with appropriate level
|
|
209
|
+
* @private
|
|
210
|
+
* @param {Object} errorInfo - Error analysis result
|
|
211
|
+
*/
|
|
212
|
+
#logError(errorInfo) {
|
|
213
|
+
const logMessage = `${errorInfo.type}: ${errorInfo.message}`;
|
|
214
|
+
const contextInfo = errorInfo.context ? ` | Context: ${JSON.stringify(errorInfo.context)}` : '';
|
|
215
|
+
const detailsInfo = errorInfo.technicalDetails ? ` | Details: ${JSON.stringify(errorInfo.technicalDetails)}` : '';
|
|
216
|
+
|
|
217
|
+
const fullLogMessage = `${logMessage}${contextInfo}${detailsInfo}`;
|
|
218
|
+
|
|
219
|
+
switch (errorInfo.severity) {
|
|
220
|
+
case 'fatal':
|
|
221
|
+
this.logger.error(`FATAL: ${fullLogMessage}`);
|
|
222
|
+
break;
|
|
223
|
+
case 'high':
|
|
224
|
+
this.logger.error(`HIGH: ${fullLogMessage}`);
|
|
225
|
+
break;
|
|
226
|
+
case 'medium':
|
|
227
|
+
this.logger.warn(`MEDIUM: ${fullLogMessage}`);
|
|
228
|
+
break;
|
|
229
|
+
case 'low':
|
|
230
|
+
this.logger.info(`LOW: ${fullLogMessage}`);
|
|
231
|
+
break;
|
|
232
|
+
default:
|
|
233
|
+
this.logger.error(fullLogMessage);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// Log stack trace in verbose mode
|
|
237
|
+
if (this.logger.isVerboseEnabled() && errorInfo.stack) {
|
|
238
|
+
this.logger.debug(`Stack trace: ${errorInfo.stack}`);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* Wrap a function with error handling
|
|
244
|
+
* @param {Function} fn - Function to wrap
|
|
245
|
+
* @param {Object} context - Context for error handling
|
|
246
|
+
* @returns {Function} Wrapped function
|
|
247
|
+
*/
|
|
248
|
+
wrapWithErrorHandling(fn, context = {}) {
|
|
249
|
+
return async (...args) => {
|
|
250
|
+
try {
|
|
251
|
+
return await fn(...args);
|
|
252
|
+
} catch (error) {
|
|
253
|
+
this.handleError(error, context);
|
|
254
|
+
throw error;
|
|
255
|
+
}
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* Create error handler middleware for async operations
|
|
261
|
+
* @param {Object} context - Context for error handling
|
|
262
|
+
* @returns {Function} Error handler middleware
|
|
263
|
+
*/
|
|
264
|
+
createAsyncErrorHandler(context = {}) {
|
|
265
|
+
return (error) => {
|
|
266
|
+
this.handleError(error, context);
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
export default ErrorHandler;
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Custom Error Types for Arela Uploader
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Base error class for Arela Uploader
|
|
7
|
+
*/
|
|
8
|
+
export class ArelaError extends Error {
|
|
9
|
+
constructor(message, code = 'ARELA_ERROR', details = null) {
|
|
10
|
+
super(message);
|
|
11
|
+
this.name = this.constructor.name;
|
|
12
|
+
this.code = code;
|
|
13
|
+
this.details = details;
|
|
14
|
+
this.timestamp = new Date().toISOString();
|
|
15
|
+
|
|
16
|
+
// Ensure proper stack trace
|
|
17
|
+
if (Error.captureStackTrace) {
|
|
18
|
+
Error.captureStackTrace(this, this.constructor);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
toJSON() {
|
|
23
|
+
return {
|
|
24
|
+
name: this.name,
|
|
25
|
+
message: this.message,
|
|
26
|
+
code: this.code,
|
|
27
|
+
details: this.details,
|
|
28
|
+
timestamp: this.timestamp,
|
|
29
|
+
stack: this.stack,
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Configuration related errors
|
|
36
|
+
*/
|
|
37
|
+
export class ConfigurationError extends ArelaError {
|
|
38
|
+
constructor(message, details = null) {
|
|
39
|
+
super(message, 'CONFIGURATION_ERROR', details);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* File operation related errors
|
|
45
|
+
*/
|
|
46
|
+
export class FileOperationError extends ArelaError {
|
|
47
|
+
constructor(message, filePath = null, details = null) {
|
|
48
|
+
super(message, 'FILE_OPERATION_ERROR', { filePath, ...details });
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Upload service related errors
|
|
54
|
+
*/
|
|
55
|
+
export class UploadServiceError extends ArelaError {
|
|
56
|
+
constructor(message, serviceName = null, details = null) {
|
|
57
|
+
super(message, 'UPLOAD_SERVICE_ERROR', { serviceName, ...details });
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* API related errors
|
|
63
|
+
*/
|
|
64
|
+
export class ApiError extends ArelaError {
|
|
65
|
+
constructor(message, statusCode = null, response = null, details = null) {
|
|
66
|
+
super(message, 'API_ERROR', { statusCode, response, ...details });
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Database operation related errors
|
|
72
|
+
*/
|
|
73
|
+
export class DatabaseError extends ArelaError {
|
|
74
|
+
constructor(message, operation = null, details = null) {
|
|
75
|
+
super(message, 'DATABASE_ERROR', { operation, ...details });
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* File detection related errors
|
|
81
|
+
*/
|
|
82
|
+
export class DetectionError extends ArelaError {
|
|
83
|
+
constructor(message, filePath = null, details = null) {
|
|
84
|
+
super(message, 'DETECTION_ERROR', { filePath, ...details });
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Validation related errors
|
|
90
|
+
*/
|
|
91
|
+
export class ValidationError extends ArelaError {
|
|
92
|
+
constructor(message, field = null, value = null, details = null) {
|
|
93
|
+
super(message, 'VALIDATION_ERROR', { field, value, ...details });
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Network related errors
|
|
99
|
+
*/
|
|
100
|
+
export class NetworkError extends ArelaError {
|
|
101
|
+
constructor(message, url = null, details = null) {
|
|
102
|
+
super(message, 'NETWORK_ERROR', { url, ...details });
|
|
103
|
+
}
|
|
104
|
+
}
|