@arela/uploader 0.2.12 ā 0.3.0
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/.env.template +66 -0
- package/.vscode/settings.json +1 -0
- package/README.md +134 -58
- package/SUPABASE_UPLOAD_FIX.md +157 -0
- package/package.json +3 -2
- package/scripts/cleanup-ds-store.js +109 -0
- package/scripts/cleanup-system-files.js +69 -0
- package/scripts/tests/phase-7-features.test.js +415 -0
- package/scripts/tests/signal-handling.test.js +275 -0
- package/scripts/tests/smart-watch-integration.test.js +554 -0
- package/scripts/tests/watch-service-integration.test.js +584 -0
- package/src/commands/UploadCommand.js +36 -2
- package/src/commands/WatchCommand.js +1305 -0
- package/src/config/config.js +113 -0
- package/src/document-type-shared.js +2 -0
- package/src/document-types/support-document.js +201 -0
- package/src/file-detection.js +2 -1
- package/src/index.js +44 -0
- package/src/services/AdvancedFilterService.js +505 -0
- package/src/services/AutoProcessingService.js +639 -0
- package/src/services/BenchmarkingService.js +381 -0
- package/src/services/DatabaseService.js +723 -170
- package/src/services/ErrorMonitor.js +275 -0
- package/src/services/LoggingService.js +419 -1
- package/src/services/MonitoringService.js +401 -0
- package/src/services/PerformanceOptimizer.js +511 -0
- package/src/services/ReportingService.js +511 -0
- package/src/services/SignalHandler.js +255 -0
- package/src/services/SmartWatchDatabaseService.js +527 -0
- package/src/services/WatchService.js +783 -0
- package/src/services/upload/ApiUploadService.js +30 -4
- package/src/services/upload/SupabaseUploadService.js +28 -6
- package/src/utils/CleanupManager.js +262 -0
- package/src/utils/FileOperations.js +41 -0
- package/src/utils/WatchEventHandler.js +517 -0
- package/supabase/migrations/001_create_initial_schema.sql +366 -0
- package/supabase/migrations/002_align_with_arela_api_schema.sql +145 -0
- package/commands.md +0 -6
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Error Monitor Service
|
|
3
|
+
* Tracks, logs, and monitors errors during signal handling and cleanup operations
|
|
4
|
+
* Provides detailed error reporting and failure recovery mechanisms
|
|
5
|
+
*/
|
|
6
|
+
export class ErrorMonitor {
|
|
7
|
+
constructor(logger, cleanupManager, signalHandler) {
|
|
8
|
+
this.logger = logger;
|
|
9
|
+
this.cleanupManager = cleanupManager;
|
|
10
|
+
this.signalHandler = signalHandler;
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Array of recorded errors
|
|
14
|
+
* @type {Array<Object>}
|
|
15
|
+
*/
|
|
16
|
+
this.errors = [];
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Error categories
|
|
20
|
+
* @type {Object}
|
|
21
|
+
*/
|
|
22
|
+
this.errorCategories = {
|
|
23
|
+
SIGNAL: 'signal_error',
|
|
24
|
+
CLEANUP: 'cleanup_error',
|
|
25
|
+
DATABASE: 'database_error',
|
|
26
|
+
WATCH_SERVICE: 'watch_service_error',
|
|
27
|
+
LOGGING: 'logging_error',
|
|
28
|
+
GENERAL: 'general_error',
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Error statistics
|
|
33
|
+
* @type {Object}
|
|
34
|
+
*/
|
|
35
|
+
this.stats = {
|
|
36
|
+
totalErrors: 0,
|
|
37
|
+
errorsByCategory: {},
|
|
38
|
+
fatalErrors: 0,
|
|
39
|
+
recoveredErrors: 0,
|
|
40
|
+
lastErrorTime: null,
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
// Initialize error category stats
|
|
44
|
+
for (const category of Object.values(this.errorCategories)) {
|
|
45
|
+
this.stats.errorsByCategory[category] = 0;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Record an error occurrence
|
|
51
|
+
* @param {string} category - Error category
|
|
52
|
+
* @param {string} message - Error message
|
|
53
|
+
* @param {Error} error - Error object
|
|
54
|
+
* @param {Object} context - Additional context
|
|
55
|
+
* @param {boolean} isFatal - Whether error is fatal
|
|
56
|
+
* @returns {Object} Error record
|
|
57
|
+
*/
|
|
58
|
+
recordError(category, message, error, context = {}, isFatal = false) {
|
|
59
|
+
const errorRecord = {
|
|
60
|
+
id: `err_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
|
|
61
|
+
timestamp: new Date().toISOString(),
|
|
62
|
+
category,
|
|
63
|
+
message,
|
|
64
|
+
isFatal,
|
|
65
|
+
stack: error?.stack || null,
|
|
66
|
+
context,
|
|
67
|
+
signal: this.signalHandler?.getSignalReceived() || null,
|
|
68
|
+
resolved: false,
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
this.errors.push(errorRecord);
|
|
72
|
+
this.stats.totalErrors++;
|
|
73
|
+
this.stats.errorsByCategory[category]++;
|
|
74
|
+
this.stats.lastErrorTime = errorRecord.timestamp;
|
|
75
|
+
|
|
76
|
+
if (isFatal) {
|
|
77
|
+
this.stats.fatalErrors++;
|
|
78
|
+
this.logger.error(
|
|
79
|
+
`šØ FATAL ERROR [${category}]: ${message} - ${error?.message}`,
|
|
80
|
+
);
|
|
81
|
+
} else {
|
|
82
|
+
this.logger.warn(
|
|
83
|
+
`ā ļø ERROR [${category}]: ${message} - ${error?.message}`,
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return errorRecord;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Mark an error as resolved
|
|
92
|
+
* @param {string} errorId - Error ID to resolve
|
|
93
|
+
* @returns {boolean} True if resolved
|
|
94
|
+
*/
|
|
95
|
+
resolveError(errorId) {
|
|
96
|
+
const errorRecord = this.errors.find((e) => e.id === errorId);
|
|
97
|
+
if (errorRecord) {
|
|
98
|
+
errorRecord.resolved = true;
|
|
99
|
+
this.stats.recoveredErrors++;
|
|
100
|
+
this.logger.info(`ā
Error resolved: ${errorId}`);
|
|
101
|
+
return true;
|
|
102
|
+
}
|
|
103
|
+
return false;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Get all unresolved errors
|
|
108
|
+
* @returns {Array<Object>} Unresolved error records
|
|
109
|
+
*/
|
|
110
|
+
getUnresolvedErrors() {
|
|
111
|
+
return this.errors.filter((e) => !e.resolved);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Get errors by category
|
|
116
|
+
* @param {string} category - Error category
|
|
117
|
+
* @returns {Array<Object>} Errors in category
|
|
118
|
+
*/
|
|
119
|
+
getErrorsByCategory(category) {
|
|
120
|
+
return this.errors.filter((e) => e.category === category);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Check if critical errors occurred
|
|
125
|
+
* @returns {boolean} True if there are fatal errors
|
|
126
|
+
*/
|
|
127
|
+
hasCriticalErrors() {
|
|
128
|
+
return this.stats.fatalErrors > 0;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Get error statistics
|
|
133
|
+
* @returns {Object} Error statistics
|
|
134
|
+
*/
|
|
135
|
+
getStatistics() {
|
|
136
|
+
return {
|
|
137
|
+
totalErrors: this.stats.totalErrors,
|
|
138
|
+
fatalErrors: this.stats.fatalErrors,
|
|
139
|
+
recoveredErrors: this.stats.recoveredErrors,
|
|
140
|
+
unresolvedErrors: this.getUnresolvedErrors().length,
|
|
141
|
+
errorsByCategory: { ...this.stats.errorsByCategory },
|
|
142
|
+
lastErrorTime: this.stats.lastErrorTime,
|
|
143
|
+
successRate:
|
|
144
|
+
this.stats.totalErrors > 0
|
|
145
|
+
? (
|
|
146
|
+
(this.stats.recoveredErrors / this.stats.totalErrors) *
|
|
147
|
+
100
|
|
148
|
+
).toFixed(2)
|
|
149
|
+
: 100,
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Generate detailed error report
|
|
155
|
+
* @returns {string} Formatted error report
|
|
156
|
+
*/
|
|
157
|
+
generateErrorReport() {
|
|
158
|
+
const stats = this.getStatistics();
|
|
159
|
+
let report =
|
|
160
|
+
'\nāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā\n';
|
|
161
|
+
report += 'ERROR MONITORING REPORT\n';
|
|
162
|
+
report += 'āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā\n\n';
|
|
163
|
+
|
|
164
|
+
report += 'STATISTICS\n';
|
|
165
|
+
report += `āā Total Errors: ${stats.totalErrors}\n`;
|
|
166
|
+
report += `āā Fatal Errors: ${stats.fatalErrors}\n`;
|
|
167
|
+
report += `āā Recovered Errors: ${stats.recoveredErrors}\n`;
|
|
168
|
+
report += `āā Unresolved Errors: ${stats.unresolvedErrors}\n`;
|
|
169
|
+
report += `āā Recovery Rate: ${stats.successRate}%\n`;
|
|
170
|
+
report += `āā Last Error: ${stats.lastErrorTime || 'None'}\n\n`;
|
|
171
|
+
|
|
172
|
+
if (this.errors.length > 0) {
|
|
173
|
+
report += 'ERROR DETAILS (Last 10)\n';
|
|
174
|
+
const recentErrors = this.errors.slice(-10);
|
|
175
|
+
recentErrors.forEach((err, idx) => {
|
|
176
|
+
const status = err.resolved ? 'ā
' : 'ā';
|
|
177
|
+
const severity = err.isFatal ? 'šØ' : 'ā ļø ';
|
|
178
|
+
report += `${idx + 1}. ${status} ${severity} [${err.category}]\n`;
|
|
179
|
+
report += ` Message: ${err.message}\n`;
|
|
180
|
+
report += ` Time: ${err.timestamp}\n`;
|
|
181
|
+
if (err.signal) {
|
|
182
|
+
report += ` Signal: ${err.signal}\n`;
|
|
183
|
+
}
|
|
184
|
+
report += '\n';
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
report += 'āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā\n';
|
|
189
|
+
return report;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Clear error history (useful after processing)
|
|
194
|
+
* @returns {void}
|
|
195
|
+
*/
|
|
196
|
+
clearErrors() {
|
|
197
|
+
const count = this.errors.length;
|
|
198
|
+
this.errors = [];
|
|
199
|
+
this.logger.debug(`ErrorMonitor: Cleared ${count} error records`);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Get error monitor status
|
|
204
|
+
* @returns {Object} Status information
|
|
205
|
+
*/
|
|
206
|
+
getStatus() {
|
|
207
|
+
return {
|
|
208
|
+
isHealthy: this.stats.fatalErrors === 0,
|
|
209
|
+
hasErrors: this.stats.totalErrors > 0,
|
|
210
|
+
unresolvedCount: this.getUnresolvedErrors().length,
|
|
211
|
+
stats: this.getStatistics(),
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Validate cleanup operation for errors
|
|
217
|
+
* @param {Object} cleanupResult - Result from cleanup operation
|
|
218
|
+
* @returns {Object} Validation result
|
|
219
|
+
*/
|
|
220
|
+
validateCleanupResult(cleanupResult) {
|
|
221
|
+
const validation = {
|
|
222
|
+
isValid: cleanupResult.failureCount === 0,
|
|
223
|
+
failureCount: cleanupResult.failureCount,
|
|
224
|
+
successCount: cleanupResult.successCount,
|
|
225
|
+
issues: [],
|
|
226
|
+
};
|
|
227
|
+
|
|
228
|
+
if (cleanupResult.results) {
|
|
229
|
+
for (const result of cleanupResult.results) {
|
|
230
|
+
if (!result.success) {
|
|
231
|
+
validation.issues.push({
|
|
232
|
+
resource: result.name,
|
|
233
|
+
error: result.error,
|
|
234
|
+
duration: result.duration,
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
this.recordError(
|
|
238
|
+
this.errorCategories.CLEANUP,
|
|
239
|
+
`Cleanup failed for ${result.name}`,
|
|
240
|
+
new Error(result.error),
|
|
241
|
+
{ resource: result.name, duration: result.duration },
|
|
242
|
+
false,
|
|
243
|
+
);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
return validation;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* Reset monitor state (for testing)
|
|
253
|
+
* @returns {void}
|
|
254
|
+
*/
|
|
255
|
+
reset() {
|
|
256
|
+
this.errors = [];
|
|
257
|
+
this.stats.totalErrors = 0;
|
|
258
|
+
this.stats.fatalErrors = 0;
|
|
259
|
+
this.stats.recoveredErrors = 0;
|
|
260
|
+
this.stats.lastErrorTime = null;
|
|
261
|
+
|
|
262
|
+
for (const category of Object.keys(this.stats.errorsByCategory)) {
|
|
263
|
+
this.stats.errorsByCategory[category] = 0;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
this.logger.debug('ErrorMonitor: Reset to initial state');
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
// Export singleton
|
|
271
|
+
export function createErrorMonitor(logger, cleanupManager, signalHandler) {
|
|
272
|
+
return new ErrorMonitor(logger, cleanupManager, signalHandler);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
export default ErrorMonitor;
|