@scpxl/nodejs-framework 1.0.17 → 1.0.20

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 +15 -22
  10. package/dist/application/base-application.d.ts.map +1 -1
  11. package/dist/application/base-application.js +145 -111
  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 +5 -8
  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 +2 -1
  71. package/dist/lifecycle/exit.d.ts.map +1 -1
  72. package/dist/lifecycle/exit.js +6 -0
  73. package/dist/lifecycle/exit.js.map +2 -2
  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 +55 -2
  79. package/dist/lifecycle/lifecycle-manager.d.ts.map +1 -1
  80. package/dist/lifecycle/lifecycle-manager.js +233 -7
  81. package/dist/lifecycle/lifecycle-manager.js.map +3 -3
  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 +51 -10
@@ -13,9 +13,9 @@ 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";
18
- import { LifecycleManager } from "../lifecycle/lifecycle-manager.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
19
  import { requestExit } from "../lifecycle/exit.js";
20
20
  class BaseApplication {
21
21
  static {
@@ -23,12 +23,8 @@ class BaseApplication {
23
23
  }
24
24
  /** Unique instance ID */
25
25
  uniqueInstanceId;
26
- /** Shutdown signals */
27
- shutdownSignals = ["SIGTERM", "SIGINT"];
28
26
  /** Application start time */
29
- startTime = [0, 0];
30
- /** Whether application is stopping */
31
- isStopping = false;
27
+ startTime = 0;
32
28
  /** Shutdown timeout (30 seconds) */
33
29
  shutdownTimeout = 3e4;
34
30
  /** Cache for application version to avoid repeated imports */
@@ -52,7 +48,9 @@ class BaseApplication {
52
48
  /** Performance monitor */
53
49
  performanceMonitor;
54
50
  /** Lifecycle manager */
55
- lifecycle = new LifecycleManager();
51
+ lifecycle;
52
+ /** Shutdown controller */
53
+ shutdownController;
56
54
  get Name() {
57
55
  return this.config.name;
58
56
  }
@@ -60,9 +58,32 @@ class BaseApplication {
60
58
  * Application constructor
61
59
  */
62
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
+ }
63
72
  const computerName = os.hostname();
64
73
  this.uniqueInstanceId = `${config.instanceId}-${computerName}-${OS.getUniqueComputerId()}`;
65
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();
66
87
  this.redisManager = new RedisManager({
67
88
  applicationConfig: this.config,
68
89
  host: this.config.redis.host,
@@ -70,10 +91,9 @@ class BaseApplication {
70
91
  password: this.config.redis.password
71
92
  });
72
93
  this.cacheManager = new CacheManager({
73
- applicationConfig: this.config,
74
94
  redisManager: this.redisManager
75
95
  });
76
- this.initializePerformanceMonitor();
96
+ PerformanceMonitorPlugin.register(this);
77
97
  this.setupGlobalErrorHandlers();
78
98
  if (this.config.database && this.config.database.enabled === true) {
79
99
  const defaultEntitiesDirectory = join(this.config.rootDirectory, "src", "database", "entities");
@@ -116,35 +136,12 @@ class BaseApplication {
116
136
  * Start application
117
137
  */
118
138
  async start() {
119
- this.startTime = process.hrtime();
139
+ this.startTime = Time.now();
120
140
  this.applicationVersion = await this.getApplicationVersion();
121
141
  const startInstanceOptions = {
122
- // onStarted: ({ startupTime }) => {
123
- // if (this.config.log?.startUp) {
124
- // Logger.info('Application started', {
125
- // Name: this.config.name,
126
- // 'PXL Framework Version': this.applicationVersion,
127
- // 'Startup Time': Time.formatTime({ time: startupTime, format: 's', numDecimals: 2, showUnit: true }),
128
- // });
129
- // }
130
- // if (this.config.events?.onStarted) {
131
- // this.config.events.onStarted({ app: this, startupTime });
132
- // }
133
- // },
134
142
  onStarted: this.onStarted.bind(this)
135
143
  };
136
144
  const stopInstanceOptions = {
137
- // onStopped: ({ runtime }) => {
138
- // if (this.config.log?.shutdown) {
139
- // Logger.info('Application stopped', {
140
- // Name: this.config.name,
141
- // 'Runtime': Time.formatTime({ time: runtime, format: 's', numDecimals: 2, showUnit: true }),
142
- // });
143
- // }
144
- // if (this.config.events?.onStopped) {
145
- // this.config.events.onStopped({ app: this, runtime });
146
- // }
147
- // },
148
145
  onStopped: this.onStopped.bind(this)
149
146
  };
150
147
  if (this.config.cluster?.enabled) {
@@ -156,9 +153,6 @@ class BaseApplication {
156
153
  clusterManager.start();
157
154
  } else {
158
155
  await this.startInstance(startInstanceOptions);
159
- this.handleShutdown({
160
- onStopped: stopInstanceOptions.onStopped
161
- });
162
156
  }
163
157
  }
164
158
  /**
@@ -192,6 +186,22 @@ class BaseApplication {
192
186
  await queueManager.registerQueues({
193
187
  queues: this.config.queue.queues
194
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
+ }
195
205
  return {
196
206
  redisInstance,
197
207
  databaseInstance,
@@ -209,28 +219,40 @@ class BaseApplication {
209
219
  */
210
220
  onStopped({ runtime: _runtime }) {
211
221
  }
212
- /**
213
- * Before application stop event
214
- */
215
- async onBeforeStop() {
216
- await this.redisManager.disconnect();
217
- if (this.databaseManager) {
218
- await this.databaseManager.disconnect();
219
- }
220
- }
221
222
  /**
222
223
  * Start application instance
223
224
  */
224
225
  async startInstance(options) {
225
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
+ }
226
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
+ }
227
242
  await this.startHandler({
228
243
  redisInstance,
229
244
  databaseInstance,
230
245
  queueManager,
231
246
  eventManager
232
247
  });
233
- 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({
234
256
  startTime: this.startTime
235
257
  });
236
258
  if (options.onStarted) {
@@ -250,38 +272,8 @@ class BaseApplication {
250
272
  /**
251
273
  * Initialize performance monitor
252
274
  */
253
- initializePerformanceMonitor() {
254
- if (!this.config.performanceMonitoring?.enabled) {
255
- return;
256
- }
257
- this.performanceMonitor = PerformanceMonitor.initialize({
258
- enabled: true,
259
- thresholds: this.config.performanceMonitoring.thresholds,
260
- maxMetricsHistory: this.config.performanceMonitoring.maxMetricsHistory,
261
- logSlowOperations: this.config.performanceMonitoring.logSlowOperations,
262
- logAllOperations: this.config.performanceMonitoring.logAllOperations
263
- });
264
- if (this.config.performanceMonitoring.monitorDatabaseOperations !== false) {
265
- DatabasePerformanceWrapper.setPerformanceMonitor(this.performanceMonitor);
266
- }
267
- if (this.config.performanceMonitoring.monitorQueueOperations !== false) {
268
- QueuePerformanceWrapper.setPerformanceMonitor(this.performanceMonitor);
269
- }
270
- if (this.config.performanceMonitoring.monitorCacheOperations !== false) {
271
- CachePerformanceWrapper.setPerformanceMonitor(this.performanceMonitor);
272
- }
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
- );
283
- }
284
- }
275
+ // initializePerformanceMonitor deprecated in favor of PerformanceMonitorPlugin
276
+ // (left intentionally absent)
285
277
  setupGlobalErrorHandlers() {
286
278
  process.on("uncaughtException", (error) => {
287
279
  Logger.error({ error, message: "Uncaught Exception" });
@@ -296,61 +288,103 @@ class BaseApplication {
296
288
  this.initiateGracefulShutdown();
297
289
  });
298
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
+ }
299
312
  /**
300
313
  * Initiate graceful shutdown
301
314
  */
302
- initiateGracefulShutdown() {
303
- if (this.isStopping) {
315
+ async initiateGracefulShutdown() {
316
+ if (this.shutdownController.isShuttingDown) {
304
317
  return;
305
318
  }
306
319
  Logger.info({ message: "Initiating graceful shutdown due to error" });
307
- 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) {
308
335
  Logger.error({
309
336
  error: error instanceof Error ? error : new Error(String(error)),
310
337
  message: "Error during graceful shutdown"
311
338
  });
312
- requestExit({ code: 1, reason: "graceful-shutdown-error", error });
313
- });
314
- }
315
- /**
316
- * Handle shutdown
317
- */
318
- handleShutdown({ onStopped }) {
319
- this.shutdownSignals.forEach((signal) => {
320
- process.on(signal, async () => {
321
- await this.stop({ onStopped });
322
- });
323
- });
339
+ this.finalizeExit({ code: 1, reason: "graceful-shutdown-error", error });
340
+ }
324
341
  }
325
342
  /**
326
- * Stop application
343
+ * Stop application using lifecycle manager
327
344
  */
328
345
  async stop({ onStopped } = {}) {
329
- if (this.isStopping) {
346
+ if (this.shutdownController.isShuttingDown) {
330
347
  return;
331
348
  }
332
- this.isStopping = true;
333
- const forceExitTimeout = setTimeout(() => {
334
- Logger.warn({ message: "Forced shutdown due to timeout" });
335
- requestExit({ code: 1, reason: "forced-timeout" });
336
- }, this.shutdownTimeout);
337
- try {
338
- await this.stopCallback();
339
- await this.onBeforeStop();
340
- if (onStopped) {
349
+ if (onStopped) {
350
+ this.lifecycle.onShutdown(() => {
341
351
  const runtime = process.uptime() * 1e3;
342
- 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" });
343
368
  }
344
- clearTimeout(forceExitTimeout);
345
- requestExit({ code: 0, reason: "shutdown-complete" });
346
369
  } catch (error) {
347
370
  Logger.error({
348
371
  error: error instanceof Error ? error : new Error(String(error)),
349
372
  message: "Error during shutdown"
350
373
  });
351
- clearTimeout(forceExitTimeout);
352
- requestExit({ code: 1, reason: "shutdown-error", error });
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;
353
386
  }
387
+ requestExit(outcome);
354
388
  }
355
389
  }
356
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';\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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",
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) {