@morojs/moro 1.5.17 → 1.6.1

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 (138) hide show
  1. package/README.md +48 -65
  2. package/dist/core/auth/morojs-adapter.js +12 -16
  3. package/dist/core/auth/morojs-adapter.js.map +1 -1
  4. package/dist/core/config/file-loader.d.ts +5 -0
  5. package/dist/core/config/file-loader.js +171 -0
  6. package/dist/core/config/file-loader.js.map +1 -1
  7. package/dist/core/config/index.d.ts +10 -39
  8. package/dist/core/config/index.js +29 -66
  9. package/dist/core/config/index.js.map +1 -1
  10. package/dist/core/config/loader.d.ts +7 -0
  11. package/dist/core/config/loader.js +269 -0
  12. package/dist/core/config/loader.js.map +1 -0
  13. package/dist/core/config/schema.js +31 -41
  14. package/dist/core/config/schema.js.map +1 -1
  15. package/dist/core/config/utils.d.ts +2 -9
  16. package/dist/core/config/utils.js +32 -19
  17. package/dist/core/config/utils.js.map +1 -1
  18. package/dist/core/config/validation.d.ts +17 -0
  19. package/dist/core/config/validation.js +131 -0
  20. package/dist/core/config/validation.js.map +1 -0
  21. package/dist/core/database/adapters/mongodb.d.ts +0 -10
  22. package/dist/core/database/adapters/mongodb.js +2 -23
  23. package/dist/core/database/adapters/mongodb.js.map +1 -1
  24. package/dist/core/database/adapters/mysql.d.ts +0 -11
  25. package/dist/core/database/adapters/mysql.js +0 -1
  26. package/dist/core/database/adapters/mysql.js.map +1 -1
  27. package/dist/core/database/adapters/postgresql.d.ts +1 -9
  28. package/dist/core/database/adapters/postgresql.js +1 -1
  29. package/dist/core/database/adapters/postgresql.js.map +1 -1
  30. package/dist/core/database/adapters/redis.d.ts +0 -9
  31. package/dist/core/database/adapters/redis.js +4 -14
  32. package/dist/core/database/adapters/redis.js.map +1 -1
  33. package/dist/core/framework.d.ts +7 -6
  34. package/dist/core/framework.js +16 -131
  35. package/dist/core/framework.js.map +1 -1
  36. package/dist/core/http/http-server.d.ts +0 -12
  37. package/dist/core/http/http-server.js +23 -151
  38. package/dist/core/http/http-server.js.map +1 -1
  39. package/dist/core/http/router.d.ts +0 -12
  40. package/dist/core/http/router.js +36 -114
  41. package/dist/core/http/router.js.map +1 -1
  42. package/dist/core/logger/filters.js +4 -12
  43. package/dist/core/logger/filters.js.map +1 -1
  44. package/dist/core/logger/index.d.ts +1 -1
  45. package/dist/core/logger/index.js +1 -2
  46. package/dist/core/logger/index.js.map +1 -1
  47. package/dist/core/logger/logger.d.ts +13 -29
  48. package/dist/core/logger/logger.js +203 -380
  49. package/dist/core/logger/logger.js.map +1 -1
  50. package/dist/core/logger/outputs.js +2 -0
  51. package/dist/core/logger/outputs.js.map +1 -1
  52. package/dist/core/middleware/built-in/auth.js +17 -88
  53. package/dist/core/middleware/built-in/auth.js.map +1 -1
  54. package/dist/core/middleware/built-in/cache.js +1 -3
  55. package/dist/core/middleware/built-in/cache.js.map +1 -1
  56. package/dist/core/middleware/built-in/index.d.ts +0 -1
  57. package/dist/core/middleware/built-in/index.js +1 -6
  58. package/dist/core/middleware/built-in/index.js.map +1 -1
  59. package/dist/core/middleware/built-in/request-logger.js +2 -3
  60. package/dist/core/middleware/built-in/request-logger.js.map +1 -1
  61. package/dist/core/middleware/built-in/sse.js +7 -9
  62. package/dist/core/middleware/built-in/sse.js.map +1 -1
  63. package/dist/core/modules/auto-discovery.d.ts +0 -17
  64. package/dist/core/modules/auto-discovery.js +12 -367
  65. package/dist/core/modules/auto-discovery.js.map +1 -1
  66. package/dist/core/modules/modules.js +2 -12
  67. package/dist/core/modules/modules.js.map +1 -1
  68. package/dist/core/networking/adapters/ws-adapter.d.ts +1 -1
  69. package/dist/core/networking/adapters/ws-adapter.js +2 -2
  70. package/dist/core/networking/adapters/ws-adapter.js.map +1 -1
  71. package/dist/core/networking/service-discovery.js +7 -7
  72. package/dist/core/networking/service-discovery.js.map +1 -1
  73. package/dist/core/routing/index.d.ts +0 -20
  74. package/dist/core/routing/index.js +13 -178
  75. package/dist/core/routing/index.js.map +1 -1
  76. package/dist/core/runtime/node-adapter.js +6 -12
  77. package/dist/core/runtime/node-adapter.js.map +1 -1
  78. package/dist/moro.d.ts +0 -48
  79. package/dist/moro.js +148 -456
  80. package/dist/moro.js.map +1 -1
  81. package/dist/types/config.d.ts +2 -58
  82. package/dist/types/core.d.ts +40 -34
  83. package/dist/types/http.d.ts +1 -16
  84. package/dist/types/logger.d.ts +0 -7
  85. package/dist/types/module.d.ts +0 -11
  86. package/package.json +2 -2
  87. package/src/core/auth/morojs-adapter.ts +13 -18
  88. package/src/core/config/file-loader.ts +233 -0
  89. package/src/core/config/index.ts +32 -77
  90. package/src/core/config/loader.ts +633 -0
  91. package/src/core/config/schema.ts +31 -41
  92. package/src/core/config/utils.ts +29 -22
  93. package/src/core/config/validation.ts +140 -0
  94. package/src/core/database/README.md +16 -26
  95. package/src/core/database/adapters/mongodb.ts +2 -30
  96. package/src/core/database/adapters/mysql.ts +0 -14
  97. package/src/core/database/adapters/postgresql.ts +2 -12
  98. package/src/core/database/adapters/redis.ts +4 -27
  99. package/src/core/framework.ts +23 -163
  100. package/src/core/http/http-server.ts +36 -176
  101. package/src/core/http/router.ts +38 -127
  102. package/src/core/logger/filters.ts +4 -12
  103. package/src/core/logger/index.ts +0 -1
  104. package/src/core/logger/logger.ts +216 -427
  105. package/src/core/logger/outputs.ts +2 -0
  106. package/src/core/middleware/built-in/auth.ts +17 -98
  107. package/src/core/middleware/built-in/cache.ts +1 -3
  108. package/src/core/middleware/built-in/index.ts +0 -8
  109. package/src/core/middleware/built-in/request-logger.ts +1 -3
  110. package/src/core/middleware/built-in/sse.ts +7 -9
  111. package/src/core/modules/auto-discovery.ts +13 -476
  112. package/src/core/modules/modules.ts +9 -20
  113. package/src/core/networking/adapters/ws-adapter.ts +5 -2
  114. package/src/core/networking/service-discovery.ts +7 -6
  115. package/src/core/routing/index.ts +14 -198
  116. package/src/core/runtime/node-adapter.ts +6 -12
  117. package/src/moro.ts +166 -554
  118. package/src/types/config.ts +2 -59
  119. package/src/types/core.ts +45 -47
  120. package/src/types/http.ts +1 -23
  121. package/src/types/logger.ts +0 -9
  122. package/src/types/module.ts +0 -12
  123. package/dist/core/config/config-manager.d.ts +0 -44
  124. package/dist/core/config/config-manager.js +0 -114
  125. package/dist/core/config/config-manager.js.map +0 -1
  126. package/dist/core/config/config-sources.d.ts +0 -21
  127. package/dist/core/config/config-sources.js +0 -502
  128. package/dist/core/config/config-sources.js.map +0 -1
  129. package/dist/core/config/config-validator.d.ts +0 -21
  130. package/dist/core/config/config-validator.js +0 -765
  131. package/dist/core/config/config-validator.js.map +0 -1
  132. package/dist/core/middleware/built-in/jwt-helpers.d.ts +0 -118
  133. package/dist/core/middleware/built-in/jwt-helpers.js +0 -221
  134. package/dist/core/middleware/built-in/jwt-helpers.js.map +0 -1
  135. package/src/core/config/config-manager.ts +0 -133
  136. package/src/core/config/config-sources.ts +0 -596
  137. package/src/core/config/config-validator.ts +0 -1078
  138. package/src/core/middleware/built-in/jwt-helpers.ts +0 -240
package/dist/moro.js CHANGED
@@ -48,6 +48,8 @@ const logger_1 = require("./core/logger");
48
48
  const middleware_1 = require("./core/middleware");
49
49
  const app_integration_1 = require("./core/routing/app-integration");
50
50
  const docs_1 = require("./core/docs");
51
+ const fs_1 = require("fs");
52
+ const path_1 = require("path");
51
53
  const events_1 = require("events");
52
54
  // Configuration System Integration
53
55
  const config_1 = require("./core/config");
@@ -58,16 +60,11 @@ class Moro extends events_1.EventEmitter {
58
60
  routes = [];
59
61
  moduleCounter = 0;
60
62
  loadedModules = new Set();
61
- lazyModules = new Map();
62
63
  routeHandlers = {};
63
- moduleDiscovery; // Store for cleanup
64
- autoDiscoveryOptions = null;
65
- autoDiscoveryInitialized = false;
66
- autoDiscoveryPromise = null;
67
64
  // Enterprise event system integration
68
65
  eventBus;
69
66
  // Application logger
70
- logger;
67
+ logger = (0, logger_1.createFrameworkLogger)('App');
71
68
  // Intelligent routing system
72
69
  intelligentRouting = new app_integration_1.IntelligentRoutingManager();
73
70
  // Documentation system
@@ -81,37 +78,73 @@ class Moro extends events_1.EventEmitter {
81
78
  middlewareManager;
82
79
  constructor(options = {}) {
83
80
  super(); // Call EventEmitter constructor
84
- // Apply logging configuration BEFORE config loading to avoid DEBUG spam
85
- // 1. Environment variables (base level)
81
+ // Configure logger from environment variables BEFORE config system initialization
82
+ // This ensures the config loading process respects the log level
86
83
  const envLogLevel = process.env.LOG_LEVEL || process.env.MORO_LOG_LEVEL;
87
84
  if (envLogLevel) {
88
85
  (0, logger_1.applyLoggingConfiguration)({ level: envLogLevel }, undefined);
89
86
  }
90
- // 2. createApp logger options (highest precedence)
87
+ // Initialize configuration system - create a deep copy for this instance
88
+ this.config = JSON.parse(JSON.stringify((0, config_1.initializeConfig)()));
89
+ // Apply logging configuration from the loaded config (this happens after config file processing)
90
+ if (this.config.logging) {
91
+ (0, logger_1.applyLoggingConfiguration)(this.config.logging, undefined);
92
+ }
93
+ // Apply additional logging configuration from createApp options (takes precedence)
91
94
  if (options.logger !== undefined) {
92
95
  (0, logger_1.applyLoggingConfiguration)(undefined, options.logger);
93
96
  }
94
- // Create logger AFTER initial configuration
95
- this.logger = (0, logger_1.createFrameworkLogger)('App');
96
- // Use simplified global configuration system
97
- this.config = (0, config_1.initializeConfig)(options);
98
- // Apply config file logging if it exists (may override createApp options if needed)
99
- if (this.config.logging && !options.logger) {
100
- (0, logger_1.applyLoggingConfiguration)(this.config.logging, undefined);
101
- // Recreate logger with updated config
102
- this.logger = (0, logger_1.createFrameworkLogger)('App');
97
+ // Apply performance configuration from createApp options (takes precedence)
98
+ if (options.performance) {
99
+ if (options.performance.clustering) {
100
+ this.config.performance.clustering = {
101
+ ...this.config.performance.clustering,
102
+ ...options.performance.clustering,
103
+ };
104
+ }
105
+ if (options.performance.compression) {
106
+ this.config.performance.compression = {
107
+ ...this.config.performance.compression,
108
+ ...options.performance.compression,
109
+ };
110
+ }
111
+ if (options.performance.circuitBreaker) {
112
+ this.config.performance.circuitBreaker = {
113
+ ...this.config.performance.circuitBreaker,
114
+ ...options.performance.circuitBreaker,
115
+ };
116
+ }
117
+ }
118
+ // Apply modules configuration from createApp options (takes precedence)
119
+ if (options.modules) {
120
+ if (options.modules.cache) {
121
+ this.config.modules.cache = {
122
+ ...this.config.modules.cache,
123
+ ...options.modules.cache,
124
+ };
125
+ }
126
+ if (options.modules.rateLimit) {
127
+ this.config.modules.rateLimit = {
128
+ ...this.config.modules.rateLimit,
129
+ ...options.modules.rateLimit,
130
+ };
131
+ }
132
+ if (options.modules.validation) {
133
+ this.config.modules.validation = {
134
+ ...this.config.modules.validation,
135
+ ...options.modules.validation,
136
+ };
137
+ }
103
138
  }
104
- this.logger.info(`Configuration system initialized: ${process.env.NODE_ENV || 'development'}:${this.config.server.port}`);
139
+ this.logger.info(`Configuration system initialized: ${this.config.server.environment}:${this.config.server.port}`);
105
140
  // Initialize runtime system
106
141
  this.runtimeType = options.runtime?.type || 'node';
107
142
  this.runtimeAdapter = options.runtime?.adapter || (0, runtime_1.createRuntimeAdapter)(this.runtimeType);
108
143
  this.logger.info(`Runtime system initialized: ${this.runtimeType}`, 'Runtime');
109
- // Pass configuration from config to framework
144
+ // Pass logging configuration from config to framework
110
145
  const frameworkOptions = {
111
146
  ...options,
112
147
  logger: this.config.logging,
113
- websocket: this.config.websocket.enabled ? options.websocket || {} : false,
114
- config: this.config,
115
148
  };
116
149
  this.coreFramework = new framework_1.Moro(frameworkOptions);
117
150
  // Initialize middleware system
@@ -136,10 +169,10 @@ class Moro extends events_1.EventEmitter {
136
169
  ...this.getDefaultOptionsFromConfig(),
137
170
  ...options,
138
171
  });
139
- // Store auto-discovery options for later initialization
140
- // IMPORTANT: Auto-discovery is deferred to ensure user middleware (like auth)
141
- // is registered before module middleware that might bypass it
142
- this.autoDiscoveryOptions = options.autoDiscover !== false ? options : null;
172
+ // Auto-discover modules if enabled
173
+ if (options.autoDiscover !== false) {
174
+ this.autoDiscoverModules(options.modulesPath || './modules');
175
+ }
143
176
  // Emit initialization event through enterprise event bus
144
177
  this.eventBus.emit('framework:initialized', {
145
178
  options,
@@ -305,11 +338,6 @@ class Moro extends events_1.EventEmitter {
305
338
  version: moduleOrPath.version || '1.0.0',
306
339
  });
307
340
  }
308
- // IMPORTANT: If modules are loaded manually after auto-discovery,
309
- // ensure the final module handler is set up to maintain middleware order
310
- if (this.autoDiscoveryInitialized) {
311
- this.coreFramework.setupFinalModuleHandler();
312
- }
313
341
  return this;
314
342
  }
315
343
  // Database helper with events
@@ -426,134 +454,32 @@ class Moro extends events_1.EventEmitter {
426
454
  if (this.routes.length > 0) {
427
455
  this.registerDirectRoutes();
428
456
  }
429
- const startServer = () => {
430
- const actualCallback = () => {
431
- const displayHost = host || 'localhost';
432
- this.logger.info('Moro Server Started', 'Server');
433
- this.logger.info(`Runtime: ${this.runtimeType}`, 'Server');
434
- this.logger.info(`HTTP API: http://${displayHost}:${port}`, 'Server');
435
- if (this.config.websocket.enabled) {
436
- this.logger.info(`WebSocket: ws://${displayHost}:${port}`, 'Server');
437
- }
438
- this.logger.info('Learn more at https://morojs.com', 'Server');
439
- // Log intelligent routes info
440
- const intelligentRoutes = this.intelligentRouting.getIntelligentRoutes();
441
- if (intelligentRoutes.length > 0) {
442
- this.logger.info(`Intelligent Routes: ${intelligentRoutes.length} registered`, 'Server');
443
- }
444
- this.eventBus.emit('server:started', { port, runtime: this.runtimeType });
445
- if (callback)
446
- callback();
447
- };
448
- if (host && typeof host === 'string') {
449
- this.coreFramework.listen(port, host, actualCallback);
450
- }
451
- else {
452
- this.coreFramework.listen(port, actualCallback);
457
+ const actualCallback = () => {
458
+ const displayHost = host || 'localhost';
459
+ this.logger.info('Moro Server Started', 'Server');
460
+ this.logger.info(`Runtime: ${this.runtimeType}`, 'Server');
461
+ this.logger.info(`HTTP API: http://${displayHost}:${port}`, 'Server');
462
+ this.logger.info(`WebSocket: ws://${displayHost}:${port}`, 'Server');
463
+ this.logger.info('Native Node.js HTTP • Zero Dependencies • Maximum Performance', 'Server');
464
+ this.logger.info('Learn more at https://morojs.com', 'Server');
465
+ // Log intelligent routes info
466
+ const intelligentRoutes = this.intelligentRouting.getIntelligentRoutes();
467
+ if (intelligentRoutes.length > 0) {
468
+ this.logger.info(`Intelligent Routes: ${intelligentRoutes.length} registered`, 'Server');
453
469
  }
470
+ this.eventBus.emit('server:started', { port, runtime: this.runtimeType });
471
+ if (callback)
472
+ callback();
454
473
  };
455
- // Ensure auto-discovery is complete before starting server
456
- this.ensureAutoDiscoveryComplete()
457
- .then(() => {
458
- startServer();
459
- })
460
- .catch(error => {
461
- this.logger.error('Auto-discovery initialization failed during server start', 'Framework', {
462
- error: error instanceof Error ? error.message : String(error),
463
- });
464
- // Start server anyway - auto-discovery failure shouldn't prevent startup
465
- startServer();
466
- });
467
- }
468
- // Public method to manually initialize auto-discovery
469
- // Useful for ensuring auth middleware is registered before auto-discovery
470
- async initializeAutoDiscoveryNow() {
471
- return this.ensureAutoDiscoveryComplete();
472
- }
473
- // Public API: Initialize modules explicitly after middleware setup
474
- // This provides users with explicit control over module loading timing
475
- // IMPORTANT: This forces module loading even if autoDiscovery.enabled is false
476
- // Usage: app.initModules() or app.initModules({ paths: ['./my-modules'] })
477
- initModules(options) {
478
- this.logger.info('User-requested module initialization', 'ModuleSystem');
479
- // If already initialized, do nothing
480
- if (this.autoDiscoveryInitialized) {
481
- this.logger.debug('Auto-discovery already completed, skipping', 'ModuleSystem');
482
- return;
483
- }
484
- // Store the options and mark that we want to force initialization
485
- this.autoDiscoveryOptions = {
486
- autoDiscover: {
487
- enabled: true, // Force enabled regardless of original config
488
- paths: options?.paths || ['./modules', './src/modules'],
489
- patterns: options?.patterns || [
490
- '**/*.module.{ts,js}',
491
- '**/index.{ts,js}',
492
- '**/*.config.{ts,js}',
493
- ],
494
- recursive: options?.recursive ?? true,
495
- loadingStrategy: options?.loadingStrategy || 'eager',
496
- watchForChanges: options?.watchForChanges ?? false,
497
- ignorePatterns: options?.ignorePatterns || [
498
- '**/*.test.{ts,js}',
499
- '**/*.spec.{ts,js}',
500
- '**/node_modules/**',
501
- ],
502
- loadOrder: options?.loadOrder || 'dependency',
503
- failOnError: options?.failOnError ?? false,
504
- maxDepth: options?.maxDepth ?? 5,
505
- },
506
- };
507
- this.logger.debug('Module initialization options stored, will execute on next listen/getHandler call', 'ModuleSystem');
508
- }
509
- // Robust method to ensure auto-discovery is complete, handling race conditions
510
- async ensureAutoDiscoveryComplete() {
511
- // If already initialized, nothing to do
512
- if (this.autoDiscoveryInitialized) {
513
- return;
514
- }
515
- // If auto-discovery is disabled, mark as initialized
516
- if (!this.autoDiscoveryOptions) {
517
- this.autoDiscoveryInitialized = true;
518
- return;
519
- }
520
- // If already in progress, wait for it to complete
521
- if (this.autoDiscoveryPromise) {
522
- return this.autoDiscoveryPromise;
523
- }
524
- // Start auto-discovery
525
- this.autoDiscoveryPromise = this.performAutoDiscovery();
526
- try {
527
- await this.autoDiscoveryPromise;
528
- this.autoDiscoveryInitialized = true;
474
+ if (host && typeof host === 'string') {
475
+ this.coreFramework.listen(port, host, actualCallback);
529
476
  }
530
- catch (error) {
531
- // Reset promise on error so it can be retried
532
- this.autoDiscoveryPromise = null;
533
- throw error;
534
- }
535
- finally {
536
- this.autoDiscoveryOptions = null; // Clear after attempt
477
+ else {
478
+ this.coreFramework.listen(port, actualCallback);
537
479
  }
538
480
  }
539
- // Perform the actual auto-discovery work
540
- async performAutoDiscovery(optionsOverride) {
541
- const optionsToUse = optionsOverride || this.autoDiscoveryOptions;
542
- if (!optionsToUse)
543
- return;
544
- this.logger.debug('Starting auto-discovery initialization', 'AutoDiscovery');
545
- await this.initializeAutoDiscovery(optionsToUse);
546
- this.logger.debug('Auto-discovery initialization completed', 'AutoDiscovery');
547
- }
548
481
  // Get handler for non-Node.js runtimes
549
482
  getHandler() {
550
- // Ensure auto-discovery is complete for non-Node.js runtimes
551
- // This handles the case where users call getHandler() immediately after createApp()
552
- this.ensureAutoDiscoveryComplete().catch(error => {
553
- this.logger.error('Auto-discovery initialization failed for runtime handler', 'Framework', {
554
- error: error instanceof Error ? error.message : String(error),
555
- });
556
- });
557
483
  // Create a unified request handler that works with the runtime adapter
558
484
  const handler = async (req, res) => {
559
485
  // Add documentation middleware first (if enabled)
@@ -841,268 +767,91 @@ class Moro extends events_1.EventEmitter {
841
767
  return true;
842
768
  }
843
769
  setupDefaultMiddleware(options) {
844
- // CORS - check config enabled property OR options.security.cors.enabled === true
845
- if (this.config.security.cors.enabled || options.security?.cors?.enabled === true) {
846
- const corsOptions = typeof options.cors === 'object'
847
- ? options.cors
848
- : this.config.security.cors
849
- ? this.config.security.cors
850
- : {};
770
+ // CORS
771
+ if (options.cors !== false) {
772
+ const corsOptions = typeof options.cors === 'object' ? options.cors : {};
851
773
  this.use(http_1.middleware.cors(corsOptions));
852
774
  }
853
- // Helmet - check config enabled property OR options.security.helmet.enabled === true
854
- if (this.config.security.helmet.enabled || options.security?.helmet?.enabled === true) {
775
+ // Helmet
776
+ if (options.helmet !== false) {
855
777
  this.use(http_1.middleware.helmet());
856
778
  }
857
- // Compression - check config enabled property OR options.performance.compression.enabled === true
858
- if (this.config.performance.compression.enabled ||
859
- options.performance?.compression?.enabled === true) {
860
- const compressionOptions = typeof options.compression === 'object'
861
- ? options.compression
862
- : this.config.performance.compression
863
- ? this.config.performance.compression
864
- : {};
779
+ // Compression
780
+ if (options.compression !== false) {
781
+ const compressionOptions = typeof options.compression === 'object' ? options.compression : {};
865
782
  this.use(http_1.middleware.compression(compressionOptions));
866
783
  }
867
784
  // Body size limiting
868
785
  this.use(http_1.middleware.bodySize({ limit: '10mb' }));
869
786
  }
870
- // Enhanced auto-discovery initialization
871
- async initializeAutoDiscovery(options) {
872
- const { ModuleDiscovery } = await Promise.resolve().then(() => __importStar(require('./core/modules/auto-discovery')));
873
- // Merge auto-discovery configuration
874
- const autoDiscoveryConfig = this.mergeAutoDiscoveryConfig(options);
875
- if (!autoDiscoveryConfig.enabled) {
876
- return;
877
- }
878
- this.moduleDiscovery = new ModuleDiscovery(process.cwd());
787
+ autoDiscoverModules(modulesPath) {
879
788
  try {
880
- // Discover modules based on configuration
881
- const modules = await this.moduleDiscovery.discoverModulesAdvanced(autoDiscoveryConfig);
882
- // Load modules based on strategy
883
- await this.loadDiscoveredModules(modules, autoDiscoveryConfig);
884
- // Setup final module handler to run after user middleware (like auth)
885
- this.coreFramework.setupFinalModuleHandler();
886
- // Setup file watching if enabled
887
- if (autoDiscoveryConfig.watchForChanges) {
888
- this.moduleDiscovery.watchModulesAdvanced(autoDiscoveryConfig, async (updatedModules) => {
889
- await this.handleModuleChanges(updatedModules);
890
- });
891
- }
892
- this.logger.info(`Auto-discovery completed: ${modules.length} modules loaded`, 'ModuleDiscovery');
893
- }
894
- catch (error) {
895
- const errorMsg = error instanceof Error ? error.message : String(error);
896
- if (autoDiscoveryConfig.failOnError) {
897
- throw new Error(`Module auto-discovery failed: ${errorMsg}`);
898
- }
899
- else {
900
- this.logger.warn(`Module auto-discovery failed: ${errorMsg}`, 'ModuleDiscovery');
901
- }
902
- }
903
- }
904
- // Merge auto-discovery configuration from multiple sources
905
- mergeAutoDiscoveryConfig(options) {
906
- const defaultConfig = this.config.modules.autoDiscovery;
907
- // Handle legacy modulesPath option
908
- if (options.modulesPath && !options.autoDiscover) {
909
- return {
910
- ...defaultConfig,
911
- paths: [options.modulesPath],
912
- };
913
- }
914
- // Handle boolean autoDiscover option
915
- if (typeof options.autoDiscover === 'boolean') {
916
- return {
917
- ...defaultConfig,
918
- enabled: options.autoDiscover,
919
- };
920
- }
921
- // Handle object autoDiscover option
922
- if (typeof options.autoDiscover === 'object') {
923
- return {
924
- ...defaultConfig,
925
- ...options.autoDiscover,
926
- };
927
- }
928
- return defaultConfig;
929
- }
930
- // Load discovered modules based on strategy
931
- async loadDiscoveredModules(modules, config) {
932
- switch (config.loadingStrategy) {
933
- case 'eager':
934
- // Load all modules immediately
935
- for (const module of modules) {
936
- await this.loadModule(module);
937
- }
938
- break;
939
- case 'lazy':
940
- // Register modules for lazy loading
941
- this.registerLazyModules(modules);
942
- break;
943
- case 'conditional':
944
- // Load modules based on conditions
945
- await this.loadConditionalModules(modules);
946
- break;
947
- default:
948
- // Default to eager loading
949
- for (const module of modules) {
950
- await this.loadModule(module);
951
- }
952
- }
953
- }
954
- // Register modules for lazy loading
955
- registerLazyModules(modules) {
956
- modules.forEach(module => {
957
- // Store module for lazy loading when first route is accessed
958
- this.lazyModules.set(module.name, module);
959
- // Register placeholder routes that trigger lazy loading
960
- if (module.routes) {
961
- module.routes.forEach(route => {
962
- const basePath = `/api/v${module.version}/${module.name}`;
963
- const fullPath = `${basePath}${route.path}`;
964
- // Note: Lazy loading will be implemented when route is accessed
965
- // For now, we'll store the module for later loading
966
- this.logger.debug(`Registered lazy route: ${route.method} ${fullPath}`, 'ModuleDiscovery');
967
- });
968
- }
969
- });
970
- this.logger.info(`Registered ${modules.length} modules for lazy loading`, 'ModuleDiscovery');
971
- }
972
- // Load modules conditionally based on environment or configuration
973
- async loadConditionalModules(modules) {
974
- for (const module of modules) {
975
- const shouldLoad = this.shouldLoadModule(module);
976
- if (shouldLoad) {
977
- await this.loadModule(module);
978
- }
979
- else {
980
- this.logger.debug(`Skipping module ${module.name} due to conditions`, 'ModuleDiscovery');
981
- }
982
- }
983
- }
984
- // Determine if a module should be loaded based on conditions
985
- shouldLoadModule(module) {
986
- const moduleConfig = module.config;
987
- // Check environment conditions
988
- if (moduleConfig?.conditions?.environment) {
989
- const requiredEnv = moduleConfig.conditions.environment;
990
- const currentEnv = process.env.NODE_ENV || 'development';
991
- if (Array.isArray(requiredEnv)) {
992
- if (!requiredEnv.includes(currentEnv)) {
993
- return false;
994
- }
995
- }
996
- else if (requiredEnv !== currentEnv) {
997
- return false;
998
- }
999
- }
1000
- // Check feature flags
1001
- if (moduleConfig?.conditions?.features) {
1002
- const requiredFeatures = moduleConfig.conditions.features;
1003
- for (const feature of requiredFeatures) {
1004
- if (!process.env[`FEATURE_${feature.toUpperCase()}`]) {
1005
- return false;
789
+ if (!(0, fs_1.statSync)(modulesPath).isDirectory())
790
+ return;
791
+ const items = (0, fs_1.readdirSync)(modulesPath);
792
+ items.forEach(item => {
793
+ const fullPath = (0, path_1.join)(modulesPath, item);
794
+ if ((0, fs_1.statSync)(fullPath).isDirectory()) {
795
+ const indexPath = (0, path_1.join)(fullPath, 'index.ts');
796
+ try {
797
+ (0, fs_1.statSync)(indexPath);
798
+ // Module directory found, will be loaded later
799
+ this.logger.debug(`Discovered module: ${item}`, 'ModuleDiscovery');
800
+ }
801
+ catch {
802
+ // No index.ts, skip
803
+ }
1006
804
  }
1007
- }
805
+ });
1008
806
  }
1009
- // Check custom conditions
1010
- if (moduleConfig?.conditions?.custom) {
1011
- const customCondition = moduleConfig.conditions.custom;
1012
- if (typeof customCondition === 'function') {
1013
- return customCondition();
1014
- }
807
+ catch {
808
+ // Modules directory doesn't exist, that's fine
1015
809
  }
1016
- return true;
1017
- }
1018
- // Handle module changes during development
1019
- async handleModuleChanges(modules) {
1020
- this.logger.info('Module changes detected, reloading...', 'ModuleDiscovery');
1021
- // Unload existing modules (if supported)
1022
- // For now, just log the change
1023
- this.eventBus.emit('modules:changed', {
1024
- modules: modules.map(m => ({ name: m.name, version: m.version })),
1025
- timestamp: new Date(),
1026
- });
1027
- }
1028
- // Legacy method for backward compatibility
1029
- autoDiscoverModules(modulesPath) {
1030
- // Redirect to new system
1031
- this.initializeAutoDiscovery({
1032
- autoDiscover: {
1033
- enabled: true,
1034
- paths: [modulesPath],
1035
- },
1036
- });
1037
810
  }
1038
811
  async importModule(modulePath) {
1039
812
  const module = await Promise.resolve(`${modulePath}`).then(s => __importStar(require(s)));
1040
813
  return module.default || module;
1041
814
  }
1042
- /**
1043
- * Node.js Clustering Implementation
1044
- * This clustering algorithm is based on published research and Node.js best practices.
1045
- *
1046
- * IPC (Inter-Process Communication) Considerations:
1047
- * - Excessive workers create IPC bottlenecks (Source: BetterStack Node.js Guide)
1048
- * - Round-robin scheduling provides better load distribution (Node.js Documentation)
1049
- * - Message passing overhead increases significantly with worker count
1050
- *
1051
- * Memory Management:
1052
- * - ~2GB per worker prevents memory pressure and GC overhead
1053
- * - Conservative heap limits reduce memory fragmentation
1054
- *
1055
- * References:
1056
- * - Node.js Cluster Documentation: https://nodejs.org/api/cluster.html
1057
- * - BetterStack Node.js Clustering: https://betterstack.com/community/guides/scaling-nodejs/node-clustering/
1058
- */
815
+ // Clustering support for massive performance gains with proper cleanup
1059
816
  clusterWorkers = new Map();
1060
817
  startWithClustering(port, host, callback) {
1061
818
  const cluster = require('cluster');
1062
819
  const os = require('os');
1063
- // Worker count calculation - respect user choice
820
+ // Smart worker count calculation based on actual bottlenecks
1064
821
  let workerCount = this.config.performance?.clustering?.workers || os.cpus().length;
1065
- // Only auto-optimize if user hasn't specified a number or set it to 'auto'
1066
- if (workerCount === 'auto') {
822
+ // Auto-optimize worker count based on system characteristics
823
+ if (workerCount === 'auto' || workerCount > 8) {
824
+ // For high-core machines, limit workers to prevent IPC/memory bottlenecks
1067
825
  const cpuCount = os.cpus().length;
1068
826
  const totalMemoryGB = os.totalmem() / (1024 * 1024 * 1024);
1069
- // Get memory per worker from config - if not set by user, calculate dynamically
1070
- let memoryPerWorkerGB = this.config.performance?.clustering?.memoryPerWorkerGB;
1071
- if (!memoryPerWorkerGB) {
1072
- // Dynamic calculation: (Total RAM - 4GB headroom) / CPU cores
1073
- const headroomGB = 4;
1074
- memoryPerWorkerGB = Math.max(0.5, Math.floor((totalMemoryGB - headroomGB) / cpuCount));
827
+ // Optimal worker count formula based on research
828
+ if (cpuCount >= 16) {
829
+ // High-core machines: focus on memory/IPC efficiency
830
+ workerCount = Math.min(Math.ceil(totalMemoryGB / 2), 4); // 2GB per worker max, cap at 4
1075
831
  }
1076
- // Conservative formula based on general guidelines:
1077
- // - Don't exceed CPU cores
1078
- // - Respect user's memory allocation preference
1079
- // - Let the system resources determine the limit
1080
- workerCount = Math.min(cpuCount, // Don't exceed CPU cores
1081
- Math.floor(totalMemoryGB / memoryPerWorkerGB) // User-configurable memory per worker
1082
- );
1083
- this.logger.info(`Auto-calculated worker count: ${workerCount} (CPU: ${cpuCount}, RAM: ${totalMemoryGB.toFixed(1)}GB, ${memoryPerWorkerGB}GB per worker)`, 'Cluster');
1084
- }
1085
- else if (typeof workerCount === 'number') {
1086
- // User specified a number - respect their choice
1087
- this.logger.info(`Using user-specified worker count: ${workerCount}`, 'Cluster');
832
+ else if (cpuCount >= 8) {
833
+ // Mid-range machines: balanced approach
834
+ workerCount = Math.min(cpuCount / 2, 4);
835
+ }
836
+ else {
837
+ // Low-core machines: use all cores
838
+ workerCount = cpuCount;
839
+ }
840
+ this.logger.info(`Auto-optimized workers: ${workerCount} (CPU: ${cpuCount}, RAM: ${totalMemoryGB.toFixed(1)}GB)`, 'Cluster');
1088
841
  }
1089
842
  if (cluster.isPrimary) {
1090
- this.logger.info(`Starting ${workerCount} workers`, 'Cluster');
843
+ this.logger.info(`🚀 Starting ${workerCount} workers for maximum performance`, 'Cluster');
1091
844
  // Optimize cluster scheduling for high concurrency
1092
- // Round-robin is the default on all platforms except Windows (Node.js docs)
1093
- // Provides better load distribution than shared socket approach
1094
- cluster.schedulingPolicy = cluster.SCHED_RR;
845
+ cluster.schedulingPolicy = cluster.SCHED_RR; // Round-robin scheduling
1095
846
  // Set cluster settings for better performance
1096
847
  cluster.setupMaster({
1097
- exec: process.argv[1] || process.execPath,
848
+ exec: process.argv[1],
1098
849
  args: process.argv.slice(2),
1099
850
  silent: false,
1100
851
  });
1101
- // IPC Optimization: Reduce communication overhead between master and workers
1102
- // Research shows excessive IPC can create bottlenecks in clustered applications
1103
- // (Source: BetterStack - Node.js Clustering Guide)
852
+ // Optimize IPC to reduce communication overhead
1104
853
  process.env.NODE_CLUSTER_SCHED_POLICY = 'rr'; // Ensure round-robin
1105
- process.env.NODE_DISABLE_COLORS = '1'; // Reduce IPC message size by disabling color codes
854
+ process.env.NODE_DISABLE_COLORS = '1'; // Reduce IPC message size
1106
855
  // Graceful shutdown handler
1107
856
  const gracefulShutdown = () => {
1108
857
  this.logger.info('Gracefully shutting down cluster...', 'Cluster');
@@ -1118,25 +867,27 @@ class Moro extends events_1.EventEmitter {
1118
867
  // Handle process signals for graceful shutdown
1119
868
  process.on('SIGINT', gracefulShutdown);
1120
869
  process.on('SIGTERM', gracefulShutdown);
1121
- // Fork workers with basic tracking
870
+ // Fork workers with proper tracking and CPU affinity
1122
871
  for (let i = 0; i < workerCount; i++) {
1123
- const worker = cluster.fork();
872
+ const worker = cluster.fork({
873
+ WORKER_ID: i,
874
+ WORKER_CPU_AFFINITY: i % os.cpus().length, // Distribute workers across CPUs
875
+ });
1124
876
  this.clusterWorkers.set(worker.process.pid, worker);
1125
- this.logger.info(`Worker ${worker.process.pid} started`, 'Cluster');
1126
- // Handle individual worker messages
877
+ this.logger.info(`Worker ${worker.process.pid} started (CPU ${i % os.cpus().length})`, 'Cluster');
878
+ // Handle individual worker messages (reuse handler)
1127
879
  worker.on('message', this.handleWorkerMessage.bind(this));
1128
880
  }
1129
- // Simple worker exit handling
881
+ // Handle worker exits with cleanup
1130
882
  cluster.on('exit', (worker, code, signal) => {
1131
- const pid = worker.process.pid;
1132
- this.clusterWorkers.delete(pid);
1133
- if (code !== 0 && !worker.exitedAfterDisconnect) {
1134
- this.logger.warn(`Worker ${pid} died unexpectedly (${signal || code}). Restarting...`, 'Cluster');
1135
- // Simple restart
1136
- const newWorker = cluster.fork();
1137
- this.clusterWorkers.set(newWorker.process.pid, newWorker);
1138
- this.logger.info(`Worker ${newWorker.process.pid} restarted`, 'Cluster');
1139
- }
883
+ // Clean up worker tracking
884
+ this.clusterWorkers.delete(worker.process.pid);
885
+ this.logger.warn(`Worker ${worker.process.pid} died (${signal || code}). Restarting...`, 'Cluster');
886
+ // Restart worker with proper tracking
887
+ const newWorker = cluster.fork();
888
+ this.clusterWorkers.set(newWorker.process.pid, newWorker);
889
+ newWorker.on('message', this.handleWorkerMessage.bind(this));
890
+ this.logger.info(`Worker ${newWorker.process.pid} started`, 'Cluster');
1140
891
  });
1141
892
  // Master process callback
1142
893
  if (callback)
@@ -1148,21 +899,12 @@ class Moro extends events_1.EventEmitter {
1148
899
  // Worker-specific optimizations for high concurrency
1149
900
  process.env.UV_THREADPOOL_SIZE = '64';
1150
901
  // Reduce logging contention in workers (major bottleneck)
1151
- // Multiple workers writing to same log files creates I/O contention
1152
902
  if (this.config.logging) {
1153
903
  // Workers log less frequently to reduce I/O contention
1154
- (0, logger_1.applyLoggingConfiguration)(undefined, { level: 'warn' }); // Only warnings and errors
904
+ this.config.logging.level = 'warn'; // Only warnings and errors
1155
905
  }
1156
- // Research-based memory optimization for workers
1157
- const os = require('os');
1158
- const totalMemoryGB = os.totalmem() / (1024 * 1024 * 1024);
1159
- const workerCount = Object.keys(require('cluster').workers || {}).length || 1;
1160
- // Conservative memory allocation
1161
- const heapSizePerWorkerMB = Math.min(Math.floor(((totalMemoryGB * 1024) / workerCount) * 0.8), // 80% of available memory
1162
- 1536 // Cap at 1.5GB (GC efficiency threshold from research)
1163
- );
1164
- process.env.NODE_OPTIONS = `--max-old-space-size=${heapSizePerWorkerMB}`;
1165
- this.logger.debug(`Worker memory allocated: ${heapSizePerWorkerMB}MB heap (${workerCount} workers, ${totalMemoryGB.toFixed(1)}GB total)`, 'Worker');
906
+ // Memory optimization for workers
907
+ process.env.NODE_OPTIONS = '--max-old-space-size=1024'; // Limit memory per worker
1166
908
  // Optimize V8 flags for better performance (Rust-level optimizations)
1167
909
  if (process.env.NODE_ENV === 'production') {
1168
910
  // Ultra-aggressive V8 optimizations for maximum performance
@@ -1246,7 +988,7 @@ class Moro extends events_1.EventEmitter {
1246
988
  }
1247
989
  }
1248
990
  }
1249
- // Simple worker message handler
991
+ // Reusable worker message handler (avoids creating new functions)
1250
992
  handleWorkerMessage(message) {
1251
993
  // Handle inter-worker communication if needed
1252
994
  if (message.type === 'health-check') {
@@ -1256,56 +998,6 @@ class Moro extends events_1.EventEmitter {
1256
998
  // Log other worker messages
1257
999
  this.logger.debug(`Worker message: ${JSON.stringify(message)}`, 'Cluster');
1258
1000
  }
1259
- /**
1260
- * Gracefully close the application and clean up resources
1261
- * This should be called in tests and during shutdown
1262
- */
1263
- async close() {
1264
- this.logger.debug('Closing Moro application...');
1265
- // Flush logger buffer before shutdown
1266
- try {
1267
- // Use flushBuffer for immediate synchronous flush
1268
- this.logger.flushBuffer();
1269
- }
1270
- catch (error) {
1271
- // Ignore flush errors during shutdown
1272
- }
1273
- // Close the core framework with timeout
1274
- if (this.coreFramework && this.coreFramework.httpServer) {
1275
- try {
1276
- await Promise.race([
1277
- new Promise(resolve => {
1278
- this.coreFramework.httpServer.close(() => {
1279
- resolve();
1280
- });
1281
- }),
1282
- new Promise(resolve => setTimeout(resolve, 2000)), // 2 second timeout
1283
- ]);
1284
- }
1285
- catch (error) {
1286
- // Force close if graceful close fails
1287
- this.logger.warn('Force closing HTTP server due to timeout');
1288
- }
1289
- }
1290
- // Clean up module discovery watchers
1291
- if (this.moduleDiscovery && typeof this.moduleDiscovery.cleanup === 'function') {
1292
- try {
1293
- this.moduleDiscovery.cleanup();
1294
- }
1295
- catch (error) {
1296
- // Ignore cleanup errors
1297
- }
1298
- }
1299
- // Clean up event listeners
1300
- try {
1301
- this.eventBus.removeAllListeners();
1302
- this.removeAllListeners();
1303
- }
1304
- catch (error) {
1305
- // Ignore cleanup errors
1306
- }
1307
- this.logger.debug('Moro application closed successfully');
1308
- }
1309
1001
  }
1310
1002
  exports.Moro = Moro;
1311
1003
  // Export convenience function