@morojs/moro 1.5.4 → 1.5.6

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 (70) hide show
  1. package/dist/core/config/config-manager.d.ts +44 -0
  2. package/dist/core/config/config-manager.js +114 -0
  3. package/dist/core/config/config-manager.js.map +1 -0
  4. package/dist/core/config/config-sources.d.ts +21 -0
  5. package/dist/core/config/config-sources.js +314 -0
  6. package/dist/core/config/config-sources.js.map +1 -0
  7. package/dist/core/config/config-validator.d.ts +21 -0
  8. package/dist/core/config/config-validator.js +737 -0
  9. package/dist/core/config/config-validator.js.map +1 -0
  10. package/dist/core/config/file-loader.d.ts +0 -5
  11. package/dist/core/config/file-loader.js +0 -171
  12. package/dist/core/config/file-loader.js.map +1 -1
  13. package/dist/core/config/index.d.ts +39 -10
  14. package/dist/core/config/index.js +66 -29
  15. package/dist/core/config/index.js.map +1 -1
  16. package/dist/core/config/schema.js +22 -31
  17. package/dist/core/config/schema.js.map +1 -1
  18. package/dist/core/config/utils.d.ts +9 -2
  19. package/dist/core/config/utils.js +19 -32
  20. package/dist/core/config/utils.js.map +1 -1
  21. package/dist/core/framework.d.ts +2 -7
  22. package/dist/core/framework.js +12 -5
  23. package/dist/core/framework.js.map +1 -1
  24. package/dist/core/http/http-server.d.ts +12 -0
  25. package/dist/core/http/http-server.js +56 -0
  26. package/dist/core/http/http-server.js.map +1 -1
  27. package/dist/core/http/router.d.ts +12 -0
  28. package/dist/core/http/router.js +114 -36
  29. package/dist/core/http/router.js.map +1 -1
  30. package/dist/core/logger/index.d.ts +1 -1
  31. package/dist/core/logger/index.js +2 -1
  32. package/dist/core/logger/index.js.map +1 -1
  33. package/dist/core/logger/logger.d.ts +10 -1
  34. package/dist/core/logger/logger.js +99 -37
  35. package/dist/core/logger/logger.js.map +1 -1
  36. package/dist/core/routing/index.d.ts +20 -0
  37. package/dist/core/routing/index.js +109 -11
  38. package/dist/core/routing/index.js.map +1 -1
  39. package/dist/moro.d.ts +22 -0
  40. package/dist/moro.js +134 -98
  41. package/dist/moro.js.map +1 -1
  42. package/dist/types/config.d.ts +39 -2
  43. package/dist/types/core.d.ts +22 -39
  44. package/dist/types/logger.d.ts +4 -0
  45. package/package.json +1 -1
  46. package/src/core/config/config-manager.ts +133 -0
  47. package/src/core/config/config-sources.ts +384 -0
  48. package/src/core/config/config-validator.ts +1035 -0
  49. package/src/core/config/file-loader.ts +0 -233
  50. package/src/core/config/index.ts +77 -32
  51. package/src/core/config/schema.ts +22 -31
  52. package/src/core/config/utils.ts +22 -29
  53. package/src/core/framework.ts +18 -11
  54. package/src/core/http/http-server.ts +66 -0
  55. package/src/core/http/router.ts +127 -38
  56. package/src/core/logger/index.ts +1 -0
  57. package/src/core/logger/logger.ts +109 -36
  58. package/src/core/routing/index.ts +116 -12
  59. package/src/moro.ts +159 -107
  60. package/src/types/config.ts +40 -2
  61. package/src/types/core.ts +32 -43
  62. package/src/types/logger.ts +6 -0
  63. package/dist/core/config/loader.d.ts +0 -7
  64. package/dist/core/config/loader.js +0 -269
  65. package/dist/core/config/loader.js.map +0 -1
  66. package/dist/core/config/validation.d.ts +0 -17
  67. package/dist/core/config/validation.js +0 -131
  68. package/dist/core/config/validation.js.map +0 -1
  69. package/src/core/config/loader.ts +0 -633
  70. package/src/core/config/validation.ts +0 -140
@@ -256,20 +256,91 @@ export class IntelligentRouteBuilder implements RouteBuilder {
256
256
 
257
257
  // Executable route with intelligent middleware ordering
258
258
  export class ExecutableRoute implements CompiledRoute {
259
- constructor(public readonly schema: RouteSchema) {}
259
+ // PERFORMANCE OPTIMIZATION: Pre-analyze route requirements
260
+ private readonly requiresAuth: boolean;
261
+ private readonly requiresValidation: boolean;
262
+ private readonly requiresRateLimit: boolean;
263
+ private readonly requiresCache: boolean;
264
+ private readonly hasBeforeMiddleware: boolean;
265
+ private readonly hasAfterMiddleware: boolean;
266
+ private readonly hasTransformMiddleware: boolean;
267
+ private readonly isFastPath: boolean;
268
+
269
+ constructor(public readonly schema: RouteSchema) {
270
+ // Pre-calculate what this route actually needs
271
+ this.requiresAuth = !!this.schema.auth;
272
+ this.requiresValidation = !!this.schema.validation;
273
+ this.requiresRateLimit = !!this.schema.rateLimit;
274
+ this.requiresCache = !!this.schema.cache;
275
+ this.hasBeforeMiddleware = !!this.schema.middleware?.before?.length;
276
+ this.hasAfterMiddleware = !!this.schema.middleware?.after?.length;
277
+ this.hasTransformMiddleware = !!this.schema.middleware?.transform?.length;
278
+
279
+ // Fast path: no middleware, no auth, no validation, no rate limiting
280
+ this.isFastPath =
281
+ !this.requiresAuth &&
282
+ !this.requiresValidation &&
283
+ !this.requiresRateLimit &&
284
+ !this.requiresCache &&
285
+ !this.hasBeforeMiddleware &&
286
+ !this.hasAfterMiddleware &&
287
+ !this.hasTransformMiddleware;
288
+
289
+ // Log fast path routes for monitoring
290
+ if (this.isFastPath) {
291
+ logger.debug(`Fast path route: ${this.schema.method} ${this.schema.path}`, 'FastPath');
292
+ }
293
+ }
260
294
 
261
295
  async execute(req: HttpRequest, res: HttpResponse): Promise<void> {
262
296
  const validatedReq = req as ValidatedRequest;
263
297
 
264
298
  try {
265
- // Execute middleware in intelligent order
266
- await this.executePhase('before', validatedReq, res);
267
- await this.executePhase('rateLimit', validatedReq, res);
268
- await this.executePhase('auth', validatedReq, res);
269
- await this.executePhase('validation', validatedReq, res);
270
- await this.executePhase('transform', validatedReq, res);
271
- await this.executePhase('cache', validatedReq, res);
272
- await this.executePhase('after', validatedReq, res);
299
+ // PERFORMANCE OPTIMIZATION: Fast path for simple routes
300
+ if (this.isFastPath) {
301
+ // Skip all middleware - execute handler directly
302
+ const result = await this.schema.handler(validatedReq, res);
303
+ if (result !== undefined && !res.headersSent) {
304
+ res.json(result);
305
+ }
306
+ return;
307
+ }
308
+
309
+ // Optimized middleware execution - only run what's needed
310
+ if (this.hasBeforeMiddleware) {
311
+ await this.executePhase('before', validatedReq, res);
312
+ if (res.headersSent) return;
313
+ }
314
+
315
+ if (this.requiresRateLimit) {
316
+ await this.executePhase('rateLimit', validatedReq, res);
317
+ if (res.headersSent) return;
318
+ }
319
+
320
+ if (this.requiresAuth) {
321
+ await this.executePhase('auth', validatedReq, res);
322
+ if (res.headersSent) return;
323
+ }
324
+
325
+ if (this.requiresValidation) {
326
+ await this.executePhase('validation', validatedReq, res);
327
+ if (res.headersSent) return;
328
+ }
329
+
330
+ if (this.hasTransformMiddleware) {
331
+ await this.executePhase('transform', validatedReq, res);
332
+ if (res.headersSent) return;
333
+ }
334
+
335
+ if (this.requiresCache) {
336
+ await this.executePhase('cache', validatedReq, res);
337
+ if (res.headersSent) return;
338
+ }
339
+
340
+ if (this.hasAfterMiddleware) {
341
+ await this.executePhase('after', validatedReq, res);
342
+ if (res.headersSent) return;
343
+ }
273
344
 
274
345
  // Execute handler last
275
346
  if (!res.headersSent) {
@@ -361,15 +432,32 @@ export class ExecutableRoute implements CompiledRoute {
361
432
  req: HttpRequest,
362
433
  res: HttpResponse
363
434
  ): Promise<void> {
435
+ // PERFORMANCE OPTIMIZATION: Reduce Promise overhead
364
436
  return new Promise((resolve, reject) => {
437
+ let resolved = false;
438
+
439
+ const next = () => {
440
+ if (!resolved) {
441
+ resolved = true;
442
+ resolve();
443
+ }
444
+ };
445
+
365
446
  try {
366
- const next = () => resolve();
367
447
  const result = middleware(req, res, next);
368
448
  if (result instanceof Promise) {
369
- result.then(() => resolve()).catch(reject);
449
+ result.then(() => !resolved && next()).catch(reject);
450
+ } else {
451
+ // Synchronous middleware - call next immediately if not called
452
+ if (!resolved) {
453
+ next();
454
+ }
370
455
  }
371
456
  } catch (error) {
372
- reject(error);
457
+ if (!resolved) {
458
+ resolved = true;
459
+ reject(error);
460
+ }
373
461
  }
374
462
  });
375
463
  }
@@ -475,6 +563,22 @@ export class ExecutableRoute implements CompiledRoute {
475
563
  config: this.schema.cache,
476
564
  });
477
565
  }
566
+
567
+ // Performance monitoring
568
+ getPerformanceInfo() {
569
+ return {
570
+ path: this.schema.path,
571
+ method: this.schema.method,
572
+ isFastPath: this.isFastPath,
573
+ requiresAuth: this.requiresAuth,
574
+ requiresValidation: this.requiresValidation,
575
+ requiresRateLimit: this.requiresRateLimit,
576
+ requiresCache: this.requiresCache,
577
+ hasBeforeMiddleware: this.hasBeforeMiddleware,
578
+ hasAfterMiddleware: this.hasAfterMiddleware,
579
+ hasTransformMiddleware: this.hasTransformMiddleware,
580
+ };
581
+ }
478
582
  }
479
583
 
480
584
  // Factory functions for creating routes
package/src/moro.ts CHANGED
@@ -11,6 +11,7 @@ import {
11
11
  logger as globalLogger,
12
12
  applyLoggingConfiguration,
13
13
  } from './core/logger';
14
+ import { Logger } from './types/logger';
14
15
  import { MiddlewareManager } from './core/middleware';
15
16
  import { IntelligentRoutingManager } from './core/routing/app-integration';
16
17
  import { RouteBuilder, RouteSchema, CompiledRoute } from './core/routing';
@@ -19,7 +20,12 @@ import { readdirSync, statSync } from 'fs';
19
20
  import { join } from 'path';
20
21
  import { EventEmitter } from 'events';
21
22
  // Configuration System Integration
22
- import { initializeConfig, getGlobalConfig, type AppConfig } from './core/config';
23
+ import {
24
+ initializeConfig,
25
+ getGlobalConfig,
26
+ loadConfigWithOptions,
27
+ type AppConfig,
28
+ } from './core/config';
23
29
  // Runtime System Integration
24
30
  import {
25
31
  RuntimeAdapter,
@@ -37,7 +43,7 @@ export class Moro extends EventEmitter {
37
43
  // Enterprise event system integration
38
44
  private eventBus: MoroEventBus;
39
45
  // Application logger
40
- private logger = createFrameworkLogger('App');
46
+ private logger!: Logger;
41
47
  // Intelligent routing system
42
48
  private intelligentRouting = new IntelligentRoutingManager();
43
49
  // Documentation system
@@ -53,72 +59,33 @@ export class Moro extends EventEmitter {
53
59
  constructor(options: MoroOptions = {}) {
54
60
  super(); // Call EventEmitter constructor
55
61
 
56
- // Configure logger from environment variables BEFORE config system initialization
57
- // This ensures the config loading process respects the log level
62
+ // Apply logging configuration BEFORE config loading to avoid DEBUG spam
63
+ // 1. Environment variables (base level)
58
64
  const envLogLevel = process.env.LOG_LEVEL || process.env.MORO_LOG_LEVEL;
59
65
  if (envLogLevel) {
60
66
  applyLoggingConfiguration({ level: envLogLevel }, undefined);
61
67
  }
62
68
 
63
- // Initialize configuration system - create a deep copy for this instance
64
- this.config = JSON.parse(JSON.stringify(initializeConfig()));
65
-
66
- // Apply logging configuration from the loaded config (this happens after config file processing)
67
- if (this.config.logging) {
68
- applyLoggingConfiguration(this.config.logging, undefined);
69
- }
70
-
71
- // Apply additional logging configuration from createApp options (takes precedence)
69
+ // 2. createApp logger options (highest precedence)
72
70
  if (options.logger !== undefined) {
73
71
  applyLoggingConfiguration(undefined, options.logger);
74
72
  }
75
73
 
76
- // Apply performance configuration from createApp options (takes precedence)
77
- if (options.performance) {
78
- if (options.performance.clustering) {
79
- this.config.performance.clustering = {
80
- ...this.config.performance.clustering,
81
- ...options.performance.clustering,
82
- };
83
- }
84
- if (options.performance.compression) {
85
- this.config.performance.compression = {
86
- ...this.config.performance.compression,
87
- ...options.performance.compression,
88
- };
89
- }
90
- if (options.performance.circuitBreaker) {
91
- this.config.performance.circuitBreaker = {
92
- ...this.config.performance.circuitBreaker,
93
- ...options.performance.circuitBreaker,
94
- };
95
- }
96
- }
74
+ // Create logger AFTER initial configuration
75
+ this.logger = createFrameworkLogger('App');
97
76
 
98
- // Apply modules configuration from createApp options (takes precedence)
99
- if (options.modules) {
100
- if (options.modules.cache) {
101
- this.config.modules.cache = {
102
- ...this.config.modules.cache,
103
- ...options.modules.cache,
104
- };
105
- }
106
- if (options.modules.rateLimit) {
107
- this.config.modules.rateLimit = {
108
- ...this.config.modules.rateLimit,
109
- ...options.modules.rateLimit,
110
- };
111
- }
112
- if (options.modules.validation) {
113
- this.config.modules.validation = {
114
- ...this.config.modules.validation,
115
- ...options.modules.validation,
116
- };
117
- }
77
+ // Use simplified global configuration system
78
+ this.config = initializeConfig(options);
79
+
80
+ // Apply config file logging if it exists (may override createApp options if needed)
81
+ if (this.config.logging && !options.logger) {
82
+ applyLoggingConfiguration(this.config.logging, undefined);
83
+ // Recreate logger with updated config
84
+ this.logger = createFrameworkLogger('App');
118
85
  }
119
86
 
120
87
  this.logger.info(
121
- `Configuration system initialized: ${this.config.server.environment}:${this.config.server.port}`
88
+ `Configuration system initialized: ${process.env.NODE_ENV || 'development'}:${this.config.server.port}`
122
89
  );
123
90
 
124
91
  // Initialize runtime system
@@ -127,10 +94,11 @@ export class Moro extends EventEmitter {
127
94
 
128
95
  this.logger.info(`Runtime system initialized: ${this.runtimeType}`, 'Runtime');
129
96
 
130
- // Pass logging configuration from config to framework
97
+ // Pass configuration from config to framework
131
98
  const frameworkOptions: any = {
132
99
  ...options,
133
100
  logger: this.config.logging,
101
+ websocket: this.config.websocket.enabled ? options.websocket || {} : false,
134
102
  };
135
103
 
136
104
  this.coreFramework = new MoroCore(frameworkOptions);
@@ -540,8 +508,9 @@ export class Moro extends EventEmitter {
540
508
  this.logger.info('Moro Server Started', 'Server');
541
509
  this.logger.info(`Runtime: ${this.runtimeType}`, 'Server');
542
510
  this.logger.info(`HTTP API: http://${displayHost}:${port}`, 'Server');
543
- this.logger.info(`WebSocket: ws://${displayHost}:${port}`, 'Server');
544
- this.logger.info('Native Node.js HTTP • Zero Dependencies • Maximum Performance', 'Server');
511
+ if (this.config.websocket.enabled) {
512
+ this.logger.info(`WebSocket: ws://${displayHost}:${port}`, 'Server');
513
+ }
545
514
  this.logger.info('Learn more at https://morojs.com', 'Server');
546
515
 
547
516
  // Log intelligent routes info
@@ -935,55 +904,84 @@ export class Moro extends EventEmitter {
935
904
  return module.default || module;
936
905
  }
937
906
 
938
- // Clustering support for massive performance gains with proper cleanup
907
+ /**
908
+ * Node.js Clustering Implementation
909
+ * This clustering algorithm is based on published research and Node.js best practices.
910
+ *
911
+ * IPC (Inter-Process Communication) Considerations:
912
+ * - Excessive workers create IPC bottlenecks (Source: BetterStack Node.js Guide)
913
+ * - Round-robin scheduling provides better load distribution (Node.js Documentation)
914
+ * - Message passing overhead increases significantly with worker count
915
+ *
916
+ * Memory Management:
917
+ * - ~2GB per worker prevents memory pressure and GC overhead
918
+ * - Conservative heap limits reduce memory fragmentation
919
+ *
920
+ * References:
921
+ * - Node.js Cluster Documentation: https://nodejs.org/api/cluster.html
922
+ * - BetterStack Node.js Clustering: https://betterstack.com/community/guides/scaling-nodejs/node-clustering/
923
+ */
939
924
  private clusterWorkers = new Map<number, any>();
925
+
940
926
  private startWithClustering(port: number, host?: string, callback?: () => void): void {
941
927
  const cluster = require('cluster');
942
928
  const os = require('os');
943
929
 
944
- // Smart worker count calculation based on actual bottlenecks
930
+ // Worker count calculation - respect user choice
945
931
  let workerCount = this.config.performance?.clustering?.workers || os.cpus().length;
946
932
 
947
- // Auto-optimize worker count based on system characteristics
948
- if (workerCount === 'auto' || workerCount > 8) {
949
- // For high-core machines, limit workers to prevent IPC/memory bottlenecks
933
+ // Only auto-optimize if user hasn't specified a number or set it to 'auto'
934
+ if (workerCount === 'auto') {
950
935
  const cpuCount = os.cpus().length;
951
936
  const totalMemoryGB = os.totalmem() / (1024 * 1024 * 1024);
952
937
 
953
- // Optimal worker count formula based on research
954
- if (cpuCount >= 16) {
955
- // High-core machines: focus on memory/IPC efficiency
956
- workerCount = Math.min(Math.ceil(totalMemoryGB / 2), 4); // 2GB per worker max, cap at 4
957
- } else if (cpuCount >= 8) {
958
- // Mid-range machines: balanced approach
959
- workerCount = Math.min(cpuCount / 2, 4);
960
- } else {
961
- // Low-core machines: use all cores
962
- workerCount = cpuCount;
938
+ // Get memory per worker from config - if not set by user, calculate dynamically
939
+ let memoryPerWorkerGB = this.config.performance?.clustering?.memoryPerWorkerGB;
940
+
941
+ if (!memoryPerWorkerGB) {
942
+ // Dynamic calculation: (Total RAM - 4GB headroom) / CPU cores
943
+ const headroomGB = 4;
944
+ memoryPerWorkerGB = Math.max(0.5, Math.floor((totalMemoryGB - headroomGB) / cpuCount));
963
945
  }
964
946
 
947
+ // Conservative formula based on general guidelines:
948
+ // - Don't exceed CPU cores
949
+ // - Respect user's memory allocation preference
950
+ // - Let the system resources determine the limit
951
+ workerCount = Math.min(
952
+ cpuCount, // Don't exceed CPU cores
953
+ Math.floor(totalMemoryGB / memoryPerWorkerGB) // User-configurable memory per worker
954
+ );
955
+
965
956
  this.logger.info(
966
- `Auto-optimized workers: ${workerCount} (CPU: ${cpuCount}, RAM: ${totalMemoryGB.toFixed(1)}GB)`,
957
+ `Auto-calculated worker count: ${workerCount} (CPU: ${cpuCount}, RAM: ${totalMemoryGB.toFixed(1)}GB, ${memoryPerWorkerGB}GB per worker)`,
967
958
  'Cluster'
968
959
  );
960
+ } else if (typeof workerCount === 'number') {
961
+ // User specified a number - respect their choice
962
+ this.logger.info(`Using user-specified worker count: ${workerCount}`, 'Cluster');
969
963
  }
970
964
 
971
965
  if (cluster.isPrimary) {
972
- this.logger.info(`🚀 Starting ${workerCount} workers for maximum performance`, 'Cluster');
966
+ this.logger.info(`Starting ${workerCount} workers`, 'Cluster');
973
967
 
974
968
  // Optimize cluster scheduling for high concurrency
975
- cluster.schedulingPolicy = cluster.SCHED_RR; // Round-robin scheduling
969
+ // Round-robin is the default on all platforms except Windows (Node.js docs)
970
+ // Provides better load distribution than shared socket approach
971
+ cluster.schedulingPolicy = cluster.SCHED_RR;
976
972
 
977
973
  // Set cluster settings for better performance
978
974
  cluster.setupMaster({
979
- exec: process.argv[1],
975
+ exec: process.argv[1] || process.execPath,
980
976
  args: process.argv.slice(2),
981
977
  silent: false,
982
978
  });
983
979
 
984
- // Optimize IPC to reduce communication overhead
980
+ // IPC Optimization: Reduce communication overhead between master and workers
981
+ // Research shows excessive IPC can create bottlenecks in clustered applications
982
+ // (Source: BetterStack - Node.js Clustering Guide)
985
983
  process.env.NODE_CLUSTER_SCHED_POLICY = 'rr'; // Ensure round-robin
986
- process.env.NODE_DISABLE_COLORS = '1'; // Reduce IPC message size
984
+ process.env.NODE_DISABLE_COLORS = '1'; // Reduce IPC message size by disabling color codes
987
985
 
988
986
  // Graceful shutdown handler
989
987
  const gracefulShutdown = () => {
@@ -1004,37 +1002,32 @@ export class Moro extends EventEmitter {
1004
1002
  process.on('SIGINT', gracefulShutdown);
1005
1003
  process.on('SIGTERM', gracefulShutdown);
1006
1004
 
1007
- // Fork workers with proper tracking and CPU affinity
1005
+ // Fork workers with basic tracking
1008
1006
  for (let i = 0; i < workerCount; i++) {
1009
- const worker = cluster.fork({
1010
- WORKER_ID: i,
1011
- WORKER_CPU_AFFINITY: i % os.cpus().length, // Distribute workers across CPUs
1012
- });
1007
+ const worker = cluster.fork();
1013
1008
  this.clusterWorkers.set(worker.process.pid!, worker);
1014
- this.logger.info(
1015
- `Worker ${worker.process.pid} started (CPU ${i % os.cpus().length})`,
1016
- 'Cluster'
1017
- );
1009
+ this.logger.info(`Worker ${worker.process.pid} started`, 'Cluster');
1018
1010
 
1019
- // Handle individual worker messages (reuse handler)
1011
+ // Handle individual worker messages
1020
1012
  worker.on('message', this.handleWorkerMessage.bind(this));
1021
1013
  }
1022
1014
 
1023
- // Handle worker exits with cleanup
1015
+ // Simple worker exit handling
1024
1016
  cluster.on('exit', (worker: any, code: number, signal: string) => {
1025
- // Clean up worker tracking
1026
- this.clusterWorkers.delete(worker.process.pid);
1027
-
1028
- this.logger.warn(
1029
- `Worker ${worker.process.pid} died (${signal || code}). Restarting...`,
1030
- 'Cluster'
1031
- );
1032
-
1033
- // Restart worker with proper tracking
1034
- const newWorker = cluster.fork();
1035
- this.clusterWorkers.set(newWorker.process.pid!, newWorker);
1036
- newWorker.on('message', this.handleWorkerMessage.bind(this));
1037
- this.logger.info(`Worker ${newWorker.process.pid} started`, 'Cluster');
1017
+ const pid = worker.process.pid;
1018
+ this.clusterWorkers.delete(pid);
1019
+
1020
+ if (code !== 0 && !worker.exitedAfterDisconnect) {
1021
+ this.logger.warn(
1022
+ `Worker ${pid} died unexpectedly (${signal || code}). Restarting...`,
1023
+ 'Cluster'
1024
+ );
1025
+
1026
+ // Simple restart
1027
+ const newWorker = cluster.fork();
1028
+ this.clusterWorkers.set(newWorker.process.pid!, newWorker);
1029
+ this.logger.info(`Worker ${newWorker.process.pid} restarted`, 'Cluster');
1030
+ }
1038
1031
  });
1039
1032
 
1040
1033
  // Master process callback
@@ -1047,13 +1040,29 @@ export class Moro extends EventEmitter {
1047
1040
  process.env.UV_THREADPOOL_SIZE = '64';
1048
1041
 
1049
1042
  // Reduce logging contention in workers (major bottleneck)
1043
+ // Multiple workers writing to same log files creates I/O contention
1050
1044
  if (this.config.logging) {
1051
1045
  // Workers log less frequently to reduce I/O contention
1052
- this.config.logging.level = 'warn'; // Only warnings and errors
1046
+ applyLoggingConfiguration(undefined, { level: 'warn' }); // Only warnings and errors
1053
1047
  }
1054
1048
 
1055
- // Memory optimization for workers
1056
- process.env.NODE_OPTIONS = '--max-old-space-size=1024'; // Limit memory per worker
1049
+ // Research-based memory optimization for workers
1050
+ const os = require('os');
1051
+ const totalMemoryGB = os.totalmem() / (1024 * 1024 * 1024);
1052
+ const workerCount = Object.keys(require('cluster').workers || {}).length || 1;
1053
+
1054
+ // Conservative memory allocation
1055
+ const heapSizePerWorkerMB = Math.min(
1056
+ Math.floor(((totalMemoryGB * 1024) / workerCount) * 0.8), // 80% of available memory
1057
+ 1536 // Cap at 1.5GB (GC efficiency threshold from research)
1058
+ );
1059
+
1060
+ process.env.NODE_OPTIONS = `--max-old-space-size=${heapSizePerWorkerMB}`;
1061
+
1062
+ this.logger.debug(
1063
+ `Worker memory allocated: ${heapSizePerWorkerMB}MB heap (${workerCount} workers, ${totalMemoryGB.toFixed(1)}GB total)`,
1064
+ 'Worker'
1065
+ );
1057
1066
 
1058
1067
  // Optimize V8 flags for better performance (Rust-level optimizations)
1059
1068
  if (process.env.NODE_ENV === 'production') {
@@ -1149,7 +1158,7 @@ export class Moro extends EventEmitter {
1149
1158
  }
1150
1159
  }
1151
1160
 
1152
- // Reusable worker message handler (avoids creating new functions)
1161
+ // Simple worker message handler
1153
1162
  private handleWorkerMessage(message: any): void {
1154
1163
  // Handle inter-worker communication if needed
1155
1164
  if (message.type === 'health-check') {
@@ -1160,6 +1169,49 @@ export class Moro extends EventEmitter {
1160
1169
  // Log other worker messages
1161
1170
  this.logger.debug(`Worker message: ${JSON.stringify(message)}`, 'Cluster');
1162
1171
  }
1172
+
1173
+ /**
1174
+ * Gracefully close the application and clean up resources
1175
+ * This should be called in tests and during shutdown
1176
+ */
1177
+ async close(): Promise<void> {
1178
+ this.logger.debug('Closing Moro application...');
1179
+
1180
+ // Flush logger buffer before shutdown
1181
+ try {
1182
+ // Use flushBuffer for immediate synchronous flush
1183
+ this.logger.flushBuffer();
1184
+ } catch (error) {
1185
+ // Ignore flush errors during shutdown
1186
+ }
1187
+
1188
+ // Close the core framework with timeout
1189
+ if (this.coreFramework && (this.coreFramework as any).httpServer) {
1190
+ try {
1191
+ await Promise.race([
1192
+ new Promise<void>(resolve => {
1193
+ (this.coreFramework as any).httpServer.close(() => {
1194
+ resolve();
1195
+ });
1196
+ }),
1197
+ new Promise<void>(resolve => setTimeout(resolve, 2000)), // 2 second timeout
1198
+ ]);
1199
+ } catch (error) {
1200
+ // Force close if graceful close fails
1201
+ this.logger.warn('Force closing HTTP server due to timeout');
1202
+ }
1203
+ }
1204
+
1205
+ // Clean up event listeners
1206
+ try {
1207
+ this.eventBus.removeAllListeners();
1208
+ this.removeAllListeners();
1209
+ } catch (error) {
1210
+ // Ignore cleanup errors
1211
+ }
1212
+
1213
+ this.logger.debug('Moro application closed successfully');
1214
+ }
1163
1215
  }
1164
1216
 
1165
1217
  // Export convenience function
@@ -3,7 +3,6 @@
3
3
  export interface ServerConfig {
4
4
  port: number;
5
5
  host: string;
6
- environment: 'development' | 'staging' | 'production';
7
6
  maxConnections: number;
8
7
  timeout: number;
9
8
  }
@@ -19,7 +18,7 @@ export interface ServiceDiscoveryConfig {
19
18
 
20
19
  export interface DatabaseConfig {
21
20
  url?: string;
22
- redis: {
21
+ redis?: {
23
22
  url: string;
24
23
  maxRetries: number;
25
24
  retryDelay: number;
@@ -35,6 +34,28 @@ export interface DatabaseConfig {
35
34
  acquireTimeout: number;
36
35
  timeout: number;
37
36
  };
37
+ postgresql?: {
38
+ host: string;
39
+ port: number;
40
+ database?: string;
41
+ user?: string;
42
+ password?: string;
43
+ connectionLimit: number;
44
+ ssl?: boolean;
45
+ };
46
+ sqlite?: {
47
+ filename: string;
48
+ memory?: boolean;
49
+ verbose?: boolean;
50
+ };
51
+ mongodb?: {
52
+ url?: string;
53
+ host?: string;
54
+ port?: number;
55
+ database?: string;
56
+ username?: string;
57
+ password?: string;
58
+ };
38
59
  }
39
60
 
40
61
  export interface ModuleDefaultsConfig {
@@ -141,6 +162,22 @@ export interface PerformanceConfig {
141
162
  clustering: {
142
163
  enabled: boolean;
143
164
  workers: number | 'auto';
165
+ memoryPerWorkerGB?: number;
166
+ };
167
+ }
168
+
169
+ export interface WebSocketConfig {
170
+ enabled: boolean;
171
+ adapter?: string | 'socket.io' | 'ws';
172
+ compression?: boolean;
173
+ customIdGenerator?: () => string;
174
+ options?: {
175
+ cors?: {
176
+ origin?: string | string[];
177
+ credentials?: boolean;
178
+ };
179
+ path?: string;
180
+ maxPayloadLength?: number;
144
181
  };
145
182
  }
146
183
 
@@ -154,4 +191,5 @@ export interface AppConfig {
154
191
  security: SecurityConfig;
155
192
  external: ExternalServicesConfig;
156
193
  performance: PerformanceConfig;
194
+ websocket: WebSocketConfig;
157
195
  }