/** * Graceful shutdown handler - registers cleanup functions and handles shutdown signals. */ import { SHUTDOWN_TIMEOUT_MS } from './constants.js' const cleanupFunctions = [] let isShuttingDown = false export function clearCleanup() { cleanupFunctions.length = 0 isShuttingDown = false } export function registerCleanup(fn) { if (typeof fn !== 'function') throw new TypeError('Cleanup function must be a function') cleanupFunctions.push(fn) } async function executeCleanup() { if (isShuttingDown) return isShuttingDown = true for (let i = cleanupFunctions.length - 1; i >= 0; i--) { try { await cleanupFunctions[i]() } catch (error) { console.error(`[shutdown] Cleanup function ${i} failed:`, error?.message || String(error)) } } } 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) }) }) } }