@spfn/core 0.2.0-beta.16 → 0.2.0-beta.17
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.
- package/dist/config/index.js +3 -3
- package/dist/config/index.js.map +1 -1
- package/dist/server/index.d.ts +126 -4
- package/dist/server/index.js +263 -36
- package/dist/server/index.js.map +1 -1
- package/package.json +1 -1
package/dist/config/index.js
CHANGED
|
@@ -237,9 +237,9 @@ var coreEnvSchema = defineEnvSchema({
|
|
|
237
237
|
examples: [3e4, 6e4, 12e4]
|
|
238
238
|
}),
|
|
239
239
|
SHUTDOWN_TIMEOUT: envNumber({
|
|
240
|
-
description: "Graceful shutdown timeout in milliseconds",
|
|
241
|
-
default:
|
|
242
|
-
examples: [
|
|
240
|
+
description: "Graceful shutdown timeout in milliseconds (must be less than k8s terminationGracePeriodSeconds minus preStop sleep, with safety margin)",
|
|
241
|
+
default: 28e4,
|
|
242
|
+
examples: [3e4, 12e4, 28e4]
|
|
243
243
|
}),
|
|
244
244
|
// ========================================================================
|
|
245
245
|
// Next.js Integration
|
package/dist/config/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/config/schema.ts","../../src/config/index.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Core Package Environment Variable Schema\n *\n * Centralized schema definition for all environment variables used in @spfn/core.\n * This provides type safety, validation, and documentation for environment configuration.\n *\n * @module config/schema\n */\n\nimport {\n defineEnvSchema,\n envEnum,\n envNumber,\n envBoolean,\n envUrl,\n envString,\n parsePostgresUrl,\n parseRedisUrl,\n} from '@spfn/core/env';\n\n/**\n * Core package environment variable schema\n *\n * Defines all environment variables with:\n * - Type information\n * - Default values\n * - Validation rules\n * - Documentation\n *\n * @example\n * ```typescript\n * import { coreEnvSchema } from '@spfn/core/config';\n *\n * // Access schema information\n * console.log(coreEnvSchema.DB_POOL_MAX.description);\n * console.log(coreEnvSchema.DB_POOL_MAX.default);\n * ```\n */\nexport const coreEnvSchema = defineEnvSchema({\n // ========================================================================\n // Core Environment\n // ========================================================================\n\n NODE_ENV: envEnum(['local', 'development', 'staging', 'production', 'test'] as const, {\n description: 'Node.js runtime environment',\n default: 'local',\n nextjs: true,\n }),\n\n // ========================================================================\n // Database - Connection\n // ========================================================================\n\n DATABASE_URL: envString({\n description: 'Primary database connection URL',\n required: false,\n sensitive: true,\n validator: parsePostgresUrl,\n examples: ['postgresql://user:password@localhost:5432/dbname'],\n }),\n\n DATABASE_WRITE_URL: envString({\n description: 'Write database URL (master-replica pattern)',\n required: false,\n sensitive: true,\n validator: parsePostgresUrl,\n examples: ['postgresql://user:password@master:5432/dbname'],\n }),\n\n DATABASE_READ_URL: envString({\n description: 'Read database URL (master-replica pattern)',\n required: false,\n sensitive: true,\n validator: parsePostgresUrl,\n examples: ['postgresql://user:password@replica:5432/dbname'],\n }),\n\n\n // ========================================================================\n // Database - Connection Pool\n // ========================================================================\n\n DB_POOL_MAX: envNumber({\n description: 'Maximum number of database connections in pool',\n default: 10,\n examples: [10, 20, 50],\n }),\n\n DB_POOL_IDLE_TIMEOUT: envNumber({\n description: 'Database connection idle timeout in seconds',\n default: 30,\n examples: [20, 30, 60],\n }),\n\n // ========================================================================\n // Database - Retry Configuration\n // ========================================================================\n\n DB_RETRY_MAX: envNumber({\n description: 'Maximum number of database connection retry attempts',\n default: 3,\n examples: [3, 5, 10],\n }),\n\n DB_RETRY_INITIAL_DELAY: envNumber({\n description: 'Initial delay between database retry attempts (milliseconds)',\n default: 100,\n examples: [50, 100, 200],\n }),\n\n DB_RETRY_MAX_DELAY: envNumber({\n description: 'Maximum delay cap for database retry attempts (milliseconds)',\n default: 10000,\n examples: [5000, 10000, 30000],\n }),\n\n DB_RETRY_FACTOR: envNumber({\n description: 'Exponential backoff factor for database retry delays',\n default: 2,\n examples: [2, 1.5, 3],\n }),\n\n // ========================================================================\n // Database - Health Check\n // ========================================================================\n\n DB_HEALTH_CHECK_ENABLED: envBoolean({\n description: 'Enable periodic database health checks',\n default: true,\n examples: [true, false],\n }),\n\n DB_HEALTH_CHECK_INTERVAL: envNumber({\n description: 'Database health check interval (milliseconds)',\n default: 60000,\n examples: [30000, 60000, 120000],\n }),\n\n DB_HEALTH_CHECK_RECONNECT: envBoolean({\n description: 'Reconnect to database on health check failure',\n default: true,\n examples: [true, false],\n }),\n\n DB_HEALTH_CHECK_MAX_RETRIES: envNumber({\n description: 'Maximum health check retry attempts before marking as failed',\n default: 3,\n examples: [3, 5, 10],\n }),\n\n DB_HEALTH_CHECK_RETRY_INTERVAL: envNumber({\n description: 'Interval between health check retry attempts (milliseconds)',\n default: 5000,\n examples: [5000, 10000, 15000],\n }),\n\n // ========================================================================\n // Database - Monitoring\n // ========================================================================\n\n DB_MONITORING_ENABLED: envBoolean({\n description: 'Enable database query performance monitoring',\n default: false,\n examples: [true, false],\n }),\n\n DB_MONITORING_SLOW_THRESHOLD: envNumber({\n description: 'Slow query threshold for monitoring (milliseconds)',\n default: 1000,\n examples: [500, 1000, 2000],\n }),\n\n DB_MONITORING_LOG_QUERIES: envBoolean({\n description: 'Log all database queries (not just slow queries)',\n default: false,\n examples: [true, false],\n }),\n\n // ========================================================================\n // Database - Transaction\n // ========================================================================\n\n TRANSACTION_TIMEOUT: envNumber({\n description: 'Transaction timeout in milliseconds',\n default: 30000,\n examples: [10000, 30000, 60000],\n }),\n\n // ========================================================================\n // Database - Development\n // ========================================================================\n\n DB_DEBUG_TRACE: envBoolean({\n description: 'Enable detailed debug tracing for database operations',\n default: false,\n examples: [true, false],\n }),\n\n // ========================================================================\n // Drizzle ORM\n // ========================================================================\n\n DRIZZLE_SCHEMA_PATH: envString({\n description: 'Path to Drizzle schema configuration',\n required: false,\n default: './src/server/entities/config.ts',\n examples: ['./src/db/schema.ts', './src/server/entities/config.ts'],\n }),\n\n DRIZZLE_OUT_DIR: envString({\n description: 'Output directory for Drizzle migrations',\n required: false,\n default: './drizzle',\n examples: ['./drizzle', './migrations'],\n }),\n\n // ========================================================================\n // Logger - Core\n // ========================================================================\n\n SPFN_LOG_LEVEL: envEnum(['debug', 'info', 'warn', 'error', 'fatal'] as const, {\n description: 'Minimum log level to output',\n default: 'info'\n }),\n\n // ========================================================================\n // Cache (Redis/Valkey)\n // ========================================================================\n\n CACHE_URL: envString({\n description: 'Single Redis/Valkey instance URL',\n required: false,\n sensitive: true,\n validator: parseRedisUrl,\n examples: ['redis://localhost:6379', 'rediss://secure.cache.com:6380'],\n }),\n\n CACHE_WRITE_URL: envString({\n description: 'Master Redis/Valkey URL for writes (master-replica pattern)',\n required: false,\n sensitive: true,\n validator: parseRedisUrl,\n examples: ['redis://master:6379'],\n }),\n\n CACHE_READ_URL: envString({\n description: 'Replica Redis/Valkey URL for reads (master-replica pattern)',\n required: false,\n sensitive: true,\n validator: parseRedisUrl,\n examples: ['redis://replica:6379'],\n }),\n\n CACHE_SENTINEL_HOSTS: envString({\n description: 'Comma-separated Redis Sentinel hosts',\n required: false,\n examples: ['sentinel1:26379,sentinel2:26379'],\n }),\n\n CACHE_CLUSTER_NODES: envString({\n description: 'Comma-separated Redis Cluster nodes',\n required: false,\n examples: ['node1:6379,node2:6379,node3:6379'],\n }),\n\n CACHE_MASTER_NAME: envString({\n description: 'Redis Sentinel master name',\n required: false,\n examples: ['mymaster'],\n }),\n\n CACHE_PASSWORD: envString({\n description: 'Redis/Valkey authentication password',\n required: false,\n sensitive: true,\n examples: ['your-redis-password'],\n }),\n\n CACHE_TLS_REJECT_UNAUTHORIZED: envBoolean({\n description: 'Verify TLS certificates for secure Redis connections',\n default: true,\n examples: [true, false],\n }),\n\n // ========================================================================\n // Server - Core\n // ========================================================================\n\n PORT: envNumber({\n description: 'Server port number',\n default: 4000,\n examples: [3000, 4000, 8080],\n }),\n\n HOST: envString({\n description: 'Server hostname',\n default: 'localhost',\n required: false,\n examples: ['localhost', '0.0.0.0', '127.0.0.1'],\n }),\n\n // ========================================================================\n // Server - Timeout\n // ========================================================================\n\n SERVER_TIMEOUT: envNumber({\n description: 'Request timeout in milliseconds',\n default: 120000,\n examples: [60000, 120000, 300000],\n }),\n\n SERVER_KEEPALIVE_TIMEOUT: envNumber({\n description: 'Keep-alive timeout in milliseconds',\n default: 65000,\n examples: [30000, 65000, 120000],\n }),\n\n SERVER_HEADERS_TIMEOUT: envNumber({\n description: 'Headers timeout in milliseconds',\n default: 60000,\n examples: [30000, 60000, 120000],\n }),\n\n SHUTDOWN_TIMEOUT: envNumber({\n description: 'Graceful shutdown timeout in milliseconds',\n default: 30000,\n examples: [10000, 30000, 60000],\n }),\n\n // ========================================================================\n // Next.js Integration\n // ========================================================================\n\n SPFN_API_URL: envUrl({\n description: 'SPFN API URL (used by Next.js to call backend)',\n required: true,\n nextjs: true,\n examples: ['http://localhost:8790', 'https://api.your-app.com'],\n }),\n\n NEXT_PUBLIC_SPFN_API_URL: envUrl({\n description: 'SPFN API URL (used by Next.js to call backend)',\n required: true,\n nextjs: true,\n examples: ['http://localhost:8790', 'https://api.your-app.com'],\n }),\n\n SPFN_APP_URL: envUrl({\n description: 'Next.js application URL (used by SPFN server)',\n required: false,\n nextjs: true,\n examples: ['http://localhost:3790', 'https://your-app.com'],\n }),\n});","/**\n * Core Package Configuration\n *\n * @example\n * ```typescript\n * import { registry } from '@spfn/core/config';\n *\n * const env = registry.validate();\n * console.log(env.DB_POOL_MAX);\n * ```\n *\n * @module config\n */\n\nimport { createEnvRegistry } from '@spfn/core/env';\nimport { coreEnvSchema } from './schema';\n\n/**\n * Core environment schema\n */\nexport { coreEnvSchema as envSchema } from './schema';\n\n/**\n * Environment registry\n *\n * @example\n * ```typescript\n * // Reset for testing\n * registry.reset();\n * ```\n */\nexport const registry = createEnvRegistry(coreEnvSchema);\n\n/**\n * Validated environment configuration\n */\nexport const env = registry.validate();"]}
|
|
1
|
+
{"version":3,"sources":["../../src/config/schema.ts","../../src/config/index.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Core Package Environment Variable Schema\n *\n * Centralized schema definition for all environment variables used in @spfn/core.\n * This provides type safety, validation, and documentation for environment configuration.\n *\n * @module config/schema\n */\n\nimport {\n defineEnvSchema,\n envEnum,\n envNumber,\n envBoolean,\n envUrl,\n envString,\n parsePostgresUrl,\n parseRedisUrl,\n} from '@spfn/core/env';\n\n/**\n * Core package environment variable schema\n *\n * Defines all environment variables with:\n * - Type information\n * - Default values\n * - Validation rules\n * - Documentation\n *\n * @example\n * ```typescript\n * import { coreEnvSchema } from '@spfn/core/config';\n *\n * // Access schema information\n * console.log(coreEnvSchema.DB_POOL_MAX.description);\n * console.log(coreEnvSchema.DB_POOL_MAX.default);\n * ```\n */\nexport const coreEnvSchema = defineEnvSchema({\n // ========================================================================\n // Core Environment\n // ========================================================================\n\n NODE_ENV: envEnum(['local', 'development', 'staging', 'production', 'test'] as const, {\n description: 'Node.js runtime environment',\n default: 'local',\n nextjs: true,\n }),\n\n // ========================================================================\n // Database - Connection\n // ========================================================================\n\n DATABASE_URL: envString({\n description: 'Primary database connection URL',\n required: false,\n sensitive: true,\n validator: parsePostgresUrl,\n examples: ['postgresql://user:password@localhost:5432/dbname'],\n }),\n\n DATABASE_WRITE_URL: envString({\n description: 'Write database URL (master-replica pattern)',\n required: false,\n sensitive: true,\n validator: parsePostgresUrl,\n examples: ['postgresql://user:password@master:5432/dbname'],\n }),\n\n DATABASE_READ_URL: envString({\n description: 'Read database URL (master-replica pattern)',\n required: false,\n sensitive: true,\n validator: parsePostgresUrl,\n examples: ['postgresql://user:password@replica:5432/dbname'],\n }),\n\n\n // ========================================================================\n // Database - Connection Pool\n // ========================================================================\n\n DB_POOL_MAX: envNumber({\n description: 'Maximum number of database connections in pool',\n default: 10,\n examples: [10, 20, 50],\n }),\n\n DB_POOL_IDLE_TIMEOUT: envNumber({\n description: 'Database connection idle timeout in seconds',\n default: 30,\n examples: [20, 30, 60],\n }),\n\n // ========================================================================\n // Database - Retry Configuration\n // ========================================================================\n\n DB_RETRY_MAX: envNumber({\n description: 'Maximum number of database connection retry attempts',\n default: 3,\n examples: [3, 5, 10],\n }),\n\n DB_RETRY_INITIAL_DELAY: envNumber({\n description: 'Initial delay between database retry attempts (milliseconds)',\n default: 100,\n examples: [50, 100, 200],\n }),\n\n DB_RETRY_MAX_DELAY: envNumber({\n description: 'Maximum delay cap for database retry attempts (milliseconds)',\n default: 10000,\n examples: [5000, 10000, 30000],\n }),\n\n DB_RETRY_FACTOR: envNumber({\n description: 'Exponential backoff factor for database retry delays',\n default: 2,\n examples: [2, 1.5, 3],\n }),\n\n // ========================================================================\n // Database - Health Check\n // ========================================================================\n\n DB_HEALTH_CHECK_ENABLED: envBoolean({\n description: 'Enable periodic database health checks',\n default: true,\n examples: [true, false],\n }),\n\n DB_HEALTH_CHECK_INTERVAL: envNumber({\n description: 'Database health check interval (milliseconds)',\n default: 60000,\n examples: [30000, 60000, 120000],\n }),\n\n DB_HEALTH_CHECK_RECONNECT: envBoolean({\n description: 'Reconnect to database on health check failure',\n default: true,\n examples: [true, false],\n }),\n\n DB_HEALTH_CHECK_MAX_RETRIES: envNumber({\n description: 'Maximum health check retry attempts before marking as failed',\n default: 3,\n examples: [3, 5, 10],\n }),\n\n DB_HEALTH_CHECK_RETRY_INTERVAL: envNumber({\n description: 'Interval between health check retry attempts (milliseconds)',\n default: 5000,\n examples: [5000, 10000, 15000],\n }),\n\n // ========================================================================\n // Database - Monitoring\n // ========================================================================\n\n DB_MONITORING_ENABLED: envBoolean({\n description: 'Enable database query performance monitoring',\n default: false,\n examples: [true, false],\n }),\n\n DB_MONITORING_SLOW_THRESHOLD: envNumber({\n description: 'Slow query threshold for monitoring (milliseconds)',\n default: 1000,\n examples: [500, 1000, 2000],\n }),\n\n DB_MONITORING_LOG_QUERIES: envBoolean({\n description: 'Log all database queries (not just slow queries)',\n default: false,\n examples: [true, false],\n }),\n\n // ========================================================================\n // Database - Transaction\n // ========================================================================\n\n TRANSACTION_TIMEOUT: envNumber({\n description: 'Transaction timeout in milliseconds',\n default: 30000,\n examples: [10000, 30000, 60000],\n }),\n\n // ========================================================================\n // Database - Development\n // ========================================================================\n\n DB_DEBUG_TRACE: envBoolean({\n description: 'Enable detailed debug tracing for database operations',\n default: false,\n examples: [true, false],\n }),\n\n // ========================================================================\n // Drizzle ORM\n // ========================================================================\n\n DRIZZLE_SCHEMA_PATH: envString({\n description: 'Path to Drizzle schema configuration',\n required: false,\n default: './src/server/entities/config.ts',\n examples: ['./src/db/schema.ts', './src/server/entities/config.ts'],\n }),\n\n DRIZZLE_OUT_DIR: envString({\n description: 'Output directory for Drizzle migrations',\n required: false,\n default: './drizzle',\n examples: ['./drizzle', './migrations'],\n }),\n\n // ========================================================================\n // Logger - Core\n // ========================================================================\n\n SPFN_LOG_LEVEL: envEnum(['debug', 'info', 'warn', 'error', 'fatal'] as const, {\n description: 'Minimum log level to output',\n default: 'info'\n }),\n\n // ========================================================================\n // Cache (Redis/Valkey)\n // ========================================================================\n\n CACHE_URL: envString({\n description: 'Single Redis/Valkey instance URL',\n required: false,\n sensitive: true,\n validator: parseRedisUrl,\n examples: ['redis://localhost:6379', 'rediss://secure.cache.com:6380'],\n }),\n\n CACHE_WRITE_URL: envString({\n description: 'Master Redis/Valkey URL for writes (master-replica pattern)',\n required: false,\n sensitive: true,\n validator: parseRedisUrl,\n examples: ['redis://master:6379'],\n }),\n\n CACHE_READ_URL: envString({\n description: 'Replica Redis/Valkey URL for reads (master-replica pattern)',\n required: false,\n sensitive: true,\n validator: parseRedisUrl,\n examples: ['redis://replica:6379'],\n }),\n\n CACHE_SENTINEL_HOSTS: envString({\n description: 'Comma-separated Redis Sentinel hosts',\n required: false,\n examples: ['sentinel1:26379,sentinel2:26379'],\n }),\n\n CACHE_CLUSTER_NODES: envString({\n description: 'Comma-separated Redis Cluster nodes',\n required: false,\n examples: ['node1:6379,node2:6379,node3:6379'],\n }),\n\n CACHE_MASTER_NAME: envString({\n description: 'Redis Sentinel master name',\n required: false,\n examples: ['mymaster'],\n }),\n\n CACHE_PASSWORD: envString({\n description: 'Redis/Valkey authentication password',\n required: false,\n sensitive: true,\n examples: ['your-redis-password'],\n }),\n\n CACHE_TLS_REJECT_UNAUTHORIZED: envBoolean({\n description: 'Verify TLS certificates for secure Redis connections',\n default: true,\n examples: [true, false],\n }),\n\n // ========================================================================\n // Server - Core\n // ========================================================================\n\n PORT: envNumber({\n description: 'Server port number',\n default: 4000,\n examples: [3000, 4000, 8080],\n }),\n\n HOST: envString({\n description: 'Server hostname',\n default: 'localhost',\n required: false,\n examples: ['localhost', '0.0.0.0', '127.0.0.1'],\n }),\n\n // ========================================================================\n // Server - Timeout\n // ========================================================================\n\n SERVER_TIMEOUT: envNumber({\n description: 'Request timeout in milliseconds',\n default: 120000,\n examples: [60000, 120000, 300000],\n }),\n\n SERVER_KEEPALIVE_TIMEOUT: envNumber({\n description: 'Keep-alive timeout in milliseconds',\n default: 65000,\n examples: [30000, 65000, 120000],\n }),\n\n SERVER_HEADERS_TIMEOUT: envNumber({\n description: 'Headers timeout in milliseconds',\n default: 60000,\n examples: [30000, 60000, 120000],\n }),\n\n SHUTDOWN_TIMEOUT: envNumber({\n description: 'Graceful shutdown timeout in milliseconds (must be less than k8s terminationGracePeriodSeconds minus preStop sleep, with safety margin)',\n default: 280000,\n examples: [30000, 120000, 280000],\n }),\n\n // ========================================================================\n // Next.js Integration\n // ========================================================================\n\n SPFN_API_URL: envUrl({\n description: 'SPFN API URL (used by Next.js to call backend)',\n required: true,\n nextjs: true,\n examples: ['http://localhost:8790', 'https://api.your-app.com'],\n }),\n\n NEXT_PUBLIC_SPFN_API_URL: envUrl({\n description: 'SPFN API URL (used by Next.js to call backend)',\n required: true,\n nextjs: true,\n examples: ['http://localhost:8790', 'https://api.your-app.com'],\n }),\n\n SPFN_APP_URL: envUrl({\n description: 'Next.js application URL (used by SPFN server)',\n required: false,\n nextjs: true,\n examples: ['http://localhost:3790', 'https://your-app.com'],\n }),\n});","/**\n * Core Package Configuration\n *\n * @example\n * ```typescript\n * import { registry } from '@spfn/core/config';\n *\n * const env = registry.validate();\n * console.log(env.DB_POOL_MAX);\n * ```\n *\n * @module config\n */\n\nimport { createEnvRegistry } from '@spfn/core/env';\nimport { coreEnvSchema } from './schema';\n\n/**\n * Core environment schema\n */\nexport { coreEnvSchema as envSchema } from './schema';\n\n/**\n * Environment registry\n *\n * @example\n * ```typescript\n * // Reset for testing\n * registry.reset();\n * ```\n */\nexport const registry = createEnvRegistry(coreEnvSchema);\n\n/**\n * Validated environment configuration\n */\nexport const env = registry.validate();"]}
|
package/dist/server/index.d.ts
CHANGED
|
@@ -319,9 +319,13 @@ interface ServerConfig {
|
|
|
319
319
|
shutdown?: {
|
|
320
320
|
/**
|
|
321
321
|
* Graceful shutdown timeout in milliseconds
|
|
322
|
-
* Maximum time to wait for
|
|
323
|
-
* After timeout, forces process
|
|
324
|
-
*
|
|
322
|
+
* Maximum time to wait for in-flight operations to drain and resource cleanup
|
|
323
|
+
* After timeout, forces process.exit() before k8s SIGKILL
|
|
324
|
+
*
|
|
325
|
+
* Formula: terminationGracePeriodSeconds - preStopSleep - safetyMargin
|
|
326
|
+
* Default: 300s - 5s - 15s = 280s
|
|
327
|
+
*
|
|
328
|
+
* @default 280000 (280 seconds)
|
|
325
329
|
* @env SHUTDOWN_TIMEOUT
|
|
326
330
|
*/
|
|
327
331
|
timeout?: number;
|
|
@@ -580,6 +584,124 @@ declare function createServer(config?: ServerConfig): Promise<Hono>;
|
|
|
580
584
|
*/
|
|
581
585
|
declare function startServer(config?: ServerConfig): Promise<ServerInstance>;
|
|
582
586
|
|
|
587
|
+
/**
|
|
588
|
+
* Shutdown Manager
|
|
589
|
+
*
|
|
590
|
+
* Manages graceful shutdown with drain behavior.
|
|
591
|
+
* All tracked operations must complete before shutdown proceeds.
|
|
592
|
+
*
|
|
593
|
+
* Features:
|
|
594
|
+
* - Hook registry: Multiple modules can register independent cleanup handlers
|
|
595
|
+
* - Operation tracking: Long-running tasks are awaited during shutdown (drain)
|
|
596
|
+
* - State management: isShuttingDown() for rejecting new work
|
|
597
|
+
*/
|
|
598
|
+
interface ShutdownHookOptions {
|
|
599
|
+
/**
|
|
600
|
+
* Timeout for this hook in milliseconds
|
|
601
|
+
* If the hook exceeds this time, it is skipped and the next hook runs
|
|
602
|
+
* @default 10000 (10s)
|
|
603
|
+
*/
|
|
604
|
+
timeout?: number;
|
|
605
|
+
/**
|
|
606
|
+
* Execution order (lower runs first)
|
|
607
|
+
* @default 100
|
|
608
|
+
*/
|
|
609
|
+
order?: number;
|
|
610
|
+
}
|
|
611
|
+
declare class ShutdownManager {
|
|
612
|
+
private state;
|
|
613
|
+
private hooks;
|
|
614
|
+
private operations;
|
|
615
|
+
private operationCounter;
|
|
616
|
+
/**
|
|
617
|
+
* Register a shutdown hook
|
|
618
|
+
*
|
|
619
|
+
* Hooks run in order during shutdown, after all tracked operations drain.
|
|
620
|
+
* Each hook has its own timeout — failure does not block subsequent hooks.
|
|
621
|
+
*
|
|
622
|
+
* @example
|
|
623
|
+
* shutdown.onShutdown('ai-service', async () => {
|
|
624
|
+
* await aiService.cancelPending();
|
|
625
|
+
* }, { timeout: 30000, order: 10 });
|
|
626
|
+
*/
|
|
627
|
+
onShutdown(name: string, handler: () => Promise<void>, options?: ShutdownHookOptions): void;
|
|
628
|
+
/**
|
|
629
|
+
* Track a long-running operation
|
|
630
|
+
*
|
|
631
|
+
* During shutdown (drain phase), the process waits for ALL tracked
|
|
632
|
+
* operations to complete before proceeding with cleanup.
|
|
633
|
+
*
|
|
634
|
+
* If shutdown has already started, the operation is rejected immediately.
|
|
635
|
+
*
|
|
636
|
+
* @returns The operation result (pass-through)
|
|
637
|
+
*
|
|
638
|
+
* @example
|
|
639
|
+
* const result = await shutdown.trackOperation(
|
|
640
|
+
* 'ai-generate',
|
|
641
|
+
* aiService.generate(prompt)
|
|
642
|
+
* );
|
|
643
|
+
*/
|
|
644
|
+
trackOperation<T>(name: string, operation: Promise<T>): Promise<T>;
|
|
645
|
+
/**
|
|
646
|
+
* Whether the server is shutting down
|
|
647
|
+
*
|
|
648
|
+
* Use this to reject new work early (e.g., return 503 in route handlers).
|
|
649
|
+
*/
|
|
650
|
+
isShuttingDown(): boolean;
|
|
651
|
+
/**
|
|
652
|
+
* Number of currently active tracked operations
|
|
653
|
+
*/
|
|
654
|
+
getActiveOperationCount(): number;
|
|
655
|
+
/**
|
|
656
|
+
* Mark shutdown as started immediately
|
|
657
|
+
*
|
|
658
|
+
* Call this at the very beginning of the shutdown sequence so that:
|
|
659
|
+
* - Health check returns 503 right away
|
|
660
|
+
* - trackOperation() rejects new work
|
|
661
|
+
* - isShuttingDown() returns true
|
|
662
|
+
*/
|
|
663
|
+
beginShutdown(): void;
|
|
664
|
+
/**
|
|
665
|
+
* Execute the full shutdown sequence
|
|
666
|
+
*
|
|
667
|
+
* 1. State → draining (reject new operations)
|
|
668
|
+
* 2. Wait for all tracked operations to complete (drain)
|
|
669
|
+
* 3. Run shutdown hooks in order
|
|
670
|
+
* 4. State → closed
|
|
671
|
+
*
|
|
672
|
+
* @param drainTimeout - Max time to wait for operations to drain (ms)
|
|
673
|
+
*/
|
|
674
|
+
execute(drainTimeout: number): Promise<void>;
|
|
675
|
+
/**
|
|
676
|
+
* Wait for all tracked operations to complete, up to drainTimeout
|
|
677
|
+
*/
|
|
678
|
+
private drain;
|
|
679
|
+
/**
|
|
680
|
+
* Execute registered shutdown hooks in order
|
|
681
|
+
*/
|
|
682
|
+
private executeHooks;
|
|
683
|
+
}
|
|
684
|
+
/**
|
|
685
|
+
* Get the global ShutdownManager instance
|
|
686
|
+
*
|
|
687
|
+
* Available after server starts. Use this to register shutdown hooks
|
|
688
|
+
* or track long-running operations.
|
|
689
|
+
*
|
|
690
|
+
* @example
|
|
691
|
+
* import { getShutdownManager } from '@spfn/core/server';
|
|
692
|
+
*
|
|
693
|
+
* const shutdown = getShutdownManager();
|
|
694
|
+
*
|
|
695
|
+
* // Register cleanup
|
|
696
|
+
* shutdown.onShutdown('my-service', async () => {
|
|
697
|
+
* await myService.close();
|
|
698
|
+
* });
|
|
699
|
+
*
|
|
700
|
+
* // Track long operation
|
|
701
|
+
* await shutdown.trackOperation('ai-task', longRunningPromise);
|
|
702
|
+
*/
|
|
703
|
+
declare function getShutdownManager(): ShutdownManager;
|
|
704
|
+
|
|
583
705
|
/**
|
|
584
706
|
* Server Config Builder
|
|
585
707
|
*
|
|
@@ -770,4 +892,4 @@ declare class ServerConfigBuilder {
|
|
|
770
892
|
*/
|
|
771
893
|
declare function defineServerConfig(): ServerConfigBuilder;
|
|
772
894
|
|
|
773
|
-
export { type AppFactory, type ServerConfig, type ServerInstance, createServer, defineServerConfig, loadEnvFiles, startServer };
|
|
895
|
+
export { type AppFactory, type ServerConfig, type ServerInstance, type ShutdownHookOptions, createServer, defineServerConfig, getShutdownManager, loadEnvFiles, startServer };
|
package/dist/server/index.js
CHANGED
|
@@ -580,8 +580,231 @@ var SSETokenManager = class {
|
|
|
580
580
|
}
|
|
581
581
|
}
|
|
582
582
|
};
|
|
583
|
+
var serverLogger = logger.child("@spfn/core:server");
|
|
584
|
+
|
|
585
|
+
// src/server/shutdown-manager.ts
|
|
586
|
+
var DEFAULT_HOOK_TIMEOUT = 1e4;
|
|
587
|
+
var DEFAULT_HOOK_ORDER = 100;
|
|
588
|
+
var DRAIN_POLL_INTERVAL = 500;
|
|
589
|
+
var ShutdownManager = class {
|
|
590
|
+
state = "running";
|
|
591
|
+
hooks = [];
|
|
592
|
+
operations = /* @__PURE__ */ new Map();
|
|
593
|
+
operationCounter = 0;
|
|
594
|
+
/**
|
|
595
|
+
* Register a shutdown hook
|
|
596
|
+
*
|
|
597
|
+
* Hooks run in order during shutdown, after all tracked operations drain.
|
|
598
|
+
* Each hook has its own timeout — failure does not block subsequent hooks.
|
|
599
|
+
*
|
|
600
|
+
* @example
|
|
601
|
+
* shutdown.onShutdown('ai-service', async () => {
|
|
602
|
+
* await aiService.cancelPending();
|
|
603
|
+
* }, { timeout: 30000, order: 10 });
|
|
604
|
+
*/
|
|
605
|
+
onShutdown(name, handler, options) {
|
|
606
|
+
this.hooks.push({
|
|
607
|
+
name,
|
|
608
|
+
handler,
|
|
609
|
+
timeout: options?.timeout ?? DEFAULT_HOOK_TIMEOUT,
|
|
610
|
+
order: options?.order ?? DEFAULT_HOOK_ORDER
|
|
611
|
+
});
|
|
612
|
+
this.hooks.sort((a, b) => a.order - b.order);
|
|
613
|
+
serverLogger.debug(`Shutdown hook registered: ${name}`, {
|
|
614
|
+
order: options?.order ?? DEFAULT_HOOK_ORDER,
|
|
615
|
+
timeout: `${options?.timeout ?? DEFAULT_HOOK_TIMEOUT}ms`
|
|
616
|
+
});
|
|
617
|
+
}
|
|
618
|
+
/**
|
|
619
|
+
* Track a long-running operation
|
|
620
|
+
*
|
|
621
|
+
* During shutdown (drain phase), the process waits for ALL tracked
|
|
622
|
+
* operations to complete before proceeding with cleanup.
|
|
623
|
+
*
|
|
624
|
+
* If shutdown has already started, the operation is rejected immediately.
|
|
625
|
+
*
|
|
626
|
+
* @returns The operation result (pass-through)
|
|
627
|
+
*
|
|
628
|
+
* @example
|
|
629
|
+
* const result = await shutdown.trackOperation(
|
|
630
|
+
* 'ai-generate',
|
|
631
|
+
* aiService.generate(prompt)
|
|
632
|
+
* );
|
|
633
|
+
*/
|
|
634
|
+
async trackOperation(name, operation) {
|
|
635
|
+
if (this.state !== "running") {
|
|
636
|
+
throw new Error(`Cannot start operation '${name}': server is shutting down`);
|
|
637
|
+
}
|
|
638
|
+
const id = `${name}-${++this.operationCounter}`;
|
|
639
|
+
this.operations.set(id, {
|
|
640
|
+
name,
|
|
641
|
+
startedAt: Date.now()
|
|
642
|
+
});
|
|
643
|
+
serverLogger.debug(`Operation tracked: ${id}`, {
|
|
644
|
+
activeOperations: this.operations.size
|
|
645
|
+
});
|
|
646
|
+
try {
|
|
647
|
+
return await operation;
|
|
648
|
+
} finally {
|
|
649
|
+
this.operations.delete(id);
|
|
650
|
+
serverLogger.debug(`Operation completed: ${id}`, {
|
|
651
|
+
activeOperations: this.operations.size
|
|
652
|
+
});
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
/**
|
|
656
|
+
* Whether the server is shutting down
|
|
657
|
+
*
|
|
658
|
+
* Use this to reject new work early (e.g., return 503 in route handlers).
|
|
659
|
+
*/
|
|
660
|
+
isShuttingDown() {
|
|
661
|
+
return this.state !== "running";
|
|
662
|
+
}
|
|
663
|
+
/**
|
|
664
|
+
* Number of currently active tracked operations
|
|
665
|
+
*/
|
|
666
|
+
getActiveOperationCount() {
|
|
667
|
+
return this.operations.size;
|
|
668
|
+
}
|
|
669
|
+
/**
|
|
670
|
+
* Mark shutdown as started immediately
|
|
671
|
+
*
|
|
672
|
+
* Call this at the very beginning of the shutdown sequence so that:
|
|
673
|
+
* - Health check returns 503 right away
|
|
674
|
+
* - trackOperation() rejects new work
|
|
675
|
+
* - isShuttingDown() returns true
|
|
676
|
+
*/
|
|
677
|
+
beginShutdown() {
|
|
678
|
+
if (this.state !== "running") {
|
|
679
|
+
return;
|
|
680
|
+
}
|
|
681
|
+
this.state = "draining";
|
|
682
|
+
serverLogger.info("Shutdown manager: state set to draining");
|
|
683
|
+
}
|
|
684
|
+
/**
|
|
685
|
+
* Execute the full shutdown sequence
|
|
686
|
+
*
|
|
687
|
+
* 1. State → draining (reject new operations)
|
|
688
|
+
* 2. Wait for all tracked operations to complete (drain)
|
|
689
|
+
* 3. Run shutdown hooks in order
|
|
690
|
+
* 4. State → closed
|
|
691
|
+
*
|
|
692
|
+
* @param drainTimeout - Max time to wait for operations to drain (ms)
|
|
693
|
+
*/
|
|
694
|
+
async execute(drainTimeout) {
|
|
695
|
+
if (this.state === "closed") {
|
|
696
|
+
serverLogger.warn("ShutdownManager.execute() called but already closed");
|
|
697
|
+
return;
|
|
698
|
+
}
|
|
699
|
+
this.state = "draining";
|
|
700
|
+
serverLogger.info("Shutdown manager: draining started", {
|
|
701
|
+
activeOperations: this.operations.size,
|
|
702
|
+
registeredHooks: this.hooks.length,
|
|
703
|
+
drainTimeout: `${drainTimeout}ms`
|
|
704
|
+
});
|
|
705
|
+
await this.drain(drainTimeout);
|
|
706
|
+
await this.executeHooks();
|
|
707
|
+
this.state = "closed";
|
|
708
|
+
serverLogger.info("Shutdown manager: all hooks executed");
|
|
709
|
+
}
|
|
710
|
+
// ========================================================================
|
|
711
|
+
// Private
|
|
712
|
+
// ========================================================================
|
|
713
|
+
/**
|
|
714
|
+
* Wait for all tracked operations to complete, up to drainTimeout
|
|
715
|
+
*/
|
|
716
|
+
async drain(drainTimeout) {
|
|
717
|
+
if (this.operations.size === 0) {
|
|
718
|
+
serverLogger.info("Shutdown manager: no active operations, drain skipped");
|
|
719
|
+
return;
|
|
720
|
+
}
|
|
721
|
+
serverLogger.info(`Shutdown manager: waiting for ${this.operations.size} operations to drain...`);
|
|
722
|
+
const deadline = Date.now() + drainTimeout;
|
|
723
|
+
while (this.operations.size > 0 && Date.now() < deadline) {
|
|
724
|
+
const remaining = deadline - Date.now();
|
|
725
|
+
const ops = Array.from(this.operations.values()).map((op) => ({
|
|
726
|
+
name: op.name,
|
|
727
|
+
elapsed: `${Math.round((Date.now() - op.startedAt) / 1e3)}s`
|
|
728
|
+
}));
|
|
729
|
+
serverLogger.info("Shutdown manager: drain in progress", {
|
|
730
|
+
activeOperations: this.operations.size,
|
|
731
|
+
remainingTimeout: `${Math.round(remaining / 1e3)}s`,
|
|
732
|
+
operations: ops
|
|
733
|
+
});
|
|
734
|
+
await sleep(Math.min(DRAIN_POLL_INTERVAL, remaining));
|
|
735
|
+
}
|
|
736
|
+
if (this.operations.size > 0) {
|
|
737
|
+
const abandoned = Array.from(this.operations.values()).map((op) => op.name);
|
|
738
|
+
serverLogger.warn("Shutdown manager: drain timeout \u2014 abandoning operations", {
|
|
739
|
+
abandoned
|
|
740
|
+
});
|
|
741
|
+
} else {
|
|
742
|
+
serverLogger.info("Shutdown manager: all operations drained successfully");
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
/**
|
|
746
|
+
* Execute registered shutdown hooks in order
|
|
747
|
+
*/
|
|
748
|
+
async executeHooks() {
|
|
749
|
+
if (this.hooks.length === 0) {
|
|
750
|
+
return;
|
|
751
|
+
}
|
|
752
|
+
serverLogger.info(`Shutdown manager: executing ${this.hooks.length} hooks...`);
|
|
753
|
+
for (const hook of this.hooks) {
|
|
754
|
+
serverLogger.debug(`Shutdown hook [${hook.name}] starting (timeout: ${hook.timeout}ms)`);
|
|
755
|
+
try {
|
|
756
|
+
await withTimeout(
|
|
757
|
+
hook.handler(),
|
|
758
|
+
hook.timeout,
|
|
759
|
+
`Shutdown hook '${hook.name}' timeout after ${hook.timeout}ms`
|
|
760
|
+
);
|
|
761
|
+
serverLogger.info(`Shutdown hook [${hook.name}] completed`);
|
|
762
|
+
} catch (error) {
|
|
763
|
+
serverLogger.error(
|
|
764
|
+
`Shutdown hook [${hook.name}] failed`,
|
|
765
|
+
error
|
|
766
|
+
);
|
|
767
|
+
}
|
|
768
|
+
}
|
|
769
|
+
}
|
|
770
|
+
};
|
|
771
|
+
var instance = null;
|
|
772
|
+
function getShutdownManager() {
|
|
773
|
+
if (!instance) {
|
|
774
|
+
instance = new ShutdownManager();
|
|
775
|
+
}
|
|
776
|
+
return instance;
|
|
777
|
+
}
|
|
778
|
+
function resetShutdownManager() {
|
|
779
|
+
instance = null;
|
|
780
|
+
}
|
|
781
|
+
function sleep(ms) {
|
|
782
|
+
return new Promise((resolve2) => setTimeout(resolve2, ms));
|
|
783
|
+
}
|
|
784
|
+
async function withTimeout(promise, timeout, message) {
|
|
785
|
+
let timeoutId;
|
|
786
|
+
return Promise.race([
|
|
787
|
+
promise.finally(() => {
|
|
788
|
+
if (timeoutId) clearTimeout(timeoutId);
|
|
789
|
+
}),
|
|
790
|
+
new Promise((_, reject) => {
|
|
791
|
+
timeoutId = setTimeout(() => {
|
|
792
|
+
reject(new Error(message));
|
|
793
|
+
}, timeout);
|
|
794
|
+
})
|
|
795
|
+
]);
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
// src/server/helpers.ts
|
|
583
799
|
function createHealthCheckHandler(detailed) {
|
|
584
800
|
return async (c) => {
|
|
801
|
+
const shutdownManager = getShutdownManager();
|
|
802
|
+
if (shutdownManager.isShuttingDown()) {
|
|
803
|
+
return c.json({
|
|
804
|
+
status: "shutting_down",
|
|
805
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
806
|
+
}, 503);
|
|
807
|
+
}
|
|
585
808
|
const response = {
|
|
586
809
|
status: "ok",
|
|
587
810
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
@@ -695,7 +918,6 @@ function buildStartupConfig(config, timeouts) {
|
|
|
695
918
|
}
|
|
696
919
|
};
|
|
697
920
|
}
|
|
698
|
-
var serverLogger = logger.child("@spfn/core:server");
|
|
699
921
|
|
|
700
922
|
// src/server/create-server.ts
|
|
701
923
|
async function createServer(config) {
|
|
@@ -1162,11 +1384,6 @@ async function startServer(config) {
|
|
|
1162
1384
|
config: finalConfig,
|
|
1163
1385
|
close: async () => {
|
|
1164
1386
|
serverLogger.info("Manual server shutdown requested");
|
|
1165
|
-
if (shutdownState.isShuttingDown) {
|
|
1166
|
-
serverLogger.warn("Shutdown already in progress, ignoring manual close request");
|
|
1167
|
-
return;
|
|
1168
|
-
}
|
|
1169
|
-
shutdownState.isShuttingDown = true;
|
|
1170
1387
|
await shutdownServer();
|
|
1171
1388
|
}
|
|
1172
1389
|
};
|
|
@@ -1310,58 +1527,67 @@ function createShutdownHandler(server, config, shutdownState) {
|
|
|
1310
1527
|
return;
|
|
1311
1528
|
}
|
|
1312
1529
|
shutdownState.isShuttingDown = true;
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
if (timeoutId) clearTimeout(timeoutId);
|
|
1319
|
-
if (err) {
|
|
1320
|
-
serverLogger.error("HTTP server close error", err);
|
|
1321
|
-
reject(err);
|
|
1322
|
-
} else {
|
|
1323
|
-
serverLogger.info("HTTP server closed");
|
|
1324
|
-
resolve2();
|
|
1325
|
-
}
|
|
1326
|
-
});
|
|
1327
|
-
}),
|
|
1328
|
-
new Promise((_, reject) => {
|
|
1329
|
-
timeoutId = setTimeout(() => {
|
|
1330
|
-
reject(new Error(`HTTP server close timeout after ${TIMEOUTS.SERVER_CLOSE}ms`));
|
|
1331
|
-
}, TIMEOUTS.SERVER_CLOSE);
|
|
1332
|
-
})
|
|
1333
|
-
]).catch((error) => {
|
|
1334
|
-
if (timeoutId) clearTimeout(timeoutId);
|
|
1335
|
-
serverLogger.warn("HTTP server close timeout, forcing shutdown", error);
|
|
1336
|
-
});
|
|
1530
|
+
const shutdownTimeout = getShutdownTimeout(config.shutdown);
|
|
1531
|
+
const shutdownManager = getShutdownManager();
|
|
1532
|
+
shutdownManager.beginShutdown();
|
|
1533
|
+
serverLogger.info("Phase 1: Closing HTTP server (stop accepting new connections)...");
|
|
1534
|
+
await closeHttpServer(server);
|
|
1337
1535
|
if (config.jobs) {
|
|
1338
|
-
serverLogger.
|
|
1536
|
+
serverLogger.info("Phase 2: Stopping pg-boss...");
|
|
1339
1537
|
try {
|
|
1340
1538
|
await stopBoss();
|
|
1539
|
+
serverLogger.info("pg-boss stopped");
|
|
1341
1540
|
} catch (error) {
|
|
1342
1541
|
serverLogger.error("pg-boss stop failed", error);
|
|
1343
1542
|
}
|
|
1344
1543
|
}
|
|
1544
|
+
const drainTimeout = Math.floor(shutdownTimeout * 0.8);
|
|
1545
|
+
serverLogger.info(`Phase 3: Draining tracked operations (timeout: ${drainTimeout}ms)...`);
|
|
1546
|
+
await shutdownManager.execute(drainTimeout);
|
|
1345
1547
|
if (config.lifecycle?.beforeShutdown) {
|
|
1346
|
-
serverLogger.
|
|
1548
|
+
serverLogger.info("Phase 4: Executing beforeShutdown lifecycle hook...");
|
|
1347
1549
|
try {
|
|
1348
1550
|
await config.lifecycle.beforeShutdown();
|
|
1349
1551
|
} catch (error) {
|
|
1350
|
-
serverLogger.error("beforeShutdown hook failed", error);
|
|
1552
|
+
serverLogger.error("beforeShutdown lifecycle hook failed", error);
|
|
1351
1553
|
}
|
|
1352
1554
|
}
|
|
1555
|
+
serverLogger.info("Phase 5: Closing infrastructure...");
|
|
1353
1556
|
const infraConfig = getInfrastructureConfig(config);
|
|
1354
1557
|
if (infraConfig.database) {
|
|
1355
|
-
serverLogger.debug("Closing database connections...");
|
|
1356
1558
|
await closeInfrastructure(closeDatabase, "Database", TIMEOUTS.DATABASE_CLOSE);
|
|
1357
1559
|
}
|
|
1358
1560
|
if (infraConfig.redis) {
|
|
1359
|
-
serverLogger.debug("Closing Redis connections...");
|
|
1360
1561
|
await closeInfrastructure(closeCache, "Redis", TIMEOUTS.REDIS_CLOSE);
|
|
1361
1562
|
}
|
|
1362
1563
|
serverLogger.info("Server shutdown completed");
|
|
1363
1564
|
};
|
|
1364
1565
|
}
|
|
1566
|
+
async function closeHttpServer(server) {
|
|
1567
|
+
let timeoutId;
|
|
1568
|
+
await Promise.race([
|
|
1569
|
+
new Promise((resolve2, reject) => {
|
|
1570
|
+
server.close((err) => {
|
|
1571
|
+
if (timeoutId) clearTimeout(timeoutId);
|
|
1572
|
+
if (err) {
|
|
1573
|
+
serverLogger.error("HTTP server close error", err);
|
|
1574
|
+
reject(err);
|
|
1575
|
+
} else {
|
|
1576
|
+
serverLogger.info("HTTP server closed");
|
|
1577
|
+
resolve2();
|
|
1578
|
+
}
|
|
1579
|
+
});
|
|
1580
|
+
}),
|
|
1581
|
+
new Promise((_, reject) => {
|
|
1582
|
+
timeoutId = setTimeout(() => {
|
|
1583
|
+
reject(new Error(`HTTP server close timeout after ${TIMEOUTS.SERVER_CLOSE}ms`));
|
|
1584
|
+
}, TIMEOUTS.SERVER_CLOSE);
|
|
1585
|
+
})
|
|
1586
|
+
]).catch((error) => {
|
|
1587
|
+
if (timeoutId) clearTimeout(timeoutId);
|
|
1588
|
+
serverLogger.warn("HTTP server close timeout, forcing shutdown", error);
|
|
1589
|
+
});
|
|
1590
|
+
}
|
|
1365
1591
|
async function closeInfrastructure(closeFn, name, timeout) {
|
|
1366
1592
|
let timeoutId;
|
|
1367
1593
|
try {
|
|
@@ -1484,6 +1710,7 @@ async function cleanupOnFailure(config) {
|
|
|
1484
1710
|
if (infraConfig.redis) {
|
|
1485
1711
|
await closeInfrastructure(closeCache, "Redis", TIMEOUTS.REDIS_CLOSE);
|
|
1486
1712
|
}
|
|
1713
|
+
resetShutdownManager();
|
|
1487
1714
|
serverLogger.debug("Cleanup completed");
|
|
1488
1715
|
} catch (cleanupError) {
|
|
1489
1716
|
serverLogger.error("Cleanup failed", cleanupError);
|
|
@@ -1767,6 +1994,6 @@ function defineServerConfig() {
|
|
|
1767
1994
|
return new ServerConfigBuilder();
|
|
1768
1995
|
}
|
|
1769
1996
|
|
|
1770
|
-
export { createServer, defineServerConfig, loadEnv, loadEnvFiles, startServer };
|
|
1997
|
+
export { createServer, defineServerConfig, getShutdownManager, loadEnv, loadEnvFiles, startServer };
|
|
1771
1998
|
//# sourceMappingURL=index.js.map
|
|
1772
1999
|
//# sourceMappingURL=index.js.map
|