@autofleet/nitur 2.2.0-beta.0 → 2.2.0-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"health-check-handlers.cjs","names":["DEFAULT_CLIENT_HEALTHCHECK_FUNC_MAP: Record<ClientType, (options: { client: any, name: string }) => Promise<void>>","CLIENT_TYPES"],"sources":["../../src/health-manager/health-check-handlers.ts"],"sourcesContent":["import type {\n RedisClient,\n SequelizeClient,\n ElasticsearchClient,\n RabbitMQClient,\n ClientType,\n} from './types';\nimport { CLIENT_TYPES } from './types';\n\nexport const DEFAULT_CLIENT_HEALTHCHECK_FUNC_MAP: Record<ClientType, (options: { client: any, name: string }) => Promise<void>> = {\n [CLIENT_TYPES.REDIS]: async ({ client, name }: { client: RedisClient, name: string }) => {\n try {\n await client.ping();\n } catch (error) {\n throw new Error(`Redis instance '${name}' ping failed: ${error instanceof Error ? error.message : String(error)}`);\n }\n },\n [CLIENT_TYPES.SEQUELIZE]: async ({ client, name }: { client: SequelizeClient, name: string }) => {\n try {\n const [[result]] = await client.query('SELECT 1 as status');\n if (!result || (result).status !== 1) {\n throw new Error('Unexpected query result');\n }\n } catch (error) {\n throw new Error(`Sequelize instance '${name}' health check failed: ${error instanceof Error ? error.message : String(error)}`);\n }\n },\n [CLIENT_TYPES.ELASTIC_SEARCH]: async ({ client, name }: { client: ElasticsearchClient, name: string }) => {\n try {\n const isAlive = await client.ping();\n if (!isAlive) {\n throw new Error('Elasticsearch ping returned false');\n }\n } catch (error) {\n throw new Error(`Elasticsearch instance '${name}' health check failed: ${error instanceof Error ? error.message : String(error)}`);\n }\n },\n [CLIENT_TYPES.RABBIT]: async ({ client, name }: { client: RabbitMQClient, name: string }) => {\n try {\n const isConnected = await client.isConnected();\n if (!isConnected) {\n throw new Error('RabbitMQ is not connected');\n }\n } catch (error) {\n throw new Error(`RabbitMQ instance '${name}' health check failed: ${error instanceof Error ? error.message : String(error)}`);\n }\n },\n};"],"mappings":"+BASaA,EAAqH,EAC/HC,EAAAA,aAAa,OAAQ,MAAO,CAAE,SAAQ,UAAkD,CACvF,GAAI,CACF,MAAM,EAAO,MAAM,OACZ,EAAO,CACd,MAAU,MAAM,mBAAmB,EAAK,iBAAiB,aAAiB,MAAQ,EAAM,QAAU,OAAO,EAAM,GAAG,IAGrHA,EAAAA,aAAa,WAAY,MAAO,CAAE,SAAQ,UAAsD,CAC/F,GAAI,CACF,GAAM,CAAC,CAAC,IAAW,MAAM,EAAO,MAAM,qBAAqB,CAC3D,GAAI,CAAC,GAAW,EAAQ,SAAW,EACjC,MAAU,MAAM,0BAA0B,OAErC,EAAO,CACd,MAAU,MAAM,uBAAuB,EAAK,yBAAyB,aAAiB,MAAQ,EAAM,QAAU,OAAO,EAAM,GAAG,IAGjIA,EAAAA,aAAa,gBAAiB,MAAO,CAAE,SAAQ,UAA0D,CACxG,GAAI,CAEF,GAAI,CADY,MAAM,EAAO,MAAM,CAEjC,MAAU,MAAM,oCAAoC,OAE/C,EAAO,CACd,MAAU,MAAM,2BAA2B,EAAK,yBAAyB,aAAiB,MAAQ,EAAM,QAAU,OAAO,EAAM,GAAG,IAGrIA,EAAAA,aAAa,QAAS,MAAO,CAAE,SAAQ,UAAqD,CAC3F,GAAI,CAEF,GAAI,CADgB,MAAM,EAAO,aAAa,CAE5C,MAAU,MAAM,4BAA4B,OAEvC,EAAO,CACd,MAAU,MAAM,sBAAsB,EAAK,yBAAyB,aAAiB,MAAQ,EAAM,QAAU,OAAO,EAAM,GAAG,GAGlI"}
1
+ {"version":3,"file":"health-check-handlers.cjs","names":["DEFAULT_CLIENT_HEALTHCHECK_FUNC_MAP: Partial<Record<ClientType, (options: { client: any, name: string }) => any>>","CLIENT_TYPES"],"sources":["../../src/health-manager/health-check-handlers.ts"],"sourcesContent":["import type {\n RedisClient,\n SequelizeClient,\n ElasticsearchClient,\n RabbitMQClient,\n ClientType,\n} from './types';\nimport { CLIENT_TYPES } from './types';\n\nexport const DEFAULT_CLIENT_HEALTHCHECK_FUNC_MAP: Partial<Record<ClientType, (options: { client: any, name: string }) => any>> = {\n [CLIENT_TYPES.REDIS]: async ({ client, name }: { client: RedisClient, name: string }) => {\n try {\n await client.ping();\n } catch (error) {\n throw new Error(`Redis instance '${name}' ping failed: ${error instanceof Error ? error.message : String(error)}`);\n }\n },\n [CLIENT_TYPES.SEQUELIZE]: async ({ client, name }: { client: SequelizeClient, name: string }) => {\n try {\n const [[result]] = await client.query('SELECT 1 as status');\n if (!result || (result).status !== 1) {\n throw new Error('Unexpected query result');\n }\n } catch (error) {\n throw new Error(`Sequelize instance '${name}' health check failed: ${error instanceof Error ? error.message : String(error)}`);\n }\n },\n [CLIENT_TYPES.ELASTIC_SEARCH]: async ({ client, name }: { client: ElasticsearchClient, name: string }) => {\n try {\n const isAlive = await client.ping();\n if (!isAlive) {\n throw new Error('Elasticsearch ping returned false');\n }\n } catch (error) {\n throw new Error(`Elasticsearch instance '${name}' health check failed: ${error instanceof Error ? error.message : String(error)}`);\n }\n },\n [CLIENT_TYPES.RABBIT]: async ({ client, name }: { client: RabbitMQClient, name: string }) => {\n try {\n const isConnected = await client.isConnected();\n if (!isConnected) {\n throw new Error('RabbitMQ is not connected');\n }\n } catch (error) {\n throw new Error(`RabbitMQ instance '${name}' health check failed: ${error instanceof Error ? error.message : String(error)}`);\n }\n },\n};"],"mappings":"+BASaA,EAAoH,EAC9HC,EAAAA,aAAa,OAAQ,MAAO,CAAE,SAAQ,UAAkD,CACvF,GAAI,CACF,MAAM,EAAO,MAAM,OACZ,EAAO,CACd,MAAU,MAAM,mBAAmB,EAAK,iBAAiB,aAAiB,MAAQ,EAAM,QAAU,OAAO,EAAM,GAAG,IAGrHA,EAAAA,aAAa,WAAY,MAAO,CAAE,SAAQ,UAAsD,CAC/F,GAAI,CACF,GAAM,CAAC,CAAC,IAAW,MAAM,EAAO,MAAM,qBAAqB,CAC3D,GAAI,CAAC,GAAW,EAAQ,SAAW,EACjC,MAAU,MAAM,0BAA0B,OAErC,EAAO,CACd,MAAU,MAAM,uBAAuB,EAAK,yBAAyB,aAAiB,MAAQ,EAAM,QAAU,OAAO,EAAM,GAAG,IAGjIA,EAAAA,aAAa,gBAAiB,MAAO,CAAE,SAAQ,UAA0D,CACxG,GAAI,CAEF,GAAI,CADY,MAAM,EAAO,MAAM,CAEjC,MAAU,MAAM,oCAAoC,OAE/C,EAAO,CACd,MAAU,MAAM,2BAA2B,EAAK,yBAAyB,aAAiB,MAAQ,EAAM,QAAU,OAAO,EAAM,GAAG,IAGrIA,EAAAA,aAAa,QAAS,MAAO,CAAE,SAAQ,UAAqD,CAC3F,GAAI,CAEF,GAAI,CADgB,MAAM,EAAO,aAAa,CAE5C,MAAU,MAAM,4BAA4B,OAEvC,EAAO,CACd,MAAU,MAAM,sBAAsB,EAAK,yBAAyB,aAAiB,MAAQ,EAAM,QAAU,OAAO,EAAM,GAAG,GAGlI"}
@@ -1 +1 @@
1
- {"version":3,"file":"health-check-handlers.js","names":["DEFAULT_CLIENT_HEALTHCHECK_FUNC_MAP: Record<ClientType, (options: { client: any, name: string }) => Promise<void>>"],"sources":["../../src/health-manager/health-check-handlers.ts"],"sourcesContent":["import type {\n RedisClient,\n SequelizeClient,\n ElasticsearchClient,\n RabbitMQClient,\n ClientType,\n} from './types';\nimport { CLIENT_TYPES } from './types';\n\nexport const DEFAULT_CLIENT_HEALTHCHECK_FUNC_MAP: Record<ClientType, (options: { client: any, name: string }) => Promise<void>> = {\n [CLIENT_TYPES.REDIS]: async ({ client, name }: { client: RedisClient, name: string }) => {\n try {\n await client.ping();\n } catch (error) {\n throw new Error(`Redis instance '${name}' ping failed: ${error instanceof Error ? error.message : String(error)}`);\n }\n },\n [CLIENT_TYPES.SEQUELIZE]: async ({ client, name }: { client: SequelizeClient, name: string }) => {\n try {\n const [[result]] = await client.query('SELECT 1 as status');\n if (!result || (result).status !== 1) {\n throw new Error('Unexpected query result');\n }\n } catch (error) {\n throw new Error(`Sequelize instance '${name}' health check failed: ${error instanceof Error ? error.message : String(error)}`);\n }\n },\n [CLIENT_TYPES.ELASTIC_SEARCH]: async ({ client, name }: { client: ElasticsearchClient, name: string }) => {\n try {\n const isAlive = await client.ping();\n if (!isAlive) {\n throw new Error('Elasticsearch ping returned false');\n }\n } catch (error) {\n throw new Error(`Elasticsearch instance '${name}' health check failed: ${error instanceof Error ? error.message : String(error)}`);\n }\n },\n [CLIENT_TYPES.RABBIT]: async ({ client, name }: { client: RabbitMQClient, name: string }) => {\n try {\n const isConnected = await client.isConnected();\n if (!isConnected) {\n throw new Error('RabbitMQ is not connected');\n }\n } catch (error) {\n throw new Error(`RabbitMQ instance '${name}' health check failed: ${error instanceof Error ? error.message : String(error)}`);\n }\n },\n};"],"mappings":"0CASA,MAAaA,EAAqH,EAC/H,EAAa,OAAQ,MAAO,CAAE,SAAQ,UAAkD,CACvF,GAAI,CACF,MAAM,EAAO,MAAM,OACZ,EAAO,CACd,MAAU,MAAM,mBAAmB,EAAK,iBAAiB,aAAiB,MAAQ,EAAM,QAAU,OAAO,EAAM,GAAG,IAGrH,EAAa,WAAY,MAAO,CAAE,SAAQ,UAAsD,CAC/F,GAAI,CACF,GAAM,CAAC,CAAC,IAAW,MAAM,EAAO,MAAM,qBAAqB,CAC3D,GAAI,CAAC,GAAW,EAAQ,SAAW,EACjC,MAAU,MAAM,0BAA0B,OAErC,EAAO,CACd,MAAU,MAAM,uBAAuB,EAAK,yBAAyB,aAAiB,MAAQ,EAAM,QAAU,OAAO,EAAM,GAAG,IAGjI,EAAa,gBAAiB,MAAO,CAAE,SAAQ,UAA0D,CACxG,GAAI,CAEF,GAAI,CADY,MAAM,EAAO,MAAM,CAEjC,MAAU,MAAM,oCAAoC,OAE/C,EAAO,CACd,MAAU,MAAM,2BAA2B,EAAK,yBAAyB,aAAiB,MAAQ,EAAM,QAAU,OAAO,EAAM,GAAG,IAGrI,EAAa,QAAS,MAAO,CAAE,SAAQ,UAAqD,CAC3F,GAAI,CAEF,GAAI,CADgB,MAAM,EAAO,aAAa,CAE5C,MAAU,MAAM,4BAA4B,OAEvC,EAAO,CACd,MAAU,MAAM,sBAAsB,EAAK,yBAAyB,aAAiB,MAAQ,EAAM,QAAU,OAAO,EAAM,GAAG,GAGlI"}
1
+ {"version":3,"file":"health-check-handlers.js","names":["DEFAULT_CLIENT_HEALTHCHECK_FUNC_MAP: Partial<Record<ClientType, (options: { client: any, name: string }) => any>>"],"sources":["../../src/health-manager/health-check-handlers.ts"],"sourcesContent":["import type {\n RedisClient,\n SequelizeClient,\n ElasticsearchClient,\n RabbitMQClient,\n ClientType,\n} from './types';\nimport { CLIENT_TYPES } from './types';\n\nexport const DEFAULT_CLIENT_HEALTHCHECK_FUNC_MAP: Partial<Record<ClientType, (options: { client: any, name: string }) => any>> = {\n [CLIENT_TYPES.REDIS]: async ({ client, name }: { client: RedisClient, name: string }) => {\n try {\n await client.ping();\n } catch (error) {\n throw new Error(`Redis instance '${name}' ping failed: ${error instanceof Error ? error.message : String(error)}`);\n }\n },\n [CLIENT_TYPES.SEQUELIZE]: async ({ client, name }: { client: SequelizeClient, name: string }) => {\n try {\n const [[result]] = await client.query('SELECT 1 as status');\n if (!result || (result).status !== 1) {\n throw new Error('Unexpected query result');\n }\n } catch (error) {\n throw new Error(`Sequelize instance '${name}' health check failed: ${error instanceof Error ? error.message : String(error)}`);\n }\n },\n [CLIENT_TYPES.ELASTIC_SEARCH]: async ({ client, name }: { client: ElasticsearchClient, name: string }) => {\n try {\n const isAlive = await client.ping();\n if (!isAlive) {\n throw new Error('Elasticsearch ping returned false');\n }\n } catch (error) {\n throw new Error(`Elasticsearch instance '${name}' health check failed: ${error instanceof Error ? error.message : String(error)}`);\n }\n },\n [CLIENT_TYPES.RABBIT]: async ({ client, name }: { client: RabbitMQClient, name: string }) => {\n try {\n const isConnected = await client.isConnected();\n if (!isConnected) {\n throw new Error('RabbitMQ is not connected');\n }\n } catch (error) {\n throw new Error(`RabbitMQ instance '${name}' health check failed: ${error instanceof Error ? error.message : String(error)}`);\n }\n },\n};"],"mappings":"0CASA,MAAaA,EAAoH,EAC9H,EAAa,OAAQ,MAAO,CAAE,SAAQ,UAAkD,CACvF,GAAI,CACF,MAAM,EAAO,MAAM,OACZ,EAAO,CACd,MAAU,MAAM,mBAAmB,EAAK,iBAAiB,aAAiB,MAAQ,EAAM,QAAU,OAAO,EAAM,GAAG,IAGrH,EAAa,WAAY,MAAO,CAAE,SAAQ,UAAsD,CAC/F,GAAI,CACF,GAAM,CAAC,CAAC,IAAW,MAAM,EAAO,MAAM,qBAAqB,CAC3D,GAAI,CAAC,GAAW,EAAQ,SAAW,EACjC,MAAU,MAAM,0BAA0B,OAErC,EAAO,CACd,MAAU,MAAM,uBAAuB,EAAK,yBAAyB,aAAiB,MAAQ,EAAM,QAAU,OAAO,EAAM,GAAG,IAGjI,EAAa,gBAAiB,MAAO,CAAE,SAAQ,UAA0D,CACxG,GAAI,CAEF,GAAI,CADY,MAAM,EAAO,MAAM,CAEjC,MAAU,MAAM,oCAAoC,OAE/C,EAAO,CACd,MAAU,MAAM,2BAA2B,EAAK,yBAAyB,aAAiB,MAAQ,EAAM,QAAU,OAAO,EAAM,GAAG,IAGrI,EAAa,QAAS,MAAO,CAAE,SAAQ,UAAqD,CAC3F,GAAI,CAEF,GAAI,CADgB,MAAM,EAAO,aAAa,CAE5C,MAAU,MAAM,4BAA4B,OAEvC,EAAO,CACd,MAAU,MAAM,sBAAsB,EAAK,yBAAyB,aAAiB,MAAQ,EAAM,QAAU,OAAO,EAAM,GAAG,GAGlI"}
@@ -1,2 +1,2 @@
1
- const e=require(`../_virtual/rolldown_runtime.cjs`),t=require(`./health-check-handlers.cjs`),n=require(`./shutdown-handlers.cjs`),r=require(`../utils.cjs`);let i=require(`node:timers/promises`);var a=class{constructor(e){this.DEFAULT_CLIENT_OPTIONS={includeInLivenessCheck:!0,includeInReadinessCheck:!0},this.DEFAULT_SERVER_OPTIONS={runDefaultShutdownHandler:!0,name:`server`},this.DEFAULT_CONFIG={shutdownDelayMs:5e3,useExit0:!0,shouldSetupSignalHandlers:!0,livenessPeriodMs:1e4,readinessPeriodMs:5e3},this.isShuttingDown=!1,this.clientsMap=new Map,this.livenessChecks=[],this.readinessChecks=[],this.config={...this.DEFAULT_CONFIG,...e.config??{}},this.logger=e.logger,e.server&&this.attachServer(e.server),this.storeClientsWithDefaults(e),this.initializeHealthChecks(),this.config.shouldSetupSignalHandlers&&this.setupSignalHandlers()}storeClientsWithDefaults(e){let r=e.clients;r&&Object.entries(r).forEach(([e,r])=>{if(!r)return;let i=(Array.isArray(r)?r:[r]).map(r=>{let i={...this.DEFAULT_CLIENT_OPTIONS,name:e,...r.options},a=r.healthCheck??(()=>t.DEFAULT_CLIENT_HEALTHCHECK_FUNC_MAP[e]({client:r.connection,name:i.name})),o=r.shutdown??(t=>n.DEFAULT_CLIENT_SHUTDOWN_HANDLERS_MAP[e]({client:r.connection,signal:t,name:i.name}));return{connection:r.connection,healthCheck:a,shutdown:o,options:i}});this.clientsMap.set(e,i)})}setupSignalHandlers(){this.logger.info(`Setting up global signal listeners for graceful shutdown`),process.on(`SIGTERM`,async()=>{await this.shutdown(`SIGTERM`)}),process.on(`SIGINT`,async()=>{await this.shutdown(`SIGINT`)})}initializeHealthChecks(){this.clientsMap.forEach(e=>{e.forEach(e=>{e.healthCheck&&(e.options?.includeInLivenessCheck&&this.livenessChecks.push(e.healthCheck),e.options?.includeInReadinessCheck&&this.readinessChecks.push(e.healthCheck))})})}attachServer(e){this.server={server:e.server,shutdown:e.shutdown??(async()=>n.defaultServerShutdownHandler(e.server)),options:{...this.DEFAULT_SERVER_OPTIONS,...e.options??{}}}}async aliveCheck(){if(this.isShuttingDown)return{statusCode:200,status:`ok`};try{let e=Promise.all(this.livenessChecks.map(e=>e()));return await Promise.race([e,r.timeoutPromise(this.config.livenessPeriodMs??1e4,`Liveness check timed out`)]),{statusCode:200,status:`ok`}}catch(e){let t=e instanceof Error?e:Error(String(e));throw t.statusCode=503,t.status=`failed`,t}}async readyCheck(){if(this.isShuttingDown){let e=Error(`Application is shutting down`);throw e.statusCode=503,e.response={status:`failed`,message:`Application is shutting down`},e}try{let e=Promise.all(this.readinessChecks.map(e=>e()));return await Promise.race([e,r.timeoutPromise(this.config.readinessPeriodMs??5e3,`Readiness check timed out`)]),{statusCode:200,status:`ok`}}catch(e){let t=e instanceof Error?e:Error(String(e));throw t.statusCode=503,t.status=`failed`,t}}async shutdown(e=`MANUAL`){if(this.isShuttingDown){this.logger.warn(`Shutdown already in progress`);return}let{shutdownDelayMs:t,useExit0:n,onShutdown:r,beforeShutdown:a}=this.config;this.isShuttingDown=!0,this.logger.info(`${e} received: setting readiness state to false`),await a?.(),this.logger.info(`Waiting ${t}ms before shutdown...`),await(0,i.setTimeout)(t),this.logger.info(`Executing shutdown hooks...`);let o=[];this.clientsMap.forEach(e=>{e.forEach(e=>{e.shutdown&&o.push(e.shutdown())})}),await Promise.allSettled(o),await r?.(),this.server?.options?.runDefaultShutdownHandler&&(this.logger.info(`Shutting down server...`),await this.server?.shutdown?.()),this.logger.info(`Shutdown process completed`),n&&(this.logger.info(`Exiting process after ${e}`),process.exit(0))}};exports.HealthManager=a;
1
+ const e=require(`../_virtual/rolldown_runtime.cjs`),t=require(`./health-check-handlers.cjs`),n=require(`./shutdown-handlers.cjs`),r=require(`../utils.cjs`);let i=require(`node:timers/promises`),a=require(`@autofleet/errors`);var o=class{constructor(e){this.DEFAULT_CLIENT_OPTIONS={includeInLivenessCheck:!0,includeInReadinessCheck:!0},this.DEFAULT_SERVER_OPTIONS={runDefaultShutdownHandler:!0,name:`server`},this.DEFAULT_CONFIG={shutdownDelayMs:5e3,useExit0:!0,shouldSetupSignalHandlers:!0,livenessPeriodMs:1e4,readinessPeriodMs:5e3,enableLivenessOptimization:!0},this.isShuttingDown=!1,this.isReady=!1,this.clientsMap=new Map,this.livenessChecks=[],this.readinessChecks=[],this.config={...this.DEFAULT_CONFIG,...e.config??{}},this.logger=e.logger,e.server&&this.attachServer(e.server),this.storeClientsWithDefaults(e),this.initializeHealthChecks(),this.config.shouldSetupSignalHandlers&&this.setupSignalHandlers()}storeClientsWithDefaults(e){let r=e.clients;r&&Object.entries(r).forEach(([e,r])=>{if(!r)return;let i=(Array.isArray(r)?r:[r]).map(r=>{let i={...this.DEFAULT_CLIENT_OPTIONS,name:e,...r.options},a=t.DEFAULT_CLIENT_HEALTHCHECK_FUNC_MAP[e],o=r.healthCheck??(a?()=>a({client:r.connection,name:i.name}):void 0),s=n.DEFAULT_CLIENT_SHUTDOWN_HANDLERS_MAP[e],c=r.shutdown??(s?e=>s({client:r.connection,signal:e,name:i.name}):void 0);return{connection:r.connection,healthCheck:o,shutdown:c,options:i}});this.clientsMap.set(e,i)})}setupSignalHandlers(){this.logger.info(`Setting up global signal listeners for graceful shutdown`),process.on(`SIGTERM`,async()=>{await this.shutdown(`SIGTERM`)}),process.on(`SIGINT`,async()=>{await this.shutdown(`SIGINT`)})}initializeHealthChecks(){let e=!0;this.clientsMap.forEach(t=>{t.forEach(t=>{if(!t.healthCheck)return;let n=t.options?.includeInLivenessCheck??!0,r=t.options?.includeInReadinessCheck??!0;(!n||!r)&&(e=!1),n&&this.livenessChecks.push(t.healthCheck),r&&this.readinessChecks.push(t.healthCheck)})}),this.config.enableLivenessOptimization=this.config.enableLivenessOptimization&&e,this.config.enableLivenessOptimization&&(e?this.logger.info(`Liveness check optimization enabled - liveness checks will be skipped when pod is ready`):this.logger.warn(`Liveness check optimization disabled - not all clients are in both liveness and readiness checks`))}attachServer(e){this.server={server:e.server,shutdown:e.shutdown??(async()=>n.defaultServerShutdownHandler(e.server)),options:{...this.DEFAULT_SERVER_OPTIONS,...e.options??{}}}}async aliveCheck(){if(this.isShuttingDown||this.config.enableLivenessOptimization&&this.isReady)return{statusCode:200,status:`ok`};try{let e=Promise.all(this.livenessChecks.map(e=>e()));return await Promise.race([e,r.timeoutPromise(this.config.livenessPeriodMs??1e4,`Liveness check timed out`)]),{statusCode:200,status:`ok`}}catch(e){throw new a.ServiceUnavailableError(`Liveness probe failed: ${e instanceof Error?e.message:String(e)}`)}}async readyCheck(){if(this.isShuttingDown)throw this.isReady=!1,new a.ServiceUnavailableError(`Readiness probe failed: Application is shutting down`);try{let e=Promise.all(this.readinessChecks.map(e=>e()));return await Promise.race([e,r.timeoutPromise(this.config.readinessPeriodMs??5e3,`Readiness check timed out`)]),this.isReady=!0,{statusCode:200,status:`ok`}}catch(e){throw this.isReady=!1,new a.ServiceUnavailableError(`Readiness probe failed: ${e instanceof Error?e.message:String(e)}`)}}async shutdown(e=`MANUAL`){if(this.isShuttingDown){this.logger.warn(`Shutdown already in progress`);return}let{shutdownDelayMs:t,useExit0:n,onShutdown:r,beforeShutdown:a}=this.config;this.isShuttingDown=!0,this.isReady=!1,this.logger.info(`${e} received: setting readiness state to false`),await a?.(),this.logger.info(`Waiting ${t}ms before shutdown...`),await(0,i.setTimeout)(t);let o=[];this.clientsMap.forEach(e=>{e.forEach(e=>{e.shutdown&&o.push(e.options?.name||`unknown`)})}),this.logger.info(`Executing shutdown hooks...`,{clients:o});let s=[];this.clientsMap.forEach(t=>{t.forEach(t=>{t.shutdown&&s.push(t.shutdown(e))})}),await Promise.allSettled(s),await r?.(),this.server?.options?.runDefaultShutdownHandler&&(this.logger.info(`Shutting down server...`),await this.server?.shutdown?.()),this.logger.info(`Shutdown process completed`),n&&(this.logger.info(`Exiting process after ${e}`),process.exit(0))}};exports.HealthManager=o;
2
2
  //# sourceMappingURL=health-manager.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"health-manager.cjs","names":["client","DEFAULT_CLIENT_HEALTHCHECK_FUNC_MAP","DEFAULT_CLIENT_SHUTDOWN_HANDLERS_MAP","defaultServerShutdownHandler","timeoutPromise","err: any","error: any","shutdownPromises: Promise<any>[]"],"sources":["../../src/health-manager/health-manager.ts"],"sourcesContent":["import { setTimeout } from 'node:timers/promises';\nimport type { \n ClientConfig,\n HealthManagerConfig,\n HealthManagerOptions,\n ClientType,\n ServerConfig,\n} from './types';\nimport { DEFAULT_CLIENT_HEALTHCHECK_FUNC_MAP } from './health-check-handlers';\nimport { DEFAULT_CLIENT_SHUTDOWN_HANDLERS_MAP, defaultServerShutdownHandler } from './shutdown-handlers';\nimport { timeoutPromise } from '../utils';\n\n/**\n * Manages application health state and graceful shutdown for Kubernetes liveness/readiness probes\n *\n * Features:\n * - Provides /alive (liveness) and /ready (readiness) endpoint handlers\n * - Auto-registers SIGTERM/SIGINT handlers for graceful shutdown\n * - Manages ready state during shutdown to fail readiness probes\n */\nexport class HealthManager {\n private readonly DEFAULT_CLIENT_OPTIONS = {\n includeInLivenessCheck: true,\n includeInReadinessCheck: true,\n };\n\n private readonly DEFAULT_SERVER_OPTIONS = {\n runDefaultShutdownHandler: true,\n name: 'server',\n };\n\n private readonly DEFAULT_CONFIG: HealthManagerConfig = {\n shutdownDelayMs: 5_000,\n useExit0: true,\n shouldSetupSignalHandlers: true,\n livenessPeriodMs: 10_000,\n readinessPeriodMs: 5_000,\n };\n\n private isShuttingDown: boolean = false;\n\n private config: HealthManagerConfig;\n\n private clientsMap: Map<string, ClientConfig<any>[]> = new Map();\n private server: Required<ServerConfig> | undefined;\n\n private livenessChecks: Array<() => Promise<void>> = [];\n private readinessChecks: Array<() => Promise<void>> = [];\n\n private logger: any;\n\n /**\n * Creates a new HealthManager instance\n *\n * @param params Configuration options for health checks and shutdown behavior\n */\n constructor(params: HealthManagerOptions) {\n this.config = {\n ...this.DEFAULT_CONFIG,\n ...(params.config ?? {}),\n };\n\n this.logger = params.logger;\n if (params.server) {\n this.attachServer(params.server);\n }\n\n this.storeClientsWithDefaults(params);\n\n this.initializeHealthChecks();\n\n if (this.config.shouldSetupSignalHandlers) {\n this.setupSignalHandlers();\n }\n }\n\n /**\n * Store clients with default health check and shutdown functions\n * @private\n */\n private storeClientsWithDefaults(params: HealthManagerOptions): void {\n const clients = params.clients;\n if (!clients) {\n return;\n }\n\n Object.entries(clients).forEach(([type, client]) => {\n if (!client) {\n return;\n }\n\n const clientsArray = Array.isArray(client) ? client : [client];\n\n // Apply default values to each client config\n const clientsWithDefaults = clientsArray.map((client) => {\n const options = {\n ...this.DEFAULT_CLIENT_OPTIONS,\n name: type,\n ...client.options,\n };\n const healthCheck = client.healthCheck ?? (() => DEFAULT_CLIENT_HEALTHCHECK_FUNC_MAP[type as ClientType]({ client: client.connection, name: options.name }));\n\n const shutdown = client.shutdown ?? ((signal?: string) => DEFAULT_CLIENT_SHUTDOWN_HANDLERS_MAP[type as ClientType]({ client: client.connection, signal, name: options.name }));\n\n return {\n connection: client.connection,\n healthCheck,\n shutdown,\n options,\n };\n });\n\n this.clientsMap.set(type, clientsWithDefaults);\n });\n }\n\n /**\n * Register SIGTERM and SIGINT listeners handlers\n * @private\n */\n private setupSignalHandlers(): void {\n this.logger.info('Setting up global signal listeners for graceful shutdown');\n\n process.on('SIGTERM', async () => {\n await this.shutdown('SIGTERM');\n });\n\n process.on('SIGINT', async () => {\n await this.shutdown('SIGINT');\n });\n }\n\n /**\n * Initialize health check arrays based on configured clients\n * @private\n */\n private initializeHealthChecks(): void {\n this.clientsMap.forEach((clients) => {\n clients.forEach((client) => {\n if (!client.healthCheck) {\n return;\n }\n\n // Add to liveness checks if enabled (default: true)\n if (client.options?.includeInLivenessCheck) {\n this.livenessChecks.push(client.healthCheck);\n }\n\n // Add to readiness checks if enabled (default: true)\n if (client.options?.includeInReadinessCheck) {\n this.readinessChecks.push(client.healthCheck);\n }\n });\n });\n }\n\n /**\n * Attach an HTTP or HTTPS server to be managed during shutdown\n * @param serverConfig \n */\n attachServer(serverConfig: ServerConfig): void {\n this.server = {\n server: serverConfig.server,\n shutdown: serverConfig.shutdown ?? (async () => defaultServerShutdownHandler(serverConfig.server)),\n options: {\n ...this.DEFAULT_SERVER_OPTIONS,\n ...(serverConfig.options ?? {}),\n },\n };\n }\n\n /**\n * Performs health checks for all configured clients\n *\n * @returns Promise that resolves to { status: 'ok' } on success\n * @throws Error with statusCode 503 if any health check fails\n */\n async aliveCheck(): Promise<{ status: string, statusCode: number }> {\n if (this.isShuttingDown) {\n return { statusCode: 200, status: 'ok' };\n }\n\n try {\n const livenessPromises = Promise.all(this.livenessChecks.map(check => check()));\n\n await Promise.race([\n livenessPromises,\n timeoutPromise(this.config.livenessPeriodMs ?? 10_000, 'Liveness check timed out'),\n ]);\n\n return { statusCode: 200, status: 'ok' };\n } catch (error) {\n const err: any = error instanceof Error ? error : new Error(String(error));\n err.statusCode = 503;\n err.status = 'failed';\n throw err;\n }\n }\n\n /**\n * Performs readiness check\n *\n * @returns Promise that resolves to { status: 'ok' } if ready\n * @throws Error with statusCode 503 if not ready or health check fails\n */\n async readyCheck(): Promise<{ status: string, statusCode: number }> {\n if (this.isShuttingDown) {\n const error: any = new Error('Application is shutting down');\n error.statusCode = 503;\n error.response = {\n status: 'failed',\n message: 'Application is shutting down',\n };\n throw error;\n }\n\n try {\n const readinessPromises = Promise.all(this.readinessChecks.map(check => check()));\n\n await Promise.race([\n readinessPromises,\n timeoutPromise(this.config.readinessPeriodMs ?? 5_000, 'Readiness check timed out'),\n ]);\n\n return { statusCode: 200, status: 'ok' };\n } catch (error) {\n const err: any = error instanceof Error ? error : new Error(String(error));\n err.statusCode = 503;\n err.status = 'failed';\n throw err;\n }\n }\n\n /**\n * Manually trigger graceful shutdown\n * Can be called manually or via signal handlers\n *\n * @param signal - Signal name (e.g., 'SIGTERM', 'SIGINT', 'MANUAL')\n */\n async shutdown(signal: string = 'MANUAL'): Promise<void> {\n if (this.isShuttingDown) {\n this.logger.warn('Shutdown already in progress');\n return;\n }\n\n const { shutdownDelayMs, useExit0, onShutdown, beforeShutdown } = this.config;\n\n this.isShuttingDown = true;\n\n this.logger.info(`${signal} received: setting readiness state to false`);\n\n await beforeShutdown?.();\n\n this.logger.info(`Waiting ${shutdownDelayMs}ms before shutdown...`);\n await setTimeout(shutdownDelayMs)\n\n this.logger.info('Executing shutdown hooks...');\n\n const shutdownPromises: Promise<any>[] = [];\n this.clientsMap.forEach((clients) => {\n clients.forEach((client) => {\n client.shutdown ? shutdownPromises.push(client.shutdown()) : undefined;\n });\n });\n\n await Promise.allSettled(shutdownPromises);\n\n await onShutdown?.();\n\n if (this.server?.options?.runDefaultShutdownHandler) {\n this.logger.info('Shutting down server...');\n await this.server?.shutdown?.();\n }\n\n this.logger.info('Shutdown process completed');\n\n if (useExit0) {\n this.logger.info(`Exiting process after ${signal}`);\n process.exit(0);\n }\n }\n}"],"mappings":"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"}
1
+ {"version":3,"file":"health-manager.cjs","names":["client","DEFAULT_CLIENT_HEALTHCHECK_FUNC_MAP","DEFAULT_CLIENT_SHUTDOWN_HANDLERS_MAP","defaultServerShutdownHandler","timeoutPromise","ServiceUnavailableError","clientNames: string[]","shutdownPromises: Promise<any>[]"],"sources":["../../src/health-manager/health-manager.ts"],"sourcesContent":["import { setTimeout } from 'node:timers/promises';\nimport { ServiceUnavailableError } from '@autofleet/errors';\nimport type {\n ClientConfig,\n HealthManagerConfig,\n HealthManagerOptions,\n ClientType,\n ServerConfig,\n} from './types';\nimport { DEFAULT_CLIENT_HEALTHCHECK_FUNC_MAP } from './health-check-handlers';\nimport { DEFAULT_CLIENT_SHUTDOWN_HANDLERS_MAP, defaultServerShutdownHandler } from './shutdown-handlers';\nimport { timeoutPromise } from '../utils';\n\n/**\n * Manages application health state and graceful shutdown for Kubernetes liveness/readiness probes\n *\n * Features:\n * - Provides aliveCheck() and readyCheck() methods for liveness/readiness probes\n * - Auto-registers SIGTERM/SIGINT handlers for graceful shutdown\n * - Manages ready state during shutdown to fail readiness probes\n */\nexport class HealthManager {\n private readonly DEFAULT_CLIENT_OPTIONS = {\n includeInLivenessCheck: true,\n includeInReadinessCheck: true,\n };\n\n private readonly DEFAULT_SERVER_OPTIONS = {\n runDefaultShutdownHandler: true,\n name: 'server',\n };\n\n private readonly DEFAULT_CONFIG: HealthManagerConfig = {\n shutdownDelayMs: 5_000,\n useExit0: true,\n shouldSetupSignalHandlers: true,\n livenessPeriodMs: 10_000,\n readinessPeriodMs: 5_000,\n enableLivenessOptimization: true,\n };\n\n private isShuttingDown: boolean = false;\n private isReady: boolean = false;\n\n private config: HealthManagerConfig;\n\n private clientsMap: Map<string, ClientConfig<any>[]> = new Map();\n private server: Required<ServerConfig> | undefined;\n\n private livenessChecks: Array<() => Promise<void>> = [];\n private readinessChecks: Array<() => Promise<void>> = [];\n\n private logger: any;\n\n /**\n * Creates a new HealthManager instance\n *\n * @param params Configuration options for health checks and shutdown behavior\n */\n constructor(params: HealthManagerOptions) {\n this.config = {\n ...this.DEFAULT_CONFIG,\n ...(params.config ?? {}),\n };\n\n this.logger = params.logger;\n if (params.server) {\n this.attachServer(params.server);\n }\n\n this.storeClientsWithDefaults(params);\n\n this.initializeHealthChecks();\n\n if (this.config.shouldSetupSignalHandlers) {\n this.setupSignalHandlers();\n }\n }\n\n /**\n * Store clients with default health check and shutdown functions\n * @private\n */\n private storeClientsWithDefaults(params: HealthManagerOptions): void {\n const clients = params.clients;\n if (!clients) {\n return;\n }\n\n Object.entries(clients).forEach(([type, client]) => {\n if (!client) {\n return;\n }\n\n const clientsArray = Array.isArray(client) ? client : [client];\n\n // Apply default values to each client config\n const clientsWithDefaults = clientsArray.map((client) => {\n const options = {\n ...this.DEFAULT_CLIENT_OPTIONS,\n name: type,\n ...client.options,\n };\n\n const defaultHealthCheck = DEFAULT_CLIENT_HEALTHCHECK_FUNC_MAP[type as ClientType];\n const healthCheck = client.healthCheck ?? (defaultHealthCheck ? () => defaultHealthCheck({ client: client.connection, name: options.name }) : undefined);\n\n const defaultShutdown = DEFAULT_CLIENT_SHUTDOWN_HANDLERS_MAP[type as ClientType];\n const shutdown = client.shutdown ?? (defaultShutdown ? (signal?: string) => defaultShutdown({ client: client.connection, signal, name: options.name }) : undefined);\n\n return {\n connection: client.connection,\n healthCheck,\n shutdown,\n options,\n };\n });\n\n this.clientsMap.set(type, clientsWithDefaults);\n });\n }\n\n /**\n * Register SIGTERM and SIGINT signal listeners\n * @private\n */\n private setupSignalHandlers(): void {\n this.logger.info('Setting up global signal listeners for graceful shutdown');\n\n process.on('SIGTERM', async () => {\n await this.shutdown('SIGTERM');\n });\n\n process.on('SIGINT', async () => {\n await this.shutdown('SIGINT');\n });\n }\n\n /**\n * Initialize health check arrays based on configured clients\n * @private\n */\n private initializeHealthChecks(): void {\n let allClientsInBothChecks = true;\n\n this.clientsMap.forEach((clients) => {\n clients.forEach((client) => {\n if (!client.healthCheck) {\n return;\n }\n\n const includeInLiveness = client.options?.includeInLivenessCheck ?? true;\n const includeInReadiness = client.options?.includeInReadinessCheck ?? true;\n\n // Check if all clients are included in both checks\n if (!includeInLiveness || !includeInReadiness) {\n allClientsInBothChecks = false;\n }\n\n if (includeInLiveness) {\n this.livenessChecks.push(client.healthCheck);\n }\n\n if (includeInReadiness) {\n this.readinessChecks.push(client.healthCheck);\n }\n });\n });\n\n // Safety mechanism: Only enable optimization if all clients are in both checks to prevent skipping liveness checks for clients that aren't in readiness\n this.config.enableLivenessOptimization = this.config.enableLivenessOptimization && allClientsInBothChecks;\n\n if (this.config.enableLivenessOptimization) {\n if (!allClientsInBothChecks) {\n this.logger.warn('Liveness check optimization disabled - not all clients are in both liveness and readiness checks');\n } else {\n this.logger.info('Liveness check optimization enabled - liveness checks will be skipped when pod is ready');\n }\n }\n }\n\n /**\n * Attach an HTTP or HTTPS server to be managed during shutdown\n * @param serverConfig Server configuration including the server instance and optional shutdown handler\n */\n attachServer(serverConfig: ServerConfig): void {\n this.server = {\n server: serverConfig.server,\n shutdown: serverConfig.shutdown ?? (async () => defaultServerShutdownHandler(serverConfig.server)),\n options: {\n ...this.DEFAULT_SERVER_OPTIONS,\n ...(serverConfig.options ?? {}),\n },\n };\n }\n\n /**\n * Performs liveness health checks for clients configured with includeInLivenessCheck\n *\n * @returns Promise that resolves to { status: 'ok', statusCode: 200 } on success\n * @throws ServiceUnavailableError with statusCode 503 if any health check fails\n */\n async aliveCheck(): Promise<{ status: string, statusCode: number }> {\n if (this.isShuttingDown) {\n return { statusCode: 200, status: 'ok' };\n }\n\n // Skip liveness check if optimization is enabled and pod is already ready\n if (this.config.enableLivenessOptimization && this.isReady) {\n return { statusCode: 200, status: 'ok' };\n }\n\n try {\n const livenessPromises = Promise.all(this.livenessChecks.map(check => check()));\n\n await Promise.race([\n livenessPromises,\n timeoutPromise(this.config.livenessPeriodMs ?? 10_000, 'Liveness check timed out'),\n ]);\n\n return { statusCode: 200, status: 'ok' };\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n throw new ServiceUnavailableError(`Liveness probe failed: ${message}`);\n }\n }\n\n /**\n * Performs readiness health checks for clients configured with includeInReadinessCheck\n *\n * @returns Promise that resolves to { status: 'ok', statusCode: 200 } if ready\n * @throws ServiceUnavailableError with statusCode 503 if not ready or health check fails\n */\n async readyCheck(): Promise<{ status: string, statusCode: number }> {\n if (this.isShuttingDown) {\n this.isReady = false;\n throw new ServiceUnavailableError('Readiness probe failed: Application is shutting down');\n }\n\n try {\n const readinessPromises = Promise.all(this.readinessChecks.map(check => check()));\n\n await Promise.race([\n readinessPromises,\n timeoutPromise(this.config.readinessPeriodMs ?? 5_000, 'Readiness check timed out'),\n ]);\n this.isReady = true;\n\n return { statusCode: 200, status: 'ok' };\n } catch (error) {\n this.isReady = false;\n const message = error instanceof Error ? error.message : String(error);\n throw new ServiceUnavailableError(`Readiness probe failed: ${message}`);\n }\n }\n\n /**\n * Triggers graceful shutdown process\n * Can be called manually or automatically via SIGTERM/SIGINT signal handlers\n *\n * @param signal - Signal name (e.g., 'SIGTERM', 'SIGINT', 'MANUAL')\n */\n async shutdown(signal: string = 'MANUAL'): Promise<void> {\n if (this.isShuttingDown) {\n this.logger.warn('Shutdown already in progress');\n return;\n }\n\n const { shutdownDelayMs, useExit0, onShutdown, beforeShutdown } = this.config;\n\n this.isShuttingDown = true;\n this.isReady = false;\n\n this.logger.info(`${signal} received: setting readiness state to false`);\n\n await beforeShutdown?.();\n\n this.logger.info(`Waiting ${shutdownDelayMs}ms before shutdown...`);\n await setTimeout(shutdownDelayMs)\n\n // Collect client names that have shutdown handlers\n const clientNames: string[] = [];\n this.clientsMap.forEach((clients) => {\n clients.forEach((client) => {\n if (client.shutdown) {\n clientNames.push(client.options?.name || 'unknown');\n }\n });\n });\n\n this.logger.info('Executing shutdown hooks...', { clients: clientNames });\n\n const shutdownPromises: Promise<any>[] = [];\n this.clientsMap.forEach((clients) => {\n clients.forEach((client) => {\n if (client.shutdown) {\n shutdownPromises.push(client.shutdown(signal));\n }\n });\n });\n\n await Promise.allSettled(shutdownPromises);\n\n await onShutdown?.();\n\n if (this.server?.options?.runDefaultShutdownHandler) {\n this.logger.info('Shutting down server...');\n await this.server?.shutdown?.();\n }\n\n this.logger.info('Shutdown process completed');\n\n if (useExit0) {\n this.logger.info(`Exiting process after ${signal}`);\n process.exit(0);\n }\n }\n}"],"mappings":"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"}
@@ -6,7 +6,7 @@ import { HealthManagerOptions, ServerConfig } from "./types.cjs";
6
6
  * Manages application health state and graceful shutdown for Kubernetes liveness/readiness probes
7
7
  *
8
8
  * Features:
9
- * - Provides /alive (liveness) and /ready (readiness) endpoint handlers
9
+ * - Provides aliveCheck() and readyCheck() methods for liveness/readiness probes
10
10
  * - Auto-registers SIGTERM/SIGINT handlers for graceful shutdown
11
11
  * - Manages ready state during shutdown to fail readiness probes
12
12
  */
@@ -15,6 +15,7 @@ declare class HealthManager {
15
15
  private readonly DEFAULT_SERVER_OPTIONS;
16
16
  private readonly DEFAULT_CONFIG;
17
17
  private isShuttingDown;
18
+ private isReady;
18
19
  private config;
19
20
  private clientsMap;
20
21
  private server;
@@ -33,7 +34,7 @@ declare class HealthManager {
33
34
  */
34
35
  private storeClientsWithDefaults;
35
36
  /**
36
- * Register SIGTERM and SIGINT listeners handlers
37
+ * Register SIGTERM and SIGINT signal listeners
37
38
  * @private
38
39
  */
39
40
  private setupSignalHandlers;
@@ -44,32 +45,32 @@ declare class HealthManager {
44
45
  private initializeHealthChecks;
45
46
  /**
46
47
  * Attach an HTTP or HTTPS server to be managed during shutdown
47
- * @param serverConfig
48
+ * @param serverConfig Server configuration including the server instance and optional shutdown handler
48
49
  */
49
50
  attachServer(serverConfig: ServerConfig): void;
50
51
  /**
51
- * Performs health checks for all configured clients
52
+ * Performs liveness health checks for clients configured with includeInLivenessCheck
52
53
  *
53
- * @returns Promise that resolves to { status: 'ok' } on success
54
- * @throws Error with statusCode 503 if any health check fails
54
+ * @returns Promise that resolves to { status: 'ok', statusCode: 200 } on success
55
+ * @throws ServiceUnavailableError with statusCode 503 if any health check fails
55
56
  */
56
57
  aliveCheck(): Promise<{
57
58
  status: string;
58
59
  statusCode: number;
59
60
  }>;
60
61
  /**
61
- * Performs readiness check
62
+ * Performs readiness health checks for clients configured with includeInReadinessCheck
62
63
  *
63
- * @returns Promise that resolves to { status: 'ok' } if ready
64
- * @throws Error with statusCode 503 if not ready or health check fails
64
+ * @returns Promise that resolves to { status: 'ok', statusCode: 200 } if ready
65
+ * @throws ServiceUnavailableError with statusCode 503 if not ready or health check fails
65
66
  */
66
67
  readyCheck(): Promise<{
67
68
  status: string;
68
69
  statusCode: number;
69
70
  }>;
70
71
  /**
71
- * Manually trigger graceful shutdown
72
- * Can be called manually or via signal handlers
72
+ * Triggers graceful shutdown process
73
+ * Can be called manually or automatically via SIGTERM/SIGINT signal handlers
73
74
  *
74
75
  * @param signal - Signal name (e.g., 'SIGTERM', 'SIGINT', 'MANUAL')
75
76
  */
@@ -6,7 +6,7 @@ import { HealthManagerOptions, ServerConfig } from "./types.js";
6
6
  * Manages application health state and graceful shutdown for Kubernetes liveness/readiness probes
7
7
  *
8
8
  * Features:
9
- * - Provides /alive (liveness) and /ready (readiness) endpoint handlers
9
+ * - Provides aliveCheck() and readyCheck() methods for liveness/readiness probes
10
10
  * - Auto-registers SIGTERM/SIGINT handlers for graceful shutdown
11
11
  * - Manages ready state during shutdown to fail readiness probes
12
12
  */
@@ -15,6 +15,7 @@ declare class HealthManager {
15
15
  private readonly DEFAULT_SERVER_OPTIONS;
16
16
  private readonly DEFAULT_CONFIG;
17
17
  private isShuttingDown;
18
+ private isReady;
18
19
  private config;
19
20
  private clientsMap;
20
21
  private server;
@@ -33,7 +34,7 @@ declare class HealthManager {
33
34
  */
34
35
  private storeClientsWithDefaults;
35
36
  /**
36
- * Register SIGTERM and SIGINT listeners handlers
37
+ * Register SIGTERM and SIGINT signal listeners
37
38
  * @private
38
39
  */
39
40
  private setupSignalHandlers;
@@ -44,32 +45,32 @@ declare class HealthManager {
44
45
  private initializeHealthChecks;
45
46
  /**
46
47
  * Attach an HTTP or HTTPS server to be managed during shutdown
47
- * @param serverConfig
48
+ * @param serverConfig Server configuration including the server instance and optional shutdown handler
48
49
  */
49
50
  attachServer(serverConfig: ServerConfig): void;
50
51
  /**
51
- * Performs health checks for all configured clients
52
+ * Performs liveness health checks for clients configured with includeInLivenessCheck
52
53
  *
53
- * @returns Promise that resolves to { status: 'ok' } on success
54
- * @throws Error with statusCode 503 if any health check fails
54
+ * @returns Promise that resolves to { status: 'ok', statusCode: 200 } on success
55
+ * @throws ServiceUnavailableError with statusCode 503 if any health check fails
55
56
  */
56
57
  aliveCheck(): Promise<{
57
58
  status: string;
58
59
  statusCode: number;
59
60
  }>;
60
61
  /**
61
- * Performs readiness check
62
+ * Performs readiness health checks for clients configured with includeInReadinessCheck
62
63
  *
63
- * @returns Promise that resolves to { status: 'ok' } if ready
64
- * @throws Error with statusCode 503 if not ready or health check fails
64
+ * @returns Promise that resolves to { status: 'ok', statusCode: 200 } if ready
65
+ * @throws ServiceUnavailableError with statusCode 503 if not ready or health check fails
65
66
  */
66
67
  readyCheck(): Promise<{
67
68
  status: string;
68
69
  statusCode: number;
69
70
  }>;
70
71
  /**
71
- * Manually trigger graceful shutdown
72
- * Can be called manually or via signal handlers
72
+ * Triggers graceful shutdown process
73
+ * Can be called manually or automatically via SIGTERM/SIGINT signal handlers
73
74
  *
74
75
  * @param signal - Signal name (e.g., 'SIGTERM', 'SIGINT', 'MANUAL')
75
76
  */
@@ -1,2 +1,2 @@
1
- import{DEFAULT_CLIENT_HEALTHCHECK_FUNC_MAP as e}from"./health-check-handlers.js";import{DEFAULT_CLIENT_SHUTDOWN_HANDLERS_MAP as t,defaultServerShutdownHandler as n}from"./shutdown-handlers.js";import{timeoutPromise as r}from"../utils.js";import{setTimeout as i}from"node:timers/promises";var a=class{constructor(e){this.DEFAULT_CLIENT_OPTIONS={includeInLivenessCheck:!0,includeInReadinessCheck:!0},this.DEFAULT_SERVER_OPTIONS={runDefaultShutdownHandler:!0,name:`server`},this.DEFAULT_CONFIG={shutdownDelayMs:5e3,useExit0:!0,shouldSetupSignalHandlers:!0,livenessPeriodMs:1e4,readinessPeriodMs:5e3},this.isShuttingDown=!1,this.clientsMap=new Map,this.livenessChecks=[],this.readinessChecks=[],this.config={...this.DEFAULT_CONFIG,...e.config??{}},this.logger=e.logger,e.server&&this.attachServer(e.server),this.storeClientsWithDefaults(e),this.initializeHealthChecks(),this.config.shouldSetupSignalHandlers&&this.setupSignalHandlers()}storeClientsWithDefaults(n){let r=n.clients;r&&Object.entries(r).forEach(([n,r])=>{if(!r)return;let i=(Array.isArray(r)?r:[r]).map(r=>{let i={...this.DEFAULT_CLIENT_OPTIONS,name:n,...r.options},a=r.healthCheck??(()=>e[n]({client:r.connection,name:i.name})),o=r.shutdown??(e=>t[n]({client:r.connection,signal:e,name:i.name}));return{connection:r.connection,healthCheck:a,shutdown:o,options:i}});this.clientsMap.set(n,i)})}setupSignalHandlers(){this.logger.info(`Setting up global signal listeners for graceful shutdown`),process.on(`SIGTERM`,async()=>{await this.shutdown(`SIGTERM`)}),process.on(`SIGINT`,async()=>{await this.shutdown(`SIGINT`)})}initializeHealthChecks(){this.clientsMap.forEach(e=>{e.forEach(e=>{e.healthCheck&&(e.options?.includeInLivenessCheck&&this.livenessChecks.push(e.healthCheck),e.options?.includeInReadinessCheck&&this.readinessChecks.push(e.healthCheck))})})}attachServer(e){this.server={server:e.server,shutdown:e.shutdown??(async()=>n(e.server)),options:{...this.DEFAULT_SERVER_OPTIONS,...e.options??{}}}}async aliveCheck(){if(this.isShuttingDown)return{statusCode:200,status:`ok`};try{let e=Promise.all(this.livenessChecks.map(e=>e()));return await Promise.race([e,r(this.config.livenessPeriodMs??1e4,`Liveness check timed out`)]),{statusCode:200,status:`ok`}}catch(e){let t=e instanceof Error?e:Error(String(e));throw t.statusCode=503,t.status=`failed`,t}}async readyCheck(){if(this.isShuttingDown){let e=Error(`Application is shutting down`);throw e.statusCode=503,e.response={status:`failed`,message:`Application is shutting down`},e}try{let e=Promise.all(this.readinessChecks.map(e=>e()));return await Promise.race([e,r(this.config.readinessPeriodMs??5e3,`Readiness check timed out`)]),{statusCode:200,status:`ok`}}catch(e){let t=e instanceof Error?e:Error(String(e));throw t.statusCode=503,t.status=`failed`,t}}async shutdown(e=`MANUAL`){if(this.isShuttingDown){this.logger.warn(`Shutdown already in progress`);return}let{shutdownDelayMs:t,useExit0:n,onShutdown:r,beforeShutdown:a}=this.config;this.isShuttingDown=!0,this.logger.info(`${e} received: setting readiness state to false`),await a?.(),this.logger.info(`Waiting ${t}ms before shutdown...`),await i(t),this.logger.info(`Executing shutdown hooks...`);let o=[];this.clientsMap.forEach(e=>{e.forEach(e=>{e.shutdown&&o.push(e.shutdown())})}),await Promise.allSettled(o),await r?.(),this.server?.options?.runDefaultShutdownHandler&&(this.logger.info(`Shutting down server...`),await this.server?.shutdown?.()),this.logger.info(`Shutdown process completed`),n&&(this.logger.info(`Exiting process after ${e}`),process.exit(0))}};export{a as HealthManager};
1
+ import{DEFAULT_CLIENT_HEALTHCHECK_FUNC_MAP as e}from"./health-check-handlers.js";import{DEFAULT_CLIENT_SHUTDOWN_HANDLERS_MAP as t,defaultServerShutdownHandler as n}from"./shutdown-handlers.js";import{timeoutPromise as r}from"../utils.js";import{setTimeout as i}from"node:timers/promises";import{ServiceUnavailableError as a}from"@autofleet/errors";var o=class{constructor(e){this.DEFAULT_CLIENT_OPTIONS={includeInLivenessCheck:!0,includeInReadinessCheck:!0},this.DEFAULT_SERVER_OPTIONS={runDefaultShutdownHandler:!0,name:`server`},this.DEFAULT_CONFIG={shutdownDelayMs:5e3,useExit0:!0,shouldSetupSignalHandlers:!0,livenessPeriodMs:1e4,readinessPeriodMs:5e3,enableLivenessOptimization:!0},this.isShuttingDown=!1,this.isReady=!1,this.clientsMap=new Map,this.livenessChecks=[],this.readinessChecks=[],this.config={...this.DEFAULT_CONFIG,...e.config??{}},this.logger=e.logger,e.server&&this.attachServer(e.server),this.storeClientsWithDefaults(e),this.initializeHealthChecks(),this.config.shouldSetupSignalHandlers&&this.setupSignalHandlers()}storeClientsWithDefaults(n){let r=n.clients;r&&Object.entries(r).forEach(([n,r])=>{if(!r)return;let i=(Array.isArray(r)?r:[r]).map(r=>{let i={...this.DEFAULT_CLIENT_OPTIONS,name:n,...r.options},a=e[n],o=r.healthCheck??(a?()=>a({client:r.connection,name:i.name}):void 0),s=t[n],c=r.shutdown??(s?e=>s({client:r.connection,signal:e,name:i.name}):void 0);return{connection:r.connection,healthCheck:o,shutdown:c,options:i}});this.clientsMap.set(n,i)})}setupSignalHandlers(){this.logger.info(`Setting up global signal listeners for graceful shutdown`),process.on(`SIGTERM`,async()=>{await this.shutdown(`SIGTERM`)}),process.on(`SIGINT`,async()=>{await this.shutdown(`SIGINT`)})}initializeHealthChecks(){let e=!0;this.clientsMap.forEach(t=>{t.forEach(t=>{if(!t.healthCheck)return;let n=t.options?.includeInLivenessCheck??!0,r=t.options?.includeInReadinessCheck??!0;(!n||!r)&&(e=!1),n&&this.livenessChecks.push(t.healthCheck),r&&this.readinessChecks.push(t.healthCheck)})}),this.config.enableLivenessOptimization=this.config.enableLivenessOptimization&&e,this.config.enableLivenessOptimization&&(e?this.logger.info(`Liveness check optimization enabled - liveness checks will be skipped when pod is ready`):this.logger.warn(`Liveness check optimization disabled - not all clients are in both liveness and readiness checks`))}attachServer(e){this.server={server:e.server,shutdown:e.shutdown??(async()=>n(e.server)),options:{...this.DEFAULT_SERVER_OPTIONS,...e.options??{}}}}async aliveCheck(){if(this.isShuttingDown||this.config.enableLivenessOptimization&&this.isReady)return{statusCode:200,status:`ok`};try{let e=Promise.all(this.livenessChecks.map(e=>e()));return await Promise.race([e,r(this.config.livenessPeriodMs??1e4,`Liveness check timed out`)]),{statusCode:200,status:`ok`}}catch(e){throw new a(`Liveness probe failed: ${e instanceof Error?e.message:String(e)}`)}}async readyCheck(){if(this.isShuttingDown)throw this.isReady=!1,new a(`Readiness probe failed: Application is shutting down`);try{let e=Promise.all(this.readinessChecks.map(e=>e()));return await Promise.race([e,r(this.config.readinessPeriodMs??5e3,`Readiness check timed out`)]),this.isReady=!0,{statusCode:200,status:`ok`}}catch(e){throw this.isReady=!1,new a(`Readiness probe failed: ${e instanceof Error?e.message:String(e)}`)}}async shutdown(e=`MANUAL`){if(this.isShuttingDown){this.logger.warn(`Shutdown already in progress`);return}let{shutdownDelayMs:t,useExit0:n,onShutdown:r,beforeShutdown:a}=this.config;this.isShuttingDown=!0,this.isReady=!1,this.logger.info(`${e} received: setting readiness state to false`),await a?.(),this.logger.info(`Waiting ${t}ms before shutdown...`),await i(t);let o=[];this.clientsMap.forEach(e=>{e.forEach(e=>{e.shutdown&&o.push(e.options?.name||`unknown`)})}),this.logger.info(`Executing shutdown hooks...`,{clients:o});let s=[];this.clientsMap.forEach(t=>{t.forEach(t=>{t.shutdown&&s.push(t.shutdown(e))})}),await Promise.allSettled(s),await r?.(),this.server?.options?.runDefaultShutdownHandler&&(this.logger.info(`Shutting down server...`),await this.server?.shutdown?.()),this.logger.info(`Shutdown process completed`),n&&(this.logger.info(`Exiting process after ${e}`),process.exit(0))}};export{o as HealthManager};
2
2
  //# sourceMappingURL=health-manager.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"health-manager.js","names":["client","err: any","error: any","shutdownPromises: Promise<any>[]"],"sources":["../../src/health-manager/health-manager.ts"],"sourcesContent":["import { setTimeout } from 'node:timers/promises';\nimport type { \n ClientConfig,\n HealthManagerConfig,\n HealthManagerOptions,\n ClientType,\n ServerConfig,\n} from './types';\nimport { DEFAULT_CLIENT_HEALTHCHECK_FUNC_MAP } from './health-check-handlers';\nimport { DEFAULT_CLIENT_SHUTDOWN_HANDLERS_MAP, defaultServerShutdownHandler } from './shutdown-handlers';\nimport { timeoutPromise } from '../utils';\n\n/**\n * Manages application health state and graceful shutdown for Kubernetes liveness/readiness probes\n *\n * Features:\n * - Provides /alive (liveness) and /ready (readiness) endpoint handlers\n * - Auto-registers SIGTERM/SIGINT handlers for graceful shutdown\n * - Manages ready state during shutdown to fail readiness probes\n */\nexport class HealthManager {\n private readonly DEFAULT_CLIENT_OPTIONS = {\n includeInLivenessCheck: true,\n includeInReadinessCheck: true,\n };\n\n private readonly DEFAULT_SERVER_OPTIONS = {\n runDefaultShutdownHandler: true,\n name: 'server',\n };\n\n private readonly DEFAULT_CONFIG: HealthManagerConfig = {\n shutdownDelayMs: 5_000,\n useExit0: true,\n shouldSetupSignalHandlers: true,\n livenessPeriodMs: 10_000,\n readinessPeriodMs: 5_000,\n };\n\n private isShuttingDown: boolean = false;\n\n private config: HealthManagerConfig;\n\n private clientsMap: Map<string, ClientConfig<any>[]> = new Map();\n private server: Required<ServerConfig> | undefined;\n\n private livenessChecks: Array<() => Promise<void>> = [];\n private readinessChecks: Array<() => Promise<void>> = [];\n\n private logger: any;\n\n /**\n * Creates a new HealthManager instance\n *\n * @param params Configuration options for health checks and shutdown behavior\n */\n constructor(params: HealthManagerOptions) {\n this.config = {\n ...this.DEFAULT_CONFIG,\n ...(params.config ?? {}),\n };\n\n this.logger = params.logger;\n if (params.server) {\n this.attachServer(params.server);\n }\n\n this.storeClientsWithDefaults(params);\n\n this.initializeHealthChecks();\n\n if (this.config.shouldSetupSignalHandlers) {\n this.setupSignalHandlers();\n }\n }\n\n /**\n * Store clients with default health check and shutdown functions\n * @private\n */\n private storeClientsWithDefaults(params: HealthManagerOptions): void {\n const clients = params.clients;\n if (!clients) {\n return;\n }\n\n Object.entries(clients).forEach(([type, client]) => {\n if (!client) {\n return;\n }\n\n const clientsArray = Array.isArray(client) ? client : [client];\n\n // Apply default values to each client config\n const clientsWithDefaults = clientsArray.map((client) => {\n const options = {\n ...this.DEFAULT_CLIENT_OPTIONS,\n name: type,\n ...client.options,\n };\n const healthCheck = client.healthCheck ?? (() => DEFAULT_CLIENT_HEALTHCHECK_FUNC_MAP[type as ClientType]({ client: client.connection, name: options.name }));\n\n const shutdown = client.shutdown ?? ((signal?: string) => DEFAULT_CLIENT_SHUTDOWN_HANDLERS_MAP[type as ClientType]({ client: client.connection, signal, name: options.name }));\n\n return {\n connection: client.connection,\n healthCheck,\n shutdown,\n options,\n };\n });\n\n this.clientsMap.set(type, clientsWithDefaults);\n });\n }\n\n /**\n * Register SIGTERM and SIGINT listeners handlers\n * @private\n */\n private setupSignalHandlers(): void {\n this.logger.info('Setting up global signal listeners for graceful shutdown');\n\n process.on('SIGTERM', async () => {\n await this.shutdown('SIGTERM');\n });\n\n process.on('SIGINT', async () => {\n await this.shutdown('SIGINT');\n });\n }\n\n /**\n * Initialize health check arrays based on configured clients\n * @private\n */\n private initializeHealthChecks(): void {\n this.clientsMap.forEach((clients) => {\n clients.forEach((client) => {\n if (!client.healthCheck) {\n return;\n }\n\n // Add to liveness checks if enabled (default: true)\n if (client.options?.includeInLivenessCheck) {\n this.livenessChecks.push(client.healthCheck);\n }\n\n // Add to readiness checks if enabled (default: true)\n if (client.options?.includeInReadinessCheck) {\n this.readinessChecks.push(client.healthCheck);\n }\n });\n });\n }\n\n /**\n * Attach an HTTP or HTTPS server to be managed during shutdown\n * @param serverConfig \n */\n attachServer(serverConfig: ServerConfig): void {\n this.server = {\n server: serverConfig.server,\n shutdown: serverConfig.shutdown ?? (async () => defaultServerShutdownHandler(serverConfig.server)),\n options: {\n ...this.DEFAULT_SERVER_OPTIONS,\n ...(serverConfig.options ?? {}),\n },\n };\n }\n\n /**\n * Performs health checks for all configured clients\n *\n * @returns Promise that resolves to { status: 'ok' } on success\n * @throws Error with statusCode 503 if any health check fails\n */\n async aliveCheck(): Promise<{ status: string, statusCode: number }> {\n if (this.isShuttingDown) {\n return { statusCode: 200, status: 'ok' };\n }\n\n try {\n const livenessPromises = Promise.all(this.livenessChecks.map(check => check()));\n\n await Promise.race([\n livenessPromises,\n timeoutPromise(this.config.livenessPeriodMs ?? 10_000, 'Liveness check timed out'),\n ]);\n\n return { statusCode: 200, status: 'ok' };\n } catch (error) {\n const err: any = error instanceof Error ? error : new Error(String(error));\n err.statusCode = 503;\n err.status = 'failed';\n throw err;\n }\n }\n\n /**\n * Performs readiness check\n *\n * @returns Promise that resolves to { status: 'ok' } if ready\n * @throws Error with statusCode 503 if not ready or health check fails\n */\n async readyCheck(): Promise<{ status: string, statusCode: number }> {\n if (this.isShuttingDown) {\n const error: any = new Error('Application is shutting down');\n error.statusCode = 503;\n error.response = {\n status: 'failed',\n message: 'Application is shutting down',\n };\n throw error;\n }\n\n try {\n const readinessPromises = Promise.all(this.readinessChecks.map(check => check()));\n\n await Promise.race([\n readinessPromises,\n timeoutPromise(this.config.readinessPeriodMs ?? 5_000, 'Readiness check timed out'),\n ]);\n\n return { statusCode: 200, status: 'ok' };\n } catch (error) {\n const err: any = error instanceof Error ? error : new Error(String(error));\n err.statusCode = 503;\n err.status = 'failed';\n throw err;\n }\n }\n\n /**\n * Manually trigger graceful shutdown\n * Can be called manually or via signal handlers\n *\n * @param signal - Signal name (e.g., 'SIGTERM', 'SIGINT', 'MANUAL')\n */\n async shutdown(signal: string = 'MANUAL'): Promise<void> {\n if (this.isShuttingDown) {\n this.logger.warn('Shutdown already in progress');\n return;\n }\n\n const { shutdownDelayMs, useExit0, onShutdown, beforeShutdown } = this.config;\n\n this.isShuttingDown = true;\n\n this.logger.info(`${signal} received: setting readiness state to false`);\n\n await beforeShutdown?.();\n\n this.logger.info(`Waiting ${shutdownDelayMs}ms before shutdown...`);\n await setTimeout(shutdownDelayMs)\n\n this.logger.info('Executing shutdown hooks...');\n\n const shutdownPromises: Promise<any>[] = [];\n this.clientsMap.forEach((clients) => {\n clients.forEach((client) => {\n client.shutdown ? shutdownPromises.push(client.shutdown()) : undefined;\n });\n });\n\n await Promise.allSettled(shutdownPromises);\n\n await onShutdown?.();\n\n if (this.server?.options?.runDefaultShutdownHandler) {\n this.logger.info('Shutting down server...');\n await this.server?.shutdown?.();\n }\n\n this.logger.info('Shutdown process completed');\n\n if (useExit0) {\n this.logger.info(`Exiting process after ${signal}`);\n process.exit(0);\n }\n }\n}"],"mappings":"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"}
1
+ {"version":3,"file":"health-manager.js","names":["client","clientNames: string[]","shutdownPromises: Promise<any>[]"],"sources":["../../src/health-manager/health-manager.ts"],"sourcesContent":["import { setTimeout } from 'node:timers/promises';\nimport { ServiceUnavailableError } from '@autofleet/errors';\nimport type {\n ClientConfig,\n HealthManagerConfig,\n HealthManagerOptions,\n ClientType,\n ServerConfig,\n} from './types';\nimport { DEFAULT_CLIENT_HEALTHCHECK_FUNC_MAP } from './health-check-handlers';\nimport { DEFAULT_CLIENT_SHUTDOWN_HANDLERS_MAP, defaultServerShutdownHandler } from './shutdown-handlers';\nimport { timeoutPromise } from '../utils';\n\n/**\n * Manages application health state and graceful shutdown for Kubernetes liveness/readiness probes\n *\n * Features:\n * - Provides aliveCheck() and readyCheck() methods for liveness/readiness probes\n * - Auto-registers SIGTERM/SIGINT handlers for graceful shutdown\n * - Manages ready state during shutdown to fail readiness probes\n */\nexport class HealthManager {\n private readonly DEFAULT_CLIENT_OPTIONS = {\n includeInLivenessCheck: true,\n includeInReadinessCheck: true,\n };\n\n private readonly DEFAULT_SERVER_OPTIONS = {\n runDefaultShutdownHandler: true,\n name: 'server',\n };\n\n private readonly DEFAULT_CONFIG: HealthManagerConfig = {\n shutdownDelayMs: 5_000,\n useExit0: true,\n shouldSetupSignalHandlers: true,\n livenessPeriodMs: 10_000,\n readinessPeriodMs: 5_000,\n enableLivenessOptimization: true,\n };\n\n private isShuttingDown: boolean = false;\n private isReady: boolean = false;\n\n private config: HealthManagerConfig;\n\n private clientsMap: Map<string, ClientConfig<any>[]> = new Map();\n private server: Required<ServerConfig> | undefined;\n\n private livenessChecks: Array<() => Promise<void>> = [];\n private readinessChecks: Array<() => Promise<void>> = [];\n\n private logger: any;\n\n /**\n * Creates a new HealthManager instance\n *\n * @param params Configuration options for health checks and shutdown behavior\n */\n constructor(params: HealthManagerOptions) {\n this.config = {\n ...this.DEFAULT_CONFIG,\n ...(params.config ?? {}),\n };\n\n this.logger = params.logger;\n if (params.server) {\n this.attachServer(params.server);\n }\n\n this.storeClientsWithDefaults(params);\n\n this.initializeHealthChecks();\n\n if (this.config.shouldSetupSignalHandlers) {\n this.setupSignalHandlers();\n }\n }\n\n /**\n * Store clients with default health check and shutdown functions\n * @private\n */\n private storeClientsWithDefaults(params: HealthManagerOptions): void {\n const clients = params.clients;\n if (!clients) {\n return;\n }\n\n Object.entries(clients).forEach(([type, client]) => {\n if (!client) {\n return;\n }\n\n const clientsArray = Array.isArray(client) ? client : [client];\n\n // Apply default values to each client config\n const clientsWithDefaults = clientsArray.map((client) => {\n const options = {\n ...this.DEFAULT_CLIENT_OPTIONS,\n name: type,\n ...client.options,\n };\n\n const defaultHealthCheck = DEFAULT_CLIENT_HEALTHCHECK_FUNC_MAP[type as ClientType];\n const healthCheck = client.healthCheck ?? (defaultHealthCheck ? () => defaultHealthCheck({ client: client.connection, name: options.name }) : undefined);\n\n const defaultShutdown = DEFAULT_CLIENT_SHUTDOWN_HANDLERS_MAP[type as ClientType];\n const shutdown = client.shutdown ?? (defaultShutdown ? (signal?: string) => defaultShutdown({ client: client.connection, signal, name: options.name }) : undefined);\n\n return {\n connection: client.connection,\n healthCheck,\n shutdown,\n options,\n };\n });\n\n this.clientsMap.set(type, clientsWithDefaults);\n });\n }\n\n /**\n * Register SIGTERM and SIGINT signal listeners\n * @private\n */\n private setupSignalHandlers(): void {\n this.logger.info('Setting up global signal listeners for graceful shutdown');\n\n process.on('SIGTERM', async () => {\n await this.shutdown('SIGTERM');\n });\n\n process.on('SIGINT', async () => {\n await this.shutdown('SIGINT');\n });\n }\n\n /**\n * Initialize health check arrays based on configured clients\n * @private\n */\n private initializeHealthChecks(): void {\n let allClientsInBothChecks = true;\n\n this.clientsMap.forEach((clients) => {\n clients.forEach((client) => {\n if (!client.healthCheck) {\n return;\n }\n\n const includeInLiveness = client.options?.includeInLivenessCheck ?? true;\n const includeInReadiness = client.options?.includeInReadinessCheck ?? true;\n\n // Check if all clients are included in both checks\n if (!includeInLiveness || !includeInReadiness) {\n allClientsInBothChecks = false;\n }\n\n if (includeInLiveness) {\n this.livenessChecks.push(client.healthCheck);\n }\n\n if (includeInReadiness) {\n this.readinessChecks.push(client.healthCheck);\n }\n });\n });\n\n // Safety mechanism: Only enable optimization if all clients are in both checks to prevent skipping liveness checks for clients that aren't in readiness\n this.config.enableLivenessOptimization = this.config.enableLivenessOptimization && allClientsInBothChecks;\n\n if (this.config.enableLivenessOptimization) {\n if (!allClientsInBothChecks) {\n this.logger.warn('Liveness check optimization disabled - not all clients are in both liveness and readiness checks');\n } else {\n this.logger.info('Liveness check optimization enabled - liveness checks will be skipped when pod is ready');\n }\n }\n }\n\n /**\n * Attach an HTTP or HTTPS server to be managed during shutdown\n * @param serverConfig Server configuration including the server instance and optional shutdown handler\n */\n attachServer(serverConfig: ServerConfig): void {\n this.server = {\n server: serverConfig.server,\n shutdown: serverConfig.shutdown ?? (async () => defaultServerShutdownHandler(serverConfig.server)),\n options: {\n ...this.DEFAULT_SERVER_OPTIONS,\n ...(serverConfig.options ?? {}),\n },\n };\n }\n\n /**\n * Performs liveness health checks for clients configured with includeInLivenessCheck\n *\n * @returns Promise that resolves to { status: 'ok', statusCode: 200 } on success\n * @throws ServiceUnavailableError with statusCode 503 if any health check fails\n */\n async aliveCheck(): Promise<{ status: string, statusCode: number }> {\n if (this.isShuttingDown) {\n return { statusCode: 200, status: 'ok' };\n }\n\n // Skip liveness check if optimization is enabled and pod is already ready\n if (this.config.enableLivenessOptimization && this.isReady) {\n return { statusCode: 200, status: 'ok' };\n }\n\n try {\n const livenessPromises = Promise.all(this.livenessChecks.map(check => check()));\n\n await Promise.race([\n livenessPromises,\n timeoutPromise(this.config.livenessPeriodMs ?? 10_000, 'Liveness check timed out'),\n ]);\n\n return { statusCode: 200, status: 'ok' };\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n throw new ServiceUnavailableError(`Liveness probe failed: ${message}`);\n }\n }\n\n /**\n * Performs readiness health checks for clients configured with includeInReadinessCheck\n *\n * @returns Promise that resolves to { status: 'ok', statusCode: 200 } if ready\n * @throws ServiceUnavailableError with statusCode 503 if not ready or health check fails\n */\n async readyCheck(): Promise<{ status: string, statusCode: number }> {\n if (this.isShuttingDown) {\n this.isReady = false;\n throw new ServiceUnavailableError('Readiness probe failed: Application is shutting down');\n }\n\n try {\n const readinessPromises = Promise.all(this.readinessChecks.map(check => check()));\n\n await Promise.race([\n readinessPromises,\n timeoutPromise(this.config.readinessPeriodMs ?? 5_000, 'Readiness check timed out'),\n ]);\n this.isReady = true;\n\n return { statusCode: 200, status: 'ok' };\n } catch (error) {\n this.isReady = false;\n const message = error instanceof Error ? error.message : String(error);\n throw new ServiceUnavailableError(`Readiness probe failed: ${message}`);\n }\n }\n\n /**\n * Triggers graceful shutdown process\n * Can be called manually or automatically via SIGTERM/SIGINT signal handlers\n *\n * @param signal - Signal name (e.g., 'SIGTERM', 'SIGINT', 'MANUAL')\n */\n async shutdown(signal: string = 'MANUAL'): Promise<void> {\n if (this.isShuttingDown) {\n this.logger.warn('Shutdown already in progress');\n return;\n }\n\n const { shutdownDelayMs, useExit0, onShutdown, beforeShutdown } = this.config;\n\n this.isShuttingDown = true;\n this.isReady = false;\n\n this.logger.info(`${signal} received: setting readiness state to false`);\n\n await beforeShutdown?.();\n\n this.logger.info(`Waiting ${shutdownDelayMs}ms before shutdown...`);\n await setTimeout(shutdownDelayMs)\n\n // Collect client names that have shutdown handlers\n const clientNames: string[] = [];\n this.clientsMap.forEach((clients) => {\n clients.forEach((client) => {\n if (client.shutdown) {\n clientNames.push(client.options?.name || 'unknown');\n }\n });\n });\n\n this.logger.info('Executing shutdown hooks...', { clients: clientNames });\n\n const shutdownPromises: Promise<any>[] = [];\n this.clientsMap.forEach((clients) => {\n clients.forEach((client) => {\n if (client.shutdown) {\n shutdownPromises.push(client.shutdown(signal));\n }\n });\n });\n\n await Promise.allSettled(shutdownPromises);\n\n await onShutdown?.();\n\n if (this.server?.options?.runDefaultShutdownHandler) {\n this.logger.info('Shutting down server...');\n await this.server?.shutdown?.();\n }\n\n this.logger.info('Shutdown process completed');\n\n if (useExit0) {\n this.logger.info(`Exiting process after ${signal}`);\n process.exit(0);\n }\n }\n}"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- const e=require(`../_virtual/rolldown_runtime.cjs`),t=require(`../logger.cjs`),n=require(`./types.cjs`);let r=require(`http-terminator`);const i={[n.CLIENT_TYPES.REDIS]:async e=>{try{await e.client.quit?.()}catch(n){throw t.logger.error(`Failed to shutdown redis client '${e.name}': ${n instanceof Error?n.message:String(n)}`),n}},[n.CLIENT_TYPES.SEQUELIZE]:async e=>{try{await e.client.close?.()}catch(n){throw t.logger.error(`Failed to shutdown sequelize client '${e.name}': ${n instanceof Error?n.message:String(n)}`),n}},[n.CLIENT_TYPES.ELASTIC_SEARCH]:async e=>{try{await e.client.close?.()}catch(n){throw t.logger.error(`Failed to shutdown elasticsearch client '${e.name}': ${n instanceof Error?n.message:String(n)}`),n}},[n.CLIENT_TYPES.RABBIT]:async e=>{try{await e.client.gracefulShutdown?.(e.signal)}catch(n){throw t.logger.error(`Failed to shutdown rabbit client '${e.name}': ${n instanceof Error?n.message:String(n)}`),n}}},a=async e=>{try{await(0,r.createHttpTerminator)({server:e}).terminate()}catch(e){throw t.logger.error(`Failed to shutdown server: ${e instanceof Error?e.message:String(e)}`),e}};exports.DEFAULT_CLIENT_SHUTDOWN_HANDLERS_MAP=i,exports.defaultServerShutdownHandler=a;
1
+ const e=require(`../_virtual/rolldown_runtime.cjs`),t=require(`../logger.cjs`),n=require(`./types.cjs`);let r=require(`http-terminator`);const i={[n.CLIENT_TYPES.REDIS]:async e=>{try{await e.client.quit?.()}catch(n){throw t.logger.error(`Failed to shutdown redis client '${e.name}': ${n instanceof Error?n.message:String(n)}`),n}},[n.CLIENT_TYPES.SEQUELIZE]:async e=>{try{await e.client.close?.()}catch(n){throw t.logger.error(`Failed to shutdown sequelize client '${e.name}': ${n instanceof Error?n.message:String(n)}`),n}},[n.CLIENT_TYPES.ELASTIC_SEARCH]:async e=>{try{await e.client.close?.()}catch(n){throw t.logger.error(`Failed to shutdown elasticsearch client '${e.name}': ${n instanceof Error?n.message:String(n)}`),n}},[n.CLIENT_TYPES.RABBIT]:async e=>{try{await e.client.gracefulShutdown?.(e.signal)}catch(n){throw t.logger.error(`Failed to shutdown rabbit client '${e.name}': ${n instanceof Error?n.message:String(n)}`),n}},[n.CLIENT_TYPES.EVENTS]:async e=>{try{await e.client.exitHandler?.()}catch(n){throw t.logger.error(`Failed to shutdown events client '${e.name}': ${n instanceof Error?n.message:String(n)}`),n}}},a=async e=>{try{await(0,r.createHttpTerminator)({server:e}).terminate()}catch(e){throw t.logger.error(`Failed to shutdown server: ${e instanceof Error?e.message:String(e)}`),e}};exports.DEFAULT_CLIENT_SHUTDOWN_HANDLERS_MAP=i,exports.defaultServerShutdownHandler=a;
2
2
  //# sourceMappingURL=shutdown-handlers.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"shutdown-handlers.cjs","names":["DEFAULT_CLIENT_SHUTDOWN_HANDLERS_MAP: Record<ClientType, (client: any, signal?: string) => Promise<void>>","CLIENT_TYPES"],"sources":["../../src/health-manager/shutdown-handlers.ts"],"sourcesContent":["import {\n createHttpTerminator,\n} from 'http-terminator';\nimport type { Server as HttpServer } from 'node:http';\nimport type { Server as HttpsServer } from 'node:https';\nimport type { ClientType } from './types';\nimport { CLIENT_TYPES } from './types';\nimport { logger } from '../logger';\nimport { P } from 'vitest/dist/chunks/worker.d.DadbA89M';\n\nexport const DEFAULT_CLIENT_SHUTDOWN_HANDLERS_MAP: Record<ClientType, (client: any, signal?: string) => Promise<void>> = {\n [CLIENT_TYPES.REDIS]: async (options: { client: any, name: string }) => {\n try {\n await options.client.quit?.();\n } catch (error) {\n logger.error(`Failed to shutdown redis client '${options.name}': ${error instanceof Error ? error.message : String(error)}`);\n throw error;\n }\n },\n [CLIENT_TYPES.SEQUELIZE]: async (options: { client: any, name: string }) => {\n try {\n await options.client.close?.();\n } catch (error) {\n logger.error(`Failed to shutdown sequelize client '${options.name}': ${error instanceof Error ? error.message : String(error)}`);\n throw error;\n }\n },\n [CLIENT_TYPES.ELASTIC_SEARCH]: async (options: { client: any, name: string }) => {\n try {\n await options.client.close?.();\n } catch (error) {\n logger.error(`Failed to shutdown elasticsearch client '${options.name}': ${error instanceof Error ? error.message : String(error)}`);\n throw error;\n }\n },\n [CLIENT_TYPES.RABBIT]: async (options: { client: any, signal?: string, name: string }) => {\n try {\n await options.client.gracefulShutdown?.(options.signal);\n } catch (error) {\n logger.error(`Failed to shutdown rabbit client '${options.name}': ${error instanceof Error ? error.message : String(error)}`);\n throw error;\n }\n },\n};\n\nexport const defaultServerShutdownHandler = async (server: HttpServer | HttpsServer): Promise<void> => {\n try {\n const httpTerminator = createHttpTerminator({\n server,\n });\n await httpTerminator.terminate();\n } catch (error) {\n logger.error(`Failed to shutdown server: ${error instanceof Error ? error.message : String(error)}`);\n throw error;\n }\n};"],"mappings":"yIAUA,MAAaA,EAA4G,EACtHC,EAAAA,aAAa,OAAQ,KAAO,IAA2C,CACtE,GAAI,CACF,MAAM,EAAQ,OAAO,QAAQ,OACtB,EAAO,CAEd,MADA,EAAA,OAAO,MAAM,oCAAoC,EAAQ,KAAK,KAAK,aAAiB,MAAQ,EAAM,QAAU,OAAO,EAAM,GAAG,CACtH,KAGTA,EAAAA,aAAa,WAAY,KAAO,IAA2C,CAC1E,GAAI,CACF,MAAM,EAAQ,OAAO,SAAS,OACvB,EAAO,CAEd,MADA,EAAA,OAAO,MAAM,wCAAwC,EAAQ,KAAK,KAAK,aAAiB,MAAQ,EAAM,QAAU,OAAO,EAAM,GAAG,CAC1H,KAGTA,EAAAA,aAAa,gBAAiB,KAAO,IAA2C,CAC/E,GAAI,CACF,MAAM,EAAQ,OAAO,SAAS,OACvB,EAAO,CAEd,MADA,EAAA,OAAO,MAAM,4CAA4C,EAAQ,KAAK,KAAK,aAAiB,MAAQ,EAAM,QAAU,OAAO,EAAM,GAAG,CAC9H,KAGTA,EAAAA,aAAa,QAAS,KAAO,IAA4D,CACxF,GAAI,CACF,MAAM,EAAQ,OAAO,mBAAmB,EAAQ,OAAO,OAChD,EAAO,CAEd,MADA,EAAA,OAAO,MAAM,qCAAqC,EAAQ,KAAK,KAAK,aAAiB,MAAQ,EAAM,QAAU,OAAO,EAAM,GAAG,CACvH,IAGX,CAEY,EAA+B,KAAO,IAAoD,CACrG,GAAI,CAIF,MAAA,EAAA,EAAA,sBAH4C,CAC1C,SACD,CAAC,CACmB,WAAW,OACzB,EAAO,CAEd,MADA,EAAA,OAAO,MAAM,8BAA8B,aAAiB,MAAQ,EAAM,QAAU,OAAO,EAAM,GAAG,CAC9F"}
1
+ {"version":3,"file":"shutdown-handlers.cjs","names":["DEFAULT_CLIENT_SHUTDOWN_HANDLERS_MAP: Record<ClientType, (client: any, signal?: string) => Promise<void>>","CLIENT_TYPES"],"sources":["../../src/health-manager/shutdown-handlers.ts"],"sourcesContent":["import {\n createHttpTerminator,\n} from 'http-terminator';\nimport type { Server as HttpServer } from 'node:http';\nimport type { Server as HttpsServer } from 'node:https';\nimport type { ClientType } from './types';\nimport { CLIENT_TYPES } from './types';\nimport { logger } from '../logger';\n\nexport const DEFAULT_CLIENT_SHUTDOWN_HANDLERS_MAP: Record<ClientType, (client: any, signal?: string) => Promise<void>> = {\n [CLIENT_TYPES.REDIS]: async (options: { client: any, name: string }) => {\n try {\n await options.client.quit?.();\n } catch (error) {\n logger.error(`Failed to shutdown redis client '${options.name}': ${error instanceof Error ? error.message : String(error)}`);\n throw error;\n }\n },\n [CLIENT_TYPES.SEQUELIZE]: async (options: { client: any, name: string }) => {\n try {\n await options.client.close?.();\n } catch (error) {\n logger.error(`Failed to shutdown sequelize client '${options.name}': ${error instanceof Error ? error.message : String(error)}`);\n throw error;\n }\n },\n [CLIENT_TYPES.ELASTIC_SEARCH]: async (options: { client: any, name: string }) => {\n try {\n await options.client.close?.();\n } catch (error) {\n logger.error(`Failed to shutdown elasticsearch client '${options.name}': ${error instanceof Error ? error.message : String(error)}`);\n throw error;\n }\n },\n [CLIENT_TYPES.RABBIT]: async (options: { client: any, signal?: string, name: string }) => {\n try {\n await options.client.gracefulShutdown?.(options.signal);\n } catch (error) {\n logger.error(`Failed to shutdown rabbit client '${options.name}': ${error instanceof Error ? error.message : String(error)}`);\n throw error;\n }\n },\n [CLIENT_TYPES.EVENTS]: async (options: { client: any, name: string }) => {\n try {\n await options.client.exitHandler?.();\n } catch (error) {\n logger.error(`Failed to shutdown events client '${options.name}': ${error instanceof Error ? error.message : String(error)}`);\n throw error;\n }\n },\n};\n\nexport const defaultServerShutdownHandler = async (server: HttpServer | HttpsServer): Promise<void> => {\n try {\n const httpTerminator = createHttpTerminator({\n server,\n });\n await httpTerminator.terminate();\n } catch (error) {\n logger.error(`Failed to shutdown server: ${error instanceof Error ? error.message : String(error)}`);\n throw error;\n }\n};"],"mappings":"yIASA,MAAaA,EAA4G,EACtHC,EAAAA,aAAa,OAAQ,KAAO,IAA2C,CACtE,GAAI,CACF,MAAM,EAAQ,OAAO,QAAQ,OACtB,EAAO,CAEd,MADA,EAAA,OAAO,MAAM,oCAAoC,EAAQ,KAAK,KAAK,aAAiB,MAAQ,EAAM,QAAU,OAAO,EAAM,GAAG,CACtH,KAGTA,EAAAA,aAAa,WAAY,KAAO,IAA2C,CAC1E,GAAI,CACF,MAAM,EAAQ,OAAO,SAAS,OACvB,EAAO,CAEd,MADA,EAAA,OAAO,MAAM,wCAAwC,EAAQ,KAAK,KAAK,aAAiB,MAAQ,EAAM,QAAU,OAAO,EAAM,GAAG,CAC1H,KAGTA,EAAAA,aAAa,gBAAiB,KAAO,IAA2C,CAC/E,GAAI,CACF,MAAM,EAAQ,OAAO,SAAS,OACvB,EAAO,CAEd,MADA,EAAA,OAAO,MAAM,4CAA4C,EAAQ,KAAK,KAAK,aAAiB,MAAQ,EAAM,QAAU,OAAO,EAAM,GAAG,CAC9H,KAGTA,EAAAA,aAAa,QAAS,KAAO,IAA4D,CACxF,GAAI,CACF,MAAM,EAAQ,OAAO,mBAAmB,EAAQ,OAAO,OAChD,EAAO,CAEd,MADA,EAAA,OAAO,MAAM,qCAAqC,EAAQ,KAAK,KAAK,aAAiB,MAAQ,EAAM,QAAU,OAAO,EAAM,GAAG,CACvH,KAGTA,EAAAA,aAAa,QAAS,KAAO,IAA2C,CACvE,GAAI,CACF,MAAM,EAAQ,OAAO,eAAe,OAC7B,EAAO,CAEd,MADA,EAAA,OAAO,MAAM,qCAAqC,EAAQ,KAAK,KAAK,aAAiB,MAAQ,EAAM,QAAU,OAAO,EAAM,GAAG,CACvH,IAGX,CAEY,EAA+B,KAAO,IAAoD,CACrG,GAAI,CAIF,MAAA,EAAA,EAAA,sBAH4C,CAC1C,SACD,CAAC,CACmB,WAAW,OACzB,EAAO,CAEd,MADA,EAAA,OAAO,MAAM,8BAA8B,aAAiB,MAAQ,EAAM,QAAU,OAAO,EAAM,GAAG,CAC9F"}
@@ -1,2 +1,2 @@
1
- import{logger as e}from"../logger.js";import{CLIENT_TYPES as t}from"./types.js";import{createHttpTerminator as n}from"http-terminator";const r={[t.REDIS]:async t=>{try{await t.client.quit?.()}catch(n){throw e.error(`Failed to shutdown redis client '${t.name}': ${n instanceof Error?n.message:String(n)}`),n}},[t.SEQUELIZE]:async t=>{try{await t.client.close?.()}catch(n){throw e.error(`Failed to shutdown sequelize client '${t.name}': ${n instanceof Error?n.message:String(n)}`),n}},[t.ELASTIC_SEARCH]:async t=>{try{await t.client.close?.()}catch(n){throw e.error(`Failed to shutdown elasticsearch client '${t.name}': ${n instanceof Error?n.message:String(n)}`),n}},[t.RABBIT]:async t=>{try{await t.client.gracefulShutdown?.(t.signal)}catch(n){throw e.error(`Failed to shutdown rabbit client '${t.name}': ${n instanceof Error?n.message:String(n)}`),n}}},i=async t=>{try{await n({server:t}).terminate()}catch(t){throw e.error(`Failed to shutdown server: ${t instanceof Error?t.message:String(t)}`),t}};export{r as DEFAULT_CLIENT_SHUTDOWN_HANDLERS_MAP,i as defaultServerShutdownHandler};
1
+ import{logger as e}from"../logger.js";import{CLIENT_TYPES as t}from"./types.js";import{createHttpTerminator as n}from"http-terminator";const r={[t.REDIS]:async t=>{try{await t.client.quit?.()}catch(n){throw e.error(`Failed to shutdown redis client '${t.name}': ${n instanceof Error?n.message:String(n)}`),n}},[t.SEQUELIZE]:async t=>{try{await t.client.close?.()}catch(n){throw e.error(`Failed to shutdown sequelize client '${t.name}': ${n instanceof Error?n.message:String(n)}`),n}},[t.ELASTIC_SEARCH]:async t=>{try{await t.client.close?.()}catch(n){throw e.error(`Failed to shutdown elasticsearch client '${t.name}': ${n instanceof Error?n.message:String(n)}`),n}},[t.RABBIT]:async t=>{try{await t.client.gracefulShutdown?.(t.signal)}catch(n){throw e.error(`Failed to shutdown rabbit client '${t.name}': ${n instanceof Error?n.message:String(n)}`),n}},[t.EVENTS]:async t=>{try{await t.client.exitHandler?.()}catch(n){throw e.error(`Failed to shutdown events client '${t.name}': ${n instanceof Error?n.message:String(n)}`),n}}},i=async t=>{try{await n({server:t}).terminate()}catch(t){throw e.error(`Failed to shutdown server: ${t instanceof Error?t.message:String(t)}`),t}};export{r as DEFAULT_CLIENT_SHUTDOWN_HANDLERS_MAP,i as defaultServerShutdownHandler};
2
2
  //# sourceMappingURL=shutdown-handlers.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"shutdown-handlers.js","names":["DEFAULT_CLIENT_SHUTDOWN_HANDLERS_MAP: Record<ClientType, (client: any, signal?: string) => Promise<void>>"],"sources":["../../src/health-manager/shutdown-handlers.ts"],"sourcesContent":["import {\n createHttpTerminator,\n} from 'http-terminator';\nimport type { Server as HttpServer } from 'node:http';\nimport type { Server as HttpsServer } from 'node:https';\nimport type { ClientType } from './types';\nimport { CLIENT_TYPES } from './types';\nimport { logger } from '../logger';\nimport { P } from 'vitest/dist/chunks/worker.d.DadbA89M';\n\nexport const DEFAULT_CLIENT_SHUTDOWN_HANDLERS_MAP: Record<ClientType, (client: any, signal?: string) => Promise<void>> = {\n [CLIENT_TYPES.REDIS]: async (options: { client: any, name: string }) => {\n try {\n await options.client.quit?.();\n } catch (error) {\n logger.error(`Failed to shutdown redis client '${options.name}': ${error instanceof Error ? error.message : String(error)}`);\n throw error;\n }\n },\n [CLIENT_TYPES.SEQUELIZE]: async (options: { client: any, name: string }) => {\n try {\n await options.client.close?.();\n } catch (error) {\n logger.error(`Failed to shutdown sequelize client '${options.name}': ${error instanceof Error ? error.message : String(error)}`);\n throw error;\n }\n },\n [CLIENT_TYPES.ELASTIC_SEARCH]: async (options: { client: any, name: string }) => {\n try {\n await options.client.close?.();\n } catch (error) {\n logger.error(`Failed to shutdown elasticsearch client '${options.name}': ${error instanceof Error ? error.message : String(error)}`);\n throw error;\n }\n },\n [CLIENT_TYPES.RABBIT]: async (options: { client: any, signal?: string, name: string }) => {\n try {\n await options.client.gracefulShutdown?.(options.signal);\n } catch (error) {\n logger.error(`Failed to shutdown rabbit client '${options.name}': ${error instanceof Error ? error.message : String(error)}`);\n throw error;\n }\n },\n};\n\nexport const defaultServerShutdownHandler = async (server: HttpServer | HttpsServer): Promise<void> => {\n try {\n const httpTerminator = createHttpTerminator({\n server,\n });\n await httpTerminator.terminate();\n } catch (error) {\n logger.error(`Failed to shutdown server: ${error instanceof Error ? error.message : String(error)}`);\n throw error;\n }\n};"],"mappings":"uIAUA,MAAaA,EAA4G,EACtH,EAAa,OAAQ,KAAO,IAA2C,CACtE,GAAI,CACF,MAAM,EAAQ,OAAO,QAAQ,OACtB,EAAO,CAEd,MADA,EAAO,MAAM,oCAAoC,EAAQ,KAAK,KAAK,aAAiB,MAAQ,EAAM,QAAU,OAAO,EAAM,GAAG,CACtH,KAGT,EAAa,WAAY,KAAO,IAA2C,CAC1E,GAAI,CACF,MAAM,EAAQ,OAAO,SAAS,OACvB,EAAO,CAEd,MADA,EAAO,MAAM,wCAAwC,EAAQ,KAAK,KAAK,aAAiB,MAAQ,EAAM,QAAU,OAAO,EAAM,GAAG,CAC1H,KAGT,EAAa,gBAAiB,KAAO,IAA2C,CAC/E,GAAI,CACF,MAAM,EAAQ,OAAO,SAAS,OACvB,EAAO,CAEd,MADA,EAAO,MAAM,4CAA4C,EAAQ,KAAK,KAAK,aAAiB,MAAQ,EAAM,QAAU,OAAO,EAAM,GAAG,CAC9H,KAGT,EAAa,QAAS,KAAO,IAA4D,CACxF,GAAI,CACF,MAAM,EAAQ,OAAO,mBAAmB,EAAQ,OAAO,OAChD,EAAO,CAEd,MADA,EAAO,MAAM,qCAAqC,EAAQ,KAAK,KAAK,aAAiB,MAAQ,EAAM,QAAU,OAAO,EAAM,GAAG,CACvH,IAGX,CAEY,EAA+B,KAAO,IAAoD,CACrG,GAAI,CAIF,MAHuB,EAAqB,CAC1C,SACD,CAAC,CACmB,WAAW,OACzB,EAAO,CAEd,MADA,EAAO,MAAM,8BAA8B,aAAiB,MAAQ,EAAM,QAAU,OAAO,EAAM,GAAG,CAC9F"}
1
+ {"version":3,"file":"shutdown-handlers.js","names":["DEFAULT_CLIENT_SHUTDOWN_HANDLERS_MAP: Record<ClientType, (client: any, signal?: string) => Promise<void>>"],"sources":["../../src/health-manager/shutdown-handlers.ts"],"sourcesContent":["import {\n createHttpTerminator,\n} from 'http-terminator';\nimport type { Server as HttpServer } from 'node:http';\nimport type { Server as HttpsServer } from 'node:https';\nimport type { ClientType } from './types';\nimport { CLIENT_TYPES } from './types';\nimport { logger } from '../logger';\n\nexport const DEFAULT_CLIENT_SHUTDOWN_HANDLERS_MAP: Record<ClientType, (client: any, signal?: string) => Promise<void>> = {\n [CLIENT_TYPES.REDIS]: async (options: { client: any, name: string }) => {\n try {\n await options.client.quit?.();\n } catch (error) {\n logger.error(`Failed to shutdown redis client '${options.name}': ${error instanceof Error ? error.message : String(error)}`);\n throw error;\n }\n },\n [CLIENT_TYPES.SEQUELIZE]: async (options: { client: any, name: string }) => {\n try {\n await options.client.close?.();\n } catch (error) {\n logger.error(`Failed to shutdown sequelize client '${options.name}': ${error instanceof Error ? error.message : String(error)}`);\n throw error;\n }\n },\n [CLIENT_TYPES.ELASTIC_SEARCH]: async (options: { client: any, name: string }) => {\n try {\n await options.client.close?.();\n } catch (error) {\n logger.error(`Failed to shutdown elasticsearch client '${options.name}': ${error instanceof Error ? error.message : String(error)}`);\n throw error;\n }\n },\n [CLIENT_TYPES.RABBIT]: async (options: { client: any, signal?: string, name: string }) => {\n try {\n await options.client.gracefulShutdown?.(options.signal);\n } catch (error) {\n logger.error(`Failed to shutdown rabbit client '${options.name}': ${error instanceof Error ? error.message : String(error)}`);\n throw error;\n }\n },\n [CLIENT_TYPES.EVENTS]: async (options: { client: any, name: string }) => {\n try {\n await options.client.exitHandler?.();\n } catch (error) {\n logger.error(`Failed to shutdown events client '${options.name}': ${error instanceof Error ? error.message : String(error)}`);\n throw error;\n }\n },\n};\n\nexport const defaultServerShutdownHandler = async (server: HttpServer | HttpsServer): Promise<void> => {\n try {\n const httpTerminator = createHttpTerminator({\n server,\n });\n await httpTerminator.terminate();\n } catch (error) {\n logger.error(`Failed to shutdown server: ${error instanceof Error ? error.message : String(error)}`);\n throw error;\n }\n};"],"mappings":"uIASA,MAAaA,EAA4G,EACtH,EAAa,OAAQ,KAAO,IAA2C,CACtE,GAAI,CACF,MAAM,EAAQ,OAAO,QAAQ,OACtB,EAAO,CAEd,MADA,EAAO,MAAM,oCAAoC,EAAQ,KAAK,KAAK,aAAiB,MAAQ,EAAM,QAAU,OAAO,EAAM,GAAG,CACtH,KAGT,EAAa,WAAY,KAAO,IAA2C,CAC1E,GAAI,CACF,MAAM,EAAQ,OAAO,SAAS,OACvB,EAAO,CAEd,MADA,EAAO,MAAM,wCAAwC,EAAQ,KAAK,KAAK,aAAiB,MAAQ,EAAM,QAAU,OAAO,EAAM,GAAG,CAC1H,KAGT,EAAa,gBAAiB,KAAO,IAA2C,CAC/E,GAAI,CACF,MAAM,EAAQ,OAAO,SAAS,OACvB,EAAO,CAEd,MADA,EAAO,MAAM,4CAA4C,EAAQ,KAAK,KAAK,aAAiB,MAAQ,EAAM,QAAU,OAAO,EAAM,GAAG,CAC9H,KAGT,EAAa,QAAS,KAAO,IAA4D,CACxF,GAAI,CACF,MAAM,EAAQ,OAAO,mBAAmB,EAAQ,OAAO,OAChD,EAAO,CAEd,MADA,EAAO,MAAM,qCAAqC,EAAQ,KAAK,KAAK,aAAiB,MAAQ,EAAM,QAAU,OAAO,EAAM,GAAG,CACvH,KAGT,EAAa,QAAS,KAAO,IAA2C,CACvE,GAAI,CACF,MAAM,EAAQ,OAAO,eAAe,OAC7B,EAAO,CAEd,MADA,EAAO,MAAM,qCAAqC,EAAQ,KAAK,KAAK,aAAiB,MAAQ,EAAM,QAAU,OAAO,EAAM,GAAG,CACvH,IAGX,CAEY,EAA+B,KAAO,IAAoD,CACrG,GAAI,CAIF,MAHuB,EAAqB,CAC1C,SACD,CAAC,CACmB,WAAW,OACzB,EAAO,CAEd,MADA,EAAO,MAAM,8BAA8B,aAAiB,MAAQ,EAAM,QAAU,OAAO,EAAM,GAAG,CAC9F"}
@@ -1,2 +1,2 @@
1
- const e={RABBIT:`rabbit`,SEQUELIZE:`sequelize`,REDIS:`redis`,ELASTIC_SEARCH:`elasticsearch`};exports.CLIENT_TYPES=e;
1
+ const e={RABBIT:`rabbit`,SEQUELIZE:`sequelize`,REDIS:`redis`,ELASTIC_SEARCH:`elasticsearch`,EVENTS:`events`};exports.CLIENT_TYPES=e;
2
2
  //# sourceMappingURL=types.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.cjs","names":[],"sources":["../../src/health-manager/types.ts"],"sourcesContent":["import type { LoggerInstanceManager } from '@autofleet/logger';\nimport type { Server as HttpServer } from 'node:http';\nimport type { Server as HttpsServer } from 'node:https';\n\n\n/**\n * Configuration for a single client (e.g., rabbit, sequelize, redis, elasticsearch)\n * Allows custom health checks and shutdown logic\n */\nexport interface ClientConfig<T = any> {\n /** The actual connection instance */\n connection: T;\n\n /** Custom health check function (optional) - will be called during alive/ready checks */\n healthCheck?: () => Promise<void>;\n\n /** Custom shutdown function (optional) - will be called during graceful shutdown */\n shutdown?: () => Promise<void>;\n\n /** Additional options specific to the client (optional) */\n options?: {\n /** Whether to include this client in liveness checks (default: true) */\n includeInLivenessCheck?: boolean;\n\n /** Whether to include this client in readiness checks (default: true) */\n includeInReadinessCheck?: boolean;\n\n /** Custom name for this client (used in logging) */\n name?: string;\n };\n}\n\n/** Configuration for an HTTP or HTTPS server managed by HealthManager */\nexport interface ServerConfig {\n /** The HTTP or HTTPS server instance */\n server: HttpServer | HttpsServer;\n\n /** Custom shutdown function for the server (optional) */\n shutdown?: () => Promise<void>;\n\n /** Additional options specific to the server (optional) */\n options?: {\n /** Custom name for this server (used in logging) */\n name?: string;\n\n /** Whether to skip the default shutdown handler for this server */\n runDefaultShutdownHandler?: boolean;\n\n }\n}\n\n/**\n * General configuration options for the HealthManager\n */\nexport interface HealthManagerConfig {\n /**\n * Delay in milliseconds before shutting down after SIGTERM/SIGINT\n * This gives time for load balancers to stop routing traffic. Should be equal to or greater than readiness probe interval.\n * @default 5000\n */\n shutdownDelayMs?: number;\n\n /**\n * Whether to call process.exit(0) after shutdown sequence completes\n * @default true\n */\n useExit0?: boolean;\n\n /** Whether to auto-setup SIGTERM/SIGINT handlers for graceful shutdown\n * @default true\n */\n shouldSetupSignalHandlers?: boolean;\n\n /**\n * Global shutdown hook called before all clients are shut down\n * @default undefined\n */\n beforeShutdown?: (() => Promise<void>) | undefined;\n\n /**\n * Global shutdown hook called after all clients are shut down\n * @default undefined\n */\n onShutdown?: (() => Promise<void>) | undefined;\n\n /** The interval in milliseconds at which the liveness checks are repeated (optional)\n * * @default 10_000 \n */\n livenessPeriodMs?: number;\n\n /** The interval in milliseconds at which the readiness checks are repeated (optional)\n * * @default 5_000 \n */\n readinessPeriodMs?: number;\n}\n\n/**\n * List of clients and general configuration for HealthManager\n */\nexport interface HealthManagerOptions {\n /** Client configurations */\n clients?: {\n /** RabbitMQ client configuration - can be a single config or array of configs */\n rabbit?: ClientConfig<any> | ClientConfig<any>[];\n\n /** Sequelize instance configuration - can be a single config or array of configs */\n sequelize?: ClientConfig<any> | ClientConfig<any>[];\n\n /** Redis client configuration - can be a single config or array of configs */\n redis?: ClientConfig<any> | ClientConfig<any>[];\n\n /** Elasticsearch client configuration - can be a single config or array of configs */\n elasticsearch?: ClientConfig<any> | ClientConfig<any>[];\n };\n\n /** General configuration options */\n config?: HealthManagerConfig;\n\n /** Logger instance for logging messages */\n logger: LoggerInstanceManager;\n\n /** Optional HTTP or HTTPS server instance to manage during shutdown */\n server?: ServerConfig\n}\n\nexport interface RedisClient {\n ping: () => Promise<string | void>;\n}\n\n/**\n * Sequelize database interface\n */\nexport interface SequelizeClient {\n query(sql: string | { query: string; values: unknown[]; }): Promise<any>;\n}\n\n/**\n * Elasticsearch client interface\n */\nexport interface ElasticsearchClient {\n ping: () => Promise<boolean>;\n}\n\n/**\n * RabbitMQ client interface\n */\nexport interface RabbitMQClient {\n isConnected: () => Promise<boolean> | boolean;\n}\n\nexport const CLIENT_TYPES = {\n RABBIT: 'rabbit',\n SEQUELIZE: 'sequelize',\n REDIS: 'redis',\n ELASTIC_SEARCH: 'elasticsearch',\n} as const;\n\nexport type ClientType = typeof CLIENT_TYPES[keyof typeof CLIENT_TYPES];\n"],"mappings":"AAsJA,MAAa,EAAe,CAC1B,OAAQ,SACR,UAAW,YACX,MAAO,QACP,eAAgB,gBACjB"}
1
+ {"version":3,"file":"types.cjs","names":[],"sources":["../../src/health-manager/types.ts"],"sourcesContent":["import type { LoggerInstanceManager } from '@autofleet/logger';\nimport type { Server as HttpServer } from 'node:http';\nimport type { Server as HttpsServer } from 'node:https';\n\n\n/**\n * Configuration for a single client (e.g., rabbit, sequelize, redis, elasticsearch)\n * Allows custom health checks and shutdown logic\n */\nexport interface ClientConfig<T = any> {\n /** The actual connection instance */\n connection: T;\n\n /** Custom health check function (optional) - will be called during alive/ready checks */\n healthCheck?: () => Promise<void>;\n\n /** Custom shutdown function (optional) - will be called during graceful shutdown with signal name (e.g., 'SIGTERM', 'SIGINT') */\n shutdown?: (signal?: string) => Promise<void>;\n\n /** Additional options specific to the client (optional) */\n options?: {\n /** Whether to include this client in liveness checks (default: true) */\n includeInLivenessCheck?: boolean;\n\n /** Whether to include this client in readiness checks (default: true) */\n includeInReadinessCheck?: boolean;\n\n /** Custom name for this client (used in logging) */\n name?: string;\n };\n}\n\n/** Configuration for an HTTP or HTTPS server managed by HealthManager */\nexport interface ServerConfig {\n /** The HTTP or HTTPS server instance */\n server: HttpServer | HttpsServer;\n\n /** Custom shutdown function for the server (optional) - receives signal name (e.g., 'SIGTERM', 'SIGINT') */\n shutdown?: (signal?: string) => Promise<void>;\n\n /** Additional options specific to the server (optional) */\n options?: {\n /** Custom name for this server (used in logging) */\n name?: string;\n\n /** Whether to skip the default shutdown handler for this server */\n runDefaultShutdownHandler?: boolean;\n\n }\n}\n\n/**\n * General configuration options for the HealthManager\n */\nexport interface HealthManagerConfig {\n /**\n * Delay in milliseconds before shutting down after SIGTERM/SIGINT\n * This gives time for load balancers to stop routing traffic. Should be equal to or greater than readiness probe interval.\n * @default 5000\n */\n shutdownDelayMs?: number;\n\n /**\n * Whether to call process.exit(0) after shutdown sequence completes\n * @default true\n */\n useExit0?: boolean;\n\n /** Whether to auto-setup SIGTERM/SIGINT handlers for graceful shutdown\n * @default true\n */\n shouldSetupSignalHandlers?: boolean;\n\n /**\n * Global shutdown hook called before all clients are shut down\n * @default undefined\n */\n beforeShutdown?: (() => Promise<void>) | undefined;\n\n /**\n * Global shutdown hook called after all clients are shut down\n * @default undefined\n */\n onShutdown?: (() => Promise<void>) | undefined;\n\n /** The interval in milliseconds at which the liveness checks are repeated (optional)\n * * @default 10_000 \n */\n livenessPeriodMs?: number;\n\n /** The interval in milliseconds at which the readiness checks are repeated (optional)\n * * @default 5_000\n */\n readinessPeriodMs?: number;\n\n /**\n * Enable liveness check optimization - if true and all clients are in both liveness and readiness checks,\n * liveness checks will be skipped when pod is already ready (readiness check passed)\n * @default true\n */\n enableLivenessOptimization?: boolean;\n}\n\n/**\n * List of clients and general configuration for HealthManager\n */\nexport interface HealthManagerOptions {\n /** Client configurations */\n clients?: {\n /** RabbitMQ client configuration - can be a single config or array of configs */\n rabbit?: ClientConfig<any> | ClientConfig<any>[];\n\n /** Sequelize instance configuration - can be a single config or array of configs */\n sequelize?: ClientConfig<any> | ClientConfig<any>[];\n\n /** Redis client configuration - can be a single config or array of configs */\n redis?: ClientConfig<any> | ClientConfig<any>[];\n\n /** Elasticsearch client configuration - can be a single config or array of configs */\n elasticsearch?: ClientConfig<any> | ClientConfig<any>[];\n };\n\n /** General configuration options */\n config?: HealthManagerConfig;\n\n /** Logger instance for logging messages */\n logger: LoggerInstanceManager;\n\n /** Optional HTTP or HTTPS server instance to manage during shutdown */\n server?: ServerConfig\n}\n\nexport interface RedisClient {\n ping: () => Promise<string | void>;\n}\n\n/**\n * Sequelize database interface\n */\nexport interface SequelizeClient {\n query(sql: string | { query: string; values: unknown[]; }): Promise<any>;\n}\n\n/**\n * Elasticsearch client interface\n */\nexport interface ElasticsearchClient {\n ping: () => Promise<boolean>;\n}\n\nexport interface EventsClient {\n exitHandler: () => Promise<void>;\n}\n\n/**\n * RabbitMQ client interface\n */\nexport interface RabbitMQClient {\n isConnected: () => Promise<boolean> | boolean;\n}\n\nexport const CLIENT_TYPES = {\n RABBIT: 'rabbit',\n SEQUELIZE: 'sequelize',\n REDIS: 'redis',\n ELASTIC_SEARCH: 'elasticsearch',\n EVENTS: 'events',\n} as const;\n\nexport type ClientType = typeof CLIENT_TYPES[keyof typeof CLIENT_TYPES];\n"],"mappings":"AAiKA,MAAa,EAAe,CAC1B,OAAQ,SACR,UAAW,YACX,MAAO,QACP,eAAgB,gBAChB,OAAQ,SACT"}
@@ -13,8 +13,8 @@ interface ClientConfig<T = any> {
13
13
  connection: T;
14
14
  /** Custom health check function (optional) - will be called during alive/ready checks */
15
15
  healthCheck?: () => Promise<void>;
16
- /** Custom shutdown function (optional) - will be called during graceful shutdown */
17
- shutdown?: () => Promise<void>;
16
+ /** Custom shutdown function (optional) - will be called during graceful shutdown with signal name (e.g., 'SIGTERM', 'SIGINT') */
17
+ shutdown?: (signal?: string) => Promise<void>;
18
18
  /** Additional options specific to the client (optional) */
19
19
  options?: {
20
20
  /** Whether to include this client in liveness checks (default: true) */
@@ -29,8 +29,8 @@ interface ClientConfig<T = any> {
29
29
  interface ServerConfig {
30
30
  /** The HTTP or HTTPS server instance */
31
31
  server: Server | Server$1;
32
- /** Custom shutdown function for the server (optional) */
33
- shutdown?: () => Promise<void>;
32
+ /** Custom shutdown function for the server (optional) - receives signal name (e.g., 'SIGTERM', 'SIGINT') */
33
+ shutdown?: (signal?: string) => Promise<void>;
34
34
  /** Additional options specific to the server (optional) */
35
35
  options?: {
36
36
  /** Custom name for this server (used in logging) */
@@ -73,9 +73,15 @@ interface HealthManagerConfig {
73
73
  */
74
74
  livenessPeriodMs?: number;
75
75
  /** The interval in milliseconds at which the readiness checks are repeated (optional)
76
- * * @default 5_000
76
+ * * @default 5_000
77
77
  */
78
78
  readinessPeriodMs?: number;
79
+ /**
80
+ * Enable liveness check optimization - if true and all clients are in both liveness and readiness checks,
81
+ * liveness checks will be skipped when pod is already ready (readiness check passed)
82
+ * @default true
83
+ */
84
+ enableLivenessOptimization?: boolean;
79
85
  }
80
86
  /**
81
87
  * List of clients and general configuration for HealthManager
@@ -13,8 +13,8 @@ interface ClientConfig<T = any> {
13
13
  connection: T;
14
14
  /** Custom health check function (optional) - will be called during alive/ready checks */
15
15
  healthCheck?: () => Promise<void>;
16
- /** Custom shutdown function (optional) - will be called during graceful shutdown */
17
- shutdown?: () => Promise<void>;
16
+ /** Custom shutdown function (optional) - will be called during graceful shutdown with signal name (e.g., 'SIGTERM', 'SIGINT') */
17
+ shutdown?: (signal?: string) => Promise<void>;
18
18
  /** Additional options specific to the client (optional) */
19
19
  options?: {
20
20
  /** Whether to include this client in liveness checks (default: true) */
@@ -29,8 +29,8 @@ interface ClientConfig<T = any> {
29
29
  interface ServerConfig {
30
30
  /** The HTTP or HTTPS server instance */
31
31
  server: Server | Server$1;
32
- /** Custom shutdown function for the server (optional) */
33
- shutdown?: () => Promise<void>;
32
+ /** Custom shutdown function for the server (optional) - receives signal name (e.g., 'SIGTERM', 'SIGINT') */
33
+ shutdown?: (signal?: string) => Promise<void>;
34
34
  /** Additional options specific to the server (optional) */
35
35
  options?: {
36
36
  /** Custom name for this server (used in logging) */
@@ -73,9 +73,15 @@ interface HealthManagerConfig {
73
73
  */
74
74
  livenessPeriodMs?: number;
75
75
  /** The interval in milliseconds at which the readiness checks are repeated (optional)
76
- * * @default 5_000
76
+ * * @default 5_000
77
77
  */
78
78
  readinessPeriodMs?: number;
79
+ /**
80
+ * Enable liveness check optimization - if true and all clients are in both liveness and readiness checks,
81
+ * liveness checks will be skipped when pod is already ready (readiness check passed)
82
+ * @default true
83
+ */
84
+ enableLivenessOptimization?: boolean;
79
85
  }
80
86
  /**
81
87
  * List of clients and general configuration for HealthManager
@@ -1,2 +1,2 @@
1
- const e={RABBIT:`rabbit`,SEQUELIZE:`sequelize`,REDIS:`redis`,ELASTIC_SEARCH:`elasticsearch`};export{e as CLIENT_TYPES};
1
+ const e={RABBIT:`rabbit`,SEQUELIZE:`sequelize`,REDIS:`redis`,ELASTIC_SEARCH:`elasticsearch`,EVENTS:`events`};export{e as CLIENT_TYPES};
2
2
  //# sourceMappingURL=types.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","names":[],"sources":["../../src/health-manager/types.ts"],"sourcesContent":["import type { LoggerInstanceManager } from '@autofleet/logger';\nimport type { Server as HttpServer } from 'node:http';\nimport type { Server as HttpsServer } from 'node:https';\n\n\n/**\n * Configuration for a single client (e.g., rabbit, sequelize, redis, elasticsearch)\n * Allows custom health checks and shutdown logic\n */\nexport interface ClientConfig<T = any> {\n /** The actual connection instance */\n connection: T;\n\n /** Custom health check function (optional) - will be called during alive/ready checks */\n healthCheck?: () => Promise<void>;\n\n /** Custom shutdown function (optional) - will be called during graceful shutdown */\n shutdown?: () => Promise<void>;\n\n /** Additional options specific to the client (optional) */\n options?: {\n /** Whether to include this client in liveness checks (default: true) */\n includeInLivenessCheck?: boolean;\n\n /** Whether to include this client in readiness checks (default: true) */\n includeInReadinessCheck?: boolean;\n\n /** Custom name for this client (used in logging) */\n name?: string;\n };\n}\n\n/** Configuration for an HTTP or HTTPS server managed by HealthManager */\nexport interface ServerConfig {\n /** The HTTP or HTTPS server instance */\n server: HttpServer | HttpsServer;\n\n /** Custom shutdown function for the server (optional) */\n shutdown?: () => Promise<void>;\n\n /** Additional options specific to the server (optional) */\n options?: {\n /** Custom name for this server (used in logging) */\n name?: string;\n\n /** Whether to skip the default shutdown handler for this server */\n runDefaultShutdownHandler?: boolean;\n\n }\n}\n\n/**\n * General configuration options for the HealthManager\n */\nexport interface HealthManagerConfig {\n /**\n * Delay in milliseconds before shutting down after SIGTERM/SIGINT\n * This gives time for load balancers to stop routing traffic. Should be equal to or greater than readiness probe interval.\n * @default 5000\n */\n shutdownDelayMs?: number;\n\n /**\n * Whether to call process.exit(0) after shutdown sequence completes\n * @default true\n */\n useExit0?: boolean;\n\n /** Whether to auto-setup SIGTERM/SIGINT handlers for graceful shutdown\n * @default true\n */\n shouldSetupSignalHandlers?: boolean;\n\n /**\n * Global shutdown hook called before all clients are shut down\n * @default undefined\n */\n beforeShutdown?: (() => Promise<void>) | undefined;\n\n /**\n * Global shutdown hook called after all clients are shut down\n * @default undefined\n */\n onShutdown?: (() => Promise<void>) | undefined;\n\n /** The interval in milliseconds at which the liveness checks are repeated (optional)\n * * @default 10_000 \n */\n livenessPeriodMs?: number;\n\n /** The interval in milliseconds at which the readiness checks are repeated (optional)\n * * @default 5_000 \n */\n readinessPeriodMs?: number;\n}\n\n/**\n * List of clients and general configuration for HealthManager\n */\nexport interface HealthManagerOptions {\n /** Client configurations */\n clients?: {\n /** RabbitMQ client configuration - can be a single config or array of configs */\n rabbit?: ClientConfig<any> | ClientConfig<any>[];\n\n /** Sequelize instance configuration - can be a single config or array of configs */\n sequelize?: ClientConfig<any> | ClientConfig<any>[];\n\n /** Redis client configuration - can be a single config or array of configs */\n redis?: ClientConfig<any> | ClientConfig<any>[];\n\n /** Elasticsearch client configuration - can be a single config or array of configs */\n elasticsearch?: ClientConfig<any> | ClientConfig<any>[];\n };\n\n /** General configuration options */\n config?: HealthManagerConfig;\n\n /** Logger instance for logging messages */\n logger: LoggerInstanceManager;\n\n /** Optional HTTP or HTTPS server instance to manage during shutdown */\n server?: ServerConfig\n}\n\nexport interface RedisClient {\n ping: () => Promise<string | void>;\n}\n\n/**\n * Sequelize database interface\n */\nexport interface SequelizeClient {\n query(sql: string | { query: string; values: unknown[]; }): Promise<any>;\n}\n\n/**\n * Elasticsearch client interface\n */\nexport interface ElasticsearchClient {\n ping: () => Promise<boolean>;\n}\n\n/**\n * RabbitMQ client interface\n */\nexport interface RabbitMQClient {\n isConnected: () => Promise<boolean> | boolean;\n}\n\nexport const CLIENT_TYPES = {\n RABBIT: 'rabbit',\n SEQUELIZE: 'sequelize',\n REDIS: 'redis',\n ELASTIC_SEARCH: 'elasticsearch',\n} as const;\n\nexport type ClientType = typeof CLIENT_TYPES[keyof typeof CLIENT_TYPES];\n"],"mappings":"AAsJA,MAAa,EAAe,CAC1B,OAAQ,SACR,UAAW,YACX,MAAO,QACP,eAAgB,gBACjB"}
1
+ {"version":3,"file":"types.js","names":[],"sources":["../../src/health-manager/types.ts"],"sourcesContent":["import type { LoggerInstanceManager } from '@autofleet/logger';\nimport type { Server as HttpServer } from 'node:http';\nimport type { Server as HttpsServer } from 'node:https';\n\n\n/**\n * Configuration for a single client (e.g., rabbit, sequelize, redis, elasticsearch)\n * Allows custom health checks and shutdown logic\n */\nexport interface ClientConfig<T = any> {\n /** The actual connection instance */\n connection: T;\n\n /** Custom health check function (optional) - will be called during alive/ready checks */\n healthCheck?: () => Promise<void>;\n\n /** Custom shutdown function (optional) - will be called during graceful shutdown with signal name (e.g., 'SIGTERM', 'SIGINT') */\n shutdown?: (signal?: string) => Promise<void>;\n\n /** Additional options specific to the client (optional) */\n options?: {\n /** Whether to include this client in liveness checks (default: true) */\n includeInLivenessCheck?: boolean;\n\n /** Whether to include this client in readiness checks (default: true) */\n includeInReadinessCheck?: boolean;\n\n /** Custom name for this client (used in logging) */\n name?: string;\n };\n}\n\n/** Configuration for an HTTP or HTTPS server managed by HealthManager */\nexport interface ServerConfig {\n /** The HTTP or HTTPS server instance */\n server: HttpServer | HttpsServer;\n\n /** Custom shutdown function for the server (optional) - receives signal name (e.g., 'SIGTERM', 'SIGINT') */\n shutdown?: (signal?: string) => Promise<void>;\n\n /** Additional options specific to the server (optional) */\n options?: {\n /** Custom name for this server (used in logging) */\n name?: string;\n\n /** Whether to skip the default shutdown handler for this server */\n runDefaultShutdownHandler?: boolean;\n\n }\n}\n\n/**\n * General configuration options for the HealthManager\n */\nexport interface HealthManagerConfig {\n /**\n * Delay in milliseconds before shutting down after SIGTERM/SIGINT\n * This gives time for load balancers to stop routing traffic. Should be equal to or greater than readiness probe interval.\n * @default 5000\n */\n shutdownDelayMs?: number;\n\n /**\n * Whether to call process.exit(0) after shutdown sequence completes\n * @default true\n */\n useExit0?: boolean;\n\n /** Whether to auto-setup SIGTERM/SIGINT handlers for graceful shutdown\n * @default true\n */\n shouldSetupSignalHandlers?: boolean;\n\n /**\n * Global shutdown hook called before all clients are shut down\n * @default undefined\n */\n beforeShutdown?: (() => Promise<void>) | undefined;\n\n /**\n * Global shutdown hook called after all clients are shut down\n * @default undefined\n */\n onShutdown?: (() => Promise<void>) | undefined;\n\n /** The interval in milliseconds at which the liveness checks are repeated (optional)\n * * @default 10_000 \n */\n livenessPeriodMs?: number;\n\n /** The interval in milliseconds at which the readiness checks are repeated (optional)\n * * @default 5_000\n */\n readinessPeriodMs?: number;\n\n /**\n * Enable liveness check optimization - if true and all clients are in both liveness and readiness checks,\n * liveness checks will be skipped when pod is already ready (readiness check passed)\n * @default true\n */\n enableLivenessOptimization?: boolean;\n}\n\n/**\n * List of clients and general configuration for HealthManager\n */\nexport interface HealthManagerOptions {\n /** Client configurations */\n clients?: {\n /** RabbitMQ client configuration - can be a single config or array of configs */\n rabbit?: ClientConfig<any> | ClientConfig<any>[];\n\n /** Sequelize instance configuration - can be a single config or array of configs */\n sequelize?: ClientConfig<any> | ClientConfig<any>[];\n\n /** Redis client configuration - can be a single config or array of configs */\n redis?: ClientConfig<any> | ClientConfig<any>[];\n\n /** Elasticsearch client configuration - can be a single config or array of configs */\n elasticsearch?: ClientConfig<any> | ClientConfig<any>[];\n };\n\n /** General configuration options */\n config?: HealthManagerConfig;\n\n /** Logger instance for logging messages */\n logger: LoggerInstanceManager;\n\n /** Optional HTTP or HTTPS server instance to manage during shutdown */\n server?: ServerConfig\n}\n\nexport interface RedisClient {\n ping: () => Promise<string | void>;\n}\n\n/**\n * Sequelize database interface\n */\nexport interface SequelizeClient {\n query(sql: string | { query: string; values: unknown[]; }): Promise<any>;\n}\n\n/**\n * Elasticsearch client interface\n */\nexport interface ElasticsearchClient {\n ping: () => Promise<boolean>;\n}\n\nexport interface EventsClient {\n exitHandler: () => Promise<void>;\n}\n\n/**\n * RabbitMQ client interface\n */\nexport interface RabbitMQClient {\n isConnected: () => Promise<boolean> | boolean;\n}\n\nexport const CLIENT_TYPES = {\n RABBIT: 'rabbit',\n SEQUELIZE: 'sequelize',\n REDIS: 'redis',\n ELASTIC_SEARCH: 'elasticsearch',\n EVENTS: 'events',\n} as const;\n\nexport type ClientType = typeof CLIENT_TYPES[keyof typeof CLIENT_TYPES];\n"],"mappings":"AAiKA,MAAa,EAAe,CAC1B,OAAQ,SACR,UAAW,YACX,MAAO,QACP,eAAgB,gBAChB,OAAQ,SACT"}
package/lib/index.d.cts CHANGED
@@ -1,3 +1,4 @@
1
1
  import { aliveEndpointMiddleware } from "./alive-endpoint/middleware.cjs";
2
+ import { HealthManagerOptions } from "./health-manager/types.cjs";
2
3
  import { HealthManager } from "./health-manager/health-manager.cjs";
3
- export { HealthManager, aliveEndpointMiddleware as aliveEndpoint };
4
+ export { HealthManager, type HealthManagerOptions, aliveEndpointMiddleware as aliveEndpoint };
package/lib/index.d.ts CHANGED
@@ -1,3 +1,4 @@
1
1
  import { aliveEndpointMiddleware } from "./alive-endpoint/middleware.js";
2
+ import { HealthManagerOptions } from "./health-manager/types.js";
2
3
  import { HealthManager } from "./health-manager/health-manager.js";
3
- export { HealthManager, aliveEndpointMiddleware as aliveEndpoint };
4
+ export { HealthManager, type HealthManagerOptions, aliveEndpointMiddleware as aliveEndpoint };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@autofleet/nitur",
3
- "version": "2.2.0-beta.0",
3
+ "version": "2.2.0-beta.2",
4
4
  "description": "A package for service monitoring",
5
5
  "type": "module",
6
6
  "main": "./lib/index.js",
@@ -47,7 +47,7 @@
47
47
  ],
48
48
  "dependencies": {
49
49
  "http-terminator": "^3.2.0",
50
- "@autofleet/logger": "^4.2.35"
50
+ "@autofleet/logger": "^4.2.36"
51
51
  },
52
52
  "engines": {
53
53
  "node": "^18 || ^20 || >=22.0.0"