@arela/uploader 0.2.4 → 0.2.6

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,278 @@
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 (
169
+ error instanceof FileOperationError ||
170
+ error instanceof DetectionError
171
+ ) {
172
+ return 'medium';
173
+ }
174
+
175
+ if (error instanceof ValidationError) {
176
+ return 'low';
177
+ }
178
+
179
+ return 'medium';
180
+ }
181
+
182
+ /**
183
+ * Determine if error is recoverable
184
+ * @private
185
+ * @param {Error} error - Error to analyze
186
+ * @returns {boolean} True if recoverable
187
+ */
188
+ #isRecoverable(error) {
189
+ if (error instanceof ConfigurationError) {
190
+ return false;
191
+ }
192
+
193
+ if (error instanceof NetworkError || error instanceof ApiError) {
194
+ // Network errors might be temporary
195
+ return true;
196
+ }
197
+
198
+ if (error instanceof FileOperationError) {
199
+ // File errors might be recoverable depending on the specific error
200
+ return true;
201
+ }
202
+
203
+ if (error instanceof DetectionError || error instanceof ValidationError) {
204
+ return true;
205
+ }
206
+
207
+ return false;
208
+ }
209
+
210
+ /**
211
+ * Log error with appropriate level
212
+ * @private
213
+ * @param {Object} errorInfo - Error analysis result
214
+ */
215
+ #logError(errorInfo) {
216
+ const logMessage = `${errorInfo.type}: ${errorInfo.message}`;
217
+ const contextInfo = errorInfo.context
218
+ ? ` | Context: ${JSON.stringify(errorInfo.context)}`
219
+ : '';
220
+ const detailsInfo = errorInfo.technicalDetails
221
+ ? ` | Details: ${JSON.stringify(errorInfo.technicalDetails)}`
222
+ : '';
223
+
224
+ const fullLogMessage = `${logMessage}${contextInfo}${detailsInfo}`;
225
+
226
+ switch (errorInfo.severity) {
227
+ case 'fatal':
228
+ this.logger.error(`FATAL: ${fullLogMessage}`);
229
+ break;
230
+ case 'high':
231
+ this.logger.error(`HIGH: ${fullLogMessage}`);
232
+ break;
233
+ case 'medium':
234
+ this.logger.warn(`MEDIUM: ${fullLogMessage}`);
235
+ break;
236
+ case 'low':
237
+ this.logger.info(`LOW: ${fullLogMessage}`);
238
+ break;
239
+ default:
240
+ this.logger.error(fullLogMessage);
241
+ }
242
+
243
+ // Log stack trace in verbose mode
244
+ if (this.logger.isVerboseEnabled() && errorInfo.stack) {
245
+ this.logger.debug(`Stack trace: ${errorInfo.stack}`);
246
+ }
247
+ }
248
+
249
+ /**
250
+ * Wrap a function with error handling
251
+ * @param {Function} fn - Function to wrap
252
+ * @param {Object} context - Context for error handling
253
+ * @returns {Function} Wrapped function
254
+ */
255
+ wrapWithErrorHandling(fn, context = {}) {
256
+ return async (...args) => {
257
+ try {
258
+ return await fn(...args);
259
+ } catch (error) {
260
+ this.handleError(error, context);
261
+ throw error;
262
+ }
263
+ };
264
+ }
265
+
266
+ /**
267
+ * Create error handler middleware for async operations
268
+ * @param {Object} context - Context for error handling
269
+ * @returns {Function} Error handler middleware
270
+ */
271
+ createAsyncErrorHandler(context = {}) {
272
+ return (error) => {
273
+ this.handleError(error, context);
274
+ };
275
+ }
276
+ }
277
+
278
+ 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
+ }