@morojs/moro 1.2.0 → 1.3.0
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.
- package/LICENSE +2 -2
- package/dist/core/config/file-loader.js +31 -25
- package/dist/core/config/file-loader.js.map +1 -1
- package/dist/core/config/schema.d.ts +2 -2
- package/dist/core/config/schema.js +1 -1
- package/dist/core/config/schema.js.map +1 -1
- package/dist/core/events/event-bus.js +4 -0
- package/dist/core/events/event-bus.js.map +1 -1
- package/dist/core/http/http-server.d.ts +33 -0
- package/dist/core/http/http-server.js +329 -28
- package/dist/core/http/http-server.js.map +1 -1
- package/dist/core/logger/logger.d.ts +1 -0
- package/dist/core/logger/logger.js +5 -2
- package/dist/core/logger/logger.js.map +1 -1
- package/dist/core/utilities/container.d.ts +1 -0
- package/dist/core/utilities/container.js +11 -1
- package/dist/core/utilities/container.js.map +1 -1
- package/dist/moro.d.ts +8 -0
- package/dist/moro.js +335 -12
- package/dist/moro.js.map +1 -1
- package/dist/types/core.d.ts +17 -0
- package/package.json +14 -10
- package/src/core/config/file-loader.ts +34 -25
- package/src/core/config/schema.ts +1 -1
- package/src/core/events/event-bus.ts +5 -0
- package/src/core/http/http-server.ts +377 -28
- package/src/core/logger/logger.ts +8 -2
- package/src/core/utilities/container.ts +14 -1
- package/src/moro.ts +399 -13
- package/src/types/core.ts +18 -0
package/src/moro.ts
CHANGED
|
@@ -63,11 +63,38 @@ export class Moro extends EventEmitter {
|
|
|
63
63
|
// Initialize configuration system
|
|
64
64
|
this.config = initializeConfig();
|
|
65
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
|
+
|
|
66
71
|
// Apply additional logging configuration from createApp options (takes precedence)
|
|
67
72
|
if (options.logger !== undefined) {
|
|
68
73
|
applyLoggingConfiguration(undefined, options.logger);
|
|
69
74
|
}
|
|
70
75
|
|
|
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
|
+
}
|
|
97
|
+
|
|
71
98
|
this.logger.info(
|
|
72
99
|
`Configuration system initialized: ${this.config.server.environment}:${this.config.server.port}`
|
|
73
100
|
);
|
|
@@ -78,7 +105,13 @@ export class Moro extends EventEmitter {
|
|
|
78
105
|
|
|
79
106
|
this.logger.info(`Runtime system initialized: ${this.runtimeType}`, 'Runtime');
|
|
80
107
|
|
|
81
|
-
|
|
108
|
+
// Pass logging configuration from config to framework
|
|
109
|
+
const frameworkOptions: any = {
|
|
110
|
+
...options,
|
|
111
|
+
logger: this.config.logging,
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
this.coreFramework = new MoroCore(frameworkOptions);
|
|
82
115
|
|
|
83
116
|
// Initialize middleware system
|
|
84
117
|
this.middlewareManager = new MiddlewareManager();
|
|
@@ -89,6 +122,15 @@ export class Moro extends EventEmitter {
|
|
|
89
122
|
httpServer.setHookManager((this.middlewareManager as any).hooks);
|
|
90
123
|
}
|
|
91
124
|
|
|
125
|
+
// Configure HTTP server performance based on config
|
|
126
|
+
if (httpServer && httpServer.configurePerformance) {
|
|
127
|
+
const performanceConfig = this.config.performance;
|
|
128
|
+
httpServer.configurePerformance({
|
|
129
|
+
compression: performanceConfig?.compression || { enabled: true },
|
|
130
|
+
minimal: performanceConfig?.compression?.enabled === false, // Enable minimal mode if compression disabled
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
|
|
92
134
|
// Access enterprise event bus from core framework
|
|
93
135
|
this.eventBus = (this.coreFramework as any).eventBus;
|
|
94
136
|
|
|
@@ -378,9 +420,14 @@ export class Moro extends EventEmitter {
|
|
|
378
420
|
}
|
|
379
421
|
|
|
380
422
|
// Start server with events (Node.js only)
|
|
423
|
+
listen(callback?: () => void): void;
|
|
381
424
|
listen(port: number, callback?: () => void): void;
|
|
382
425
|
listen(port: number, host: string, callback?: () => void): void;
|
|
383
|
-
listen(
|
|
426
|
+
listen(
|
|
427
|
+
portOrCallback?: number | (() => void),
|
|
428
|
+
hostOrCallback?: string | (() => void),
|
|
429
|
+
callback?: () => void
|
|
430
|
+
) {
|
|
384
431
|
// Only available for Node.js runtime
|
|
385
432
|
if (this.runtimeType !== 'node') {
|
|
386
433
|
throw new Error(
|
|
@@ -388,10 +435,46 @@ export class Moro extends EventEmitter {
|
|
|
388
435
|
);
|
|
389
436
|
}
|
|
390
437
|
|
|
391
|
-
// Handle overloaded parameters
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
438
|
+
// Handle overloaded parameters - supports:
|
|
439
|
+
// listen(callback)
|
|
440
|
+
// listen(port, callback)
|
|
441
|
+
// listen(port, host, callback)
|
|
442
|
+
let port: number;
|
|
443
|
+
let host: string | undefined;
|
|
444
|
+
|
|
445
|
+
if (typeof portOrCallback === 'function') {
|
|
446
|
+
// listen(callback) - use port from config
|
|
447
|
+
callback = portOrCallback;
|
|
448
|
+
port = this.config.server.port;
|
|
449
|
+
host = this.config.server.host;
|
|
450
|
+
} else if (typeof portOrCallback === 'number') {
|
|
451
|
+
// listen(port, ...) variants
|
|
452
|
+
port = portOrCallback;
|
|
453
|
+
if (typeof hostOrCallback === 'function') {
|
|
454
|
+
// listen(port, callback)
|
|
455
|
+
callback = hostOrCallback;
|
|
456
|
+
host = undefined;
|
|
457
|
+
} else {
|
|
458
|
+
// listen(port, host, callback)
|
|
459
|
+
host = hostOrCallback;
|
|
460
|
+
}
|
|
461
|
+
} else {
|
|
462
|
+
// listen() - use config defaults
|
|
463
|
+
port = this.config.server.port;
|
|
464
|
+
host = this.config.server.host;
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
// Validate that we have a valid port
|
|
468
|
+
if (!port || typeof port !== 'number') {
|
|
469
|
+
throw new Error(
|
|
470
|
+
'Port not specified and not found in configuration. Please provide a port number or configure it in moro.config.js/ts'
|
|
471
|
+
);
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
// Check if clustering is enabled for massive performance gains
|
|
475
|
+
if (this.config.performance?.clustering?.enabled) {
|
|
476
|
+
this.startWithClustering(port, host as string, callback);
|
|
477
|
+
return;
|
|
395
478
|
}
|
|
396
479
|
this.eventBus.emit('server:starting', { port, runtime: this.runtimeType });
|
|
397
480
|
|
|
@@ -488,7 +571,7 @@ export class Moro extends EventEmitter {
|
|
|
488
571
|
const matches = req.path.match(route.pattern);
|
|
489
572
|
if (matches) {
|
|
490
573
|
req.params = {};
|
|
491
|
-
route.paramNames.forEach((name, index) => {
|
|
574
|
+
route.paramNames.forEach((name: string, index: number) => {
|
|
492
575
|
req.params[name] = matches[index + 1];
|
|
493
576
|
});
|
|
494
577
|
}
|
|
@@ -552,11 +635,38 @@ export class Moro extends EventEmitter {
|
|
|
552
635
|
}
|
|
553
636
|
}
|
|
554
637
|
|
|
555
|
-
//
|
|
638
|
+
// Advanced route matching with caching and optimization
|
|
639
|
+
private routeCache = new Map<string, { pattern: RegExp; paramNames: string[] }>();
|
|
640
|
+
private staticRouteMap = new Map<string, any>();
|
|
641
|
+
private dynamicRoutesBySegments = new Map<number, any[]>();
|
|
642
|
+
|
|
556
643
|
private findMatchingRoute(method: string, path: string) {
|
|
557
|
-
|
|
644
|
+
// Phase 1: O(1) static route lookup
|
|
645
|
+
const staticKey = `${method}:${path}`;
|
|
646
|
+
const staticRoute = this.staticRouteMap.get(staticKey);
|
|
647
|
+
if (staticRoute) {
|
|
648
|
+
return {
|
|
649
|
+
...staticRoute,
|
|
650
|
+
pattern: /^.*$/, // Dummy pattern for static routes
|
|
651
|
+
paramNames: [],
|
|
652
|
+
};
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
// Phase 2: Optimized dynamic route matching by segment count
|
|
656
|
+
const segments = path.split('/').filter(s => s.length > 0);
|
|
657
|
+
const segmentCount = segments.length;
|
|
658
|
+
const candidateRoutes = this.dynamicRoutesBySegments.get(segmentCount) || [];
|
|
659
|
+
|
|
660
|
+
for (const route of candidateRoutes) {
|
|
558
661
|
if (route.method === method) {
|
|
559
|
-
const
|
|
662
|
+
const cacheKey = `${method}:${route.path}`;
|
|
663
|
+
let pattern = this.routeCache.get(cacheKey);
|
|
664
|
+
|
|
665
|
+
if (!pattern) {
|
|
666
|
+
pattern = this.pathToRegex(route.path);
|
|
667
|
+
this.routeCache.set(cacheKey, pattern);
|
|
668
|
+
}
|
|
669
|
+
|
|
560
670
|
if (pattern.pattern.test(path)) {
|
|
561
671
|
return {
|
|
562
672
|
...route,
|
|
@@ -566,6 +676,7 @@ export class Moro extends EventEmitter {
|
|
|
566
676
|
}
|
|
567
677
|
}
|
|
568
678
|
}
|
|
679
|
+
|
|
569
680
|
return null;
|
|
570
681
|
}
|
|
571
682
|
|
|
@@ -597,7 +708,7 @@ export class Moro extends EventEmitter {
|
|
|
597
708
|
private addRoute(method: string, path: string, handler: Function, options: any = {}) {
|
|
598
709
|
const handlerName = `handler_${this.routes.length}`;
|
|
599
710
|
|
|
600
|
-
|
|
711
|
+
const route = {
|
|
601
712
|
method: method as any,
|
|
602
713
|
path,
|
|
603
714
|
handler: handlerName,
|
|
@@ -605,7 +716,12 @@ export class Moro extends EventEmitter {
|
|
|
605
716
|
rateLimit: options.rateLimit,
|
|
606
717
|
cache: options.cache,
|
|
607
718
|
middleware: options.middleware,
|
|
608
|
-
}
|
|
719
|
+
};
|
|
720
|
+
|
|
721
|
+
this.routes.push(route);
|
|
722
|
+
|
|
723
|
+
// Organize routes for optimal lookup
|
|
724
|
+
this.organizeRouteForLookup(route);
|
|
609
725
|
|
|
610
726
|
// Store handler for later module creation
|
|
611
727
|
this.routeHandlers[handlerName] = handler;
|
|
@@ -613,6 +729,23 @@ export class Moro extends EventEmitter {
|
|
|
613
729
|
return this;
|
|
614
730
|
}
|
|
615
731
|
|
|
732
|
+
private organizeRouteForLookup(route: any): void {
|
|
733
|
+
if (!route.path.includes(':')) {
|
|
734
|
+
// Static route - add to static map for O(1) lookup
|
|
735
|
+
const staticKey = `${route.method}:${route.path}`;
|
|
736
|
+
this.staticRouteMap.set(staticKey, route);
|
|
737
|
+
} else {
|
|
738
|
+
// Dynamic route - organize by segment count
|
|
739
|
+
const segments = route.path.split('/').filter((s: string) => s.length > 0);
|
|
740
|
+
const segmentCount = segments.length;
|
|
741
|
+
|
|
742
|
+
if (!this.dynamicRoutesBySegments.has(segmentCount)) {
|
|
743
|
+
this.dynamicRoutesBySegments.set(segmentCount, []);
|
|
744
|
+
}
|
|
745
|
+
this.dynamicRoutesBySegments.get(segmentCount)!.push(route);
|
|
746
|
+
}
|
|
747
|
+
}
|
|
748
|
+
|
|
616
749
|
private registerDirectRoutes() {
|
|
617
750
|
// Register routes directly with the HTTP server for optimal performance
|
|
618
751
|
// This provides the intuitive developer experience users expect
|
|
@@ -761,11 +894,264 @@ export class Moro extends EventEmitter {
|
|
|
761
894
|
const module = await import(modulePath);
|
|
762
895
|
return module.default || module;
|
|
763
896
|
}
|
|
897
|
+
|
|
898
|
+
// Clustering support for massive performance gains with proper cleanup
|
|
899
|
+
private clusterWorkers = new Map<number, any>();
|
|
900
|
+
private startWithClustering(port: number, host?: string, callback?: () => void): void {
|
|
901
|
+
const cluster = require('cluster');
|
|
902
|
+
const os = require('os');
|
|
903
|
+
|
|
904
|
+
// Smart worker count calculation based on actual bottlenecks
|
|
905
|
+
let workerCount = this.config.performance?.clustering?.workers || os.cpus().length;
|
|
906
|
+
|
|
907
|
+
// Auto-optimize worker count based on system characteristics
|
|
908
|
+
if (workerCount === 'auto' || workerCount > 8) {
|
|
909
|
+
// For high-core machines, limit workers to prevent IPC/memory bottlenecks
|
|
910
|
+
const cpuCount = os.cpus().length;
|
|
911
|
+
const totalMemoryGB = os.totalmem() / (1024 * 1024 * 1024);
|
|
912
|
+
|
|
913
|
+
// Optimal worker count formula based on research
|
|
914
|
+
if (cpuCount >= 16) {
|
|
915
|
+
// High-core machines: focus on memory/IPC efficiency
|
|
916
|
+
workerCount = Math.min(Math.ceil(totalMemoryGB / 2), 4); // 2GB per worker max, cap at 4
|
|
917
|
+
} else if (cpuCount >= 8) {
|
|
918
|
+
// Mid-range machines: balanced approach
|
|
919
|
+
workerCount = Math.min(cpuCount / 2, 4);
|
|
920
|
+
} else {
|
|
921
|
+
// Low-core machines: use all cores
|
|
922
|
+
workerCount = cpuCount;
|
|
923
|
+
}
|
|
924
|
+
|
|
925
|
+
this.logger.info(
|
|
926
|
+
`Auto-optimized workers: ${workerCount} (CPU: ${cpuCount}, RAM: ${totalMemoryGB.toFixed(1)}GB)`,
|
|
927
|
+
'Cluster'
|
|
928
|
+
);
|
|
929
|
+
}
|
|
930
|
+
|
|
931
|
+
if (cluster.isPrimary) {
|
|
932
|
+
this.logger.info(`🚀 Starting ${workerCount} workers for maximum performance`, 'Cluster');
|
|
933
|
+
|
|
934
|
+
// Optimize cluster scheduling for high concurrency
|
|
935
|
+
cluster.schedulingPolicy = cluster.SCHED_RR; // Round-robin scheduling
|
|
936
|
+
|
|
937
|
+
// Set cluster settings for better performance
|
|
938
|
+
cluster.setupMaster({
|
|
939
|
+
exec: process.argv[1],
|
|
940
|
+
args: process.argv.slice(2),
|
|
941
|
+
silent: false,
|
|
942
|
+
});
|
|
943
|
+
|
|
944
|
+
// Optimize IPC to reduce communication overhead
|
|
945
|
+
process.env.NODE_CLUSTER_SCHED_POLICY = 'rr'; // Ensure round-robin
|
|
946
|
+
process.env.NODE_DISABLE_COLORS = '1'; // Reduce IPC message size
|
|
947
|
+
|
|
948
|
+
// Graceful shutdown handler
|
|
949
|
+
const gracefulShutdown = () => {
|
|
950
|
+
this.logger.info('Gracefully shutting down cluster...', 'Cluster');
|
|
951
|
+
|
|
952
|
+
// Clean up all workers
|
|
953
|
+
for (const [pid, worker] of this.clusterWorkers) {
|
|
954
|
+
worker.removeAllListeners();
|
|
955
|
+
worker.kill('SIGTERM');
|
|
956
|
+
}
|
|
957
|
+
|
|
958
|
+
// Clean up cluster listeners
|
|
959
|
+
cluster.removeAllListeners();
|
|
960
|
+
process.exit(0);
|
|
961
|
+
};
|
|
962
|
+
|
|
963
|
+
// Handle process signals for graceful shutdown
|
|
964
|
+
process.on('SIGINT', gracefulShutdown);
|
|
965
|
+
process.on('SIGTERM', gracefulShutdown);
|
|
966
|
+
|
|
967
|
+
// Fork workers with proper tracking and CPU affinity
|
|
968
|
+
for (let i = 0; i < workerCount; i++) {
|
|
969
|
+
const worker = cluster.fork({
|
|
970
|
+
WORKER_ID: i,
|
|
971
|
+
WORKER_CPU_AFFINITY: i % os.cpus().length, // Distribute workers across CPUs
|
|
972
|
+
});
|
|
973
|
+
this.clusterWorkers.set(worker.process.pid!, worker);
|
|
974
|
+
this.logger.info(
|
|
975
|
+
`Worker ${worker.process.pid} started (CPU ${i % os.cpus().length})`,
|
|
976
|
+
'Cluster'
|
|
977
|
+
);
|
|
978
|
+
|
|
979
|
+
// Handle individual worker messages (reuse handler)
|
|
980
|
+
worker.on('message', this.handleWorkerMessage.bind(this));
|
|
981
|
+
}
|
|
982
|
+
|
|
983
|
+
// Handle worker exits with cleanup
|
|
984
|
+
cluster.on('exit', (worker: any, code: number, signal: string) => {
|
|
985
|
+
// Clean up worker tracking
|
|
986
|
+
this.clusterWorkers.delete(worker.process.pid);
|
|
987
|
+
|
|
988
|
+
this.logger.warn(
|
|
989
|
+
`Worker ${worker.process.pid} died (${signal || code}). Restarting...`,
|
|
990
|
+
'Cluster'
|
|
991
|
+
);
|
|
992
|
+
|
|
993
|
+
// Restart worker with proper tracking
|
|
994
|
+
const newWorker = cluster.fork();
|
|
995
|
+
this.clusterWorkers.set(newWorker.process.pid!, newWorker);
|
|
996
|
+
newWorker.on('message', this.handleWorkerMessage.bind(this));
|
|
997
|
+
this.logger.info(`Worker ${newWorker.process.pid} started`, 'Cluster');
|
|
998
|
+
});
|
|
999
|
+
|
|
1000
|
+
// Master process callback
|
|
1001
|
+
if (callback) callback();
|
|
1002
|
+
} else {
|
|
1003
|
+
// Worker process - start the actual server with proper cleanup
|
|
1004
|
+
this.logger.info(`Worker ${process.pid} initializing`, 'Worker');
|
|
1005
|
+
|
|
1006
|
+
// Worker-specific optimizations for high concurrency
|
|
1007
|
+
process.env.UV_THREADPOOL_SIZE = '64';
|
|
1008
|
+
|
|
1009
|
+
// Reduce logging contention in workers (major bottleneck)
|
|
1010
|
+
if (this.config.logging) {
|
|
1011
|
+
// Workers log less frequently to reduce I/O contention
|
|
1012
|
+
this.config.logging.level = 'warn'; // Only warnings and errors
|
|
1013
|
+
}
|
|
1014
|
+
|
|
1015
|
+
// Memory optimization for workers
|
|
1016
|
+
process.env.NODE_OPTIONS = '--max-old-space-size=1024'; // Limit memory per worker
|
|
1017
|
+
|
|
1018
|
+
// Optimize V8 flags for better performance (Rust-level optimizations)
|
|
1019
|
+
if (process.env.NODE_ENV === 'production') {
|
|
1020
|
+
// Ultra-aggressive V8 optimizations for maximum performance
|
|
1021
|
+
const v8Flags = [
|
|
1022
|
+
'--optimize-for-size', // Trade memory for speed
|
|
1023
|
+
'--always-opt', // Always optimize functions
|
|
1024
|
+
'--turbo-fast-api-calls', // Optimize API calls
|
|
1025
|
+
'--turbo-escape-analysis', // Escape analysis optimization
|
|
1026
|
+
'--turbo-inline-api-calls', // Inline API calls
|
|
1027
|
+
'--max-old-space-size=1024', // Limit memory to prevent GC pressure
|
|
1028
|
+
];
|
|
1029
|
+
process.env.NODE_OPTIONS = (process.env.NODE_OPTIONS || '') + ' ' + v8Flags.join(' ');
|
|
1030
|
+
}
|
|
1031
|
+
|
|
1032
|
+
// Optimize garbage collection for workers
|
|
1033
|
+
// eslint-disable-next-line no-undef
|
|
1034
|
+
if ((global as any).gc) {
|
|
1035
|
+
setInterval(() => {
|
|
1036
|
+
// eslint-disable-next-line no-undef
|
|
1037
|
+
if ((global as any).gc) (global as any).gc();
|
|
1038
|
+
}, 60000); // GC every 60 seconds (less frequent)
|
|
1039
|
+
}
|
|
1040
|
+
|
|
1041
|
+
// Graceful shutdown for worker
|
|
1042
|
+
const workerShutdown = () => {
|
|
1043
|
+
this.logger.info(`Worker ${process.pid} shutting down gracefully...`, 'Worker');
|
|
1044
|
+
|
|
1045
|
+
// Clean up event listeners
|
|
1046
|
+
this.eventBus.removeAllListeners();
|
|
1047
|
+
this.removeAllListeners();
|
|
1048
|
+
|
|
1049
|
+
// Close server gracefully
|
|
1050
|
+
if (this.coreFramework) {
|
|
1051
|
+
const server = (this.coreFramework as any).server;
|
|
1052
|
+
if (server) {
|
|
1053
|
+
server.close(() => {
|
|
1054
|
+
process.exit(0);
|
|
1055
|
+
});
|
|
1056
|
+
}
|
|
1057
|
+
}
|
|
1058
|
+
};
|
|
1059
|
+
|
|
1060
|
+
// Handle worker shutdown signals
|
|
1061
|
+
process.on('SIGTERM', workerShutdown);
|
|
1062
|
+
process.on('SIGINT', workerShutdown);
|
|
1063
|
+
|
|
1064
|
+
// Continue with normal server startup for this worker
|
|
1065
|
+
this.eventBus.emit('server:starting', {
|
|
1066
|
+
port,
|
|
1067
|
+
runtime: this.runtimeType,
|
|
1068
|
+
worker: process.pid,
|
|
1069
|
+
});
|
|
1070
|
+
|
|
1071
|
+
// Add documentation middleware first (if enabled)
|
|
1072
|
+
try {
|
|
1073
|
+
const docsMiddleware = this.documentation.getDocsMiddleware();
|
|
1074
|
+
this.coreFramework.addMiddleware(docsMiddleware);
|
|
1075
|
+
} catch (error) {
|
|
1076
|
+
// Documentation not enabled, that's fine
|
|
1077
|
+
}
|
|
1078
|
+
|
|
1079
|
+
// Add intelligent routing middleware
|
|
1080
|
+
this.coreFramework.addMiddleware(
|
|
1081
|
+
async (req: HttpRequest, res: HttpResponse, next: () => void) => {
|
|
1082
|
+
const handled = await this.intelligentRouting.handleIntelligentRoute(req, res);
|
|
1083
|
+
if (!handled) {
|
|
1084
|
+
next();
|
|
1085
|
+
}
|
|
1086
|
+
}
|
|
1087
|
+
);
|
|
1088
|
+
|
|
1089
|
+
// Register direct routes
|
|
1090
|
+
if (this.routes.length > 0) {
|
|
1091
|
+
this.registerDirectRoutes();
|
|
1092
|
+
}
|
|
1093
|
+
|
|
1094
|
+
const workerCallback = () => {
|
|
1095
|
+
const displayHost = host || 'localhost';
|
|
1096
|
+
this.logger.info(`Worker ${process.pid} ready on ${displayHost}:${port}`, 'Worker');
|
|
1097
|
+
this.eventBus.emit('server:started', {
|
|
1098
|
+
port,
|
|
1099
|
+
runtime: this.runtimeType,
|
|
1100
|
+
worker: process.pid,
|
|
1101
|
+
});
|
|
1102
|
+
};
|
|
1103
|
+
|
|
1104
|
+
if (host) {
|
|
1105
|
+
this.coreFramework.listen(port, host, workerCallback);
|
|
1106
|
+
} else {
|
|
1107
|
+
this.coreFramework.listen(port, workerCallback);
|
|
1108
|
+
}
|
|
1109
|
+
}
|
|
1110
|
+
}
|
|
1111
|
+
|
|
1112
|
+
// Reusable worker message handler (avoids creating new functions)
|
|
1113
|
+
private handleWorkerMessage(message: any): void {
|
|
1114
|
+
// Handle inter-worker communication if needed
|
|
1115
|
+
if (message.type === 'health-check') {
|
|
1116
|
+
// Worker health check response
|
|
1117
|
+
return;
|
|
1118
|
+
}
|
|
1119
|
+
|
|
1120
|
+
// Log other worker messages
|
|
1121
|
+
this.logger.debug(`Worker message: ${JSON.stringify(message)}`, 'Cluster');
|
|
1122
|
+
}
|
|
764
1123
|
}
|
|
765
1124
|
|
|
766
1125
|
// Export convenience function
|
|
767
1126
|
export function createApp(options?: MoroOptions): Moro {
|
|
768
|
-
|
|
1127
|
+
// Load global config from moro.config.js and merge with passed options
|
|
1128
|
+
const globalConfig = initializeConfig();
|
|
1129
|
+
|
|
1130
|
+
// Convert global config to MoroOptions format for createApp
|
|
1131
|
+
const configAsOptions: Partial<MoroOptions> = {
|
|
1132
|
+
performance: globalConfig.performance,
|
|
1133
|
+
logger: globalConfig.logging
|
|
1134
|
+
? {
|
|
1135
|
+
level: globalConfig.logging.level,
|
|
1136
|
+
enableColors: globalConfig.logging.enableColors,
|
|
1137
|
+
enableTimestamp: globalConfig.logging.enableTimestamp,
|
|
1138
|
+
}
|
|
1139
|
+
: undefined,
|
|
1140
|
+
// Add other relevant config mappings as needed
|
|
1141
|
+
};
|
|
1142
|
+
|
|
1143
|
+
// Merge config file options with passed options (passed options take precedence)
|
|
1144
|
+
const mergedOptions = {
|
|
1145
|
+
...configAsOptions,
|
|
1146
|
+
...options,
|
|
1147
|
+
// Deep merge performance settings
|
|
1148
|
+
performance: {
|
|
1149
|
+
...configAsOptions.performance,
|
|
1150
|
+
...options?.performance,
|
|
1151
|
+
},
|
|
1152
|
+
};
|
|
1153
|
+
|
|
1154
|
+
return new Moro(mergedOptions);
|
|
769
1155
|
}
|
|
770
1156
|
|
|
771
1157
|
// Runtime-specific convenience functions
|
package/src/types/core.ts
CHANGED
|
@@ -14,4 +14,22 @@ export interface MoroOptions {
|
|
|
14
14
|
runtime?: RuntimeConfig;
|
|
15
15
|
// Logger configuration
|
|
16
16
|
logger?: LoggerOptions | boolean;
|
|
17
|
+
// Performance configuration
|
|
18
|
+
performance?: {
|
|
19
|
+
clustering?: {
|
|
20
|
+
enabled?: boolean;
|
|
21
|
+
workers?: number | 'auto';
|
|
22
|
+
};
|
|
23
|
+
compression?: {
|
|
24
|
+
enabled?: boolean;
|
|
25
|
+
level?: number;
|
|
26
|
+
threshold?: number;
|
|
27
|
+
};
|
|
28
|
+
circuitBreaker?: {
|
|
29
|
+
enabled?: boolean;
|
|
30
|
+
failureThreshold?: number;
|
|
31
|
+
resetTimeout?: number;
|
|
32
|
+
monitoringPeriod?: number;
|
|
33
|
+
};
|
|
34
|
+
};
|
|
17
35
|
}
|