@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,255 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Signal Handler Service
|
|
3
|
+
* Centralizes handling of system signals for graceful shutdown
|
|
4
|
+
* Manages SIGINT, SIGTERM, SIGHUP, SIGQUIT with cleanup coordination
|
|
5
|
+
*/
|
|
6
|
+
export class SignalHandler {
|
|
7
|
+
/**
|
|
8
|
+
* Create a new SignalHandler instance
|
|
9
|
+
* @param {Object} logger - Logger service instance
|
|
10
|
+
* @param {Object} cleanupManager - Cleanup manager instance
|
|
11
|
+
*/
|
|
12
|
+
constructor(logger, cleanupManager) {
|
|
13
|
+
this.logger = logger;
|
|
14
|
+
this.cleanupManager = cleanupManager;
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Flag to prevent concurrent signal handling
|
|
18
|
+
* @type {boolean}
|
|
19
|
+
*/
|
|
20
|
+
this.shutdownInProgress = false;
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Signal received by process
|
|
24
|
+
* @type {string|null}
|
|
25
|
+
*/
|
|
26
|
+
this.signalReceived = null;
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Timestamp when signal was received
|
|
30
|
+
* @type {number|null}
|
|
31
|
+
*/
|
|
32
|
+
this.signalTime = null;
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Map of registered signal handlers
|
|
36
|
+
* @type {Map<string, Function>}
|
|
37
|
+
*/
|
|
38
|
+
this.signalHandlers = new Map();
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Signals to handle
|
|
42
|
+
* @type {string[]}
|
|
43
|
+
*/
|
|
44
|
+
this.supportedSignals = ['SIGINT', 'SIGTERM', 'SIGHUP', 'SIGQUIT'];
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Register signal handlers
|
|
49
|
+
* @param {Object} handlers - Map of signal handlers { signal: handler }
|
|
50
|
+
* @returns {void}
|
|
51
|
+
*/
|
|
52
|
+
registerSignalHandlers(handlers = {}) {
|
|
53
|
+
for (const signal of this.supportedSignals) {
|
|
54
|
+
const handler = handlers[signal] || this.#createDefaultHandler(signal);
|
|
55
|
+
|
|
56
|
+
// Store handler reference
|
|
57
|
+
this.signalHandlers.set(signal, handler);
|
|
58
|
+
|
|
59
|
+
// Register with process
|
|
60
|
+
process.on(signal, () => this.#handleSignal(signal, handler));
|
|
61
|
+
|
|
62
|
+
this.logger.debug(`SignalHandler: Registered handler for ${signal}`);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Handle incoming signal
|
|
68
|
+
* @private
|
|
69
|
+
* @param {string} signal - Signal name
|
|
70
|
+
* @param {Function} handler - Handler function
|
|
71
|
+
* @returns {Promise<void>}
|
|
72
|
+
*/
|
|
73
|
+
async #handleSignal(signal, handler) {
|
|
74
|
+
// Prevent concurrent handling
|
|
75
|
+
if (this.shutdownInProgress) {
|
|
76
|
+
this.logger.warn(
|
|
77
|
+
`SignalHandler: Shutdown already in progress, ignoring ${signal}`,
|
|
78
|
+
);
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
this.shutdownInProgress = true;
|
|
83
|
+
this.signalReceived = signal;
|
|
84
|
+
this.signalTime = Date.now();
|
|
85
|
+
|
|
86
|
+
this.logger.warn(
|
|
87
|
+
`\n\n🛑 SignalHandler: Received ${signal}, initiating graceful shutdown...`,
|
|
88
|
+
);
|
|
89
|
+
|
|
90
|
+
// Log signal details
|
|
91
|
+
this.#logSignalEvent(signal);
|
|
92
|
+
|
|
93
|
+
try {
|
|
94
|
+
// Perform cleanup
|
|
95
|
+
const cleanupResult = await this.cleanupManager.performCleanup(signal);
|
|
96
|
+
|
|
97
|
+
if (!cleanupResult.success) {
|
|
98
|
+
this.logger.error(
|
|
99
|
+
`SignalHandler: Cleanup had ${cleanupResult.failureCount} failures, but continuing with shutdown`,
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Execute signal-specific handler
|
|
104
|
+
if (handler && typeof handler === 'function') {
|
|
105
|
+
await handler();
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Determine exit code based on signal
|
|
109
|
+
const exitCode = this.#getExitCode(signal);
|
|
110
|
+
this.logger.info(
|
|
111
|
+
`SignalHandler: Shutdown complete, exiting with code ${exitCode}`,
|
|
112
|
+
);
|
|
113
|
+
|
|
114
|
+
process.exit(exitCode);
|
|
115
|
+
} catch (error) {
|
|
116
|
+
this.logger.error(
|
|
117
|
+
`SignalHandler: Error during signal handling: ${error.message}`,
|
|
118
|
+
);
|
|
119
|
+
process.exit(1);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Create default handler for a signal
|
|
125
|
+
* @private
|
|
126
|
+
* @param {string} signal - Signal name
|
|
127
|
+
* @returns {Function} Default handler function
|
|
128
|
+
*/
|
|
129
|
+
#createDefaultHandler(signal) {
|
|
130
|
+
return async () => {
|
|
131
|
+
this.logger.info(
|
|
132
|
+
`SignalHandler: Executing default handler for ${signal}`,
|
|
133
|
+
);
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Get appropriate exit code for signal
|
|
139
|
+
* @private
|
|
140
|
+
* @param {string} signal - Signal name
|
|
141
|
+
* @returns {number} Exit code
|
|
142
|
+
*/
|
|
143
|
+
#getExitCode(signal) {
|
|
144
|
+
const signalExitCodes = {
|
|
145
|
+
SIGINT: 130, // 128 + 2
|
|
146
|
+
SIGTERM: 143, // 128 + 15
|
|
147
|
+
SIGHUP: 129, // 128 + 1
|
|
148
|
+
SIGQUIT: 131, // 128 + 3
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
return signalExitCodes[signal] || 128;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Log signal event details
|
|
156
|
+
* @private
|
|
157
|
+
* @param {string} signal - Signal name
|
|
158
|
+
* @returns {void}
|
|
159
|
+
*/
|
|
160
|
+
#logSignalEvent(signal) {
|
|
161
|
+
const signalDescriptions = {
|
|
162
|
+
SIGINT: 'Interrupt (Ctrl+C)',
|
|
163
|
+
SIGTERM: 'Termination (kill command)',
|
|
164
|
+
SIGHUP: 'Hangup (terminal closed)',
|
|
165
|
+
SIGQUIT: 'Quit (Ctrl+\\)',
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
const description = signalDescriptions[signal] || 'Unknown signal';
|
|
169
|
+
|
|
170
|
+
this.logger.info(`SignalHandler: Signal Details`);
|
|
171
|
+
this.logger.info(`├─ Signal: ${signal} (${description})`);
|
|
172
|
+
this.logger.info(`├─ Process ID: ${process.pid}`);
|
|
173
|
+
this.logger.info(`├─ Timestamp: ${new Date().toISOString()}`);
|
|
174
|
+
this.logger.info(
|
|
175
|
+
`├─ Memory Usage: ${Math.round(process.memoryUsage().heapUsed / 1024 / 1024)}MB`,
|
|
176
|
+
);
|
|
177
|
+
this.logger.info(`└─ Uptime: ${process.uptime().toFixed(2)}s`);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Check if shutdown is in progress
|
|
182
|
+
* @returns {boolean} True if shutdown is happening
|
|
183
|
+
*/
|
|
184
|
+
isShuttingDown() {
|
|
185
|
+
return this.shutdownInProgress;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Get the signal that was received
|
|
190
|
+
* @returns {string|null} Signal name or null
|
|
191
|
+
*/
|
|
192
|
+
getSignalReceived() {
|
|
193
|
+
return this.signalReceived;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Get timestamp when signal was received
|
|
198
|
+
* @returns {number|null} Timestamp in milliseconds or null
|
|
199
|
+
*/
|
|
200
|
+
getSignalTime() {
|
|
201
|
+
return this.signalTime;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Generate signal event report
|
|
206
|
+
* @returns {string} Formatted report
|
|
207
|
+
*/
|
|
208
|
+
generateSignalReport() {
|
|
209
|
+
if (!this.signalReceived) {
|
|
210
|
+
return 'No signal received yet';
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
const duration = this.signalTime ? Date.now() - this.signalTime : 0;
|
|
214
|
+
|
|
215
|
+
let report =
|
|
216
|
+
'\n═══════════════════════════════════════════════════════════\n';
|
|
217
|
+
report += 'SIGNAL HANDLING REPORT\n';
|
|
218
|
+
report += '═══════════════════════════════════════════════════════════\n\n';
|
|
219
|
+
report += `Signal: ${this.signalReceived}\n`;
|
|
220
|
+
report += `Process ID: ${process.pid}\n`;
|
|
221
|
+
report += `Received At: ${new Date(this.signalTime).toISOString()}\n`;
|
|
222
|
+
report += `Shutdown Duration: ${duration}ms\n`;
|
|
223
|
+
report += `Shutdown Status: ${this.shutdownInProgress ? 'IN PROGRESS' : 'COMPLETE'}\n`;
|
|
224
|
+
report += '═══════════════════════════════════════════════════════════\n';
|
|
225
|
+
|
|
226
|
+
return report;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Reset signal handler state (useful for testing)
|
|
231
|
+
* @returns {void}
|
|
232
|
+
*/
|
|
233
|
+
reset() {
|
|
234
|
+
this.shutdownInProgress = false;
|
|
235
|
+
this.signalReceived = null;
|
|
236
|
+
this.signalTime = null;
|
|
237
|
+
this.logger.debug('SignalHandler: Reset to initial state');
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// Export as singleton
|
|
242
|
+
let instance = null;
|
|
243
|
+
|
|
244
|
+
export function createSignalHandler(logger, cleanupManager) {
|
|
245
|
+
if (!instance) {
|
|
246
|
+
instance = new SignalHandler(logger, cleanupManager);
|
|
247
|
+
}
|
|
248
|
+
return instance;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
export function getSignalHandler() {
|
|
252
|
+
return instance;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
export default SignalHandler;
|