@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
JavaScript
/**
* 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