@scpxl/nodejs-framework 1.0.14 → 1.0.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/application/base-application.d.ts +3 -0
- package/dist/application/base-application.d.ts.map +1 -1
- package/dist/application/base-application.js +23 -14
- package/dist/application/base-application.js.map +2 -2
- package/dist/cluster/cluster-manager.d.ts.map +1 -1
- package/dist/cluster/cluster-manager.js +2 -1
- package/dist/cluster/cluster-manager.js.map +2 -2
- package/dist/lifecycle/exit.d.ts +10 -0
- package/dist/lifecycle/exit.d.ts.map +1 -0
- package/dist/lifecycle/exit.js +23 -0
- package/dist/lifecycle/exit.js.map +7 -0
- package/dist/lifecycle/lifecycle-manager.d.ts +13 -0
- package/dist/lifecycle/lifecycle-manager.d.ts.map +1 -0
- package/dist/lifecycle/lifecycle-manager.js +54 -0
- package/dist/lifecycle/lifecycle-manager.js.map +7 -0
- package/package.json +18 -16
|
@@ -6,6 +6,7 @@ import type RedisInstance from '../redis/instance.js';
|
|
|
6
6
|
import CacheManager from '../cache/manager.js';
|
|
7
7
|
import EventManager from '../event/manager.js';
|
|
8
8
|
import { PerformanceMonitor } from '../performance/performance-monitor.js';
|
|
9
|
+
import { LifecycleManager } from '../lifecycle/lifecycle-manager.js';
|
|
9
10
|
export type { ApplicationConfig } from './base-application.interface.js';
|
|
10
11
|
export default abstract class BaseApplication {
|
|
11
12
|
/** Unique instance ID */
|
|
@@ -38,6 +39,8 @@ export default abstract class BaseApplication {
|
|
|
38
39
|
eventManager?: EventManager;
|
|
39
40
|
/** Performance monitor */
|
|
40
41
|
performanceMonitor?: PerformanceMonitor;
|
|
42
|
+
/** Lifecycle manager */
|
|
43
|
+
protected lifecycle: LifecycleManager;
|
|
41
44
|
get Name(): string;
|
|
42
45
|
/**
|
|
43
46
|
* Application constructor
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"base-application.d.ts","sourceRoot":"","sources":["../../src/application/base-application.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,gBAAgB,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAC9E,OAAO,YAAY,MAAM,qBAAqB,CAAC;AAC/C,OAAO,YAAY,MAAM,qBAAqB,CAAC;AAC/C,OAAO,KAAK,EACV,iBAAiB,EAGlB,MAAM,iCAAiC,CAAC;AAEzC,OAAO,KAAK,aAAa,MAAM,sBAAsB,CAAC;AAEtD,OAAO,YAAY,MAAM,qBAAqB,CAAC;AAE/C,OAAO,YAAY,MAAM,qBAAqB,CAAC;AAE/C,OAAO,EAAE,kBAAkB,EAAE,MAAM,uCAAuC,CAAC;
|
|
1
|
+
{"version":3,"file":"base-application.d.ts","sourceRoot":"","sources":["../../src/application/base-application.ts"],"names":[],"mappings":"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"}
|
|
@@ -15,6 +15,8 @@ import EventManager from "../event/manager.js";
|
|
|
15
15
|
import Logger from "../logger/logger.js";
|
|
16
16
|
import { PerformanceMonitor } from "../performance/performance-monitor.js";
|
|
17
17
|
import { CachePerformanceWrapper, DatabasePerformanceWrapper, QueuePerformanceWrapper } from "../performance/index.js";
|
|
18
|
+
import { LifecycleManager } from "../lifecycle/lifecycle-manager.js";
|
|
19
|
+
import { requestExit } from "../lifecycle/exit.js";
|
|
18
20
|
class BaseApplication {
|
|
19
21
|
static {
|
|
20
22
|
__name(this, "BaseApplication");
|
|
@@ -49,6 +51,8 @@ class BaseApplication {
|
|
|
49
51
|
eventManager;
|
|
50
52
|
/** Performance monitor */
|
|
51
53
|
performanceMonitor;
|
|
54
|
+
/** Lifecycle manager */
|
|
55
|
+
lifecycle = new LifecycleManager();
|
|
52
56
|
get Name() {
|
|
53
57
|
return this.config.name;
|
|
54
58
|
}
|
|
@@ -233,8 +237,11 @@ class BaseApplication {
|
|
|
233
237
|
await options.onStarted({ startupTime });
|
|
234
238
|
}
|
|
235
239
|
} catch (error) {
|
|
236
|
-
|
|
237
|
-
|
|
240
|
+
Logger.error({
|
|
241
|
+
error: error instanceof Error ? error : new Error(String(error)),
|
|
242
|
+
message: "startInstance failure"
|
|
243
|
+
});
|
|
244
|
+
throw error;
|
|
238
245
|
}
|
|
239
246
|
}
|
|
240
247
|
/**
|
|
@@ -263,14 +270,16 @@ class BaseApplication {
|
|
|
263
270
|
if (this.config.performanceMonitoring.monitorCacheOperations !== false) {
|
|
264
271
|
CachePerformanceWrapper.setPerformanceMonitor(this.performanceMonitor);
|
|
265
272
|
}
|
|
266
|
-
if (this.config.performanceMonitoring
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
273
|
+
if (this.config.performanceMonitoring?.reportInterval && this.config.performanceMonitoring.reportInterval > 0) {
|
|
274
|
+
this.lifecycle.trackInterval(
|
|
275
|
+
setInterval(() => {
|
|
276
|
+
const reportFormat = this.config.performanceMonitoring?.reportFormat ?? "detailed";
|
|
277
|
+
const report = this.performanceMonitor?.generateFormattedReport(reportFormat);
|
|
278
|
+
if (report) {
|
|
279
|
+
Logger.info({ message: report });
|
|
280
|
+
}
|
|
281
|
+
}, this.config.performanceMonitoring.reportInterval)
|
|
282
|
+
);
|
|
274
283
|
}
|
|
275
284
|
}
|
|
276
285
|
setupGlobalErrorHandlers() {
|
|
@@ -300,7 +309,7 @@ class BaseApplication {
|
|
|
300
309
|
error: error instanceof Error ? error : new Error(String(error)),
|
|
301
310
|
message: "Error during graceful shutdown"
|
|
302
311
|
});
|
|
303
|
-
|
|
312
|
+
requestExit({ code: 1, reason: "graceful-shutdown-error", error });
|
|
304
313
|
});
|
|
305
314
|
}
|
|
306
315
|
/**
|
|
@@ -323,7 +332,7 @@ class BaseApplication {
|
|
|
323
332
|
this.isStopping = true;
|
|
324
333
|
const forceExitTimeout = setTimeout(() => {
|
|
325
334
|
Logger.warn({ message: "Forced shutdown due to timeout" });
|
|
326
|
-
|
|
335
|
+
requestExit({ code: 1, reason: "forced-timeout" });
|
|
327
336
|
}, this.shutdownTimeout);
|
|
328
337
|
try {
|
|
329
338
|
await this.stopCallback();
|
|
@@ -333,14 +342,14 @@ class BaseApplication {
|
|
|
333
342
|
await onStopped({ runtime });
|
|
334
343
|
}
|
|
335
344
|
clearTimeout(forceExitTimeout);
|
|
336
|
-
|
|
345
|
+
requestExit({ code: 0, reason: "shutdown-complete" });
|
|
337
346
|
} catch (error) {
|
|
338
347
|
Logger.error({
|
|
339
348
|
error: error instanceof Error ? error : new Error(String(error)),
|
|
340
349
|
message: "Error during shutdown"
|
|
341
350
|
});
|
|
342
351
|
clearTimeout(forceExitTimeout);
|
|
343
|
-
|
|
352
|
+
requestExit({ code: 1, reason: "shutdown-error", error });
|
|
344
353
|
}
|
|
345
354
|
}
|
|
346
355
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/application/base-application.ts"],
|
|
4
|
-
"sourcesContent": ["import cluster from 'cluster';\nimport { existsSync, readFileSync } from 'fs';\nimport { fileURLToPath } from 'url';\nimport { dirname, join, resolve } from 'path';\nimport { type DatabaseInstance, DatabaseManager } from '../database/index.js';\nimport QueueManager from '../queue/manager.js';\nimport RedisManager from '../redis/manager.js';\nimport type {\n ApplicationConfig,\n ApplicationStartInstanceOptions,\n ApplicationStopInstanceOptions,\n} from './base-application.interface.js';\nimport ClusterManager from '../cluster/cluster-manager.js';\nimport type RedisInstance from '../redis/instance.js';\nimport { OS, Time } from '../util/index.js';\nimport CacheManager from '../cache/manager.js';\nimport os from 'os';\nimport EventManager from '../event/manager.js';\nimport Logger from '../logger/logger.js';\nimport { PerformanceMonitor } from '../performance/performance-monitor.js';\nimport { CachePerformanceWrapper, DatabasePerformanceWrapper, QueuePerformanceWrapper } from '../performance/index.js';\n\n// Re-export types for external use\nexport type { ApplicationConfig } from './base-application.interface.js';\n\nexport default abstract class BaseApplication {\n /** Unique instance ID */\n public uniqueInstanceId: string;\n\n /** Shutdown signals */\n protected shutdownSignals: NodeJS.Signals[] = ['SIGTERM', 'SIGINT'];\n\n /** Application start time */\n protected startTime: [number, number] = [0, 0];\n\n /** Whether application is stopping */\n protected isStopping = false;\n\n /** Shutdown timeout (30 seconds) */\n protected shutdownTimeout = 30000;\n\n /** Cache for application version to avoid repeated imports */\n private static applicationVersionCache: string | undefined;\n\n /** Cluster worker ID */\n protected workerId = cluster.isWorker && cluster.worker ? cluster.worker.id : null;\n\n /** Application config */\n protected config: ApplicationConfig;\n\n /** Application version */\n protected applicationVersion?: string;\n\n /** Redis manager */\n public redisManager: RedisManager;\n\n /** Cache manager */\n public cacheManager: CacheManager;\n\n /** Database manager */\n public databaseManager?: DatabaseManager;\n\n /** Queue manager */\n public queueManager?: QueueManager;\n\n /** Event manager */\n public eventManager?: EventManager;\n\n /** Performance monitor */\n public performanceMonitor?: PerformanceMonitor;\n\n public get Name() {\n return this.config.name;\n }\n\n /**\n * Application constructor\n */\n constructor(config: ApplicationConfig) {\n const computerName = os.hostname();\n\n this.uniqueInstanceId = `${config.instanceId}-${computerName}-${OS.getUniqueComputerId()}`;\n\n this.config = config;\n\n // const schema = Joi.object({\n // name: Joi.string().required(),\n\n // redis: {\n // host: Joi.string().required(),\n // port: Joi.number().required(),\n // password: Joi.string().allow('').optional(),\n // },\n\n // database: {\n // host: Joi.string().required(),\n // port: Joi.number().required(),\n // username: Joi.string().required(),\n // password: Joi.string().required(),\n // databaseName: Joi.string().required(),\n // },\n // });\n\n // // Validation application constructor props\n // const validationResult = schema.validate(props);\n\n // if (validationResult.error) {\n // throw new Error(validationResult.error.message);\n // }\n\n // Initialize Redis manager\n this.redisManager = new RedisManager({\n applicationConfig: this.config,\n host: this.config.redis.host,\n port: this.config.redis.port,\n password: this.config.redis.password,\n });\n\n // Initialize cache manager\n this.cacheManager = new CacheManager({\n applicationConfig: this.config,\n redisManager: this.redisManager,\n });\n\n // Initialize performance monitor\n this.initializePerformanceMonitor();\n\n // Set up global error handlers\n this.setupGlobalErrorHandlers();\n\n if (this.config.database && this.config.database.enabled === true) {\n const defaultEntitiesDirectory = join(this.config.rootDirectory, 'src', 'database', 'entities');\n\n if (!this.config.database.entitiesDirectory) {\n this.config.database.entitiesDirectory = defaultEntitiesDirectory;\n }\n\n if (!existsSync(this.config.database.entitiesDirectory)) {\n throw new Error(`Database entities directory not found (Path: ${this.config.database.entitiesDirectory})`);\n }\n\n // Initialize Database manager\n this.databaseManager = new DatabaseManager({\n applicationConfig: this.config,\n host: this.config.database.host,\n port: this.config.database.port,\n username: this.config.database.username,\n password: this.config.database.password,\n databaseName: this.config.database.databaseName,\n entitiesDirectory: this.config.database.entitiesDirectory,\n });\n }\n }\n\n /**\n * Get application version\n */\n public async getApplicationVersion(): Promise<string> {\n // Return cached version if available\n if (BaseApplication.applicationVersionCache !== undefined) {\n return BaseApplication.applicationVersionCache;\n }\n\n // Resolve the path to package.json\n const __filename = fileURLToPath(import.meta.url);\n const __dirname = dirname(__filename);\n const packageJsonPath = resolve(__dirname, '../../package.json');\n\n // Read and parse the file\n const fileContents = readFileSync(packageJsonPath, 'utf-8');\n const packageJson = JSON.parse(fileContents);\n\n if (!packageJson?.version) {\n throw new Error('Application version not found');\n }\n\n // Cache and return the version\n BaseApplication.applicationVersionCache = packageJson.version;\n\n return packageJson.version;\n }\n\n /**\n * Start application\n */\n public async start(): Promise<void> {\n // Start application timer\n this.startTime = process.hrtime();\n\n // Get application version`\n this.applicationVersion = await this.getApplicationVersion();\n\n const startInstanceOptions: ApplicationStartInstanceOptions = {\n // onStarted: ({ startupTime }) => {\n // if (this.config.log?.startUp) {\n // Logger.info('Application started', {\n // Name: this.config.name,\n // 'PXL Framework Version': this.applicationVersion,\n // 'Startup Time': Time.formatTime({ time: startupTime, format: 's', numDecimals: 2, showUnit: true }),\n // });\n // }\n\n // if (this.config.events?.onStarted) {\n // this.config.events.onStarted({ app: this, startupTime });\n // }\n // },\n onStarted: this.onStarted.bind(this),\n };\n\n const stopInstanceOptions: ApplicationStopInstanceOptions = {\n // onStopped: ({ runtime }) => {\n // if (this.config.log?.shutdown) {\n // Logger.info('Application stopped', {\n // Name: this.config.name,\n // 'Runtime': Time.formatTime({ time: runtime, format: 's', numDecimals: 2, showUnit: true }),\n // });\n // }\n\n // if (this.config.events?.onStopped) {\n // this.config.events.onStopped({ app: this, runtime });\n // }\n // },\n onStopped: this.onStopped.bind(this),\n };\n\n if (this.config.cluster?.enabled) {\n // Initialize clustered application\n const clusterManager = new ClusterManager({\n config: this.config.cluster,\n\n startApplicationCallback: () => this.startInstance(startInstanceOptions),\n stopApplicationCallback: () => this.stop(stopInstanceOptions),\n });\n\n // Start cluster\n clusterManager.start();\n } else {\n // Start standalone application\n await this.startInstance(startInstanceOptions);\n\n // Handle standalone application shutdown\n this.handleShutdown({\n onStopped: stopInstanceOptions.onStopped,\n });\n }\n }\n\n /**\n * Before application start\n */\n private async onBeforeStart(): Promise<{\n redisInstance: RedisInstance;\n databaseInstance: DatabaseInstance | null;\n queueManager: QueueManager;\n eventManager?: EventManager;\n }> {\n // Connect to Redis\n const redisInstance = await this.redisManager.connect();\n\n // Connect to database\n const databaseInstance = this.databaseManager ? await this.databaseManager.connect() : null;\n\n let eventManager: EventManager | undefined;\n\n if (this.config.event?.enabled) {\n eventManager = new EventManager({\n applicationConfig: this.config,\n options: this.config.event,\n events: this.config.event.events || [],\n redisInstance,\n databaseInstance,\n // queueManager,\n });\n\n eventManager.load();\n }\n\n // Initialize queue\n const queueManager = new QueueManager({\n applicationConfig: this.config,\n options: {\n processorsDirectory: this.config.queue.processorsDirectory,\n },\n queues: this.config.queue.queues,\n redisInstance,\n databaseInstance,\n eventManager,\n });\n\n // Register queues\n await queueManager.registerQueues({\n queues: this.config.queue.queues,\n });\n\n return {\n redisInstance,\n databaseInstance,\n queueManager,\n eventManager,\n };\n }\n\n /**\n * Application started event\n */\n protected onStarted({ startupTime: _startupTime }: { startupTime: number }): void {}\n\n /**\n * Application stopped event\n */\n protected onStopped({ runtime: _runtime }: { runtime: number }): void {}\n\n /**\n * Before application stop event\n */\n private async onBeforeStop(): Promise<void> {\n // Disconnect from Redis\n await this.redisManager.disconnect();\n\n if (this.databaseManager) {\n // Disconnect from database\n await this.databaseManager.disconnect();\n }\n }\n\n /**\n * Start application instance\n */\n private async startInstance(options: ApplicationStartInstanceOptions): Promise<void> {\n try {\n // Before application start\n const { redisInstance, databaseInstance, queueManager, eventManager } = await this.onBeforeStart();\n\n // Start application\n await this.startHandler({\n redisInstance,\n databaseInstance,\n queueManager,\n eventManager,\n });\n\n // Calculate application startup time\n const startupTime = Time.calculateElapsedTime({\n startTime: this.startTime,\n });\n\n // On application started\n if (options.onStarted) {\n await options.onStarted({ startupTime });\n }\n } catch (error) {\n // Log error\n console.error(error);\n\n process.exit(1);\n }\n }\n\n protected abstract startHandler({\n redisInstance,\n databaseInstance,\n queueManager,\n eventManager,\n }: {\n redisInstance: RedisInstance;\n databaseInstance?: DatabaseInstance | null;\n queueManager: QueueManager;\n eventManager?: EventManager | null;\n }): Promise<void>;\n\n protected abstract stopCallback(): void;\n\n /**\n * Set up global error handlers\n */\n /**\n * Initialize performance monitor\n */\n private initializePerformanceMonitor(): void {\n // Check if performance monitoring is enabled\n if (!this.config.performanceMonitoring?.enabled) {\n return;\n }\n\n // Initialize performance monitor with configuration\n this.performanceMonitor = PerformanceMonitor.initialize({\n enabled: true,\n thresholds: this.config.performanceMonitoring.thresholds,\n maxMetricsHistory: this.config.performanceMonitoring.maxMetricsHistory,\n logSlowOperations: this.config.performanceMonitoring.logSlowOperations,\n logAllOperations: this.config.performanceMonitoring.logAllOperations,\n });\n\n // Set up performance monitoring for different components\n if (this.config.performanceMonitoring.monitorDatabaseOperations !== false) {\n DatabasePerformanceWrapper.setPerformanceMonitor(this.performanceMonitor);\n }\n\n if (this.config.performanceMonitoring.monitorQueueOperations !== false) {\n QueuePerformanceWrapper.setPerformanceMonitor(this.performanceMonitor);\n }\n\n if (this.config.performanceMonitoring.monitorCacheOperations !== false) {\n CachePerformanceWrapper.setPerformanceMonitor(this.performanceMonitor);\n }\n\n // Set up periodic performance reports if configured\n if (this.config.performanceMonitoring.reportInterval && this.config.performanceMonitoring.reportInterval > 0) {\n setInterval(() => {\n const reportFormat = this.config.performanceMonitoring?.reportFormat ?? 'detailed';\n const report = this.performanceMonitor?.generateFormattedReport(reportFormat);\n\n if (report) {\n Logger.info({ message: report });\n }\n }, this.config.performanceMonitoring.reportInterval);\n }\n }\n\n private setupGlobalErrorHandlers(): void {\n // Handle uncaught exceptions\n process.on('uncaughtException', error => {\n Logger.error({ error, message: 'Uncaught Exception' });\n this.initiateGracefulShutdown();\n });\n\n // Handle unhandled promise rejections\n process.on('unhandledRejection', (reason, promise) => {\n Logger.error({\n error: reason instanceof Error ? reason : new Error(String(reason)),\n message: 'Unhandled Rejection',\n meta: { promise },\n });\n this.initiateGracefulShutdown();\n });\n }\n\n /**\n * Initiate graceful shutdown\n */\n private initiateGracefulShutdown(): void {\n if (this.isStopping) {\n return;\n }\n\n Logger.info({ message: 'Initiating graceful shutdown due to error' });\n this.stop().catch(error => {\n Logger.error({\n error: error instanceof Error ? error : new Error(String(error)),\n message: 'Error during graceful shutdown',\n });\n process.exit(1);\n });\n }\n\n /**\n * Handle shutdown\n */\n public handleShutdown({ onStopped }: { onStopped?: ({ runtime }: { runtime: number }) => void }): void {\n this.shutdownSignals.forEach(signal => {\n process.on(signal, async () => {\n // Stop application\n await this.stop({ onStopped });\n });\n });\n }\n\n /**\n * Stop application\n */\n private async stop({ onStopped }: ApplicationStopInstanceOptions = {}): Promise<void> {\n if (this.isStopping) {\n return;\n }\n\n this.isStopping = true;\n\n // Set timeout for forced termination\n const forceExitTimeout = setTimeout(() => {\n Logger.warn({ message: 'Forced shutdown due to timeout' });\n process.exit(1);\n }, this.shutdownTimeout);\n\n try {\n // Stop callback\n await this.stopCallback();\n\n // Disconnect\n await this.onBeforeStop();\n\n if (onStopped) {\n // Calculate runtime\n const runtime = process.uptime() * 1000;\n\n // Emit stopped event\n await onStopped({ runtime });\n }\n\n clearTimeout(forceExitTimeout);\n process.exit(0);\n } catch (error) {\n Logger.error({\n error: error instanceof Error ? error : new Error(String(error)),\n message: 'Error during shutdown',\n });\n clearTimeout(forceExitTimeout);\n process.exit(1);\n }\n }\n}\n"],
|
|
5
|
-
"mappings": ";;AAAA,OAAO,aAAa;AACpB,SAAS,YAAY,oBAAoB;AACzC,SAAS,qBAAqB;AAC9B,SAAS,SAAS,MAAM,eAAe;AACvC,SAAgC,uBAAuB;AACvD,OAAO,kBAAkB;AACzB,OAAO,kBAAkB;AAMzB,OAAO,oBAAoB;AAE3B,SAAS,IAAI,YAAY;AACzB,OAAO,kBAAkB;AACzB,OAAO,QAAQ;AACf,OAAO,kBAAkB;AACzB,OAAO,YAAY;AACnB,SAAS,0BAA0B;AACnC,SAAS,yBAAyB,4BAA4B,+BAA+B;
|
|
4
|
+
"sourcesContent": ["import cluster from 'cluster';\nimport { existsSync, readFileSync } from 'fs';\nimport { fileURLToPath } from 'url';\nimport { dirname, join, resolve } from 'path';\nimport { type DatabaseInstance, DatabaseManager } from '../database/index.js';\nimport QueueManager from '../queue/manager.js';\nimport RedisManager from '../redis/manager.js';\nimport type {\n ApplicationConfig,\n ApplicationStartInstanceOptions,\n ApplicationStopInstanceOptions,\n} from './base-application.interface.js';\nimport ClusterManager from '../cluster/cluster-manager.js';\nimport type RedisInstance from '../redis/instance.js';\nimport { OS, Time } from '../util/index.js';\nimport CacheManager from '../cache/manager.js';\nimport os from 'os';\nimport EventManager from '../event/manager.js';\nimport Logger from '../logger/logger.js';\nimport { PerformanceMonitor } from '../performance/performance-monitor.js';\nimport { CachePerformanceWrapper, DatabasePerformanceWrapper, QueuePerformanceWrapper } from '../performance/index.js';\nimport { LifecycleManager } from '../lifecycle/lifecycle-manager.js';\nimport { requestExit } from '../lifecycle/exit.js';\n\n// Re-export types for external use\nexport type { ApplicationConfig } from './base-application.interface.js';\n\nexport default abstract class BaseApplication {\n /** Unique instance ID */\n public uniqueInstanceId: string;\n\n /** Shutdown signals */\n protected shutdownSignals: NodeJS.Signals[] = ['SIGTERM', 'SIGINT'];\n\n /** Application start time */\n protected startTime: [number, number] = [0, 0];\n\n /** Whether application is stopping */\n protected isStopping = false;\n\n /** Shutdown timeout (30 seconds) */\n protected shutdownTimeout = 30000;\n\n /** Cache for application version to avoid repeated imports */\n private static applicationVersionCache: string | undefined;\n\n /** Cluster worker ID */\n protected workerId = cluster.isWorker && cluster.worker ? cluster.worker.id : null;\n\n /** Application config */\n protected config: ApplicationConfig;\n\n /** Application version */\n protected applicationVersion?: string;\n\n /** Redis manager */\n public redisManager: RedisManager;\n\n /** Cache manager */\n public cacheManager: CacheManager;\n\n /** Database manager */\n public databaseManager?: DatabaseManager;\n\n /** Queue manager */\n public queueManager?: QueueManager;\n\n /** Event manager */\n public eventManager?: EventManager;\n\n /** Performance monitor */\n public performanceMonitor?: PerformanceMonitor;\n\n /** Lifecycle manager */\n protected lifecycle: LifecycleManager = new LifecycleManager();\n\n public get Name() {\n return this.config.name;\n }\n\n /**\n * Application constructor\n */\n constructor(config: ApplicationConfig) {\n const computerName = os.hostname();\n\n this.uniqueInstanceId = `${config.instanceId}-${computerName}-${OS.getUniqueComputerId()}`;\n\n this.config = config;\n\n // const schema = Joi.object({\n // name: Joi.string().required(),\n\n // redis: {\n // host: Joi.string().required(),\n // port: Joi.number().required(),\n // password: Joi.string().allow('').optional(),\n // },\n\n // database: {\n // host: Joi.string().required(),\n // port: Joi.number().required(),\n // username: Joi.string().required(),\n // password: Joi.string().required(),\n // databaseName: Joi.string().required(),\n // },\n // });\n\n // // Validation application constructor props\n // const validationResult = schema.validate(props);\n\n // if (validationResult.error) {\n // throw new Error(validationResult.error.message);\n // }\n\n // Initialize Redis manager\n this.redisManager = new RedisManager({\n applicationConfig: this.config,\n host: this.config.redis.host,\n port: this.config.redis.port,\n password: this.config.redis.password,\n });\n\n // Initialize cache manager\n this.cacheManager = new CacheManager({\n applicationConfig: this.config,\n redisManager: this.redisManager,\n });\n\n // Initialize performance monitor\n this.initializePerformanceMonitor();\n\n // Set up global error handlers\n this.setupGlobalErrorHandlers();\n\n if (this.config.database && this.config.database.enabled === true) {\n const defaultEntitiesDirectory = join(this.config.rootDirectory, 'src', 'database', 'entities');\n\n if (!this.config.database.entitiesDirectory) {\n this.config.database.entitiesDirectory = defaultEntitiesDirectory;\n }\n\n if (!existsSync(this.config.database.entitiesDirectory)) {\n throw new Error(`Database entities directory not found (Path: ${this.config.database.entitiesDirectory})`);\n }\n\n // Initialize Database manager\n this.databaseManager = new DatabaseManager({\n applicationConfig: this.config,\n host: this.config.database.host,\n port: this.config.database.port,\n username: this.config.database.username,\n password: this.config.database.password,\n databaseName: this.config.database.databaseName,\n entitiesDirectory: this.config.database.entitiesDirectory,\n });\n }\n }\n\n /**\n * Get application version\n */\n public async getApplicationVersion(): Promise<string> {\n // Return cached version if available\n if (BaseApplication.applicationVersionCache !== undefined) {\n return BaseApplication.applicationVersionCache;\n }\n\n // Resolve the path to package.json\n const __filename = fileURLToPath(import.meta.url);\n const __dirname = dirname(__filename);\n const packageJsonPath = resolve(__dirname, '../../package.json');\n\n // Read and parse the file\n const fileContents = readFileSync(packageJsonPath, 'utf-8');\n const packageJson = JSON.parse(fileContents);\n\n if (!packageJson?.version) {\n throw new Error('Application version not found');\n }\n\n // Cache and return the version\n BaseApplication.applicationVersionCache = packageJson.version;\n\n return packageJson.version;\n }\n\n /**\n * Start application\n */\n public async start(): Promise<void> {\n // Start application timer\n this.startTime = process.hrtime();\n\n // Get application version`\n this.applicationVersion = await this.getApplicationVersion();\n\n const startInstanceOptions: ApplicationStartInstanceOptions = {\n // onStarted: ({ startupTime }) => {\n // if (this.config.log?.startUp) {\n // Logger.info('Application started', {\n // Name: this.config.name,\n // 'PXL Framework Version': this.applicationVersion,\n // 'Startup Time': Time.formatTime({ time: startupTime, format: 's', numDecimals: 2, showUnit: true }),\n // });\n // }\n\n // if (this.config.events?.onStarted) {\n // this.config.events.onStarted({ app: this, startupTime });\n // }\n // },\n onStarted: this.onStarted.bind(this),\n };\n\n const stopInstanceOptions: ApplicationStopInstanceOptions = {\n // onStopped: ({ runtime }) => {\n // if (this.config.log?.shutdown) {\n // Logger.info('Application stopped', {\n // Name: this.config.name,\n // 'Runtime': Time.formatTime({ time: runtime, format: 's', numDecimals: 2, showUnit: true }),\n // });\n // }\n\n // if (this.config.events?.onStopped) {\n // this.config.events.onStopped({ app: this, runtime });\n // }\n // },\n onStopped: this.onStopped.bind(this),\n };\n\n if (this.config.cluster?.enabled) {\n // Initialize clustered application\n const clusterManager = new ClusterManager({\n config: this.config.cluster,\n\n startApplicationCallback: () => this.startInstance(startInstanceOptions),\n stopApplicationCallback: () => this.stop(stopInstanceOptions),\n });\n\n // Start cluster\n clusterManager.start();\n } else {\n // Start standalone application\n await this.startInstance(startInstanceOptions);\n\n // Handle standalone application shutdown\n this.handleShutdown({\n onStopped: stopInstanceOptions.onStopped,\n });\n }\n }\n\n /**\n * Before application start\n */\n private async onBeforeStart(): Promise<{\n redisInstance: RedisInstance;\n databaseInstance: DatabaseInstance | null;\n queueManager: QueueManager;\n eventManager?: EventManager;\n }> {\n // Connect to Redis\n const redisInstance = await this.redisManager.connect();\n\n // Connect to database\n const databaseInstance = this.databaseManager ? await this.databaseManager.connect() : null;\n\n let eventManager: EventManager | undefined;\n\n if (this.config.event?.enabled) {\n eventManager = new EventManager({\n applicationConfig: this.config,\n options: this.config.event,\n events: this.config.event.events || [],\n redisInstance,\n databaseInstance,\n // queueManager,\n });\n\n eventManager.load();\n }\n\n // Initialize queue\n const queueManager = new QueueManager({\n applicationConfig: this.config,\n options: {\n processorsDirectory: this.config.queue.processorsDirectory,\n },\n queues: this.config.queue.queues,\n redisInstance,\n databaseInstance,\n eventManager,\n });\n\n // Register queues\n await queueManager.registerQueues({\n queues: this.config.queue.queues,\n });\n\n return {\n redisInstance,\n databaseInstance,\n queueManager,\n eventManager,\n };\n }\n\n /**\n * Application started event\n */\n protected onStarted({ startupTime: _startupTime }: { startupTime: number }): void {}\n\n /**\n * Application stopped event\n */\n protected onStopped({ runtime: _runtime }: { runtime: number }): void {}\n\n /**\n * Before application stop event\n */\n private async onBeforeStop(): Promise<void> {\n // Disconnect from Redis\n await this.redisManager.disconnect();\n\n if (this.databaseManager) {\n // Disconnect from database\n await this.databaseManager.disconnect();\n }\n }\n\n /**\n * Start application instance\n */\n private async startInstance(options: ApplicationStartInstanceOptions): Promise<void> {\n try {\n // Before application start\n const { redisInstance, databaseInstance, queueManager, eventManager } = await this.onBeforeStart();\n\n // Start application\n await this.startHandler({\n redisInstance,\n databaseInstance,\n queueManager,\n eventManager,\n });\n\n // Calculate application startup time\n const startupTime = Time.calculateElapsedTime({\n startTime: this.startTime,\n });\n\n // On application started\n if (options.onStarted) {\n await options.onStarted({ startupTime });\n }\n } catch (error) {\n Logger.error({\n error: error instanceof Error ? error : new Error(String(error)),\n message: 'startInstance failure',\n });\n throw error;\n }\n }\n\n protected abstract startHandler({\n redisInstance,\n databaseInstance,\n queueManager,\n eventManager,\n }: {\n redisInstance: RedisInstance;\n databaseInstance?: DatabaseInstance | null;\n queueManager: QueueManager;\n eventManager?: EventManager | null;\n }): Promise<void>;\n\n protected abstract stopCallback(): void;\n\n /**\n * Set up global error handlers\n */\n /**\n * Initialize performance monitor\n */\n private initializePerformanceMonitor(): void {\n // Check if performance monitoring is enabled\n if (!this.config.performanceMonitoring?.enabled) {\n return;\n }\n\n // Initialize performance monitor with configuration\n this.performanceMonitor = PerformanceMonitor.initialize({\n enabled: true,\n thresholds: this.config.performanceMonitoring.thresholds,\n maxMetricsHistory: this.config.performanceMonitoring.maxMetricsHistory,\n logSlowOperations: this.config.performanceMonitoring.logSlowOperations,\n logAllOperations: this.config.performanceMonitoring.logAllOperations,\n });\n\n // Set up performance monitoring for different components\n if (this.config.performanceMonitoring.monitorDatabaseOperations !== false) {\n DatabasePerformanceWrapper.setPerformanceMonitor(this.performanceMonitor);\n }\n\n if (this.config.performanceMonitoring.monitorQueueOperations !== false) {\n QueuePerformanceWrapper.setPerformanceMonitor(this.performanceMonitor);\n }\n\n if (this.config.performanceMonitoring.monitorCacheOperations !== false) {\n CachePerformanceWrapper.setPerformanceMonitor(this.performanceMonitor);\n }\n\n // Set up periodic performance reports if configured\n if (this.config.performanceMonitoring?.reportInterval && this.config.performanceMonitoring.reportInterval > 0) {\n this.lifecycle.trackInterval(\n setInterval(() => {\n const reportFormat = this.config.performanceMonitoring?.reportFormat ?? 'detailed';\n const report = this.performanceMonitor?.generateFormattedReport(reportFormat);\n\n if (report) {\n Logger.info({ message: report });\n }\n }, this.config.performanceMonitoring.reportInterval),\n );\n }\n }\n\n private setupGlobalErrorHandlers(): void {\n // Handle uncaught exceptions\n process.on('uncaughtException', error => {\n Logger.error({ error, message: 'Uncaught Exception' });\n this.initiateGracefulShutdown();\n });\n\n // Handle unhandled promise rejections\n process.on('unhandledRejection', (reason, promise) => {\n Logger.error({\n error: reason instanceof Error ? reason : new Error(String(reason)),\n message: 'Unhandled Rejection',\n meta: { promise },\n });\n this.initiateGracefulShutdown();\n });\n }\n\n /**\n * Initiate graceful shutdown\n */\n private initiateGracefulShutdown(): void {\n if (this.isStopping) {\n return;\n }\n\n Logger.info({ message: 'Initiating graceful shutdown due to error' });\n this.stop().catch(error => {\n Logger.error({\n error: error instanceof Error ? error : new Error(String(error)),\n message: 'Error during graceful shutdown',\n });\n requestExit({ code: 1, reason: 'graceful-shutdown-error', error });\n });\n }\n\n /**\n * Handle shutdown\n */\n public handleShutdown({ onStopped }: { onStopped?: ({ runtime }: { runtime: number }) => void }): void {\n this.shutdownSignals.forEach(signal => {\n process.on(signal, async () => {\n // Stop application\n await this.stop({ onStopped });\n });\n });\n }\n\n /**\n * Stop application\n */\n private async stop({ onStopped }: ApplicationStopInstanceOptions = {}): Promise<void> {\n if (this.isStopping) {\n return;\n }\n\n this.isStopping = true;\n\n // Set timeout for forced termination\n const forceExitTimeout = setTimeout(() => {\n Logger.warn({ message: 'Forced shutdown due to timeout' });\n requestExit({ code: 1, reason: 'forced-timeout' });\n }, this.shutdownTimeout);\n\n try {\n // Stop callback\n await this.stopCallback();\n\n // Disconnect\n await this.onBeforeStop();\n\n if (onStopped) {\n // Calculate runtime\n const runtime = process.uptime() * 1000;\n // Emit stopped event\n await onStopped({ runtime });\n }\n\n clearTimeout(forceExitTimeout);\n requestExit({ code: 0, reason: 'shutdown-complete' });\n } catch (error) {\n Logger.error({\n error: error instanceof Error ? error : new Error(String(error)),\n message: 'Error during shutdown',\n });\n clearTimeout(forceExitTimeout);\n requestExit({ code: 1, reason: 'shutdown-error', error });\n }\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cluster-manager.d.ts","sourceRoot":"","sources":["../../src/cluster/cluster-manager.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAEV,mBAAmB,EAEpB,MAAM,gCAAgC,CAAC;
|
|
1
|
+
{"version":3,"file":"cluster-manager.d.ts","sourceRoot":"","sources":["../../src/cluster/cluster-manager.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAEV,mBAAmB,EAEpB,MAAM,gCAAgC,CAAC;AAIxC,MAAM,CAAC,OAAO,OAAO,cAAc;IACjC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAuB;IAE9C,OAAO,CAAC,wBAAwB,CAAsB;IACtD,OAAO,CAAC,uBAAuB,CAAsB;IAErD,OAAO,CAAC,eAAe,CAA2C;IAClE,OAAO,CAAC,cAAc,CAAS;gBAEnB,EAAE,MAAM,EAAE,wBAAwB,EAAE,uBAAuB,EAAE,EAAE,mBAAmB;IAOvF,KAAK,IAAI,IAAI;IAUpB,OAAO,CAAC,YAAY;YAqCN,WAAW;IAkBzB,OAAO,CAAC,cAAc;YAMR,gBAAgB;CAkC/B"}
|
|
@@ -3,6 +3,7 @@ var __name = (target, value) => __defProp(target, "name", { value, configurable:
|
|
|
3
3
|
import cluster from "node:cluster";
|
|
4
4
|
import { cpus } from "node:os";
|
|
5
5
|
import { Logger } from "../logger/index.js";
|
|
6
|
+
import { requestExit } from "../lifecycle/exit.js";
|
|
6
7
|
class ClusterManager {
|
|
7
8
|
static {
|
|
8
9
|
__name(this, "ClusterManager");
|
|
@@ -92,7 +93,7 @@ class ClusterManager {
|
|
|
92
93
|
const clusterWorkers = cluster.workers ?? {};
|
|
93
94
|
const numClusterWorkers = Object.keys(clusterWorkers).length;
|
|
94
95
|
if (exitedWorkers === numClusterWorkers) {
|
|
95
|
-
|
|
96
|
+
requestExit({ code: 0, reason: "cluster-workers-exited" });
|
|
96
97
|
}
|
|
97
98
|
});
|
|
98
99
|
} else {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/cluster/cluster-manager.ts"],
|
|
4
|
-
"sourcesContent": ["import cluster from 'node:cluster';\nimport { cpus } from 'node:os';\nimport type {\n ClusterManagerConfig,\n ClusterManagerProps,\n ClusterManagerWorkerModeManualConfig,\n} from './cluster-manager.interface.js';\nimport { Logger } from '../logger/index.js';\n\nexport default class ClusterManager {\n private readonly config: ClusterManagerConfig;\n\n private startApplicationCallback: () => Promise<void>;\n private stopApplicationCallback: () => Promise<void>;\n\n private shutdownSignals: NodeJS.Signals[] = ['SIGTERM', 'SIGINT'];\n private isShuttingDown = false;\n\n constructor({ config, startApplicationCallback, stopApplicationCallback }: ClusterManagerProps) {\n this.config = config;\n\n this.startApplicationCallback = startApplicationCallback;\n this.stopApplicationCallback = stopApplicationCallback;\n }\n\n public start(): void {\n if (cluster.isPrimary) {\n this.setupPrimary();\n } else {\n this.setupWorker();\n }\n\n this.handleShutdown();\n }\n\n private setupPrimary(): void {\n const numCPUs: number = cpus().length;\n\n const numClusterWorkers =\n this.config.workerMode === 'auto' ? numCPUs : (this.config as ClusterManagerWorkerModeManualConfig).workerCount;\n\n for (let workerIndex = 0; workerIndex < numClusterWorkers; workerIndex++) {\n cluster.fork();\n }\n\n cluster.on('online', worker => {\n Logger.debug({\n message: 'Started cluster worker',\n meta: {\n ID: worker.id,\n PID: worker.process.pid,\n },\n });\n });\n\n cluster.on('exit', () => {\n if (!this.isShuttingDown) {\n // Restart worker on unexpected exit\n cluster.fork();\n }\n });\n\n Logger.debug({\n message: 'Started cluster master',\n meta: {\n Mode: this.config.workerMode,\n 'Worker Count': numClusterWorkers,\n CPUs: numCPUs,\n },\n });\n }\n\n private async setupWorker(): Promise<void> {\n await this.startApplicationCallback();\n\n process.on('message', async message => {\n if (message === 'shutdown') {\n Logger.debug({\n message: 'Worker received shutdown message, stopping...',\n meta: {\n PID: process.pid,\n },\n });\n\n // Stop application\n await this.stopApplicationCallback();\n }\n });\n }\n\n private handleShutdown(): void {\n this.shutdownSignals.forEach(signal => {\n process.on(signal, async () => await this.initiateShutdown());\n });\n }\n\n private async initiateShutdown(): Promise<void> {\n if (this.isShuttingDown) {\n return;\n }\n\n this.isShuttingDown = true;\n\n if (cluster.isPrimary) {\n for (const id in cluster.workers) {\n const worker = cluster.workers[id];\n\n if (!worker) {\n continue;\n }\n\n worker.send('shutdown');\n }\n\n let exitedWorkers = 0;\n\n cluster.on('exit', () => {\n exitedWorkers++;\n\n const clusterWorkers = cluster.workers ?? {};\n const numClusterWorkers = Object.keys(clusterWorkers).length;\n\n if (exitedWorkers === numClusterWorkers) {\n
|
|
5
|
-
"mappings": ";;AAAA,OAAO,aAAa;AACpB,SAAS,YAAY;AAMrB,SAAS,cAAc;
|
|
4
|
+
"sourcesContent": ["import cluster from 'node:cluster';\nimport { cpus } from 'node:os';\nimport type {\n ClusterManagerConfig,\n ClusterManagerProps,\n ClusterManagerWorkerModeManualConfig,\n} from './cluster-manager.interface.js';\nimport { Logger } from '../logger/index.js';\nimport { requestExit } from '../lifecycle/exit.js';\n\nexport default class ClusterManager {\n private readonly config: ClusterManagerConfig;\n\n private startApplicationCallback: () => Promise<void>;\n private stopApplicationCallback: () => Promise<void>;\n\n private shutdownSignals: NodeJS.Signals[] = ['SIGTERM', 'SIGINT'];\n private isShuttingDown = false;\n\n constructor({ config, startApplicationCallback, stopApplicationCallback }: ClusterManagerProps) {\n this.config = config;\n\n this.startApplicationCallback = startApplicationCallback;\n this.stopApplicationCallback = stopApplicationCallback;\n }\n\n public start(): void {\n if (cluster.isPrimary) {\n this.setupPrimary();\n } else {\n this.setupWorker();\n }\n\n this.handleShutdown();\n }\n\n private setupPrimary(): void {\n const numCPUs: number = cpus().length;\n\n const numClusterWorkers =\n this.config.workerMode === 'auto' ? numCPUs : (this.config as ClusterManagerWorkerModeManualConfig).workerCount;\n\n for (let workerIndex = 0; workerIndex < numClusterWorkers; workerIndex++) {\n cluster.fork();\n }\n\n cluster.on('online', worker => {\n Logger.debug({\n message: 'Started cluster worker',\n meta: {\n ID: worker.id,\n PID: worker.process.pid,\n },\n });\n });\n\n cluster.on('exit', () => {\n if (!this.isShuttingDown) {\n // Restart worker on unexpected exit\n cluster.fork();\n }\n });\n\n Logger.debug({\n message: 'Started cluster master',\n meta: {\n Mode: this.config.workerMode,\n 'Worker Count': numClusterWorkers,\n CPUs: numCPUs,\n },\n });\n }\n\n private async setupWorker(): Promise<void> {\n await this.startApplicationCallback();\n\n process.on('message', async message => {\n if (message === 'shutdown') {\n Logger.debug({\n message: 'Worker received shutdown message, stopping...',\n meta: {\n PID: process.pid,\n },\n });\n\n // Stop application\n await this.stopApplicationCallback();\n }\n });\n }\n\n private handleShutdown(): void {\n this.shutdownSignals.forEach(signal => {\n process.on(signal, async () => await this.initiateShutdown());\n });\n }\n\n private async initiateShutdown(): Promise<void> {\n if (this.isShuttingDown) {\n return;\n }\n\n this.isShuttingDown = true;\n\n if (cluster.isPrimary) {\n for (const id in cluster.workers) {\n const worker = cluster.workers[id];\n\n if (!worker) {\n continue;\n }\n\n worker.send('shutdown');\n }\n\n let exitedWorkers = 0;\n\n cluster.on('exit', () => {\n exitedWorkers++;\n\n const clusterWorkers = cluster.workers ?? {};\n const numClusterWorkers = Object.keys(clusterWorkers).length;\n\n if (exitedWorkers === numClusterWorkers) {\n requestExit({ code: 0, reason: 'cluster-workers-exited' });\n }\n });\n } else {\n await this.stopApplicationCallback();\n }\n }\n}\n"],
|
|
5
|
+
"mappings": ";;AAAA,OAAO,aAAa;AACpB,SAAS,YAAY;AAMrB,SAAS,cAAc;AACvB,SAAS,mBAAmB;AAE5B,MAAO,eAA6B;AAAA,EAVpC,OAUoC;AAAA;AAAA;AAAA,EACjB;AAAA,EAET;AAAA,EACA;AAAA,EAEA,kBAAoC,CAAC,WAAW,QAAQ;AAAA,EACxD,iBAAiB;AAAA,EAEzB,YAAY,EAAE,QAAQ,0BAA0B,wBAAwB,GAAwB;AAC9F,SAAK,SAAS;AAEd,SAAK,2BAA2B;AAChC,SAAK,0BAA0B;AAAA,EACjC;AAAA,EAEO,QAAc;AACnB,QAAI,QAAQ,WAAW;AACrB,WAAK,aAAa;AAAA,IACpB,OAAO;AACL,WAAK,YAAY;AAAA,IACnB;AAEA,SAAK,eAAe;AAAA,EACtB;AAAA,EAEQ,eAAqB;AAC3B,UAAM,UAAkB,KAAK,EAAE;AAE/B,UAAM,oBACJ,KAAK,OAAO,eAAe,SAAS,UAAW,KAAK,OAAgD;AAEtG,aAAS,cAAc,GAAG,cAAc,mBAAmB,eAAe;AACxE,cAAQ,KAAK;AAAA,IACf;AAEA,YAAQ,GAAG,UAAU,YAAU;AAC7B,aAAO,MAAM;AAAA,QACX,SAAS;AAAA,QACT,MAAM;AAAA,UACJ,IAAI,OAAO;AAAA,UACX,KAAK,OAAO,QAAQ;AAAA,QACtB;AAAA,MACF,CAAC;AAAA,IACH,CAAC;AAED,YAAQ,GAAG,QAAQ,MAAM;AACvB,UAAI,CAAC,KAAK,gBAAgB;AAExB,gBAAQ,KAAK;AAAA,MACf;AAAA,IACF,CAAC;AAED,WAAO,MAAM;AAAA,MACX,SAAS;AAAA,MACT,MAAM;AAAA,QACJ,MAAM,KAAK,OAAO;AAAA,QAClB,gBAAgB;AAAA,QAChB,MAAM;AAAA,MACR;AAAA,IACF,CAAC;AAAA,EACH;AAAA,EAEA,MAAc,cAA6B;AACzC,UAAM,KAAK,yBAAyB;AAEpC,YAAQ,GAAG,WAAW,OAAM,YAAW;AACrC,UAAI,YAAY,YAAY;AAC1B,eAAO,MAAM;AAAA,UACX,SAAS;AAAA,UACT,MAAM;AAAA,YACJ,KAAK,QAAQ;AAAA,UACf;AAAA,QACF,CAAC;AAGD,cAAM,KAAK,wBAAwB;AAAA,MACrC;AAAA,IACF,CAAC;AAAA,EACH;AAAA,EAEQ,iBAAuB;AAC7B,SAAK,gBAAgB,QAAQ,YAAU;AACrC,cAAQ,GAAG,QAAQ,YAAY,MAAM,KAAK,iBAAiB,CAAC;AAAA,IAC9D,CAAC;AAAA,EACH;AAAA,EAEA,MAAc,mBAAkC;AAC9C,QAAI,KAAK,gBAAgB;AACvB;AAAA,IACF;AAEA,SAAK,iBAAiB;AAEtB,QAAI,QAAQ,WAAW;AACrB,iBAAW,MAAM,QAAQ,SAAS;AAChC,cAAM,SAAS,QAAQ,QAAQ,EAAE;AAEjC,YAAI,CAAC,QAAQ;AACX;AAAA,QACF;AAEA,eAAO,KAAK,UAAU;AAAA,MACxB;AAEA,UAAI,gBAAgB;AAEpB,cAAQ,GAAG,QAAQ,MAAM;AACvB;AAEA,cAAM,iBAAiB,QAAQ,WAAW,CAAC;AAC3C,cAAM,oBAAoB,OAAO,KAAK,cAAc,EAAE;AAEtD,YAAI,kBAAkB,mBAAmB;AACvC,sBAAY,EAAE,MAAM,GAAG,QAAQ,yBAAyB,CAAC;AAAA,QAC3D;AAAA,MACF,CAAC;AAAA,IACH,OAAO;AACL,YAAM,KAAK,wBAAwB;AAAA,IACrC;AAAA,EACF;AACF;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export interface ExitOutcome {
|
|
2
|
+
code: number;
|
|
3
|
+
reason: string;
|
|
4
|
+
error?: unknown;
|
|
5
|
+
}
|
|
6
|
+
type ExitHandler = (outcome: ExitOutcome) => void;
|
|
7
|
+
export declare function setExitHandler(next: ExitHandler): void;
|
|
8
|
+
export declare function requestExit(outcome: ExitOutcome): void;
|
|
9
|
+
export {};
|
|
10
|
+
//# sourceMappingURL=exit.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"exit.d.ts","sourceRoot":"","sources":["../../src/lifecycle/exit.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED,KAAK,WAAW,GAAG,CAAC,OAAO,EAAE,WAAW,KAAK,IAAI,CAAC;AAMlD,wBAAgB,cAAc,CAAC,IAAI,EAAE,WAAW,QAE/C;AAED,wBAAgB,WAAW,CAAC,OAAO,EAAE,WAAW,QAO/C"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
3
|
+
let handler = /* @__PURE__ */ __name((outcome) => {
|
|
4
|
+
process.exit(outcome.code);
|
|
5
|
+
}, "handler");
|
|
6
|
+
function setExitHandler(next) {
|
|
7
|
+
handler = next;
|
|
8
|
+
}
|
|
9
|
+
__name(setExitHandler, "setExitHandler");
|
|
10
|
+
function requestExit(outcome) {
|
|
11
|
+
try {
|
|
12
|
+
handler(outcome);
|
|
13
|
+
} catch (err) {
|
|
14
|
+
console.error("Exit handler failure", err);
|
|
15
|
+
process.exit(outcome.code);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
__name(requestExit, "requestExit");
|
|
19
|
+
export {
|
|
20
|
+
requestExit,
|
|
21
|
+
setExitHandler
|
|
22
|
+
};
|
|
23
|
+
//# sourceMappingURL=exit.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/lifecycle/exit.ts"],
|
|
4
|
+
"sourcesContent": ["export interface ExitOutcome {\n code: number;\n reason: string;\n error?: unknown;\n}\n\ntype ExitHandler = (outcome: ExitOutcome) => void;\n\nlet handler: ExitHandler = outcome => {\n process.exit(outcome.code);\n};\n\nexport function setExitHandler(next: ExitHandler) {\n handler = next;\n}\n\nexport function requestExit(outcome: ExitOutcome) {\n try {\n handler(outcome);\n } catch (err) {\n console.error('Exit handler failure', err);\n process.exit(outcome.code);\n }\n}\n"],
|
|
5
|
+
"mappings": ";;AAQA,IAAI,UAAuB,oCAAW;AACpC,UAAQ,KAAK,QAAQ,IAAI;AAC3B,GAF2B;AAIpB,SAAS,eAAe,MAAmB;AAChD,YAAU;AACZ;AAFgB;AAIT,SAAS,YAAY,SAAsB;AAChD,MAAI;AACF,YAAQ,OAAO;AAAA,EACjB,SAAS,KAAK;AACZ,YAAQ,MAAM,wBAAwB,GAAG;AACzC,YAAQ,KAAK,QAAQ,IAAI;AAAA,EAC3B;AACF;AAPgB;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export declare class LifecycleManager {
|
|
2
|
+
private shutdownHooks;
|
|
3
|
+
private intervals;
|
|
4
|
+
private timeouts;
|
|
5
|
+
private shuttingDown;
|
|
6
|
+
onShutdown(fn: () => void | Promise<void>): () => void;
|
|
7
|
+
trackInterval(id: NodeJS.Timeout): NodeJS.Timeout;
|
|
8
|
+
trackTimeout(id: NodeJS.Timeout): NodeJS.Timeout;
|
|
9
|
+
shutdown(): Promise<{
|
|
10
|
+
errors: unknown[];
|
|
11
|
+
}>;
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=lifecycle-manager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lifecycle-manager.d.ts","sourceRoot":"","sources":["../../src/lifecycle/lifecycle-manager.ts"],"names":[],"mappings":"AAAA,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,aAAa,CAAyC;IAC9D,OAAO,CAAC,SAAS,CAA6B;IAC9C,OAAO,CAAC,QAAQ,CAA6B;IAC7C,OAAO,CAAC,YAAY,CAAS;IAE7B,UAAU,CAAC,EAAE,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IAQzC,aAAa,CAAC,EAAE,EAAE,MAAM,CAAC,OAAO;IAKhC,YAAY,CAAC,EAAE,EAAE,MAAM,CAAC,OAAO;IAKzB,QAAQ,IAAI,OAAO,CAAC;QAAE,MAAM,EAAE,OAAO,EAAE,CAAA;KAAE,CAAC;CA0BjD"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
3
|
+
class LifecycleManager {
|
|
4
|
+
static {
|
|
5
|
+
__name(this, "LifecycleManager");
|
|
6
|
+
}
|
|
7
|
+
shutdownHooks = [];
|
|
8
|
+
intervals = /* @__PURE__ */ new Set();
|
|
9
|
+
timeouts = /* @__PURE__ */ new Set();
|
|
10
|
+
shuttingDown = false;
|
|
11
|
+
onShutdown(fn) {
|
|
12
|
+
this.shutdownHooks.push(fn);
|
|
13
|
+
return () => {
|
|
14
|
+
const i = this.shutdownHooks.indexOf(fn);
|
|
15
|
+
if (i >= 0) this.shutdownHooks.splice(i, 1);
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
trackInterval(id) {
|
|
19
|
+
this.intervals.add(id);
|
|
20
|
+
return id;
|
|
21
|
+
}
|
|
22
|
+
trackTimeout(id) {
|
|
23
|
+
this.timeouts.add(id);
|
|
24
|
+
return id;
|
|
25
|
+
}
|
|
26
|
+
async shutdown() {
|
|
27
|
+
if (this.shuttingDown) {
|
|
28
|
+
return { errors: [] };
|
|
29
|
+
}
|
|
30
|
+
this.shuttingDown = true;
|
|
31
|
+
for (const id of this.intervals) {
|
|
32
|
+
clearInterval(id);
|
|
33
|
+
}
|
|
34
|
+
for (const id of this.timeouts) {
|
|
35
|
+
clearTimeout(id);
|
|
36
|
+
}
|
|
37
|
+
this.intervals.clear();
|
|
38
|
+
this.timeouts.clear();
|
|
39
|
+
const errors = [];
|
|
40
|
+
for (let i = this.shutdownHooks.length - 1; i >= 0; i--) {
|
|
41
|
+
const hook = this.shutdownHooks[i];
|
|
42
|
+
try {
|
|
43
|
+
await hook();
|
|
44
|
+
} catch (e) {
|
|
45
|
+
errors.push(e);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return { errors };
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
export {
|
|
52
|
+
LifecycleManager
|
|
53
|
+
};
|
|
54
|
+
//# sourceMappingURL=lifecycle-manager.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/lifecycle/lifecycle-manager.ts"],
|
|
4
|
+
"sourcesContent": ["export class LifecycleManager {\n private shutdownHooks: Array<() => void | Promise<void>> = [];\n private intervals = new Set<NodeJS.Timeout>();\n private timeouts = new Set<NodeJS.Timeout>();\n private shuttingDown = false;\n\n onShutdown(fn: () => void | Promise<void>) {\n this.shutdownHooks.push(fn);\n return () => {\n const i = this.shutdownHooks.indexOf(fn);\n if (i >= 0) this.shutdownHooks.splice(i, 1);\n };\n }\n\n trackInterval(id: NodeJS.Timeout) {\n this.intervals.add(id);\n return id;\n }\n\n trackTimeout(id: NodeJS.Timeout) {\n this.timeouts.add(id);\n return id;\n }\n\n async shutdown(): Promise<{ errors: unknown[] }> {\n if (this.shuttingDown) {\n return { errors: [] };\n }\n this.shuttingDown = true;\n\n for (const id of this.intervals) {\n clearInterval(id);\n }\n for (const id of this.timeouts) {\n clearTimeout(id);\n }\n this.intervals.clear();\n this.timeouts.clear();\n\n const errors: unknown[] = [];\n for (let i = this.shutdownHooks.length - 1; i >= 0; i--) {\n const hook = this.shutdownHooks[i];\n try {\n await hook();\n } catch (e) {\n errors.push(e);\n }\n }\n return { errors };\n }\n}\n"],
|
|
5
|
+
"mappings": ";;AAAO,MAAM,iBAAiB;AAAA,EAA9B,OAA8B;AAAA;AAAA;AAAA,EACpB,gBAAmD,CAAC;AAAA,EACpD,YAAY,oBAAI,IAAoB;AAAA,EACpC,WAAW,oBAAI,IAAoB;AAAA,EACnC,eAAe;AAAA,EAEvB,WAAW,IAAgC;AACzC,SAAK,cAAc,KAAK,EAAE;AAC1B,WAAO,MAAM;AACX,YAAM,IAAI,KAAK,cAAc,QAAQ,EAAE;AACvC,UAAI,KAAK,EAAG,MAAK,cAAc,OAAO,GAAG,CAAC;AAAA,IAC5C;AAAA,EACF;AAAA,EAEA,cAAc,IAAoB;AAChC,SAAK,UAAU,IAAI,EAAE;AACrB,WAAO;AAAA,EACT;AAAA,EAEA,aAAa,IAAoB;AAC/B,SAAK,SAAS,IAAI,EAAE;AACpB,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,WAA2C;AAC/C,QAAI,KAAK,cAAc;AACrB,aAAO,EAAE,QAAQ,CAAC,EAAE;AAAA,IACtB;AACA,SAAK,eAAe;AAEpB,eAAW,MAAM,KAAK,WAAW;AAC/B,oBAAc,EAAE;AAAA,IAClB;AACA,eAAW,MAAM,KAAK,UAAU;AAC9B,mBAAa,EAAE;AAAA,IACjB;AACA,SAAK,UAAU,MAAM;AACrB,SAAK,SAAS,MAAM;AAEpB,UAAM,SAAoB,CAAC;AAC3B,aAAS,IAAI,KAAK,cAAc,SAAS,GAAG,KAAK,GAAG,KAAK;AACvD,YAAM,OAAO,KAAK,cAAc,CAAC;AACjC,UAAI;AACF,cAAM,KAAK;AAAA,MACb,SAAS,GAAG;AACV,eAAO,KAAK,CAAC;AAAA,MACf;AAAA,IACF;AACA,WAAO,EAAE,OAAO;AAAA,EAClB;AACF;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@scpxl/nodejs-framework",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.17",
|
|
4
4
|
"description": "PXL Node.js Framework",
|
|
5
5
|
"engines": {
|
|
6
6
|
"node": ">=22.0.0"
|
|
@@ -69,25 +69,25 @@
|
|
|
69
69
|
"build": "npm run clean && node esbuild.config.js",
|
|
70
70
|
"build:tsc": "npm run clean && tsc",
|
|
71
71
|
"build:local": "npm run clean && node esbuild.config.js && yalc push",
|
|
72
|
-
"docs": "
|
|
73
|
-
"docs:
|
|
74
|
-
"docs:
|
|
75
|
-
"docs:site:preview": "vitepress preview docs",
|
|
72
|
+
"docs:dev": "vitepress dev docs",
|
|
73
|
+
"docs:build": "vitepress build docs",
|
|
74
|
+
"docs:preview": "vitepress preview docs",
|
|
76
75
|
"release": "node scripts/release.js",
|
|
77
76
|
"prepare": "husky",
|
|
78
77
|
"postinstall": "npm run build || echo 'Build failed, dist files may be missing'",
|
|
79
78
|
"lint": "eslint .",
|
|
80
79
|
"lint:fix": "eslint . --fix",
|
|
81
|
-
"
|
|
82
|
-
"
|
|
80
|
+
"prettier": "prettier --check .",
|
|
81
|
+
"prettier:fix": "prettier --write .",
|
|
83
82
|
"typecheck": "tsc --noEmit",
|
|
84
|
-
"test": "
|
|
85
|
-
"test:watch": "
|
|
86
|
-
"test:coverage": "
|
|
87
|
-
"test:unit": "
|
|
88
|
-
"test:integration": "
|
|
89
|
-
"test:e2e": "
|
|
90
|
-
"
|
|
83
|
+
"test": "vitest run",
|
|
84
|
+
"test:watch": "vitest",
|
|
85
|
+
"test:coverage": "vitest run --coverage",
|
|
86
|
+
"test:unit": "vitest run test/unit",
|
|
87
|
+
"test:integration": "vitest run test/integration",
|
|
88
|
+
"test:e2e": "vitest run test/e2e",
|
|
89
|
+
"test:ui": "vitest --ui",
|
|
90
|
+
"check-all": "npm run lint && npm run prettier && npm run typecheck",
|
|
91
91
|
"yalc:publish": "yalc publish",
|
|
92
92
|
"yalc:push": "yalc push",
|
|
93
93
|
"dev:yalc": "npm run build && npm run yalc:push"
|
|
@@ -104,6 +104,8 @@
|
|
|
104
104
|
"@types/yargs": "^17.0.33",
|
|
105
105
|
"@typescript-eslint/eslint-plugin": "^8.1.0",
|
|
106
106
|
"@typescript-eslint/parser": "^8.1.0",
|
|
107
|
+
"@vitest/coverage-v8": "^3.2.4",
|
|
108
|
+
"@vitest/ui": "^3.2.4",
|
|
107
109
|
"del-cli": "^6.0.0",
|
|
108
110
|
"esbuild": "^0.25.5",
|
|
109
111
|
"eslint": "^9.9.0",
|
|
@@ -119,8 +121,8 @@
|
|
|
119
121
|
"ts-node": "^10.9.2",
|
|
120
122
|
"tsc-alias": "^1.8.16",
|
|
121
123
|
"tsconfig-paths": "^4.2.0",
|
|
122
|
-
"
|
|
123
|
-
"
|
|
124
|
+
"vitepress": "^1.6.4",
|
|
125
|
+
"vitest": "^3.2.4"
|
|
124
126
|
},
|
|
125
127
|
"dependencies": {
|
|
126
128
|
"@aws-sdk/client-s3": "^3.496.0",
|