@scpxl/nodejs-framework 1.0.14 → 1.0.19

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.
Files changed (192) hide show
  1. package/README.md +29 -0
  2. package/dist/api-requester/api-requester.d.ts +27 -6
  3. package/dist/api-requester/api-requester.d.ts.map +1 -1
  4. package/dist/api-requester/api-requester.js +188 -26
  5. package/dist/api-requester/api-requester.js.map +2 -2
  6. package/dist/api-requester/index.d.ts +1 -0
  7. package/dist/api-requester/index.d.ts.map +1 -1
  8. package/dist/api-requester/index.js.map +1 -1
  9. package/dist/application/base-application.d.ts +16 -20
  10. package/dist/application/base-application.d.ts.map +1 -1
  11. package/dist/application/base-application.js +152 -109
  12. package/dist/application/base-application.js.map +2 -2
  13. package/dist/application/command-application.d.ts.map +1 -1
  14. package/dist/application/command-application.js +3 -4
  15. package/dist/application/command-application.js.map +2 -2
  16. package/dist/application/web-application.d.ts.map +1 -1
  17. package/dist/application/web-application.js +9 -2
  18. package/dist/application/web-application.js.map +2 -2
  19. package/dist/cache/manager.d.ts +87 -6
  20. package/dist/cache/manager.d.ts.map +1 -1
  21. package/dist/cache/manager.js +77 -30
  22. package/dist/cache/manager.js.map +2 -2
  23. package/dist/cluster/cluster-manager.d.ts.map +1 -1
  24. package/dist/cluster/cluster-manager.js +7 -9
  25. package/dist/cluster/cluster-manager.js.map +2 -2
  26. package/dist/config/env.d.ts +11 -0
  27. package/dist/config/env.d.ts.map +1 -0
  28. package/dist/config/env.js +103 -0
  29. package/dist/config/env.js.map +7 -0
  30. package/dist/config/index.d.ts +3 -0
  31. package/dist/config/index.d.ts.map +1 -0
  32. package/dist/config/index.js +3 -0
  33. package/dist/config/index.js.map +7 -0
  34. package/dist/config/schema.d.ts +408 -0
  35. package/dist/config/schema.d.ts.map +1 -0
  36. package/dist/config/schema.js +218 -0
  37. package/dist/config/schema.js.map +7 -0
  38. package/dist/database/dynamic-entity.d.ts.map +1 -1
  39. package/dist/database/dynamic-entity.js +6 -2
  40. package/dist/database/dynamic-entity.js.map +2 -2
  41. package/dist/database/instance.d.ts.map +1 -1
  42. package/dist/database/instance.js +0 -8
  43. package/dist/database/instance.js.map +2 -2
  44. package/dist/database/manager.d.ts.map +1 -1
  45. package/dist/database/manager.js +71 -9
  46. package/dist/database/manager.js.map +2 -2
  47. package/dist/error/error-reporter.d.ts +96 -0
  48. package/dist/error/error-reporter.d.ts.map +1 -0
  49. package/dist/error/error-reporter.js +228 -0
  50. package/dist/error/error-reporter.js.map +7 -0
  51. package/dist/error/error.interface.d.ts +126 -0
  52. package/dist/error/error.interface.d.ts.map +1 -0
  53. package/dist/error/error.interface.js +45 -0
  54. package/dist/error/error.interface.js.map +7 -0
  55. package/dist/error/framework-errors.d.ts +113 -0
  56. package/dist/error/framework-errors.d.ts.map +1 -0
  57. package/dist/error/framework-errors.js +176 -0
  58. package/dist/error/framework-errors.js.map +7 -0
  59. package/dist/error/index.d.ts +6 -0
  60. package/dist/error/index.d.ts.map +1 -0
  61. package/dist/error/index.js +34 -0
  62. package/dist/error/index.js.map +7 -0
  63. package/dist/event/manager.d.ts.map +1 -1
  64. package/dist/event/manager.js +2 -9
  65. package/dist/event/manager.js.map +2 -2
  66. package/dist/index.d.ts +5 -1
  67. package/dist/index.d.ts.map +1 -1
  68. package/dist/index.js +5 -1
  69. package/dist/index.js.map +2 -2
  70. package/dist/lifecycle/exit.d.ts +11 -0
  71. package/dist/lifecycle/exit.d.ts.map +1 -0
  72. package/dist/lifecycle/exit.js +29 -0
  73. package/dist/lifecycle/exit.js.map +7 -0
  74. package/dist/lifecycle/index.d.ts +7 -0
  75. package/dist/lifecycle/index.d.ts.map +1 -0
  76. package/dist/lifecycle/index.js +12 -0
  77. package/dist/lifecycle/index.js.map +7 -0
  78. package/dist/lifecycle/lifecycle-manager.d.ts +66 -0
  79. package/dist/lifecycle/lifecycle-manager.d.ts.map +1 -0
  80. package/dist/lifecycle/lifecycle-manager.js +280 -0
  81. package/dist/lifecycle/lifecycle-manager.js.map +7 -0
  82. package/dist/lifecycle/shutdown-controller.d.ts +15 -0
  83. package/dist/lifecycle/shutdown-controller.d.ts.map +1 -0
  84. package/dist/lifecycle/shutdown-controller.js +38 -0
  85. package/dist/lifecycle/shutdown-controller.js.map +7 -0
  86. package/dist/lifecycle/types.d.ts +28 -0
  87. package/dist/lifecycle/types.d.ts.map +1 -0
  88. package/dist/lifecycle/types.js +13 -0
  89. package/dist/lifecycle/types.js.map +7 -0
  90. package/dist/logger/logger.d.ts +2 -1
  91. package/dist/logger/logger.d.ts.map +1 -1
  92. package/dist/logger/logger.js +35 -11
  93. package/dist/logger/logger.js.map +2 -2
  94. package/dist/performance/cache-performance.d.ts +6 -0
  95. package/dist/performance/cache-performance.d.ts.map +1 -1
  96. package/dist/performance/cache-performance.js +16 -0
  97. package/dist/performance/cache-performance.js.map +2 -2
  98. package/dist/performance/index.d.ts +1 -0
  99. package/dist/performance/index.d.ts.map +1 -1
  100. package/dist/performance/index.js +1 -0
  101. package/dist/performance/index.js.map +2 -2
  102. package/dist/performance/performance-monitor.d.ts.map +1 -1
  103. package/dist/performance/performance-monitor.js +47 -18
  104. package/dist/performance/performance-monitor.js.map +2 -2
  105. package/dist/performance/performance-monitor.plugin.d.ts +24 -0
  106. package/dist/performance/performance-monitor.plugin.d.ts.map +1 -0
  107. package/dist/performance/performance-monitor.plugin.js +89 -0
  108. package/dist/performance/performance-monitor.plugin.js.map +7 -0
  109. package/dist/performance/webserver-performance.js +1 -1
  110. package/dist/performance/webserver-performance.js.map +2 -2
  111. package/dist/queue/manager.d.ts.map +1 -1
  112. package/dist/queue/manager.js +3 -10
  113. package/dist/queue/manager.js.map +2 -2
  114. package/dist/queue/worker.d.ts.map +1 -1
  115. package/dist/queue/worker.js +2 -2
  116. package/dist/queue/worker.js.map +2 -2
  117. package/dist/redis/manager.d.ts.map +1 -1
  118. package/dist/redis/manager.js +228 -33
  119. package/dist/redis/manager.js.map +2 -2
  120. package/dist/request-context/index.d.ts +3 -0
  121. package/dist/request-context/index.d.ts.map +1 -0
  122. package/dist/request-context/index.js +25 -0
  123. package/dist/request-context/index.js.map +7 -0
  124. package/dist/request-context/request-context.d.ts +108 -0
  125. package/dist/request-context/request-context.d.ts.map +1 -0
  126. package/dist/request-context/request-context.interface.d.ts +46 -0
  127. package/dist/request-context/request-context.interface.d.ts.map +1 -0
  128. package/dist/request-context/request-context.interface.js +1 -0
  129. package/dist/request-context/request-context.interface.js.map +7 -0
  130. package/dist/request-context/request-context.js +79 -0
  131. package/dist/request-context/request-context.js.map +7 -0
  132. package/dist/services/aws/s3.js +4 -6
  133. package/dist/services/aws/s3.js.map +2 -2
  134. package/dist/util/file.d.ts +13 -0
  135. package/dist/util/file.d.ts.map +1 -1
  136. package/dist/util/file.js +46 -9
  137. package/dist/util/file.js.map +2 -2
  138. package/dist/util/helper.d.ts +16 -1
  139. package/dist/util/helper.d.ts.map +1 -1
  140. package/dist/util/helper.js +19 -43
  141. package/dist/util/helper.js.map +2 -2
  142. package/dist/util/index.d.ts +1 -0
  143. package/dist/util/index.d.ts.map +1 -1
  144. package/dist/util/index.js +18 -16
  145. package/dist/util/index.js.map +2 -2
  146. package/dist/util/loader.d.ts.map +1 -1
  147. package/dist/util/loader.js +13 -2
  148. package/dist/util/loader.js.map +2 -2
  149. package/dist/util/os.d.ts.map +1 -1
  150. package/dist/util/os.js +8 -14
  151. package/dist/util/os.js.map +2 -2
  152. package/dist/util/time.d.ts +8 -2
  153. package/dist/util/time.d.ts.map +1 -1
  154. package/dist/util/time.js +12 -7
  155. package/dist/util/time.js.map +2 -2
  156. package/dist/util/timing.d.ts +36 -0
  157. package/dist/util/timing.d.ts.map +1 -0
  158. package/dist/util/timing.interface.d.ts +47 -0
  159. package/dist/util/timing.interface.d.ts.map +1 -0
  160. package/dist/util/timing.interface.js +1 -0
  161. package/dist/util/timing.interface.js.map +7 -0
  162. package/dist/util/timing.js +98 -0
  163. package/dist/util/timing.js.map +7 -0
  164. package/dist/util/url.js +1 -1
  165. package/dist/util/url.js.map +2 -2
  166. package/dist/webserver/controller/base.d.ts +3 -1
  167. package/dist/webserver/controller/base.d.ts.map +1 -1
  168. package/dist/webserver/controller/base.interface.d.ts +2 -0
  169. package/dist/webserver/controller/base.interface.d.ts.map +1 -1
  170. package/dist/webserver/controller/base.js +4 -1
  171. package/dist/webserver/controller/base.js.map +2 -2
  172. package/dist/webserver/controller/health.d.ts +8 -1
  173. package/dist/webserver/controller/health.d.ts.map +1 -1
  174. package/dist/webserver/controller/health.js +36 -22
  175. package/dist/webserver/controller/health.js.map +2 -2
  176. package/dist/webserver/webserver.d.ts +16 -2
  177. package/dist/webserver/webserver.d.ts.map +1 -1
  178. package/dist/webserver/webserver.interface.d.ts +37 -0
  179. package/dist/webserver/webserver.interface.d.ts.map +1 -1
  180. package/dist/webserver/webserver.interface.js.map +2 -2
  181. package/dist/webserver/webserver.js +117 -20
  182. package/dist/webserver/webserver.js.map +2 -2
  183. package/dist/websocket/controllers/server/system.d.ts.map +1 -1
  184. package/dist/websocket/controllers/server/system.js.map +2 -2
  185. package/dist/websocket/websocket-base.d.ts.map +1 -1
  186. package/dist/websocket/websocket-base.js +2 -3
  187. package/dist/websocket/websocket-base.js.map +2 -2
  188. package/dist/websocket/websocket-server.d.ts +1 -1
  189. package/dist/websocket/websocket-server.d.ts.map +1 -1
  190. package/dist/websocket/websocket-server.js +7 -31
  191. package/dist/websocket/websocket-server.js.map +2 -2
  192. package/package.json +68 -25
@@ -13,20 +13,18 @@ import CacheManager from "../cache/manager.js";
13
13
  import os from "os";
14
14
  import EventManager from "../event/manager.js";
15
15
  import Logger from "../logger/logger.js";
16
- import { PerformanceMonitor } from "../performance/performance-monitor.js";
17
- import { CachePerformanceWrapper, DatabasePerformanceWrapper, QueuePerformanceWrapper } from "../performance/index.js";
16
+ import { PerformanceMonitorPlugin } from "../performance/performance-monitor.plugin.js";
17
+ import { LifecycleManager, ShutdownController } from "../lifecycle/index.js";
18
+ import { ConfigValidationError, formatConfigIssues, validateFrameworkConfig } from "../config/schema.js";
19
+ import { requestExit } from "../lifecycle/exit.js";
18
20
  class BaseApplication {
19
21
  static {
20
22
  __name(this, "BaseApplication");
21
23
  }
22
24
  /** Unique instance ID */
23
25
  uniqueInstanceId;
24
- /** Shutdown signals */
25
- shutdownSignals = ["SIGTERM", "SIGINT"];
26
26
  /** Application start time */
27
- startTime = [0, 0];
28
- /** Whether application is stopping */
29
- isStopping = false;
27
+ startTime = 0;
30
28
  /** Shutdown timeout (30 seconds) */
31
29
  shutdownTimeout = 3e4;
32
30
  /** Cache for application version to avoid repeated imports */
@@ -49,6 +47,10 @@ class BaseApplication {
49
47
  eventManager;
50
48
  /** Performance monitor */
51
49
  performanceMonitor;
50
+ /** Lifecycle manager */
51
+ lifecycle;
52
+ /** Shutdown controller */
53
+ shutdownController;
52
54
  get Name() {
53
55
  return this.config.name;
54
56
  }
@@ -56,9 +58,32 @@ class BaseApplication {
56
58
  * Application constructor
57
59
  */
58
60
  constructor(config) {
61
+ try {
62
+ const validated = validateFrameworkConfig(config);
63
+ config = validated;
64
+ } catch (err) {
65
+ if (err instanceof ConfigValidationError) {
66
+ const formatted = formatConfigIssues(err.issues);
67
+ throw new Error(`Configuration validation failed:
68
+ ${formatted}`);
69
+ }
70
+ throw err;
71
+ }
59
72
  const computerName = os.hostname();
60
73
  this.uniqueInstanceId = `${config.instanceId}-${computerName}-${OS.getUniqueComputerId()}`;
61
74
  this.config = config;
75
+ const lifecycleConfig = {
76
+ gracefulShutdown: {
77
+ timeoutMs: this.shutdownTimeout
78
+ },
79
+ readiness: {
80
+ timeoutMs: 3e4,
81
+ checkIntervalMs: 100
82
+ }
83
+ };
84
+ this.lifecycle = new LifecycleManager(lifecycleConfig);
85
+ this.shutdownController = new ShutdownController(this.lifecycle);
86
+ this.registerShutdownHooks();
62
87
  this.redisManager = new RedisManager({
63
88
  applicationConfig: this.config,
64
89
  host: this.config.redis.host,
@@ -66,10 +91,9 @@ class BaseApplication {
66
91
  password: this.config.redis.password
67
92
  });
68
93
  this.cacheManager = new CacheManager({
69
- applicationConfig: this.config,
70
94
  redisManager: this.redisManager
71
95
  });
72
- this.initializePerformanceMonitor();
96
+ PerformanceMonitorPlugin.register(this);
73
97
  this.setupGlobalErrorHandlers();
74
98
  if (this.config.database && this.config.database.enabled === true) {
75
99
  const defaultEntitiesDirectory = join(this.config.rootDirectory, "src", "database", "entities");
@@ -112,35 +136,12 @@ class BaseApplication {
112
136
  * Start application
113
137
  */
114
138
  async start() {
115
- this.startTime = process.hrtime();
139
+ this.startTime = Time.now();
116
140
  this.applicationVersion = await this.getApplicationVersion();
117
141
  const startInstanceOptions = {
118
- // onStarted: ({ startupTime }) => {
119
- // if (this.config.log?.startUp) {
120
- // Logger.info('Application started', {
121
- // Name: this.config.name,
122
- // 'PXL Framework Version': this.applicationVersion,
123
- // 'Startup Time': Time.formatTime({ time: startupTime, format: 's', numDecimals: 2, showUnit: true }),
124
- // });
125
- // }
126
- // if (this.config.events?.onStarted) {
127
- // this.config.events.onStarted({ app: this, startupTime });
128
- // }
129
- // },
130
142
  onStarted: this.onStarted.bind(this)
131
143
  };
132
144
  const stopInstanceOptions = {
133
- // onStopped: ({ runtime }) => {
134
- // if (this.config.log?.shutdown) {
135
- // Logger.info('Application stopped', {
136
- // Name: this.config.name,
137
- // 'Runtime': Time.formatTime({ time: runtime, format: 's', numDecimals: 2, showUnit: true }),
138
- // });
139
- // }
140
- // if (this.config.events?.onStopped) {
141
- // this.config.events.onStopped({ app: this, runtime });
142
- // }
143
- // },
144
145
  onStopped: this.onStopped.bind(this)
145
146
  };
146
147
  if (this.config.cluster?.enabled) {
@@ -152,9 +153,6 @@ class BaseApplication {
152
153
  clusterManager.start();
153
154
  } else {
154
155
  await this.startInstance(startInstanceOptions);
155
- this.handleShutdown({
156
- onStopped: stopInstanceOptions.onStopped
157
- });
158
156
  }
159
157
  }
160
158
  /**
@@ -188,6 +186,22 @@ class BaseApplication {
188
186
  await queueManager.registerQueues({
189
187
  queues: this.config.queue.queues
190
188
  });
189
+ this.lifecycle.addReadinessCheck("redis", async () => {
190
+ try {
191
+ return await redisInstance.isConnected();
192
+ } catch {
193
+ return false;
194
+ }
195
+ });
196
+ if (databaseInstance) {
197
+ this.lifecycle.addReadinessCheck("database", async () => {
198
+ try {
199
+ return await databaseInstance.isConnected();
200
+ } catch {
201
+ return false;
202
+ }
203
+ });
204
+ }
191
205
  return {
192
206
  redisInstance,
193
207
  databaseInstance,
@@ -205,36 +219,51 @@ class BaseApplication {
205
219
  */
206
220
  onStopped({ runtime: _runtime }) {
207
221
  }
208
- /**
209
- * Before application stop event
210
- */
211
- async onBeforeStop() {
212
- await this.redisManager.disconnect();
213
- if (this.databaseManager) {
214
- await this.databaseManager.disconnect();
215
- }
216
- }
217
222
  /**
218
223
  * Start application instance
219
224
  */
220
225
  async startInstance(options) {
221
226
  try {
227
+ const initResult = await this.lifecycle.initialize();
228
+ if (initResult.errors.length > 0) {
229
+ Logger.warn({
230
+ message: "Lifecycle init phase encountered errors",
231
+ meta: { errors: initResult.errors.map((e) => e instanceof Error ? e.message : String(e)) }
232
+ });
233
+ }
222
234
  const { redisInstance, databaseInstance, queueManager, eventManager } = await this.onBeforeStart();
235
+ const startResult = await this.lifecycle.start();
236
+ if (startResult.errors.length > 0) {
237
+ Logger.warn({
238
+ message: "Lifecycle start phase encountered errors",
239
+ meta: { errors: startResult.errors.map((e) => e instanceof Error ? e.message : String(e)) }
240
+ });
241
+ }
223
242
  await this.startHandler({
224
243
  redisInstance,
225
244
  databaseInstance,
226
245
  queueManager,
227
246
  eventManager
228
247
  });
229
- const startupTime = Time.calculateElapsedTime({
248
+ const readyResult = await this.lifecycle.ready();
249
+ if (readyResult.errors.length > 0) {
250
+ Logger.warn({
251
+ message: "Lifecycle ready phase encountered errors",
252
+ meta: { errors: readyResult.errors.map((e) => e instanceof Error ? e.message : String(e)) }
253
+ });
254
+ }
255
+ const startupTime = Time.calculateElapsedTimeMs({
230
256
  startTime: this.startTime
231
257
  });
232
258
  if (options.onStarted) {
233
259
  await options.onStarted({ startupTime });
234
260
  }
235
261
  } catch (error) {
236
- console.error(error);
237
- process.exit(1);
262
+ Logger.error({
263
+ error: error instanceof Error ? error : new Error(String(error)),
264
+ message: "startInstance failure"
265
+ });
266
+ throw error;
238
267
  }
239
268
  }
240
269
  /**
@@ -243,36 +272,8 @@ class BaseApplication {
243
272
  /**
244
273
  * Initialize performance monitor
245
274
  */
246
- initializePerformanceMonitor() {
247
- if (!this.config.performanceMonitoring?.enabled) {
248
- return;
249
- }
250
- this.performanceMonitor = PerformanceMonitor.initialize({
251
- enabled: true,
252
- thresholds: this.config.performanceMonitoring.thresholds,
253
- maxMetricsHistory: this.config.performanceMonitoring.maxMetricsHistory,
254
- logSlowOperations: this.config.performanceMonitoring.logSlowOperations,
255
- logAllOperations: this.config.performanceMonitoring.logAllOperations
256
- });
257
- if (this.config.performanceMonitoring.monitorDatabaseOperations !== false) {
258
- DatabasePerformanceWrapper.setPerformanceMonitor(this.performanceMonitor);
259
- }
260
- if (this.config.performanceMonitoring.monitorQueueOperations !== false) {
261
- QueuePerformanceWrapper.setPerformanceMonitor(this.performanceMonitor);
262
- }
263
- if (this.config.performanceMonitoring.monitorCacheOperations !== false) {
264
- CachePerformanceWrapper.setPerformanceMonitor(this.performanceMonitor);
265
- }
266
- if (this.config.performanceMonitoring.reportInterval && this.config.performanceMonitoring.reportInterval > 0) {
267
- setInterval(() => {
268
- const reportFormat = this.config.performanceMonitoring?.reportFormat ?? "detailed";
269
- const report = this.performanceMonitor?.generateFormattedReport(reportFormat);
270
- if (report) {
271
- Logger.info({ message: report });
272
- }
273
- }, this.config.performanceMonitoring.reportInterval);
274
- }
275
- }
275
+ // initializePerformanceMonitor deprecated in favor of PerformanceMonitorPlugin
276
+ // (left intentionally absent)
276
277
  setupGlobalErrorHandlers() {
277
278
  process.on("uncaughtException", (error) => {
278
279
  Logger.error({ error, message: "Uncaught Exception" });
@@ -287,61 +288,103 @@ class BaseApplication {
287
288
  this.initiateGracefulShutdown();
288
289
  });
289
290
  }
291
+ /**
292
+ * Register shutdown hooks for proper cleanup
293
+ */
294
+ registerShutdownHooks() {
295
+ this.lifecycle.onShutdown(async () => {
296
+ Logger.info({ message: "Executing custom stop callback" });
297
+ await this.stopCallback();
298
+ });
299
+ this.lifecycle.onShutdown(async () => {
300
+ if (this.redisManager) {
301
+ Logger.info({ message: "Disconnecting from Redis" });
302
+ await this.redisManager.disconnect();
303
+ }
304
+ });
305
+ this.lifecycle.onShutdown(async () => {
306
+ if (this.databaseManager) {
307
+ Logger.info({ message: "Disconnecting from database" });
308
+ await this.databaseManager.disconnect();
309
+ }
310
+ });
311
+ }
290
312
  /**
291
313
  * Initiate graceful shutdown
292
314
  */
293
- initiateGracefulShutdown() {
294
- if (this.isStopping) {
315
+ async initiateGracefulShutdown() {
316
+ if (this.shutdownController.isShuttingDown) {
295
317
  return;
296
318
  }
297
319
  Logger.info({ message: "Initiating graceful shutdown due to error" });
298
- this.stop().catch((error) => {
320
+ try {
321
+ const result = await this.shutdownController.initiate("error-triggered");
322
+ if (result.errors.length > 0) {
323
+ Logger.error({
324
+ message: "Errors during shutdown",
325
+ error: result.errors
326
+ });
327
+ this.finalizeExit({ code: 1, reason: "graceful-shutdown-error", error: result.errors });
328
+ } else if (result.timedOut) {
329
+ Logger.warn({ message: "Shutdown timed out" });
330
+ this.finalizeExit({ code: 1, reason: "shutdown-timeout" });
331
+ } else {
332
+ this.finalizeExit({ code: 0, reason: "error-shutdown-complete" });
333
+ }
334
+ } catch (error) {
299
335
  Logger.error({
300
336
  error: error instanceof Error ? error : new Error(String(error)),
301
337
  message: "Error during graceful shutdown"
302
338
  });
303
- process.exit(1);
304
- });
305
- }
306
- /**
307
- * Handle shutdown
308
- */
309
- handleShutdown({ onStopped }) {
310
- this.shutdownSignals.forEach((signal) => {
311
- process.on(signal, async () => {
312
- await this.stop({ onStopped });
313
- });
314
- });
339
+ this.finalizeExit({ code: 1, reason: "graceful-shutdown-error", error });
340
+ }
315
341
  }
316
342
  /**
317
- * Stop application
343
+ * Stop application using lifecycle manager
318
344
  */
319
345
  async stop({ onStopped } = {}) {
320
- if (this.isStopping) {
346
+ if (this.shutdownController.isShuttingDown) {
321
347
  return;
322
348
  }
323
- this.isStopping = true;
324
- const forceExitTimeout = setTimeout(() => {
325
- Logger.warn({ message: "Forced shutdown due to timeout" });
326
- process.exit(1);
327
- }, this.shutdownTimeout);
328
- try {
329
- await this.stopCallback();
330
- await this.onBeforeStop();
331
- if (onStopped) {
349
+ if (onStopped) {
350
+ this.lifecycle.onShutdown(() => {
332
351
  const runtime = process.uptime() * 1e3;
333
- await onStopped({ runtime });
352
+ onStopped({ runtime });
353
+ });
354
+ }
355
+ try {
356
+ const result = await this.shutdownController.initiate("manual-stop");
357
+ if (result.errors.length > 0) {
358
+ Logger.error({
359
+ message: "Errors during shutdown",
360
+ error: result.errors
361
+ });
362
+ this.finalizeExit({ code: 1, reason: "shutdown-error", error: result.errors });
363
+ } else if (result.timedOut) {
364
+ Logger.warn({ message: "Shutdown timed out" });
365
+ this.finalizeExit({ code: 1, reason: "shutdown-timeout" });
366
+ } else {
367
+ this.finalizeExit({ code: 0, reason: "shutdown-complete" });
334
368
  }
335
- clearTimeout(forceExitTimeout);
336
- process.exit(0);
337
369
  } catch (error) {
338
370
  Logger.error({
339
371
  error: error instanceof Error ? error : new Error(String(error)),
340
372
  message: "Error during shutdown"
341
373
  });
342
- clearTimeout(forceExitTimeout);
343
- process.exit(1);
374
+ this.finalizeExit({ code: 1, reason: "shutdown-error", error });
375
+ }
376
+ }
377
+ /**
378
+ * Finalize exit: during tests, suppress actual process exit to avoid failing vitest runs.
379
+ */
380
+ finalizeExit(outcome) {
381
+ const nodeEnv = process.env.NODE_ENV ?? "";
382
+ const isTestEnv = nodeEnv.toLowerCase() === "test" || "VITEST" in process.env || "VITEST_WORKER_ID" in process.env || process.argv.some((a) => a.includes("vitest")) || typeof globalThis.afterAll === "function";
383
+ if (isTestEnv) {
384
+ Logger.info({ message: `Skipping process exit in test environment (${outcome.reason})`, code: outcome.code });
385
+ return;
344
386
  }
387
+ requestExit(outcome);
345
388
  }
346
389
  }
347
390
  export {
@@ -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": "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",
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 type { PerformanceMonitor } from '../performance/performance-monitor.js';\n// Performance monitoring now pluginized\nimport { PerformanceMonitorPlugin } from '../performance/performance-monitor.plugin.js';\nimport { type LifecycleConfig, LifecycleManager, ShutdownController } from '../lifecycle/index.js';\nimport { ConfigValidationError, formatConfigIssues, validateFrameworkConfig } from '../config/schema.js';\nimport { type ExitOutcome, 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 /** Application start time */\n protected startTime: number = 0;\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 public lifecycle: LifecycleManager;\n\n /** Shutdown controller */\n public shutdownController: ShutdownController;\n\n public get Name() {\n return this.config.name;\n }\n\n /**\n * Application constructor\n */\n constructor(config: ApplicationConfig) {\n // Validate configuration early (fail-fast before side effects)\n try {\n const validated = validateFrameworkConfig(config as any);\n config = validated as unknown as ApplicationConfig;\n } catch (err) {\n if (err instanceof ConfigValidationError) {\n const formatted = formatConfigIssues(err.issues);\n throw new Error(`Configuration validation failed:\\n${formatted}`);\n }\n throw err;\n }\n const computerName = os.hostname();\n\n this.uniqueInstanceId = `${config.instanceId}-${computerName}-${OS.getUniqueComputerId()}`;\n this.config = config;\n\n // Initialize lifecycle management\n const lifecycleConfig: Partial<LifecycleConfig> = {\n gracefulShutdown: {\n timeoutMs: this.shutdownTimeout,\n },\n readiness: {\n timeoutMs: 30000,\n checkIntervalMs: 100,\n },\n };\n this.lifecycle = new LifecycleManager(lifecycleConfig);\n this.shutdownController = new ShutdownController(this.lifecycle);\n\n // Register shutdown hooks for cleanup\n this.registerShutdownHooks();\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 redisManager: this.redisManager,\n });\n\n // Register performance monitor plugin (idempotent & opt-in)\n PerformanceMonitorPlugin.register(this);\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 = Time.now();\n\n // Get application version`\n this.applicationVersion = await this.getApplicationVersion();\n\n const startInstanceOptions: ApplicationStartInstanceOptions = {\n onStarted: this.onStarted.bind(this),\n };\n\n const stopInstanceOptions: ApplicationStopInstanceOptions = {\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 // Note: Signal handling should be implemented at the application launcher level\n // The lifecycle manager provides the stop() method for programmatic shutdown\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 // Register readiness checks for key services\n this.lifecycle.addReadinessCheck('redis', async () => {\n try {\n return await redisInstance.isConnected();\n } catch {\n return false;\n }\n });\n\n if (databaseInstance) {\n this.lifecycle.addReadinessCheck('database', async () => {\n try {\n return await databaseInstance.isConnected();\n } catch {\n return false;\n }\n });\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 * Start application instance\n */\n private async startInstance(options: ApplicationStartInstanceOptions): Promise<void> {\n try {\n // Phase 1: Initialize (resource setup)\n const initResult = await this.lifecycle.initialize();\n if (initResult.errors.length > 0) {\n Logger.warn({\n message: 'Lifecycle init phase encountered errors',\n meta: { errors: initResult.errors.map(e => (e instanceof Error ? e.message : String(e))) },\n });\n }\n\n // Before application start\n const { redisInstance, databaseInstance, queueManager, eventManager } = await this.onBeforeStart();\n\n // Phase 2: Start (component startup)\n const startResult = await this.lifecycle.start();\n if (startResult.errors.length > 0) {\n Logger.warn({\n message: 'Lifecycle start phase encountered errors',\n meta: { errors: startResult.errors.map(e => (e instanceof Error ? e.message : String(e))) },\n });\n }\n\n // Start application\n await this.startHandler({\n redisInstance,\n databaseInstance,\n queueManager,\n eventManager,\n });\n\n // Phase 3: Ready (application accepting traffic)\n const readyResult = await this.lifecycle.ready();\n if (readyResult.errors.length > 0) {\n Logger.warn({\n message: 'Lifecycle ready phase encountered errors',\n meta: { errors: readyResult.errors.map(e => (e instanceof Error ? e.message : String(e))) },\n });\n }\n\n // Calculate application startup time\n const startupTime = Time.calculateElapsedTimeMs({\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 // initializePerformanceMonitor deprecated in favor of PerformanceMonitorPlugin\n // (left intentionally absent)\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 * Register shutdown hooks for proper cleanup\n */\n private registerShutdownHooks(): void {\n // Register shutdown hooks in reverse dependency order\n this.lifecycle.onShutdown(async () => {\n Logger.info({ message: 'Executing custom stop callback' });\n await this.stopCallback();\n });\n\n this.lifecycle.onShutdown(async () => {\n if (this.redisManager) {\n Logger.info({ message: 'Disconnecting from Redis' });\n await this.redisManager.disconnect();\n }\n });\n\n this.lifecycle.onShutdown(async () => {\n if (this.databaseManager) {\n Logger.info({ message: 'Disconnecting from database' });\n await this.databaseManager.disconnect();\n }\n });\n\n // Performance monitor is handled via trackInterval, so it will be cleaned up automatically\n }\n\n /**\n * Initiate graceful shutdown\n */\n private async initiateGracefulShutdown(): Promise<void> {\n if (this.shutdownController.isShuttingDown) {\n return;\n }\n\n Logger.info({ message: 'Initiating graceful shutdown due to error' });\n try {\n const result = await this.shutdownController.initiate('error-triggered');\n if (result.errors.length > 0) {\n Logger.error({\n message: 'Errors during shutdown',\n error: result.errors,\n });\n this.finalizeExit({ code: 1, reason: 'graceful-shutdown-error', error: result.errors });\n } else if (result.timedOut) {\n Logger.warn({ message: 'Shutdown timed out' });\n this.finalizeExit({ code: 1, reason: 'shutdown-timeout' });\n } else {\n this.finalizeExit({ code: 0, reason: 'error-shutdown-complete' });\n }\n } catch (error) {\n Logger.error({\n error: error instanceof Error ? error : new Error(String(error)),\n message: 'Error during graceful shutdown',\n });\n this.finalizeExit({ code: 1, reason: 'graceful-shutdown-error', error });\n }\n }\n\n /**\n * Stop application using lifecycle manager\n */\n public async stop({ onStopped }: ApplicationStopInstanceOptions = {}): Promise<void> {\n if (this.shutdownController.isShuttingDown) {\n return;\n }\n\n // Register the onStopped callback if provided\n if (onStopped) {\n this.lifecycle.onShutdown(() => {\n const runtime = process.uptime() * 1000;\n onStopped({ runtime });\n });\n }\n\n try {\n const result = await this.shutdownController.initiate('manual-stop');\n if (result.errors.length > 0) {\n Logger.error({\n message: 'Errors during shutdown',\n error: result.errors,\n });\n this.finalizeExit({ code: 1, reason: 'shutdown-error', error: result.errors });\n } else if (result.timedOut) {\n Logger.warn({ message: 'Shutdown timed out' });\n this.finalizeExit({ code: 1, reason: 'shutdown-timeout' });\n } else {\n this.finalizeExit({ code: 0, reason: 'shutdown-complete' });\n }\n } catch (error) {\n Logger.error({\n error: error instanceof Error ? error : new Error(String(error)),\n message: 'Error during shutdown',\n });\n this.finalizeExit({ code: 1, reason: 'shutdown-error', error });\n }\n }\n\n /**\n * Finalize exit: during tests, suppress actual process exit to avoid failing vitest runs.\n */\n private finalizeExit(outcome: ExitOutcome): void {\n const nodeEnv = process.env.NODE_ENV ?? '';\n const isTestEnv =\n nodeEnv.toLowerCase() === 'test' ||\n 'VITEST' in process.env ||\n 'VITEST_WORKER_ID' in process.env ||\n process.argv.some(a => a.includes('vitest')) ||\n typeof (globalThis as any).afterAll === 'function';\n\n if (isTestEnv) {\n Logger.info({ message: `Skipping process exit in test environment (${outcome.reason})`, code: outcome.code });\n return;\n }\n requestExit(outcome);\n }\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -1 +1 @@
1
- {"version":3,"file":"command-application.d.ts","sourceRoot":"","sources":["../../src/application/command-application.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,gBAAgB,MAAM,yBAAyB,CAAC;AAE5D,OAAO,KAAK,YAAY,MAAM,qBAAqB,CAAC;AACpD,OAAO,KAAK,aAAa,MAAM,sBAAsB,CAAC;AACtD,OAAO,eAAe,MAAM,uBAAuB,CAAC;AACpD,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,oCAAoC,CAAC;AAGnF,MAAM,CAAC,OAAO,OAAO,kBAAmB,SAAQ,eAAe;IAC7D,iCAAiC;IACjC,SAAS,CAAC,MAAM,EAAE,wBAAwB,CAAC;gBAE/B,MAAM,EAAE,wBAAwB;cA0B5B,YAAY,CAAC,EAC3B,aAAa,EACb,gBAAgB,EAChB,YAAY,GACb,EAAE;QACD,aAAa,EAAE,aAAa,CAAC;QAC7B,gBAAgB,EAAE,gBAAgB,CAAC;QACnC,YAAY,EAAE,YAAY,CAAC;KAC5B,GAAG,OAAO,CAAC,IAAI,CAAC;IA4FjB,OAAO,CAAC,WAAW;IAKnB,SAAS,CAAC,YAAY,IAAI,IAAI;CAG/B"}
1
+ {"version":3,"file":"command-application.d.ts","sourceRoot":"","sources":["../../src/application/command-application.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,gBAAgB,MAAM,yBAAyB,CAAC;AAE5D,OAAO,KAAK,YAAY,MAAM,qBAAqB,CAAC;AACpD,OAAO,KAAK,aAAa,MAAM,sBAAsB,CAAC;AACtD,OAAO,eAAe,MAAM,uBAAuB,CAAC;AACpD,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,oCAAoC,CAAC;AAGnF,MAAM,CAAC,OAAO,OAAO,kBAAmB,SAAQ,eAAe;IAC7D,iCAAiC;IACjC,SAAS,CAAC,MAAM,EAAE,wBAAwB,CAAC;gBAE/B,MAAM,EAAE,wBAAwB;cA0B5B,YAAY,CAAC,EAC3B,aAAa,EACb,gBAAgB,EAChB,YAAY,GACb,EAAE;QACD,aAAa,EAAE,aAAa,CAAC;QAC7B,gBAAgB,EAAE,gBAAgB,CAAC;QACnC,YAAY,EAAE,YAAY,CAAC;KAC5B,GAAG,OAAO,CAAC,IAAI,CAAC;IA4FjB,OAAO,CAAC,WAAW;IAKnB,SAAS,CAAC,YAAY,IAAI,IAAI;CAG/B"}
@@ -1,9 +1,8 @@
1
1
  var __defProp = Object.defineProperty;
2
2
  var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
3
- import { existsSync } from "fs";
4
3
  import { Logger } from "../logger/index.js";
5
4
  import BaseApplication from "./base-application.js";
6
- import { Helper, Loader, Time } from "../util/index.js";
5
+ import { File, Helper, Loader, Time } from "../util/index.js";
7
6
  class CommandApplication extends BaseApplication {
8
7
  static {
9
8
  __name(this, "CommandApplication");
@@ -43,7 +42,7 @@ class CommandApplication extends BaseApplication {
43
42
  return;
44
43
  }
45
44
  const inputCommandName = parsedArgv._[0];
46
- const commandsDirectoryExists = await existsSync(this.config.commandsDirectory);
45
+ const commandsDirectoryExists = await File.pathExists(this.config.commandsDirectory);
47
46
  if (!commandsDirectoryExists) {
48
47
  Logger.warn({
49
48
  message: "Commands directory not found",
@@ -98,7 +97,7 @@ class CommandApplication extends BaseApplication {
98
97
  this.stopCommand();
99
98
  }
100
99
  stopCommand() {
101
- this.handleShutdown({ onStopped: this.onStopped.bind(this) });
100
+ void this.stop({ onStopped: this.onStopped.bind(this) });
102
101
  }
103
102
  stopCallback() {
104
103
  Logger.info({ message: "Command stopped" });
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/application/command-application.ts"],
4
- "sourcesContent": ["import { existsSync } from 'fs';\nimport type DatabaseInstance from '../database/instance.js';\nimport { Logger } from '../logger/index.js';\nimport type QueueManager from '../queue/manager.js';\nimport type RedisInstance from '../redis/instance.js';\nimport BaseApplication from './base-application.js';\nimport type { CommandApplicationConfig } from './command-application.interface.js';\nimport { Helper, Loader, Time } from '../util/index.js';\n\nexport default class CommandApplication extends BaseApplication {\n /** Command application config */\n protected config: CommandApplicationConfig;\n\n constructor(config: CommandApplicationConfig) {\n super(config);\n\n const defaultConfig: Partial<CommandApplicationConfig> = {\n cluster: {\n enabled: false,\n },\n\n log: {\n startUp: false,\n },\n\n debug: {\n measureExecutionTime: false,\n },\n };\n\n const mergedConfig: CommandApplicationConfig = Helper.defaultsDeep(config, defaultConfig);\n\n if (mergedConfig.cluster) {\n mergedConfig.cluster.enabled = false;\n }\n\n this.config = mergedConfig;\n }\n\n protected async startHandler({\n redisInstance,\n databaseInstance,\n queueManager,\n }: {\n redisInstance: RedisInstance;\n databaseInstance: DatabaseInstance;\n queueManager: QueueManager;\n }): Promise<void> {\n const startTime = performance.now();\n\n // get argv (yargs) input args\n const argv = this.config.commandManager.argv;\n\n const parsedArgv = argv.parseSync();\n\n if (parsedArgv._.length === 0) {\n Logger.warn({ message: 'No command provided' });\n\n this.stopCommand();\n\n return;\n }\n\n const inputCommandName = parsedArgv._[0];\n\n const commandsDirectoryExists = await existsSync(this.config.commandsDirectory);\n\n if (!commandsDirectoryExists) {\n Logger.warn({\n message: 'Commands directory not found',\n meta: {\n Directory: this.config.commandsDirectory,\n },\n });\n\n return;\n }\n\n // Load commands\n const commands = await Loader.loadModulesInDirectory({\n directory: this.config.commandsDirectory,\n extensions: ['.ts', '.js'],\n });\n\n // Find command by name - use safe property access to prevent object injection\n let CommandClass = null;\n if (Object.prototype.hasOwnProperty.call(commands, inputCommandName)) {\n CommandClass = Reflect.get(commands, inputCommandName);\n }\n\n if (!CommandClass) {\n Logger.warn({\n message: 'Command not found',\n meta: { Command: inputCommandName },\n });\n\n return;\n }\n\n // Initialize command\n const command = new CommandClass({\n applicationConfig: this.config,\n redisInstance,\n queueManager,\n databaseInstance,\n });\n\n Logger.info({\n message: 'Command started',\n meta: { Command: inputCommandName },\n });\n\n // Run command\n await command.run(parsedArgv);\n\n const commandCompletedLogParams: Record<string, unknown> = {\n Command: inputCommandName,\n };\n\n if (this.config.debug?.measureExecutionTime) {\n const endTime = performance.now();\n const executionTime = endTime - startTime;\n\n commandCompletedLogParams['Execution Time'] = Time.formatTime({\n time: executionTime,\n numDecimals: 2,\n showUnit: true,\n });\n }\n\n Logger.info({\n message: 'Command completed',\n meta: commandCompletedLogParams,\n });\n\n // Call shutdown signtal to stop the command\n this.stopCommand();\n }\n\n private stopCommand(): void {\n // Use existing graceful shutdown mechanism instead of self-termination\n this.handleShutdown({ onStopped: this.onStopped.bind(this) });\n }\n\n protected stopCallback(): void {\n Logger.info({ message: 'Command stopped' });\n }\n}\n"],
5
- "mappings": ";;AAAA,SAAS,kBAAkB;AAE3B,SAAS,cAAc;AAGvB,OAAO,qBAAqB;AAE5B,SAAS,QAAQ,QAAQ,YAAY;AAErC,MAAO,2BAAyC,gBAAgB;AAAA,EAThE,OASgE;AAAA;AAAA;AAAA;AAAA,EAEpD;AAAA,EAEV,YAAY,QAAkC;AAC5C,UAAM,MAAM;AAEZ,UAAM,gBAAmD;AAAA,MACvD,SAAS;AAAA,QACP,SAAS;AAAA,MACX;AAAA,MAEA,KAAK;AAAA,QACH,SAAS;AAAA,MACX;AAAA,MAEA,OAAO;AAAA,QACL,sBAAsB;AAAA,MACxB;AAAA,IACF;AAEA,UAAM,eAAyC,OAAO,aAAa,QAAQ,aAAa;AAExF,QAAI,aAAa,SAAS;AACxB,mBAAa,QAAQ,UAAU;AAAA,IACjC;AAEA,SAAK,SAAS;AAAA,EAChB;AAAA,EAEA,MAAgB,aAAa;AAAA,IAC3B;AAAA,IACA;AAAA,IACA;AAAA,EACF,GAIkB;AAChB,UAAM,YAAY,YAAY,IAAI;AAGlC,UAAM,OAAO,KAAK,OAAO,eAAe;AAExC,UAAM,aAAa,KAAK,UAAU;AAElC,QAAI,WAAW,EAAE,WAAW,GAAG;AAC7B,aAAO,KAAK,EAAE,SAAS,sBAAsB,CAAC;AAE9C,WAAK,YAAY;AAEjB;AAAA,IACF;AAEA,UAAM,mBAAmB,WAAW,EAAE,CAAC;AAEvC,UAAM,0BAA0B,MAAM,WAAW,KAAK,OAAO,iBAAiB;AAE9E,QAAI,CAAC,yBAAyB;AAC5B,aAAO,KAAK;AAAA,QACV,SAAS;AAAA,QACT,MAAM;AAAA,UACJ,WAAW,KAAK,OAAO;AAAA,QACzB;AAAA,MACF,CAAC;AAED;AAAA,IACF;AAGA,UAAM,WAAW,MAAM,OAAO,uBAAuB;AAAA,MACnD,WAAW,KAAK,OAAO;AAAA,MACvB,YAAY,CAAC,OAAO,KAAK;AAAA,IAC3B,CAAC;AAGD,QAAI,eAAe;AACnB,QAAI,OAAO,UAAU,eAAe,KAAK,UAAU,gBAAgB,GAAG;AACpE,qBAAe,QAAQ,IAAI,UAAU,gBAAgB;AAAA,IACvD;AAEA,QAAI,CAAC,cAAc;AACjB,aAAO,KAAK;AAAA,QACV,SAAS;AAAA,QACT,MAAM,EAAE,SAAS,iBAAiB;AAAA,MACpC,CAAC;AAED;AAAA,IACF;AAGA,UAAM,UAAU,IAAI,aAAa;AAAA,MAC/B,mBAAmB,KAAK;AAAA,MACxB;AAAA,MACA;AAAA,MACA;AAAA,IACF,CAAC;AAED,WAAO,KAAK;AAAA,MACV,SAAS;AAAA,MACT,MAAM,EAAE,SAAS,iBAAiB;AAAA,IACpC,CAAC;AAGD,UAAM,QAAQ,IAAI,UAAU;AAE5B,UAAM,4BAAqD;AAAA,MACzD,SAAS;AAAA,IACX;AAEA,QAAI,KAAK,OAAO,OAAO,sBAAsB;AAC3C,YAAM,UAAU,YAAY,IAAI;AAChC,YAAM,gBAAgB,UAAU;AAEhC,gCAA0B,gBAAgB,IAAI,KAAK,WAAW;AAAA,QAC5D,MAAM;AAAA,QACN,aAAa;AAAA,QACb,UAAU;AAAA,MACZ,CAAC;AAAA,IACH;AAEA,WAAO,KAAK;AAAA,MACV,SAAS;AAAA,MACT,MAAM;AAAA,IACR,CAAC;AAGD,SAAK,YAAY;AAAA,EACnB;AAAA,EAEQ,cAAoB;AAE1B,SAAK,eAAe,EAAE,WAAW,KAAK,UAAU,KAAK,IAAI,EAAE,CAAC;AAAA,EAC9D;AAAA,EAEU,eAAqB;AAC7B,WAAO,KAAK,EAAE,SAAS,kBAAkB,CAAC;AAAA,EAC5C;AACF;",
4
+ "sourcesContent": ["import type DatabaseInstance from '../database/instance.js';\nimport { Logger } from '../logger/index.js';\nimport type QueueManager from '../queue/manager.js';\nimport type RedisInstance from '../redis/instance.js';\nimport BaseApplication from './base-application.js';\nimport type { CommandApplicationConfig } from './command-application.interface.js';\nimport { File, Helper, Loader, Time } from '../util/index.js';\n\nexport default class CommandApplication extends BaseApplication {\n /** Command application config */\n protected config: CommandApplicationConfig;\n\n constructor(config: CommandApplicationConfig) {\n super(config);\n\n const defaultConfig: Partial<CommandApplicationConfig> = {\n cluster: {\n enabled: false,\n },\n\n log: {\n startUp: false,\n },\n\n debug: {\n measureExecutionTime: false,\n },\n };\n\n const mergedConfig: CommandApplicationConfig = Helper.defaultsDeep(config, defaultConfig);\n\n if (mergedConfig.cluster) {\n mergedConfig.cluster.enabled = false;\n }\n\n this.config = mergedConfig;\n }\n\n protected async startHandler({\n redisInstance,\n databaseInstance,\n queueManager,\n }: {\n redisInstance: RedisInstance;\n databaseInstance: DatabaseInstance;\n queueManager: QueueManager;\n }): Promise<void> {\n const startTime = performance.now();\n\n // get argv (yargs) input args\n const argv = this.config.commandManager.argv;\n\n const parsedArgv = argv.parseSync();\n\n if (parsedArgv._.length === 0) {\n Logger.warn({ message: 'No command provided' });\n\n this.stopCommand();\n\n return;\n }\n\n const inputCommandName = parsedArgv._[0];\n\n const commandsDirectoryExists = await File.pathExists(this.config.commandsDirectory);\n\n if (!commandsDirectoryExists) {\n Logger.warn({\n message: 'Commands directory not found',\n meta: {\n Directory: this.config.commandsDirectory,\n },\n });\n\n return;\n }\n\n // Load commands\n const commands = await Loader.loadModulesInDirectory({\n directory: this.config.commandsDirectory,\n extensions: ['.ts', '.js'],\n });\n\n // Find command by name - use safe property access to prevent object injection\n let CommandClass = null;\n if (Object.prototype.hasOwnProperty.call(commands, inputCommandName)) {\n CommandClass = Reflect.get(commands, inputCommandName);\n }\n\n if (!CommandClass) {\n Logger.warn({\n message: 'Command not found',\n meta: { Command: inputCommandName },\n });\n\n return;\n }\n\n // Initialize command\n const command = new CommandClass({\n applicationConfig: this.config,\n redisInstance,\n queueManager,\n databaseInstance,\n });\n\n Logger.info({\n message: 'Command started',\n meta: { Command: inputCommandName },\n });\n\n // Run command\n await command.run(parsedArgv);\n\n const commandCompletedLogParams: Record<string, unknown> = {\n Command: inputCommandName,\n };\n\n if (this.config.debug?.measureExecutionTime) {\n const endTime = performance.now();\n const executionTime = endTime - startTime;\n\n commandCompletedLogParams['Execution Time'] = Time.formatTime({\n time: executionTime,\n numDecimals: 2,\n showUnit: true,\n });\n }\n\n Logger.info({\n message: 'Command completed',\n meta: commandCompletedLogParams,\n });\n\n // Call shutdown signtal to stop the command\n this.stopCommand();\n }\n\n private stopCommand(): void {\n // Use modern lifecycle management for graceful shutdown\n void this.stop({ onStopped: this.onStopped.bind(this) });\n }\n\n protected stopCallback(): void {\n Logger.info({ message: 'Command stopped' });\n }\n}\n"],
5
+ "mappings": ";;AACA,SAAS,cAAc;AAGvB,OAAO,qBAAqB;AAE5B,SAAS,MAAM,QAAQ,QAAQ,YAAY;AAE3C,MAAO,2BAAyC,gBAAgB;AAAA,EARhE,OAQgE;AAAA;AAAA;AAAA;AAAA,EAEpD;AAAA,EAEV,YAAY,QAAkC;AAC5C,UAAM,MAAM;AAEZ,UAAM,gBAAmD;AAAA,MACvD,SAAS;AAAA,QACP,SAAS;AAAA,MACX;AAAA,MAEA,KAAK;AAAA,QACH,SAAS;AAAA,MACX;AAAA,MAEA,OAAO;AAAA,QACL,sBAAsB;AAAA,MACxB;AAAA,IACF;AAEA,UAAM,eAAyC,OAAO,aAAa,QAAQ,aAAa;AAExF,QAAI,aAAa,SAAS;AACxB,mBAAa,QAAQ,UAAU;AAAA,IACjC;AAEA,SAAK,SAAS;AAAA,EAChB;AAAA,EAEA,MAAgB,aAAa;AAAA,IAC3B;AAAA,IACA;AAAA,IACA;AAAA,EACF,GAIkB;AAChB,UAAM,YAAY,YAAY,IAAI;AAGlC,UAAM,OAAO,KAAK,OAAO,eAAe;AAExC,UAAM,aAAa,KAAK,UAAU;AAElC,QAAI,WAAW,EAAE,WAAW,GAAG;AAC7B,aAAO,KAAK,EAAE,SAAS,sBAAsB,CAAC;AAE9C,WAAK,YAAY;AAEjB;AAAA,IACF;AAEA,UAAM,mBAAmB,WAAW,EAAE,CAAC;AAEvC,UAAM,0BAA0B,MAAM,KAAK,WAAW,KAAK,OAAO,iBAAiB;AAEnF,QAAI,CAAC,yBAAyB;AAC5B,aAAO,KAAK;AAAA,QACV,SAAS;AAAA,QACT,MAAM;AAAA,UACJ,WAAW,KAAK,OAAO;AAAA,QACzB;AAAA,MACF,CAAC;AAED;AAAA,IACF;AAGA,UAAM,WAAW,MAAM,OAAO,uBAAuB;AAAA,MACnD,WAAW,KAAK,OAAO;AAAA,MACvB,YAAY,CAAC,OAAO,KAAK;AAAA,IAC3B,CAAC;AAGD,QAAI,eAAe;AACnB,QAAI,OAAO,UAAU,eAAe,KAAK,UAAU,gBAAgB,GAAG;AACpE,qBAAe,QAAQ,IAAI,UAAU,gBAAgB;AAAA,IACvD;AAEA,QAAI,CAAC,cAAc;AACjB,aAAO,KAAK;AAAA,QACV,SAAS;AAAA,QACT,MAAM,EAAE,SAAS,iBAAiB;AAAA,MACpC,CAAC;AAED;AAAA,IACF;AAGA,UAAM,UAAU,IAAI,aAAa;AAAA,MAC/B,mBAAmB,KAAK;AAAA,MACxB;AAAA,MACA;AAAA,MACA;AAAA,IACF,CAAC;AAED,WAAO,KAAK;AAAA,MACV,SAAS;AAAA,MACT,MAAM,EAAE,SAAS,iBAAiB;AAAA,IACpC,CAAC;AAGD,UAAM,QAAQ,IAAI,UAAU;AAE5B,UAAM,4BAAqD;AAAA,MACzD,SAAS;AAAA,IACX;AAEA,QAAI,KAAK,OAAO,OAAO,sBAAsB;AAC3C,YAAM,UAAU,YAAY,IAAI;AAChC,YAAM,gBAAgB,UAAU;AAEhC,gCAA0B,gBAAgB,IAAI,KAAK,WAAW;AAAA,QAC5D,MAAM;AAAA,QACN,aAAa;AAAA,QACb,UAAU;AAAA,MACZ,CAAC;AAAA,IACH;AAEA,WAAO,KAAK;AAAA,MACV,SAAS;AAAA,MACT,MAAM;AAAA,IACR,CAAC;AAGD,SAAK,YAAY;AAAA,EACnB;AAAA,EAEQ,cAAoB;AAE1B,SAAK,KAAK,KAAK,EAAE,WAAW,KAAK,UAAU,KAAK,IAAI,EAAE,CAAC;AAAA,EACzD;AAAA,EAEU,eAAqB;AAC7B,WAAO,KAAK,EAAE,SAAS,kBAAkB,CAAC;AAAA,EAC5C;AACF;",
6
6
  "names": []
7
7
  }
@@ -1 +1 @@
1
- {"version":3,"file":"web-application.d.ts","sourceRoot":"","sources":["../../src/application/web-application.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,aAAa,MAAM,sBAAsB,CAAC;AACtD,OAAO,KAAK,gBAAgB,MAAM,yBAAyB,CAAC;AAC5D,OAAO,SAAS,MAAM,2BAA2B,CAAC;AAClD,OAAO,KAAK,YAAY,MAAM,qBAAqB,CAAC;AACpD,OAAO,eAAe,MAAM,uBAAuB,CAAC;AACpD,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,gCAAgC,CAAC;AAG3E,OAAO,eAAe,MAAM,kCAAkC,CAAC;AAC/D,OAAO,eAAe,MAAM,kCAAkC,CAAC;AAC/D,OAAO,KAAK,YAAY,MAAM,qBAAqB,CAAC;AAGpD;;GAEG;AACH,MAAM,CAAC,OAAO,OAAO,cAAe,SAAQ,eAAe;IACzD,6BAA6B;IAC7B,SAAS,CAAC,MAAM,EAAE,oBAAoB,CAAC;IAEvC,iBAAiB;IACV,SAAS,CAAC,EAAE,SAAS,CAAC;IAE7B,uBAAuB;IAChB,eAAe,CAAC,EAAE,eAAe,CAAC;IAEzC,uBAAuB;IAChB,eAAe,CAAC,EAAE,eAAe,CAAC;gBAE7B,MAAM,EAAE,oBAAoB;cAcxB,YAAY,CAAC,EAC3B,aAAa,EACb,gBAAgB,EAChB,YAAY,EACZ,YAAY,GACb,EAAE;QACD,aAAa,EAAE,aAAa,CAAC;QAC7B,gBAAgB,EAAE,gBAAgB,CAAC;QACnC,YAAY,EAAE,YAAY,CAAC;QAC3B,YAAY,EAAE,YAAY,CAAC;KAC5B,GAAG,OAAO,CAAC,IAAI,CAAC;IA2GjB;;OAEG;cACa,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC;IAY7C;;OAEG;cACa,SAAS,CAAC,EAAE,WAAW,EAAE,EAAE;QAAE,WAAW,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC;cA0BlE,SAAS,CAAC,EAAE,OAAO,EAAE,EAAE;QAAE,OAAO,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC;CAqB3E"}
1
+ {"version":3,"file":"web-application.d.ts","sourceRoot":"","sources":["../../src/application/web-application.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,aAAa,MAAM,sBAAsB,CAAC;AACtD,OAAO,KAAK,gBAAgB,MAAM,yBAAyB,CAAC;AAC5D,OAAO,SAAS,MAAM,2BAA2B,CAAC;AAClD,OAAO,KAAK,YAAY,MAAM,qBAAqB,CAAC;AACpD,OAAO,eAAe,MAAM,uBAAuB,CAAC;AACpD,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,gCAAgC,CAAC;AAG3E,OAAO,eAAe,MAAM,kCAAkC,CAAC;AAC/D,OAAO,eAAe,MAAM,kCAAkC,CAAC;AAC/D,OAAO,KAAK,YAAY,MAAM,qBAAqB,CAAC;AAGpD;;GAEG;AACH,MAAM,CAAC,OAAO,OAAO,cAAe,SAAQ,eAAe;IACzD,6BAA6B;IAC7B,SAAS,CAAC,MAAM,EAAE,oBAAoB,CAAC;IAEvC,iBAAiB;IACV,SAAS,CAAC,EAAE,SAAS,CAAC;IAE7B,uBAAuB;IAChB,eAAe,CAAC,EAAE,eAAe,CAAC;IAEzC,uBAAuB;IAChB,eAAe,CAAC,EAAE,eAAe,CAAC;gBAE7B,MAAM,EAAE,oBAAoB;cAcxB,YAAY,CAAC,EAC3B,aAAa,EACb,gBAAgB,EAChB,YAAY,EACZ,YAAY,GACb,EAAE;QACD,aAAa,EAAE,aAAa,CAAC;QAC7B,gBAAgB,EAAE,gBAAgB,CAAC;QACnC,YAAY,EAAE,YAAY,CAAC;QAC3B,YAAY,EAAE,YAAY,CAAC;KAC5B,GAAG,OAAO,CAAC,IAAI,CAAC;IAkHjB;;OAEG;cACa,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC;IAY7C;;OAEG;cACa,SAAS,CAAC,EAAE,WAAW,EAAE,EAAE;QAAE,WAAW,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC;cA0BlE,SAAS,CAAC,EAAE,OAAO,EAAE,EAAE;QAAE,OAAO,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC;CAqB3E"}
@@ -38,7 +38,6 @@ class WebApplication extends BaseApplication {
38
38
  if (this.config.webServer?.enabled) {
39
39
  this.webServer = new WebServer({
40
40
  applicationConfig: this.config,
41
- // config: this.config.webServer,
42
41
  options: {
43
42
  host: this.config.webServer.host,
44
43
  port: this.config.webServer.port,
@@ -51,13 +50,21 @@ class WebApplication extends BaseApplication {
51
50
  redisInstance,
52
51
  databaseInstance,
53
52
  queueManager,
54
- eventManager
53
+ eventManager,
54
+ lifecycleManager: this.lifecycle
55
55
  });
56
56
  if (this.performanceMonitor && this.config.performanceMonitoring?.monitorHttpRequests !== false) {
57
57
  WebServerPerformanceWrapper.setPerformanceMonitor(this.performanceMonitor);
58
58
  }
59
59
  await this.webServer.load();
60
60
  await this.webServer.start();
61
+ this.lifecycle.addReadinessCheck("webserver", async () => {
62
+ try {
63
+ return this.webServer?.isReady() ?? false;
64
+ } catch {
65
+ return false;
66
+ }
67
+ });
61
68
  }
62
69
  if (this.config.webSocket?.enabled) {
63
70
  if (!this.webServer) {