UNPKG

@tryloop/oats

Version:

🌾 OATS - OpenAPI TypeScript Sync. The missing link between your OpenAPI specs and TypeScript applications. Automatically watch, generate, and sync TypeScript clients from your API definitions.

151 lines • 4.93 kB
/** * Centralized shutdown management for OATS * * Handles graceful shutdown of all services and cleanup operations * * @module @oatsjs/utils/shutdown-manager */ import chalk from 'chalk'; import { Logger } from './logger.js'; export class ShutdownManager { static instance; logger; isShuttingDown = false; shutdownHandlers = []; signalHandlersSetup = false; constructor() { this.logger = new Logger('ShutdownManager'); } /** * Get singleton instance */ static getInstance() { if (!ShutdownManager.instance) { ShutdownManager.instance = new ShutdownManager(); } return ShutdownManager.instance; } /** * Register a custom shutdown handler */ registerHandler(handler) { this.shutdownHandlers.push(handler); } /** * Perform graceful shutdown of all services */ async shutdown(services, options = {}) { if (this.isShuttingDown) { this.logger.warn('Shutdown already in progress'); return; } this.isShuttingDown = true; const { keepConfigWatcher = false, exitProcess = true } = options; console.log(chalk.yellow.bold('\nšŸ›‘ Shutting down services...\n')); try { // Stop sync engine if (services.syncEngine) { await services.syncEngine.stop(); } // Stop all services for (const service of services.services.values()) { try { await service.stop(); } catch (err) { const serviceName = service.getInfo().name; this.logger.error(`Failed to stop ${serviceName}:`, err); } } // Kill any remaining processes await services.processManager.killAll(); // Unlink packages if (services.unlinkPackages) { this.logger.debug('Starting package unlinking...'); await services.unlinkPackages(); this.logger.debug('Package unlinking completed'); } else { this.logger.debug('No unlinkPackages handler provided'); } // Run custom shutdown handlers for (const handler of this.shutdownHandlers) { try { await handler(); } catch (err) { this.logger.error('Shutdown handler failed:', err); } } // Stop config watcher if requested if (!keepConfigWatcher && services.configWatcher) { await services.configWatcher.close(); } console.log(chalk.green.bold('\nāœ… Shutdown complete\n')); if (exitProcess) { process.exit(0); } } catch (error) { this.logger.error('Shutdown failed:', error); if (exitProcess) { process.exit(1); } throw error; } finally { this.isShuttingDown = false; } } /** * Setup signal handlers for graceful shutdown */ setupSignalHandlers(shutdownCallback) { // Only set up handlers once if (this.signalHandlersSetup) { return; } this.signalHandlersSetup = true; const signals = ['SIGINT', 'SIGTERM']; signals.forEach((signal) => { process.on(signal, async () => { // Prevent duplicate handling if (this.isShuttingDown) { return; } console.log(chalk.yellow(`\nšŸ“ Received ${signal}`)); // Ensure logs appear before terminal prompt process.stderr.write(''); process.stdout.write(''); try { await shutdownCallback(); } catch (err) { console.error(chalk.red('Shutdown error:'), err); process.exit(1); } }); }); process.on('uncaughtException', (error) => { console.error(chalk.red('\nāŒ Uncaught exception:'), error); // Ensure logs appear before terminal prompt process.stderr.write(''); process.stdout.write(''); shutdownCallback() .then(() => { // Exit is handled by shutdown method }) .catch((err) => { console.error(chalk.red('Shutdown error:'), err); process.exit(1); }); }); } /** * Check if shutdown is in progress */ isShutdownInProgress() { return this.isShuttingDown; } } //# sourceMappingURL=shutdown-manager.js.map