@morojs/moro 1.5.5 → 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 +9 -1
  34. package/dist/core/logger/logger.js +36 -3
  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 +7 -20
  40. package/dist/moro.js +97 -192
  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 +43 -4
  58. package/src/core/routing/index.ts +116 -12
  59. package/src/moro.ts +105 -225
  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
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
@@ -936,14 +905,8 @@ export class Moro extends EventEmitter {
936
905
  }
937
906
 
938
907
  /**
939
- * Node.js Clustering Implementation with Empirical Optimizations
940
- *
941
- * This clustering algorithm is based on empirical testing and Node.js best practices.
942
- * Key findings from research and testing:
943
- *
944
- * Performance Benefits:
945
- * - Clustering can improve performance by up to 66% (Source: Medium - Danish Siddiq)
946
- * - Enables utilization of multiple CPU cores in Node.js applications
908
+ * Node.js Clustering Implementation
909
+ * This clustering algorithm is based on published research and Node.js best practices.
947
910
  *
948
911
  * IPC (Inter-Process Communication) Considerations:
949
912
  * - Excessive workers create IPC bottlenecks (Source: BetterStack Node.js Guide)
@@ -954,71 +917,53 @@ export class Moro extends EventEmitter {
954
917
  * - ~2GB per worker prevents memory pressure and GC overhead
955
918
  * - Conservative heap limits reduce memory fragmentation
956
919
  *
957
- * Empirical Findings (MoroJS Testing):
958
- * - 4-worker cap provides optimal performance regardless of core count
959
- * - IPC becomes the primary bottleneck on high-core machines (16+ cores)
960
- * - Memory allocation per worker more important than CPU utilization
961
- *
962
920
  * References:
963
921
  * - Node.js Cluster Documentation: https://nodejs.org/api/cluster.html
964
922
  * - BetterStack Node.js Clustering: https://betterstack.com/community/guides/scaling-nodejs/node-clustering/
965
923
  */
966
924
  private clusterWorkers = new Map<number, any>();
967
- private workerStats = new Map<
968
- number,
969
- { cpu: number; memory: number; requests: number; lastCheck: number }
970
- >();
971
- private adaptiveScalingEnabled = true;
972
- private lastScalingCheck = 0;
973
- private readonly SCALING_INTERVAL = 30000; // 30 seconds
974
925
 
975
926
  private startWithClustering(port: number, host?: string, callback?: () => void): void {
976
927
  const cluster = require('cluster');
977
928
  const os = require('os');
978
929
 
979
- // Smart worker count calculation to prevent IPC bottlenecks and optimize resource usage
980
- // Based on empirical testing and Node.js clustering best practices
930
+ // Worker count calculation - respect user choice
981
931
  let workerCount = this.config.performance?.clustering?.workers || os.cpus().length;
982
932
 
983
- // Auto-optimize worker count based on system characteristics
984
- // Research shows clustering can improve performance by up to 66% but excessive workers
985
- // cause IPC overhead that degrades performance (Source: Medium - Clustering in Node.js)
986
- if (workerCount === 'auto' || workerCount > 8) {
933
+ // Only auto-optimize if user hasn't specified a number or set it to 'auto'
934
+ if (workerCount === 'auto') {
987
935
  const cpuCount = os.cpus().length;
988
936
  const totalMemoryGB = os.totalmem() / (1024 * 1024 * 1024);
989
937
 
990
- // Improved worker count optimization based on research findings
991
- // Algorithm considers CPU, memory, and IPC overhead holistically
992
- const memoryPerWorkerGB = 1.5; // Optimal based on GC performance testing
993
- const maxWorkersFromMemory = Math.floor(totalMemoryGB / memoryPerWorkerGB);
994
- if (cpuCount >= 16) {
995
- // High-core machines: IPC saturation point reached quickly
996
- // Research shows diminishing returns after 4 workers due to message passing
997
- workerCount = Math.min(maxWorkersFromMemory, 4);
998
- } else if (cpuCount >= 8) {
999
- // Mid-range machines: optimal ratio found to be CPU/3 for IPC efficiency
1000
- // Avoids context switching overhead while maintaining throughput
1001
- workerCount = Math.min(Math.ceil(cpuCount / 3), maxWorkersFromMemory, 6);
1002
- } else if (cpuCount >= 4) {
1003
- // Standard machines: use 3/4 of cores to leave room for OS processes
1004
- workerCount = Math.min(Math.ceil(cpuCount * 0.75), maxWorkersFromMemory, 4);
1005
- } else {
1006
- // Low-core machines: use all cores but cap for memory safety
1007
- workerCount = Math.min(cpuCount, maxWorkersFromMemory, 2);
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));
1008
945
  }
1009
946
 
1010
- this.logger.info(
1011
- `Auto-optimized workers: ${workerCount} (CPU: ${cpuCount}, RAM: ${totalMemoryGB.toFixed(1)}GB)`,
1012
- 'Cluster'
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
1013
954
  );
1014
- this.logger.debug(
1015
- `Worker optimization strategy: ${cpuCount >= 16 ? 'IPC-limited' : cpuCount >= 8 ? 'balanced' : 'CPU-bound'}`,
955
+
956
+ this.logger.info(
957
+ `Auto-calculated worker count: ${workerCount} (CPU: ${cpuCount}, RAM: ${totalMemoryGB.toFixed(1)}GB, ${memoryPerWorkerGB}GB per worker)`,
1016
958
  'Cluster'
1017
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');
1018
963
  }
1019
964
 
1020
965
  if (cluster.isPrimary) {
1021
- this.logger.info(`🚀 Starting ${workerCount} workers for maximum performance`, 'Cluster');
966
+ this.logger.info(`Starting ${workerCount} workers`, 'Cluster');
1022
967
 
1023
968
  // Optimize cluster scheduling for high concurrency
1024
969
  // Round-robin is the default on all platforms except Windows (Node.js docs)
@@ -1027,7 +972,7 @@ export class Moro extends EventEmitter {
1027
972
 
1028
973
  // Set cluster settings for better performance
1029
974
  cluster.setupMaster({
1030
- exec: process.argv[1],
975
+ exec: process.argv[1] || process.execPath,
1031
976
  args: process.argv.slice(2),
1032
977
  silent: false,
1033
978
  });
@@ -1057,50 +1002,34 @@ export class Moro extends EventEmitter {
1057
1002
  process.on('SIGINT', gracefulShutdown);
1058
1003
  process.on('SIGTERM', gracefulShutdown);
1059
1004
 
1060
- // Fork workers with proper tracking and CPU affinity
1005
+ // Fork workers with basic tracking
1061
1006
  for (let i = 0; i < workerCount; i++) {
1062
- const worker = cluster.fork({
1063
- WORKER_ID: i,
1064
- WORKER_CPU_AFFINITY: i % os.cpus().length, // Distribute workers across CPUs
1065
- });
1007
+ const worker = cluster.fork();
1066
1008
  this.clusterWorkers.set(worker.process.pid!, worker);
1067
- this.logger.info(
1068
- `Worker ${worker.process.pid} started (CPU ${i % os.cpus().length})`,
1069
- 'Cluster'
1070
- );
1009
+ this.logger.info(`Worker ${worker.process.pid} started`, 'Cluster');
1071
1010
 
1072
- // Handle individual worker messages (reuse handler)
1011
+ // Handle individual worker messages
1073
1012
  worker.on('message', this.handleWorkerMessage.bind(this));
1074
1013
  }
1075
1014
 
1076
- // Enhanced worker exit handling with adaptive monitoring
1015
+ // Simple worker exit handling
1077
1016
  cluster.on('exit', (worker: any, code: number, signal: string) => {
1078
1017
  const pid = worker.process.pid;
1079
-
1080
- // Clean up worker tracking and stats
1081
1018
  this.clusterWorkers.delete(pid);
1082
- this.workerStats.delete(pid);
1083
1019
 
1084
1020
  if (code !== 0 && !worker.exitedAfterDisconnect) {
1085
1021
  this.logger.warn(
1086
- `Worker ${pid} died unexpectedly (${signal || code}). Analyzing performance...`,
1022
+ `Worker ${pid} died unexpectedly (${signal || code}). Restarting...`,
1087
1023
  'Cluster'
1088
1024
  );
1089
1025
 
1090
- // Check if we should scale workers based on performance
1091
- this.evaluateWorkerPerformance();
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');
1092
1030
  }
1093
-
1094
- // Restart worker with enhanced tracking
1095
- const newWorker = this.forkWorkerWithMonitoring();
1096
- this.logger.info(`Worker ${newWorker.process.pid} started with monitoring`, 'Cluster');
1097
1031
  });
1098
1032
 
1099
- // Start adaptive scaling system
1100
- if (this.adaptiveScalingEnabled) {
1101
- this.startAdaptiveScaling();
1102
- }
1103
-
1104
1033
  // Master process callback
1105
1034
  if (callback) callback();
1106
1035
  } else {
@@ -1114,25 +1043,24 @@ export class Moro extends EventEmitter {
1114
1043
  // Multiple workers writing to same log files creates I/O contention
1115
1044
  if (this.config.logging) {
1116
1045
  // Workers log less frequently to reduce I/O contention
1117
- this.config.logging.level = 'warn'; // Only warnings and errors
1046
+ applyLoggingConfiguration(undefined, { level: 'warn' }); // Only warnings and errors
1118
1047
  }
1119
1048
 
1120
- // Enhanced memory optimization for workers
1121
- // Dynamic heap sizing based on available system memory and worker count
1049
+ // Research-based memory optimization for workers
1122
1050
  const os = require('os');
1123
1051
  const totalMemoryGB = os.totalmem() / (1024 * 1024 * 1024);
1124
1052
  const workerCount = Object.keys(require('cluster').workers || {}).length || 1;
1125
1053
 
1126
- // Allocate memory more intelligently based on system resources
1054
+ // Conservative memory allocation
1127
1055
  const heapSizePerWorkerMB = Math.min(
1128
- Math.floor((totalMemoryGB * 1024) / (workerCount * 1.5)), // Leave buffer for OS
1129
- 1536 // Cap at 1.5GB per worker to prevent excessive GC
1056
+ Math.floor(((totalMemoryGB * 1024) / workerCount) * 0.8), // 80% of available memory
1057
+ 1536 // Cap at 1.5GB (GC efficiency threshold from research)
1130
1058
  );
1131
1059
 
1132
1060
  process.env.NODE_OPTIONS = `--max-old-space-size=${heapSizePerWorkerMB}`;
1133
1061
 
1134
1062
  this.logger.debug(
1135
- `Worker memory optimized: ${heapSizePerWorkerMB}MB heap (${workerCount} workers, ${totalMemoryGB.toFixed(1)}GB total)`,
1063
+ `Worker memory allocated: ${heapSizePerWorkerMB}MB heap (${workerCount} workers, ${totalMemoryGB.toFixed(1)}GB total)`,
1136
1064
  'Worker'
1137
1065
  );
1138
1066
 
@@ -1230,20 +1158,8 @@ export class Moro extends EventEmitter {
1230
1158
  }
1231
1159
  }
1232
1160
 
1233
- // Enhanced worker message handler with performance monitoring
1161
+ // Simple worker message handler
1234
1162
  private handleWorkerMessage(message: any): void {
1235
- // Handle performance monitoring messages
1236
- if (message.type === 'performance') {
1237
- const pid = message.pid;
1238
- this.workerStats.set(pid, {
1239
- cpu: message.cpu || 0,
1240
- memory: message.memory || 0,
1241
- requests: message.requests || 0,
1242
- lastCheck: Date.now(),
1243
- });
1244
- return;
1245
- }
1246
-
1247
1163
  // Handle inter-worker communication if needed
1248
1164
  if (message.type === 'health-check') {
1249
1165
  // Worker health check response
@@ -1254,83 +1170,47 @@ export class Moro extends EventEmitter {
1254
1170
  this.logger.debug(`Worker message: ${JSON.stringify(message)}`, 'Cluster');
1255
1171
  }
1256
1172
 
1257
- private forkWorkerWithMonitoring(): any {
1258
- const cluster = require('cluster');
1259
- const os = require('os');
1260
-
1261
- const worker = cluster.fork({
1262
- WORKER_ID: this.clusterWorkers.size,
1263
- WORKER_CPU_AFFINITY: this.clusterWorkers.size % os.cpus().length,
1264
- });
1265
-
1266
- this.clusterWorkers.set(worker.process.pid!, worker);
1267
- worker.on('message', this.handleWorkerMessage.bind(this));
1268
-
1269
- return worker;
1270
- }
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...');
1271
1179
 
1272
- private evaluateWorkerPerformance(): void {
1273
- const now = Date.now();
1274
- const currentWorkerCount = this.clusterWorkers.size;
1275
-
1276
- // Calculate average CPU and memory usage across workers
1277
- let totalCpu = 0;
1278
- let totalMemory = 0;
1279
- let activeWorkers = 0;
1280
-
1281
- for (const [pid, stats] of this.workerStats) {
1282
- if (now - stats.lastCheck < 60000) {
1283
- // Data less than 1 minute old
1284
- totalCpu += stats.cpu;
1285
- totalMemory += stats.memory;
1286
- activeWorkers++;
1287
- }
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
1288
1186
  }
1289
1187
 
1290
- if (activeWorkers === 0) return;
1291
-
1292
- const avgCpu = totalCpu / activeWorkers;
1293
- const avgMemory = totalMemory / activeWorkers;
1294
-
1295
- this.logger.debug(
1296
- `Performance analysis: ${activeWorkers} workers, avg CPU: ${avgCpu.toFixed(1)}%, avg memory: ${avgMemory.toFixed(1)}MB`,
1297
- 'Cluster'
1298
- );
1299
-
1300
- // Research-based adaptive scaling decisions
1301
- // High CPU threshold indicates IPC saturation point approaching
1302
- if (avgCpu > 80 && currentWorkerCount < 6) {
1303
- this.logger.info(
1304
- 'High CPU load detected, system may benefit from additional worker',
1305
- 'Cluster'
1306
- );
1307
- } else if (avgCpu < 25 && currentWorkerCount > 2) {
1308
- this.logger.info(
1309
- 'Low CPU utilization detected, excessive workers may be causing IPC overhead',
1310
- 'Cluster'
1311
- );
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
+ }
1312
1203
  }
1313
1204
 
1314
- // Memory pressure monitoring
1315
- if (avgMemory > 1200) {
1316
- // MB
1317
- this.logger.warn(
1318
- 'High memory usage per worker detected, may need worker restart or scaling adjustment',
1319
- 'Cluster'
1320
- );
1205
+ // Clean up event listeners
1206
+ try {
1207
+ this.eventBus.removeAllListeners();
1208
+ this.removeAllListeners();
1209
+ } catch (error) {
1210
+ // Ignore cleanup errors
1321
1211
  }
1322
- }
1323
-
1324
- private startAdaptiveScaling(): void {
1325
- setInterval(() => {
1326
- const now = Date.now();
1327
- if (now - this.lastScalingCheck > this.SCALING_INTERVAL) {
1328
- this.evaluateWorkerPerformance();
1329
- this.lastScalingCheck = now;
1330
- }
1331
- }, this.SCALING_INTERVAL);
1332
1212
 
1333
- this.logger.info('Adaptive performance monitoring system started', 'Cluster');
1213
+ this.logger.debug('Moro application closed successfully');
1334
1214
  }
1335
1215
  }
1336
1216
 
@@ -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
  }
package/src/types/core.ts CHANGED
@@ -1,56 +1,45 @@
1
1
  // Core Framework Types
2
2
  import { RuntimeConfig } from './runtime';
3
3
  import { LogLevel, LoggerOptions } from './logger';
4
+ import { AppConfig } from './config';
4
5
 
5
6
  export interface MoroOptions {
6
7
  autoDiscover?: boolean;
7
8
  modulesPath?: string;
8
9
  middleware?: any[];
9
- database?: any;
10
+
11
+ // Runtime configuration
12
+ runtime?: RuntimeConfig;
13
+
14
+ // HTTP/WebSocket options
15
+ http2?: boolean;
16
+ https?: {
17
+ key: string | Buffer;
18
+ cert: string | Buffer;
19
+ ca?: string | Buffer;
20
+ };
21
+ websocket?:
22
+ | {
23
+ enabled?: boolean;
24
+ adapter?: any;
25
+ compression?: boolean;
26
+ customIdGenerator?: () => string;
27
+ options?: any;
28
+ }
29
+ | false;
30
+
31
+ // Simplified config options (these map to the full config)
10
32
  cors?: boolean | object;
11
33
  compression?: boolean | object;
12
34
  helmet?: boolean | object;
13
- // Runtime configuration
14
- runtime?: RuntimeConfig;
15
- // Logger configuration
16
35
  logger?: LoggerOptions | boolean;
17
- // Module configuration
18
- modules?: {
19
- cache?: {
20
- enabled?: boolean;
21
- defaultTtl?: number;
22
- maxSize?: number;
23
- strategy?: 'lru' | 'lfu' | 'fifo';
24
- };
25
- rateLimit?: {
26
- enabled?: boolean;
27
- defaultRequests?: number;
28
- defaultWindow?: number;
29
- skipSuccessfulRequests?: boolean;
30
- skipFailedRequests?: boolean;
31
- };
32
- validation?: {
33
- enabled?: boolean;
34
- stripUnknown?: boolean;
35
- abortEarly?: boolean;
36
- };
37
- };
38
- // Performance configuration
39
- performance?: {
40
- clustering?: {
41
- enabled?: boolean;
42
- workers?: number | 'auto';
43
- };
44
- compression?: {
45
- enabled?: boolean;
46
- level?: number;
47
- threshold?: number;
48
- };
49
- circuitBreaker?: {
50
- enabled?: boolean;
51
- failureThreshold?: number;
52
- resetTimeout?: number;
53
- monitoringPeriod?: number;
54
- };
55
- };
36
+
37
+ // Direct config overrides (partial)
38
+ server?: Partial<AppConfig['server']>;
39
+ database?: Partial<AppConfig['database']>;
40
+ modules?: Partial<AppConfig['modules']>;
41
+ logging?: Partial<AppConfig['logging']>;
42
+ security?: Partial<AppConfig['security']>;
43
+ external?: Partial<AppConfig['external']>;
44
+ performance?: Partial<AppConfig['performance']>;
56
45
  }
@@ -61,11 +61,17 @@ export interface Logger {
61
61
 
62
62
  // Configuration
63
63
  setLevel(level: LogLevel): void;
64
+ getLevel(): LogLevel;
64
65
  addOutput(output: LogOutput): void;
65
66
  removeOutput(name: string): void;
66
67
  addFilter(filter: LogFilter): void;
67
68
  removeFilter(name: string): void;
68
69
 
70
+ // Cleanup
71
+ flush(): void;
72
+ flushBuffer(): void;
73
+ destroy(): void;
74
+
69
75
  // Metrics and history
70
76
  getHistory(count?: number): LogEntry[];
71
77
  getMetrics(): LogMetrics;