UNPKG

@kadi.build/local-remote-file-manager-ability

Version:

Local & Remote File Management System with S3-compatible container registry, HTTP server provider, file streaming, and comprehensive testing suite

160 lines (132 loc) โ€ข 4.52 kB
// Process Management Utility class ProcessManager { constructor() { this.cleanupHandlers = new Set(); this.isShuttingDown = false; this.shutdownTimer = null; this.forceExitTimer = null; this.setupSignalHandlers(); } setupSignalHandlers() { // Handle various termination signals const signals = ['SIGINT', 'SIGTERM', 'SIGQUIT']; signals.forEach(signal => { process.on(signal, () => { console.log(`\n๐Ÿ”„ Received ${signal}, initiating graceful shutdown...`); this.gracefulShutdown(`${signal} signal`); }); }); // Handle uncaught exceptions process.on('uncaughtException', (error) => { console.error('๐Ÿ’ฅ Uncaught Exception:', error); this.forceShutdown('uncaught exception'); }); // Handle unhandled promise rejections process.on('unhandledRejection', (reason, promise) => { console.error('๐Ÿ’ฅ Unhandled Rejection at:', promise, 'reason:', reason); this.forceShutdown('unhandled rejection'); }); } // Register a cleanup handler registerCleanupHandler(handler, description = 'cleanup') { if (typeof handler !== 'function') { throw new Error('Cleanup handler must be a function'); } const cleanupItem = { handler, description }; this.cleanupHandlers.add(cleanupItem); // Return a function to unregister the handler return () => this.cleanupHandlers.delete(cleanupItem); } // Set up automatic shutdown after delay scheduleAutoShutdown(delaySeconds, reason = 'auto-shutdown timer') { if (this.shutdownTimer) { clearTimeout(this.shutdownTimer); } if (delaySeconds > 0) { console.log(`โฐ Auto-shutdown scheduled in ${delaySeconds} seconds`); this.shutdownTimer = setTimeout(() => { this.gracefulShutdown(reason); }, delaySeconds * 1000); } } // Cancel automatic shutdown cancelAutoShutdown() { if (this.shutdownTimer) { clearTimeout(this.shutdownTimer); this.shutdownTimer = null; console.log('โฐ Auto-shutdown cancelled'); } } // Perform graceful shutdown async gracefulShutdown(reason = 'shutdown', forceExitAfter = 10000) { if (this.isShuttingDown) { console.log('๐Ÿ”„ Shutdown already in progress...'); return; } this.isShuttingDown = true; console.log(`๐Ÿ”„ Starting graceful shutdown (${reason})...`); // Set up force exit timer as safety net this.forceExitTimer = setTimeout(() => { console.log('โš ๏ธ Force exit after timeout'); process.exit(1); }, forceExitAfter); try { // Cancel any pending auto-shutdown this.cancelAutoShutdown(); // Run cleanup handlers in reverse order (LIFO) const handlers = Array.from(this.cleanupHandlers).reverse(); for (const { handler, description } of handlers) { try { console.log(` ๐Ÿงน Running ${description}...`); await handler(); } catch (error) { console.warn(` โš ๏ธ ${description} failed: ${error.message}`); } } console.log('โœ… Graceful shutdown completed'); } catch (error) { console.error('โŒ Error during shutdown:', error.message); } finally { // Clear force exit timer if (this.forceExitTimer) { clearTimeout(this.forceExitTimer); } process.exit(0); } } // Force immediate shutdown forceShutdown(reason = 'force') { console.log(`๐Ÿ’ฅ Force shutdown (${reason})`); process.exit(1); } // Keep process alive until shutdown keepAlive() { return new Promise(() => { }); // Infinite promise } // Keep alive with periodic heartbeat keepAliveWithHeartbeat(intervalSeconds = 60, onHeartbeat = null) { const heartbeatInterval = setInterval(() => { if (this.isShuttingDown) { clearInterval(heartbeatInterval); return; } if (onHeartbeat) { try { onHeartbeat(); } catch (error) { console.warn('โš ๏ธ Heartbeat callback error:', error.message); } } else { console.log(`๐Ÿ’“ Process heartbeat - ${new Date().toLocaleTimeString()}`); } }, intervalSeconds * 1000); // Register cleanup to stop heartbeat this.registerCleanupHandler(() => { clearInterval(heartbeatInterval); }, 'heartbeat cleanup'); return this.keepAlive(); } } // Singleton instance const processManager = new ProcessManager(); export { ProcessManager, processManager };