/** * Graceful shutdown handler - registers cleanup functions and handles shutdown signals. */ import { SHUTDOWN_TIMEOUT_MS } from './constants.js' const cleanupFunctions = [] const shutdownState = { isShuttingDown: false, } export function clearCleanup() { cleanupFunctions.length = 0 shutdownState.isShuttingDown = false } export function registerCleanup(fn) { if (typeof fn !== 'function') throw new TypeError('Cleanup function must be a function') cleanupFunctions.push(fn) } const executeCleanupFunction = async (fn, index) => { try { await fn() } catch (error) { console.error(`[shutdown] Cleanup function ${index} failed:`, error?.message || String(error)) } } const executeCleanupReverse = async (functions, index = functions.length - 1) => { if (index < 0) return await executeCleanupFunction(functions[index], index) return executeCleanupReverse(functions, index - 1) } async function executeCleanup() { if (shutdownState.isShuttingDown) return shutdownState.isShuttingDown = true await executeCleanupReverse(cleanupFunctions) } export async function graceful(error) { if (error) { console.error('[shutdown] Shutting down due to error:', error?.message || String(error)) if (error.stack) console.error('[shutdown] Stack trace:', error.stack) } else { console.log('[shutdown] Initiating graceful shutdown') } const timeout = setTimeout(() => { console.error('[shutdown] Shutdown timeout exceeded, forcing exit') process.exit(1) }, SHUTDOWN_TIMEOUT_MS) try { await executeCleanup() clearTimeout(timeout) console.log('[shutdown] Cleanup complete') process.exit(error ? 1 : 0) } catch (err) { clearTimeout(timeout) console.error('[shutdown] Error during cleanup:', err?.message || String(err)) process.exit(1) } } export function initShutdownHandlers() { for (const signal of ['SIGTERM', 'SIGINT']) { process.on(signal, () => { console.log(`[shutdown] Received ${signal}`) graceful().catch((err) => { console.error('[shutdown] Error in graceful shutdown:', err) process.exit(1) }) }) } }