@morojs/moro 1.6.0 → 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.
- package/README.md +256 -74
- package/dist/core/auth/morojs-adapter.js +20 -20
- package/dist/core/auth/morojs-adapter.js.map +1 -1
- package/dist/core/config/file-loader.d.ts +6 -1
- package/dist/core/config/file-loader.js +249 -19
- package/dist/core/config/file-loader.js.map +1 -1
- package/dist/core/config/index.d.ts +12 -41
- package/dist/core/config/index.js +54 -65
- package/dist/core/config/index.js.map +1 -1
- package/dist/core/config/loader.d.ts +7 -0
- package/dist/core/config/loader.js +269 -0
- package/dist/core/config/loader.js.map +1 -0
- package/dist/core/config/schema.d.ts +2 -2
- package/dist/core/config/schema.js +44 -52
- package/dist/core/config/schema.js.map +1 -1
- package/dist/core/config/utils.d.ts +3 -10
- package/dist/core/config/utils.js +58 -31
- package/dist/core/config/utils.js.map +1 -1
- package/dist/core/config/validation.d.ts +17 -0
- package/dist/core/config/validation.js +131 -0
- package/dist/core/config/validation.js.map +1 -0
- package/dist/core/database/adapters/drizzle.d.ts +1 -1
- package/dist/core/database/adapters/drizzle.js +11 -18
- package/dist/core/database/adapters/drizzle.js.map +1 -1
- package/dist/core/database/adapters/index.d.ts +7 -7
- package/dist/core/database/adapters/index.js +29 -19
- package/dist/core/database/adapters/index.js.map +1 -1
- package/dist/core/database/adapters/mongodb.d.ts +1 -13
- package/dist/core/database/adapters/mongodb.js +10 -46
- package/dist/core/database/adapters/mongodb.js.map +1 -1
- package/dist/core/database/adapters/mysql.d.ts +1 -14
- package/dist/core/database/adapters/mysql.js +9 -19
- package/dist/core/database/adapters/mysql.js.map +1 -1
- package/dist/core/database/adapters/postgresql.d.ts +2 -12
- package/dist/core/database/adapters/postgresql.js +9 -19
- package/dist/core/database/adapters/postgresql.js.map +1 -1
- package/dist/core/database/adapters/redis.d.ts +1 -12
- package/dist/core/database/adapters/redis.js +13 -48
- package/dist/core/database/adapters/redis.js.map +1 -1
- package/dist/core/database/adapters/sqlite.d.ts +1 -3
- package/dist/core/database/adapters/sqlite.js +8 -19
- package/dist/core/database/adapters/sqlite.js.map +1 -1
- package/dist/core/database/index.d.ts +2 -2
- package/dist/core/database/index.js +18 -2
- package/dist/core/database/index.js.map +1 -1
- package/dist/core/docs/index.d.ts +9 -9
- package/dist/core/docs/index.js +35 -14
- package/dist/core/docs/index.js.map +1 -1
- package/dist/core/docs/openapi-generator.d.ts +2 -2
- package/dist/core/docs/openapi-generator.js +16 -11
- package/dist/core/docs/openapi-generator.js.map +1 -1
- package/dist/core/docs/schema-to-openapi.d.ts +2 -2
- package/dist/core/docs/schema-to-openapi.js +11 -5
- package/dist/core/docs/schema-to-openapi.js.map +1 -1
- package/dist/core/docs/simple-docs.d.ts +1 -1
- package/dist/core/docs/simple-docs.js +9 -4
- package/dist/core/docs/simple-docs.js.map +1 -1
- package/dist/core/docs/swagger-ui.d.ts +2 -2
- package/dist/core/docs/swagger-ui.js +29 -26
- package/dist/core/docs/swagger-ui.js.map +1 -1
- package/dist/core/docs/zod-to-openapi.js +28 -31
- package/dist/core/docs/zod-to-openapi.js.map +1 -1
- package/dist/core/events/event-bus.d.ts +1 -1
- package/dist/core/events/event-bus.js +10 -6
- package/dist/core/events/event-bus.js.map +1 -1
- package/dist/core/events/index.d.ts +2 -2
- package/dist/core/events/index.js +5 -1
- package/dist/core/events/index.js.map +1 -1
- package/dist/core/framework.d.ts +12 -19
- package/dist/core/framework.js +100 -277
- package/dist/core/framework.js.map +1 -1
- package/dist/core/http/http-server.d.ts +1 -13
- package/dist/core/http/http-server.js +85 -175
- package/dist/core/http/http-server.js.map +1 -1
- package/dist/core/http/index.d.ts +3 -4
- package/dist/core/http/index.js +8 -3
- package/dist/core/http/index.js.map +1 -1
- package/dist/core/http/router.d.ts +1 -13
- package/dist/core/http/router.js +43 -117
- package/dist/core/http/router.js.map +1 -1
- package/dist/core/logger/filters.d.ts +1 -1
- package/dist/core/logger/filters.js +23 -20
- package/dist/core/logger/filters.js.map +1 -1
- package/dist/core/logger/index.d.ts +3 -3
- package/dist/core/logger/index.js +24 -2
- package/dist/core/logger/index.js.map +1 -1
- package/dist/core/logger/logger.d.ts +14 -30
- package/dist/core/logger/logger.js +223 -398
- package/dist/core/logger/logger.js.map +1 -1
- package/dist/core/logger/outputs.d.ts +1 -1
- package/dist/core/logger/outputs.js +17 -8
- package/dist/core/logger/outputs.js.map +1 -1
- package/dist/core/middleware/built-in/adapters/cache/file.d.ts +1 -1
- package/dist/core/middleware/built-in/adapters/cache/file.js +47 -10
- package/dist/core/middleware/built-in/adapters/cache/file.js.map +1 -1
- package/dist/core/middleware/built-in/adapters/cache/index.d.ts +4 -4
- package/dist/core/middleware/built-in/adapters/cache/index.js +17 -10
- package/dist/core/middleware/built-in/adapters/cache/index.js.map +1 -1
- package/dist/core/middleware/built-in/adapters/cache/memory.d.ts +1 -1
- package/dist/core/middleware/built-in/adapters/cache/memory.js +7 -3
- package/dist/core/middleware/built-in/adapters/cache/memory.js.map +1 -1
- package/dist/core/middleware/built-in/adapters/cache/redis.d.ts +1 -3
- package/dist/core/middleware/built-in/adapters/cache/redis.js +9 -11
- package/dist/core/middleware/built-in/adapters/cache/redis.js.map +1 -1
- package/dist/core/middleware/built-in/adapters/cdn/azure.d.ts +1 -1
- package/dist/core/middleware/built-in/adapters/cdn/azure.js +7 -3
- package/dist/core/middleware/built-in/adapters/cdn/azure.js.map +1 -1
- package/dist/core/middleware/built-in/adapters/cdn/cloudflare.d.ts +1 -1
- package/dist/core/middleware/built-in/adapters/cdn/cloudflare.js +7 -3
- package/dist/core/middleware/built-in/adapters/cdn/cloudflare.js.map +1 -1
- package/dist/core/middleware/built-in/adapters/cdn/cloudfront.d.ts +1 -3
- package/dist/core/middleware/built-in/adapters/cdn/cloudfront.js +10 -12
- package/dist/core/middleware/built-in/adapters/cdn/cloudfront.js.map +1 -1
- package/dist/core/middleware/built-in/adapters/cdn/index.d.ts +4 -4
- package/dist/core/middleware/built-in/adapters/cdn/index.js +17 -10
- package/dist/core/middleware/built-in/adapters/cdn/index.js.map +1 -1
- package/dist/core/middleware/built-in/adapters/index.d.ts +4 -4
- package/dist/core/middleware/built-in/adapters/index.js +23 -4
- package/dist/core/middleware/built-in/adapters/index.js.map +1 -1
- package/dist/core/middleware/built-in/auth-helpers.js +22 -11
- package/dist/core/middleware/built-in/auth-helpers.js.map +1 -1
- package/dist/core/middleware/built-in/auth-providers.d.ts +1 -1
- package/dist/core/middleware/built-in/auth-providers.js +9 -4
- package/dist/core/middleware/built-in/auth-providers.js.map +1 -1
- package/dist/core/middleware/built-in/auth.d.ts +2 -2
- package/dist/core/middleware/built-in/auth.js +26 -93
- package/dist/core/middleware/built-in/auth.js.map +1 -1
- package/dist/core/middleware/built-in/cache.d.ts +2 -2
- package/dist/core/middleware/built-in/cache.js +12 -11
- package/dist/core/middleware/built-in/cache.js.map +1 -1
- package/dist/core/middleware/built-in/cdn.d.ts +2 -2
- package/dist/core/middleware/built-in/cdn.js +9 -5
- package/dist/core/middleware/built-in/cdn.js.map +1 -1
- package/dist/core/middleware/built-in/cookie.d.ts +1 -1
- package/dist/core/middleware/built-in/cookie.js +7 -3
- package/dist/core/middleware/built-in/cookie.js.map +1 -1
- package/dist/core/middleware/built-in/cors.d.ts +1 -1
- package/dist/core/middleware/built-in/cors.js +7 -3
- package/dist/core/middleware/built-in/cors.js.map +1 -1
- package/dist/core/middleware/built-in/csp.d.ts +1 -1
- package/dist/core/middleware/built-in/csp.js +8 -5
- package/dist/core/middleware/built-in/csp.js.map +1 -1
- package/dist/core/middleware/built-in/csrf.d.ts +1 -1
- package/dist/core/middleware/built-in/csrf.js +8 -5
- package/dist/core/middleware/built-in/csrf.js.map +1 -1
- package/dist/core/middleware/built-in/error-tracker.js +7 -3
- package/dist/core/middleware/built-in/error-tracker.js.map +1 -1
- package/dist/core/middleware/built-in/index.d.ts +27 -28
- package/dist/core/middleware/built-in/index.js +78 -48
- package/dist/core/middleware/built-in/index.js.map +1 -1
- package/dist/core/middleware/built-in/performance-monitor.js +7 -3
- package/dist/core/middleware/built-in/performance-monitor.js.map +1 -1
- package/dist/core/middleware/built-in/rate-limit.d.ts +1 -1
- package/dist/core/middleware/built-in/rate-limit.js +7 -3
- package/dist/core/middleware/built-in/rate-limit.js.map +1 -1
- package/dist/core/middleware/built-in/request-logger.js +8 -5
- package/dist/core/middleware/built-in/request-logger.js.map +1 -1
- package/dist/core/middleware/built-in/session.d.ts +2 -2
- package/dist/core/middleware/built-in/session.js +15 -11
- package/dist/core/middleware/built-in/session.js.map +1 -1
- package/dist/core/middleware/built-in/sse.d.ts +1 -1
- package/dist/core/middleware/built-in/sse.js +14 -12
- package/dist/core/middleware/built-in/sse.js.map +1 -1
- package/dist/core/middleware/built-in/validation.d.ts +1 -1
- package/dist/core/middleware/built-in/validation.js +7 -3
- package/dist/core/middleware/built-in/validation.js.map +1 -1
- package/dist/core/middleware/index.d.ts +4 -4
- package/dist/core/middleware/index.js +28 -8
- package/dist/core/middleware/index.js.map +1 -1
- package/dist/core/modules/auto-discovery.d.ts +2 -19
- package/dist/core/modules/auto-discovery.js +74 -391
- package/dist/core/modules/auto-discovery.js.map +1 -1
- package/dist/core/modules/index.d.ts +2 -2
- package/dist/core/modules/index.js +9 -2
- package/dist/core/modules/index.js.map +1 -1
- package/dist/core/modules/modules.d.ts +3 -3
- package/dist/core/modules/modules.js +54 -23
- package/dist/core/modules/modules.js.map +1 -1
- package/dist/core/networking/adapters/index.d.ts +3 -4
- package/dist/core/networking/adapters/index.js +7 -3
- package/dist/core/networking/adapters/index.js.map +1 -1
- package/dist/core/networking/adapters/socketio-adapter.d.ts +1 -1
- package/dist/core/networking/adapters/socketio-adapter.js +40 -5
- package/dist/core/networking/adapters/socketio-adapter.js.map +1 -1
- package/dist/core/networking/adapters/ws-adapter.d.ts +2 -2
- package/dist/core/networking/adapters/ws-adapter.js +43 -8
- package/dist/core/networking/adapters/ws-adapter.js.map +1 -1
- package/dist/core/networking/index.d.ts +2 -3
- package/dist/core/networking/index.js +7 -2
- package/dist/core/networking/index.js.map +1 -1
- package/dist/core/networking/service-discovery.js +12 -8
- package/dist/core/networking/service-discovery.js.map +1 -1
- package/dist/core/networking/websocket-adapter.js +2 -1
- package/dist/core/networking/websocket-adapter.js.map +1 -1
- package/dist/core/networking/websocket-manager.d.ts +3 -3
- package/dist/core/networking/websocket-manager.js +11 -9
- package/dist/core/networking/websocket-manager.js.map +1 -1
- package/dist/core/routing/app-integration.d.ts +2 -2
- package/dist/core/routing/app-integration.js +12 -7
- package/dist/core/routing/app-integration.js.map +1 -1
- package/dist/core/routing/index.d.ts +2 -22
- package/dist/core/routing/index.js +27 -185
- package/dist/core/routing/index.js.map +1 -1
- package/dist/core/runtime/aws-lambda-adapter.d.ts +3 -3
- package/dist/core/runtime/aws-lambda-adapter.js +6 -2
- package/dist/core/runtime/aws-lambda-adapter.js.map +1 -1
- package/dist/core/runtime/base-adapter.d.ts +2 -2
- package/dist/core/runtime/base-adapter.js +7 -3
- package/dist/core/runtime/base-adapter.js.map +1 -1
- package/dist/core/runtime/cloudflare-workers-adapter.d.ts +3 -3
- package/dist/core/runtime/cloudflare-workers-adapter.js +6 -2
- package/dist/core/runtime/cloudflare-workers-adapter.js.map +1 -1
- package/dist/core/runtime/index.d.ts +12 -12
- package/dist/core/runtime/index.js +35 -22
- package/dist/core/runtime/index.js.map +1 -1
- package/dist/core/runtime/node-adapter.d.ts +4 -4
- package/dist/core/runtime/node-adapter.js +49 -18
- package/dist/core/runtime/node-adapter.js.map +1 -1
- package/dist/core/runtime/vercel-edge-adapter.d.ts +3 -3
- package/dist/core/runtime/vercel-edge-adapter.js +6 -2
- package/dist/core/runtime/vercel-edge-adapter.js.map +1 -1
- package/dist/core/utilities/circuit-breaker.js +5 -1
- package/dist/core/utilities/circuit-breaker.js.map +1 -1
- package/dist/core/utilities/container.js +22 -12
- package/dist/core/utilities/container.js.map +1 -1
- package/dist/core/utilities/hooks.d.ts +2 -2
- package/dist/core/utilities/hooks.js +12 -7
- package/dist/core/utilities/hooks.js.map +1 -1
- package/dist/core/utilities/index.d.ts +4 -5
- package/dist/core/utilities/index.js +19 -5
- package/dist/core/utilities/index.js.map +1 -1
- package/dist/core/validation/adapters.d.ts +1 -1
- package/dist/core/validation/adapters.js +26 -15
- package/dist/core/validation/adapters.js.map +1 -1
- package/dist/core/validation/index.d.ts +4 -6
- package/dist/core/validation/index.js +28 -57
- package/dist/core/validation/index.js.map +1 -1
- package/dist/core/validation/schema-interface.js +9 -3
- package/dist/core/validation/schema-interface.js.map +1 -1
- package/dist/index.d.ts +52 -51
- package/dist/index.js +132 -23
- package/dist/index.js.map +1 -1
- package/dist/moro.d.ts +10 -61
- package/dist/moro.js +237 -575
- package/dist/moro.js.map +1 -1
- package/dist/types/auth.js +9 -3
- package/dist/types/auth.js.map +1 -1
- package/dist/types/cache.js +2 -1
- package/dist/types/cdn.js +2 -1
- package/dist/types/config.d.ts +2 -70
- package/dist/types/config.js +2 -1
- package/dist/types/config.js.map +1 -1
- package/dist/types/core.d.ts +42 -36
- package/dist/types/core.js +2 -1
- package/dist/types/database.js +2 -1
- package/dist/types/discovery.js +2 -1
- package/dist/types/events.js +2 -1
- package/dist/types/hooks.d.ts +1 -1
- package/dist/types/hooks.js +2 -1
- package/dist/types/http.d.ts +1 -16
- package/dist/types/http.js +2 -1
- package/dist/types/logger.d.ts +0 -7
- package/dist/types/logger.js +2 -1
- package/dist/types/module.d.ts +0 -11
- package/dist/types/module.js +2 -1
- package/dist/types/runtime.d.ts +1 -1
- package/dist/types/runtime.js +2 -1
- package/dist/types/session.js +2 -1
- package/package.json +52 -19
- package/src/core/auth/morojs-adapter.ts +13 -18
- package/src/core/config/file-loader.ts +273 -13
- package/src/core/config/index.ts +34 -83
- package/src/core/config/loader.ts +633 -0
- package/src/core/config/schema.ts +33 -44
- package/src/core/config/utils.ts +31 -24
- package/src/core/config/validation.ts +140 -0
- package/src/core/database/README.md +16 -26
- package/src/core/database/adapters/drizzle.ts +6 -18
- package/src/core/database/adapters/index.ts +13 -13
- package/src/core/database/adapters/mongodb.ts +5 -53
- package/src/core/database/adapters/mysql.ts +4 -32
- package/src/core/database/adapters/postgresql.ts +5 -30
- package/src/core/database/adapters/redis.ts +8 -61
- package/src/core/database/adapters/sqlite.ts +3 -19
- package/src/core/database/index.ts +2 -2
- package/src/core/docs/index.ts +8 -8
- package/src/core/docs/openapi-generator.ts +4 -4
- package/src/core/docs/schema-to-openapi.ts +6 -3
- package/src/core/docs/simple-docs.ts +2 -2
- package/src/core/docs/swagger-ui.ts +16 -19
- package/src/core/docs/zod-to-openapi.ts +34 -34
- package/src/core/events/event-bus.ts +2 -2
- package/src/core/events/index.ts +2 -2
- package/src/core/framework.ts +67 -311
- package/src/core/http/http-server.ts +45 -190
- package/src/core/http/index.ts +3 -4
- package/src/core/http/router.ts +40 -129
- package/src/core/logger/filters.ts +5 -13
- package/src/core/logger/index.ts +3 -4
- package/src/core/logger/logger.ts +216 -435
- package/src/core/logger/outputs.ts +3 -1
- package/src/core/middleware/built-in/adapters/cache/file.ts +3 -3
- package/src/core/middleware/built-in/adapters/cache/index.ts +7 -7
- package/src/core/middleware/built-in/adapters/cache/memory.ts +2 -2
- package/src/core/middleware/built-in/adapters/cache/redis.ts +4 -18
- package/src/core/middleware/built-in/adapters/cdn/azure.ts +2 -2
- package/src/core/middleware/built-in/adapters/cdn/cloudflare.ts +2 -2
- package/src/core/middleware/built-in/adapters/cdn/cloudfront.ts +5 -16
- package/src/core/middleware/built-in/adapters/cdn/index.ts +7 -7
- package/src/core/middleware/built-in/adapters/index.ts +4 -4
- package/src/core/middleware/built-in/auth-helpers.ts +1 -1
- package/src/core/middleware/built-in/auth-providers.ts +1 -1
- package/src/core/middleware/built-in/auth.ts +21 -102
- package/src/core/middleware/built-in/cache.ts +6 -8
- package/src/core/middleware/built-in/cdn.ts +4 -4
- package/src/core/middleware/built-in/cookie.ts +2 -2
- package/src/core/middleware/built-in/cors.ts +2 -2
- package/src/core/middleware/built-in/csp.ts +3 -3
- package/src/core/middleware/built-in/csrf.ts +3 -3
- package/src/core/middleware/built-in/error-tracker.ts +1 -1
- package/src/core/middleware/built-in/index.ts +30 -38
- package/src/core/middleware/built-in/performance-monitor.ts +1 -1
- package/src/core/middleware/built-in/rate-limit.ts +2 -2
- package/src/core/middleware/built-in/request-logger.ts +1 -3
- package/src/core/middleware/built-in/session.ts +8 -7
- package/src/core/middleware/built-in/sse.ts +9 -11
- package/src/core/middleware/built-in/validation.ts +2 -2
- package/src/core/middleware/index.ts +6 -6
- package/src/core/modules/auto-discovery.ts +15 -478
- package/src/core/modules/index.ts +2 -2
- package/src/core/modules/modules.ts +12 -23
- package/src/core/networking/adapters/index.ts +3 -4
- package/src/core/networking/adapters/socketio-adapter.ts +3 -5
- package/src/core/networking/adapters/ws-adapter.ts +9 -8
- package/src/core/networking/index.ts +2 -3
- package/src/core/networking/service-discovery.ts +7 -6
- package/src/core/networking/websocket-manager.ts +7 -7
- package/src/core/routing/app-integration.ts +3 -3
- package/src/core/routing/index.ts +17 -201
- package/src/core/runtime/aws-lambda-adapter.ts +3 -3
- package/src/core/runtime/base-adapter.ts +2 -2
- package/src/core/runtime/cloudflare-workers-adapter.ts +3 -3
- package/src/core/runtime/index.ts +13 -13
- package/src/core/runtime/node-adapter.ts +10 -16
- package/src/core/runtime/vercel-edge-adapter.ts +3 -3
- package/src/core/utilities/hooks.ts +3 -3
- package/src/core/utilities/index.ts +4 -5
- package/src/core/validation/adapters.ts +1 -1
- package/src/core/validation/index.ts +16 -68
- package/src/index.ts +66 -73
- package/src/moro.ts +204 -685
- package/src/types/config.ts +2 -71
- package/src/types/core.ts +47 -49
- package/src/types/hooks.ts +1 -1
- package/src/types/http.ts +1 -23
- package/src/types/logger.ts +0 -9
- package/src/types/module.ts +0 -12
- package/src/types/runtime.ts +1 -1
- package/tsconfig.json +2 -4
- package/dist/core/config/config-manager.d.ts +0 -44
- package/dist/core/config/config-manager.js +0 -104
- package/dist/core/config/config-manager.js.map +0 -1
- package/dist/core/config/config-sources.d.ts +0 -21
- package/dist/core/config/config-sources.js +0 -499
- package/dist/core/config/config-sources.js.map +0 -1
- package/dist/core/config/config-validator.d.ts +0 -21
- package/dist/core/config/config-validator.js +0 -788
- package/dist/core/config/config-validator.js.map +0 -1
- package/dist/core/http/uws-http-server.d.ts +0 -64
- package/dist/core/http/uws-http-server.js +0 -688
- package/dist/core/http/uws-http-server.js.map +0 -1
- package/dist/core/middleware/built-in/jwt-helpers.d.ts +0 -118
- package/dist/core/middleware/built-in/jwt-helpers.js +0 -218
- package/dist/core/middleware/built-in/jwt-helpers.js.map +0 -1
- package/dist/core/networking/adapters/uws-adapter.d.ts +0 -44
- package/dist/core/networking/adapters/uws-adapter.js +0 -513
- package/dist/core/networking/adapters/uws-adapter.js.map +0 -1
- package/dist/core/utilities/package-utils.d.ts +0 -38
- package/dist/core/utilities/package-utils.js +0 -57
- package/dist/core/utilities/package-utils.js.map +0 -1
- package/jest.config.mjs +0 -41
- package/src/core/config/config-manager.ts +0 -133
- package/src/core/config/config-sources.ts +0 -596
- package/src/core/config/config-validator.ts +0 -1113
- package/src/core/http/uws-http-server.ts +0 -794
- package/src/core/middleware/built-in/jwt-helpers.ts +0 -243
- package/src/core/networking/adapters/uws-adapter.ts +0 -619
- package/src/core/utilities/package-utils.ts +0 -59
package/src/moro.ts
CHANGED
|
@@ -1,90 +1,124 @@
|
|
|
1
1
|
// Moro Framework - Modern TypeScript API Framework
|
|
2
2
|
// Built for developers who demand performance, elegance, and zero compromises
|
|
3
3
|
// Event-driven • Modular • Enterprise-ready • Developer-first
|
|
4
|
-
import { Moro as MoroCore } from './core/framework
|
|
5
|
-
import { HttpRequest, HttpResponse, middleware } from './core/http
|
|
6
|
-
import { ModuleConfig, InternalRouteDefinition } from './types/module
|
|
7
|
-
import { MoroOptions } from './types/core
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
4
|
+
import { Moro as MoroCore } from './core/framework';
|
|
5
|
+
import { HttpRequest, HttpResponse, middleware } from './core/http';
|
|
6
|
+
import { ModuleConfig, InternalRouteDefinition } from './types/module';
|
|
7
|
+
import { MoroOptions } from './types/core';
|
|
8
|
+
import { MoroEventBus } from './core/events';
|
|
9
|
+
import {
|
|
10
|
+
createFrameworkLogger,
|
|
11
|
+
logger as globalLogger,
|
|
12
|
+
applyLoggingConfiguration,
|
|
13
|
+
} from './core/logger';
|
|
14
|
+
import { MiddlewareManager } from './core/middleware';
|
|
15
|
+
import { IntelligentRoutingManager } from './core/routing/app-integration';
|
|
16
|
+
import { RouteBuilder, RouteSchema, CompiledRoute } from './core/routing';
|
|
17
|
+
import { AppDocumentationManager, DocsConfig } from './core/docs';
|
|
18
|
+
import { readdirSync, statSync } from 'fs';
|
|
19
|
+
import { join } from 'path';
|
|
16
20
|
import { EventEmitter } from 'events';
|
|
17
|
-
import cluster from 'cluster';
|
|
18
|
-
import os from 'os';
|
|
19
|
-
import { normalizeValidationError } from './core/validation/schema-interface.js';
|
|
20
21
|
// Configuration System Integration
|
|
21
|
-
import { initializeConfig, type AppConfig } from './core/config
|
|
22
|
+
import { initializeConfig, getGlobalConfig, type AppConfig } from './core/config';
|
|
22
23
|
// Runtime System Integration
|
|
23
|
-
import {
|
|
24
|
+
import {
|
|
25
|
+
RuntimeAdapter,
|
|
26
|
+
RuntimeType,
|
|
27
|
+
createRuntimeAdapter,
|
|
28
|
+
NodeRuntimeAdapter,
|
|
29
|
+
} from './core/runtime';
|
|
24
30
|
|
|
25
31
|
export class Moro extends EventEmitter {
|
|
26
|
-
private coreFramework
|
|
32
|
+
private coreFramework: MoroCore;
|
|
27
33
|
private routes: InternalRouteDefinition[] = [];
|
|
28
34
|
private moduleCounter = 0;
|
|
29
35
|
private loadedModules = new Set<string>();
|
|
30
|
-
private lazyModules = new Map<string, ModuleConfig>();
|
|
31
36
|
private routeHandlers: Record<string, Function> = {};
|
|
32
|
-
private moduleDiscovery?: any; // Store for cleanup
|
|
33
|
-
private autoDiscoveryOptions: MoroOptions | null = null;
|
|
34
|
-
private autoDiscoveryInitialized = false;
|
|
35
|
-
private autoDiscoveryPromise: Promise<void> | null = null;
|
|
36
37
|
// Enterprise event system integration
|
|
37
|
-
private eventBus
|
|
38
|
+
private eventBus: MoroEventBus;
|
|
38
39
|
// Application logger
|
|
39
|
-
private logger
|
|
40
|
+
private logger = createFrameworkLogger('App');
|
|
40
41
|
// Intelligent routing system
|
|
41
42
|
private intelligentRouting = new IntelligentRoutingManager();
|
|
42
43
|
// Documentation system
|
|
43
44
|
private documentation = new AppDocumentationManager();
|
|
44
45
|
// Configuration system
|
|
45
|
-
private config
|
|
46
|
+
private config: AppConfig;
|
|
46
47
|
// Runtime system
|
|
47
|
-
private runtimeAdapter
|
|
48
|
-
private runtimeType
|
|
48
|
+
private runtimeAdapter: RuntimeAdapter;
|
|
49
|
+
private runtimeType: RuntimeType;
|
|
49
50
|
// Middleware system
|
|
50
|
-
private middlewareManager
|
|
51
|
-
// Queued WebSocket registrations (for async adapter detection)
|
|
52
|
-
private queuedWebSocketRegistrations: Array<{
|
|
53
|
-
namespace: string;
|
|
54
|
-
handlers: Record<string, Function>;
|
|
55
|
-
processed: boolean;
|
|
56
|
-
}> = [];
|
|
51
|
+
private middlewareManager: MiddlewareManager;
|
|
57
52
|
|
|
58
53
|
constructor(options: MoroOptions = {}) {
|
|
59
54
|
super(); // Call EventEmitter constructor
|
|
60
55
|
|
|
61
|
-
//
|
|
62
|
-
//
|
|
56
|
+
// Configure logger from environment variables BEFORE config system initialization
|
|
57
|
+
// This ensures the config loading process respects the log level
|
|
63
58
|
const envLogLevel = process.env.LOG_LEVEL || process.env.MORO_LOG_LEVEL;
|
|
64
59
|
if (envLogLevel) {
|
|
65
60
|
applyLoggingConfiguration({ level: envLogLevel }, undefined);
|
|
66
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
72
|
if (options.logger !== undefined) {
|
|
70
73
|
applyLoggingConfiguration(undefined, options.logger);
|
|
71
74
|
}
|
|
72
75
|
|
|
73
|
-
//
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
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
|
+
}
|
|
78
97
|
|
|
79
|
-
// Apply
|
|
80
|
-
if (
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
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
|
+
}
|
|
84
118
|
}
|
|
85
119
|
|
|
86
120
|
this.logger.info(
|
|
87
|
-
`Configuration system initialized: ${
|
|
121
|
+
`Configuration system initialized: ${this.config.server.environment}:${this.config.server.port}`
|
|
88
122
|
);
|
|
89
123
|
|
|
90
124
|
// Initialize runtime system
|
|
@@ -93,16 +127,10 @@ export class Moro extends EventEmitter {
|
|
|
93
127
|
|
|
94
128
|
this.logger.info(`Runtime system initialized: ${this.runtimeType}`, 'Runtime');
|
|
95
129
|
|
|
96
|
-
// Pass configuration from config to framework
|
|
130
|
+
// Pass logging configuration from config to framework
|
|
97
131
|
const frameworkOptions: any = {
|
|
98
132
|
...options,
|
|
99
133
|
logger: this.config.logging,
|
|
100
|
-
// Enable websockets if either config has it enabled OR user passed websocket options
|
|
101
|
-
websocket:
|
|
102
|
-
this.config.websocket.enabled || options.websocket
|
|
103
|
-
? options.websocket || this.config.websocket || {}
|
|
104
|
-
: false,
|
|
105
|
-
config: this.config,
|
|
106
134
|
};
|
|
107
135
|
|
|
108
136
|
this.coreFramework = new MoroCore(frameworkOptions);
|
|
@@ -134,10 +162,10 @@ export class Moro extends EventEmitter {
|
|
|
134
162
|
...options,
|
|
135
163
|
});
|
|
136
164
|
|
|
137
|
-
//
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
165
|
+
// Auto-discover modules if enabled
|
|
166
|
+
if (options.autoDiscover !== false) {
|
|
167
|
+
this.autoDiscoverModules(options.modulesPath || './modules');
|
|
168
|
+
}
|
|
141
169
|
|
|
142
170
|
// Emit initialization event through enterprise event bus
|
|
143
171
|
this.eventBus.emit('framework:initialized', {
|
|
@@ -366,13 +394,6 @@ export class Moro extends EventEmitter {
|
|
|
366
394
|
version: moduleOrPath.version || '1.0.0',
|
|
367
395
|
});
|
|
368
396
|
}
|
|
369
|
-
|
|
370
|
-
// IMPORTANT: If modules are loaded manually after auto-discovery,
|
|
371
|
-
// ensure the final module handler is set up to maintain middleware order
|
|
372
|
-
if (this.autoDiscoveryInitialized) {
|
|
373
|
-
this.coreFramework.setupFinalModuleHandler();
|
|
374
|
-
}
|
|
375
|
-
|
|
376
397
|
return this;
|
|
377
398
|
}
|
|
378
399
|
|
|
@@ -388,45 +409,34 @@ export class Moro extends EventEmitter {
|
|
|
388
409
|
|
|
389
410
|
// WebSocket helper with events
|
|
390
411
|
websocket(namespace: string, handlers: Record<string, Function>) {
|
|
391
|
-
// Queue the registration to be processed after adapter initialization
|
|
392
|
-
const registration = { namespace, handlers, processed: false };
|
|
393
|
-
this.queuedWebSocketRegistrations.push(registration);
|
|
394
|
-
|
|
395
|
-
// Try to process immediately if adapter is already ready
|
|
396
412
|
const adapter = this.coreFramework.getWebSocketAdapter();
|
|
397
|
-
if (adapter
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
413
|
+
if (!adapter) {
|
|
414
|
+
throw new Error(
|
|
415
|
+
'WebSocket features require a WebSocket adapter. Install socket.io or configure an adapter:\n' +
|
|
416
|
+
'npm install socket.io\n' +
|
|
417
|
+
'or\n' +
|
|
418
|
+
'new Moro({ websocket: { adapter: new SocketIOAdapter() } })'
|
|
419
|
+
);
|
|
401
420
|
}
|
|
402
|
-
// Otherwise, it will be processed when the server starts
|
|
403
|
-
|
|
404
|
-
return this;
|
|
405
|
-
}
|
|
406
421
|
|
|
407
|
-
private processWebSocketRegistration(
|
|
408
|
-
namespace: string,
|
|
409
|
-
handlers: Record<string, Function>,
|
|
410
|
-
adapter: any
|
|
411
|
-
) {
|
|
412
422
|
this.emit('websocket:registering', { namespace, handlers });
|
|
413
423
|
|
|
414
424
|
const ns = adapter.createNamespace(namespace);
|
|
415
425
|
|
|
416
426
|
Object.entries(handlers).forEach(([event, handler]) => {
|
|
417
|
-
ns.on('connection',
|
|
427
|
+
ns.on('connection', socket => {
|
|
418
428
|
this.emit('websocket:connection', { namespace, event, socket });
|
|
419
429
|
|
|
420
|
-
socket.on(event, (data
|
|
430
|
+
socket.on(event, (data, callback) => {
|
|
421
431
|
this.emit('websocket:event', { namespace, event, data });
|
|
422
432
|
|
|
423
433
|
Promise.resolve(handler(socket, data))
|
|
424
|
-
.then(
|
|
434
|
+
.then(result => {
|
|
425
435
|
this.emit('websocket:response', { namespace, event, result });
|
|
426
436
|
if (callback) callback(result);
|
|
427
437
|
else if (result) socket.emit(`${event}:response`, result);
|
|
428
438
|
})
|
|
429
|
-
.catch(
|
|
439
|
+
.catch(error => {
|
|
430
440
|
this.emit('websocket:error', { namespace, event, error });
|
|
431
441
|
const errorResponse = { success: false, error: error.message };
|
|
432
442
|
if (callback) callback(errorResponse);
|
|
@@ -437,40 +447,7 @@ export class Moro extends EventEmitter {
|
|
|
437
447
|
});
|
|
438
448
|
|
|
439
449
|
this.emit('websocket:registered', { namespace, handlers });
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
private async processQueuedWebSocketRegistrations() {
|
|
443
|
-
// Wait for WebSocket adapter to be ready
|
|
444
|
-
await this.coreFramework.ensureWebSocketReady();
|
|
445
|
-
|
|
446
|
-
const adapter = this.coreFramework.getWebSocketAdapter();
|
|
447
|
-
|
|
448
|
-
// Check if any unprocessed registrations exist
|
|
449
|
-
const unprocessedRegistrations = this.queuedWebSocketRegistrations.filter(r => !r.processed);
|
|
450
|
-
|
|
451
|
-
if (!adapter && unprocessedRegistrations.length > 0) {
|
|
452
|
-
throw new Error(
|
|
453
|
-
'WebSocket features require a WebSocket adapter.\n\n' +
|
|
454
|
-
'Option 1: Install socket.io (auto-detected):\n' +
|
|
455
|
-
' npm install socket.io\n' +
|
|
456
|
-
' const app = new Moro({ websocket: {} });\n\n' +
|
|
457
|
-
'Option 2: Configure a specific adapter:\n' +
|
|
458
|
-
" import { SocketIOAdapter } from '@morojs/moro';\n" +
|
|
459
|
-
' const app = new Moro({ websocket: { adapter: new SocketIOAdapter() } });\n\n' +
|
|
460
|
-
'Option 3: Enable in config file (moro.config.js):\n' +
|
|
461
|
-
' export default { websocket: { enabled: true } };'
|
|
462
|
-
);
|
|
463
|
-
}
|
|
464
|
-
|
|
465
|
-
if (adapter) {
|
|
466
|
-
// Process all unprocessed registrations
|
|
467
|
-
for (const registration of this.queuedWebSocketRegistrations) {
|
|
468
|
-
if (!registration.processed) {
|
|
469
|
-
this.processWebSocketRegistration(registration.namespace, registration.handlers, adapter);
|
|
470
|
-
registration.processed = true;
|
|
471
|
-
}
|
|
472
|
-
}
|
|
473
|
-
}
|
|
450
|
+
return this;
|
|
474
451
|
}
|
|
475
452
|
|
|
476
453
|
// Start server with events (Node.js only)
|
|
@@ -526,21 +503,9 @@ export class Moro extends EventEmitter {
|
|
|
526
503
|
}
|
|
527
504
|
|
|
528
505
|
// Check if clustering is enabled for massive performance gains
|
|
529
|
-
// NOTE: uWebSockets.js does NOT support Node.js clustering - it's single-threaded only
|
|
530
|
-
const usingUWebSockets = this.config.server?.useUWebSockets || false;
|
|
531
|
-
|
|
532
506
|
if (this.config.performance?.clustering?.enabled) {
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
'Clustering is not supported with uWebSockets.js - running in single-threaded mode. ' +
|
|
536
|
-
'uWebSockets is so fast that single-threaded performance often exceeds multi-threaded Node.js!',
|
|
537
|
-
'Cluster'
|
|
538
|
-
);
|
|
539
|
-
// Continue without clustering
|
|
540
|
-
} else {
|
|
541
|
-
this.startWithClustering(port, host as string, callback);
|
|
542
|
-
return;
|
|
543
|
-
}
|
|
507
|
+
this.startWithClustering(port, host as string, callback);
|
|
508
|
+
return;
|
|
544
509
|
}
|
|
545
510
|
this.eventBus.emit('server:starting', { port, runtime: this.runtimeType });
|
|
546
511
|
|
|
@@ -570,169 +535,34 @@ export class Moro extends EventEmitter {
|
|
|
570
535
|
this.registerDirectRoutes();
|
|
571
536
|
}
|
|
572
537
|
|
|
573
|
-
const
|
|
574
|
-
const
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
if (intelligentRoutes.length > 0) {
|
|
587
|
-
this.logger.info(`Intelligent Routes: ${intelligentRoutes.length} registered`, 'Server');
|
|
588
|
-
}
|
|
589
|
-
|
|
590
|
-
this.eventBus.emit('server:started', { port, runtime: this.runtimeType });
|
|
591
|
-
if (callback) callback();
|
|
592
|
-
};
|
|
593
|
-
|
|
594
|
-
if (host && typeof host === 'string') {
|
|
595
|
-
this.coreFramework.listen(port, host, actualCallback);
|
|
596
|
-
} else {
|
|
597
|
-
this.coreFramework.listen(port, actualCallback);
|
|
538
|
+
const actualCallback = () => {
|
|
539
|
+
const displayHost = host || 'localhost';
|
|
540
|
+
this.logger.info('Moro Server Started', 'Server');
|
|
541
|
+
this.logger.info(`Runtime: ${this.runtimeType}`, 'Server');
|
|
542
|
+
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');
|
|
545
|
+
this.logger.info('Learn more at https://morojs.com', 'Server');
|
|
546
|
+
|
|
547
|
+
// Log intelligent routes info
|
|
548
|
+
const intelligentRoutes = this.intelligentRouting.getIntelligentRoutes();
|
|
549
|
+
if (intelligentRoutes.length > 0) {
|
|
550
|
+
this.logger.info(`Intelligent Routes: ${intelligentRoutes.length} registered`, 'Server');
|
|
598
551
|
}
|
|
599
|
-
};
|
|
600
552
|
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
.then(() => {
|
|
604
|
-
startServer();
|
|
605
|
-
})
|
|
606
|
-
.catch(error => {
|
|
607
|
-
this.logger.error('Initialization failed during server start', 'Framework', {
|
|
608
|
-
error: error instanceof Error ? error.message : String(error),
|
|
609
|
-
});
|
|
610
|
-
// For auto-discovery failures, start server anyway
|
|
611
|
-
// For WebSocket failures with queued registrations, error will propagate
|
|
612
|
-
if (
|
|
613
|
-
error instanceof Error &&
|
|
614
|
-
error.message.includes('WebSocket features require a WebSocket adapter')
|
|
615
|
-
) {
|
|
616
|
-
throw error;
|
|
617
|
-
}
|
|
618
|
-
startServer();
|
|
619
|
-
});
|
|
620
|
-
}
|
|
621
|
-
|
|
622
|
-
// Public method to manually initialize auto-discovery
|
|
623
|
-
// Useful for ensuring auth middleware is registered before auto-discovery
|
|
624
|
-
async initializeAutoDiscoveryNow(): Promise<void> {
|
|
625
|
-
return this.ensureAutoDiscoveryComplete();
|
|
626
|
-
}
|
|
627
|
-
|
|
628
|
-
// Public API: Initialize modules explicitly after middleware setup
|
|
629
|
-
// This provides users with explicit control over module loading timing
|
|
630
|
-
// IMPORTANT: This forces module loading even if autoDiscovery.enabled is false
|
|
631
|
-
// Usage: app.initModules() or app.initModules({ paths: ['./my-modules'] })
|
|
632
|
-
initModules(options?: {
|
|
633
|
-
paths?: string[];
|
|
634
|
-
patterns?: string[];
|
|
635
|
-
recursive?: boolean;
|
|
636
|
-
loadingStrategy?: 'eager' | 'lazy' | 'conditional';
|
|
637
|
-
watchForChanges?: boolean;
|
|
638
|
-
ignorePatterns?: string[];
|
|
639
|
-
loadOrder?: 'alphabetical' | 'dependency' | 'custom';
|
|
640
|
-
failOnError?: boolean;
|
|
641
|
-
maxDepth?: number;
|
|
642
|
-
}): void {
|
|
643
|
-
this.logger.info('User-requested module initialization', 'ModuleSystem');
|
|
644
|
-
|
|
645
|
-
// If already initialized, do nothing
|
|
646
|
-
if (this.autoDiscoveryInitialized) {
|
|
647
|
-
this.logger.debug('Auto-discovery already completed, skipping', 'ModuleSystem');
|
|
648
|
-
return;
|
|
649
|
-
}
|
|
650
|
-
|
|
651
|
-
// Store the options and mark that we want to force initialization
|
|
652
|
-
this.autoDiscoveryOptions = {
|
|
653
|
-
autoDiscover: {
|
|
654
|
-
enabled: true, // Force enabled regardless of original config
|
|
655
|
-
paths: options?.paths || ['./modules', './src/modules'],
|
|
656
|
-
patterns: options?.patterns || [
|
|
657
|
-
'**/*.module.{ts,js}',
|
|
658
|
-
'**/index.{ts,js}',
|
|
659
|
-
'**/*.config.{ts,js}',
|
|
660
|
-
],
|
|
661
|
-
recursive: options?.recursive ?? true,
|
|
662
|
-
loadingStrategy: options?.loadingStrategy || ('eager' as const),
|
|
663
|
-
watchForChanges: options?.watchForChanges ?? false,
|
|
664
|
-
ignorePatterns: options?.ignorePatterns || [
|
|
665
|
-
'**/*.test.{ts,js}',
|
|
666
|
-
'**/*.spec.{ts,js}',
|
|
667
|
-
'**/node_modules/**',
|
|
668
|
-
],
|
|
669
|
-
loadOrder: options?.loadOrder || ('dependency' as const),
|
|
670
|
-
failOnError: options?.failOnError ?? false,
|
|
671
|
-
maxDepth: options?.maxDepth ?? 5,
|
|
672
|
-
},
|
|
553
|
+
this.eventBus.emit('server:started', { port, runtime: this.runtimeType });
|
|
554
|
+
if (callback) callback();
|
|
673
555
|
};
|
|
674
556
|
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
}
|
|
680
|
-
|
|
681
|
-
// Robust method to ensure auto-discovery is complete, handling race conditions
|
|
682
|
-
private async ensureAutoDiscoveryComplete(): Promise<void> {
|
|
683
|
-
// If already initialized, nothing to do
|
|
684
|
-
if (this.autoDiscoveryInitialized) {
|
|
685
|
-
return;
|
|
686
|
-
}
|
|
687
|
-
|
|
688
|
-
// If auto-discovery is disabled, mark as initialized
|
|
689
|
-
if (!this.autoDiscoveryOptions) {
|
|
690
|
-
this.autoDiscoveryInitialized = true;
|
|
691
|
-
return;
|
|
692
|
-
}
|
|
693
|
-
|
|
694
|
-
// If already in progress, wait for it to complete
|
|
695
|
-
if (this.autoDiscoveryPromise) {
|
|
696
|
-
return this.autoDiscoveryPromise;
|
|
697
|
-
}
|
|
698
|
-
|
|
699
|
-
// Start auto-discovery
|
|
700
|
-
this.autoDiscoveryPromise = this.performAutoDiscovery();
|
|
701
|
-
|
|
702
|
-
try {
|
|
703
|
-
await this.autoDiscoveryPromise;
|
|
704
|
-
this.autoDiscoveryInitialized = true;
|
|
705
|
-
} catch (error) {
|
|
706
|
-
// Reset promise on error so it can be retried
|
|
707
|
-
this.autoDiscoveryPromise = null;
|
|
708
|
-
throw error;
|
|
709
|
-
} finally {
|
|
710
|
-
this.autoDiscoveryOptions = null; // Clear after attempt
|
|
557
|
+
if (host && typeof host === 'string') {
|
|
558
|
+
this.coreFramework.listen(port, host, actualCallback);
|
|
559
|
+
} else {
|
|
560
|
+
this.coreFramework.listen(port, actualCallback);
|
|
711
561
|
}
|
|
712
562
|
}
|
|
713
563
|
|
|
714
|
-
// Perform the actual auto-discovery work
|
|
715
|
-
private async performAutoDiscovery(optionsOverride?: MoroOptions): Promise<void> {
|
|
716
|
-
const optionsToUse = optionsOverride || this.autoDiscoveryOptions;
|
|
717
|
-
if (!optionsToUse) return;
|
|
718
|
-
|
|
719
|
-
this.logger.debug('Starting auto-discovery initialization', 'AutoDiscovery');
|
|
720
|
-
|
|
721
|
-
await this.initializeAutoDiscovery(optionsToUse);
|
|
722
|
-
|
|
723
|
-
this.logger.debug('Auto-discovery initialization completed', 'AutoDiscovery');
|
|
724
|
-
}
|
|
725
|
-
|
|
726
564
|
// Get handler for non-Node.js runtimes
|
|
727
565
|
getHandler() {
|
|
728
|
-
// Ensure auto-discovery is complete for non-Node.js runtimes
|
|
729
|
-
// This handles the case where users call getHandler() immediately after createApp()
|
|
730
|
-
this.ensureAutoDiscoveryComplete().catch(error => {
|
|
731
|
-
this.logger.error('Auto-discovery initialization failed for runtime handler', 'Framework', {
|
|
732
|
-
error: error instanceof Error ? error.message : String(error),
|
|
733
|
-
});
|
|
734
|
-
});
|
|
735
|
-
|
|
736
566
|
// Create a unified request handler that works with the runtime adapter
|
|
737
567
|
const handler = async (req: HttpRequest, res: HttpResponse) => {
|
|
738
568
|
// Add documentation middleware first (if enabled)
|
|
@@ -977,6 +807,7 @@ export class Moro extends EventEmitter {
|
|
|
977
807
|
req.body = validated;
|
|
978
808
|
} catch (error: any) {
|
|
979
809
|
// Handle universal validation errors
|
|
810
|
+
const { normalizeValidationError } = require('./core/validation/schema-interface');
|
|
980
811
|
const normalizedError = normalizeValidationError(error);
|
|
981
812
|
res.status(400).json({
|
|
982
813
|
success: false,
|
|
@@ -1055,33 +886,20 @@ export class Moro extends EventEmitter {
|
|
|
1055
886
|
}
|
|
1056
887
|
|
|
1057
888
|
private setupDefaultMiddleware(options: MoroOptions) {
|
|
1058
|
-
// CORS
|
|
1059
|
-
if (
|
|
1060
|
-
const corsOptions =
|
|
1061
|
-
typeof options.cors === 'object'
|
|
1062
|
-
? options.cors
|
|
1063
|
-
: this.config.security.cors
|
|
1064
|
-
? this.config.security.cors
|
|
1065
|
-
: {};
|
|
889
|
+
// CORS
|
|
890
|
+
if (options.cors !== false) {
|
|
891
|
+
const corsOptions = typeof options.cors === 'object' ? options.cors : {};
|
|
1066
892
|
this.use(middleware.cors(corsOptions));
|
|
1067
893
|
}
|
|
1068
894
|
|
|
1069
|
-
// Helmet
|
|
1070
|
-
if (
|
|
895
|
+
// Helmet
|
|
896
|
+
if (options.helmet !== false) {
|
|
1071
897
|
this.use(middleware.helmet());
|
|
1072
898
|
}
|
|
1073
899
|
|
|
1074
|
-
// Compression
|
|
1075
|
-
if (
|
|
1076
|
-
|
|
1077
|
-
options.performance?.compression?.enabled === true
|
|
1078
|
-
) {
|
|
1079
|
-
const compressionOptions =
|
|
1080
|
-
typeof options.compression === 'object'
|
|
1081
|
-
? options.compression
|
|
1082
|
-
: this.config.performance.compression
|
|
1083
|
-
? this.config.performance.compression
|
|
1084
|
-
: {};
|
|
900
|
+
// Compression
|
|
901
|
+
if (options.compression !== false) {
|
|
902
|
+
const compressionOptions = typeof options.compression === 'object' ? options.compression : {};
|
|
1085
903
|
this.use(middleware.compression(compressionOptions));
|
|
1086
904
|
}
|
|
1087
905
|
|
|
@@ -1089,298 +907,83 @@ export class Moro extends EventEmitter {
|
|
|
1089
907
|
this.use(middleware.bodySize({ limit: '10mb' }));
|
|
1090
908
|
}
|
|
1091
909
|
|
|
1092
|
-
|
|
1093
|
-
private async initializeAutoDiscovery(options: MoroOptions): Promise<void> {
|
|
1094
|
-
const { ModuleDiscovery } = await import('./core/modules/auto-discovery.js');
|
|
1095
|
-
|
|
1096
|
-
// Merge auto-discovery configuration
|
|
1097
|
-
const autoDiscoveryConfig = this.mergeAutoDiscoveryConfig(options);
|
|
1098
|
-
|
|
1099
|
-
if (!autoDiscoveryConfig.enabled) {
|
|
1100
|
-
return;
|
|
1101
|
-
}
|
|
1102
|
-
|
|
1103
|
-
this.moduleDiscovery = new ModuleDiscovery(process.cwd());
|
|
1104
|
-
|
|
910
|
+
private autoDiscoverModules(modulesPath: string) {
|
|
1105
911
|
try {
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
async (updatedModules: ModuleConfig[]) => {
|
|
1120
|
-
await this.handleModuleChanges(updatedModules);
|
|
912
|
+
if (!statSync(modulesPath).isDirectory()) return;
|
|
913
|
+
|
|
914
|
+
const items = readdirSync(modulesPath);
|
|
915
|
+
items.forEach(item => {
|
|
916
|
+
const fullPath = join(modulesPath, item);
|
|
917
|
+
if (statSync(fullPath).isDirectory()) {
|
|
918
|
+
const indexPath = join(fullPath, 'index.ts');
|
|
919
|
+
try {
|
|
920
|
+
statSync(indexPath);
|
|
921
|
+
// Module directory found, will be loaded later
|
|
922
|
+
this.logger.debug(`Discovered module: ${item}`, 'ModuleDiscovery');
|
|
923
|
+
} catch {
|
|
924
|
+
// No index.ts, skip
|
|
1121
925
|
}
|
|
1122
|
-
);
|
|
1123
|
-
}
|
|
1124
|
-
|
|
1125
|
-
this.logger.info(
|
|
1126
|
-
`Auto-discovery completed: ${modules.length} modules loaded`,
|
|
1127
|
-
'ModuleDiscovery'
|
|
1128
|
-
);
|
|
1129
|
-
} catch (error) {
|
|
1130
|
-
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
1131
|
-
|
|
1132
|
-
if (autoDiscoveryConfig.failOnError) {
|
|
1133
|
-
throw new Error(`Module auto-discovery failed: ${errorMsg}`);
|
|
1134
|
-
} else {
|
|
1135
|
-
this.logger.warn(`Module auto-discovery failed: ${errorMsg}`, 'ModuleDiscovery');
|
|
1136
|
-
}
|
|
1137
|
-
}
|
|
1138
|
-
}
|
|
1139
|
-
|
|
1140
|
-
// Merge auto-discovery configuration from multiple sources
|
|
1141
|
-
private mergeAutoDiscoveryConfig(options: MoroOptions) {
|
|
1142
|
-
const defaultConfig = this.config.modules.autoDiscovery;
|
|
1143
|
-
|
|
1144
|
-
// Handle legacy modulesPath option
|
|
1145
|
-
if (options.modulesPath && !options.autoDiscover) {
|
|
1146
|
-
return {
|
|
1147
|
-
...defaultConfig,
|
|
1148
|
-
paths: [options.modulesPath],
|
|
1149
|
-
};
|
|
1150
|
-
}
|
|
1151
|
-
|
|
1152
|
-
// Handle boolean autoDiscover option
|
|
1153
|
-
if (typeof options.autoDiscover === 'boolean') {
|
|
1154
|
-
return {
|
|
1155
|
-
...defaultConfig,
|
|
1156
|
-
enabled: options.autoDiscover,
|
|
1157
|
-
};
|
|
1158
|
-
}
|
|
1159
|
-
|
|
1160
|
-
// Handle object autoDiscover option
|
|
1161
|
-
if (typeof options.autoDiscover === 'object') {
|
|
1162
|
-
return {
|
|
1163
|
-
...defaultConfig,
|
|
1164
|
-
...options.autoDiscover,
|
|
1165
|
-
};
|
|
1166
|
-
}
|
|
1167
|
-
|
|
1168
|
-
return defaultConfig;
|
|
1169
|
-
}
|
|
1170
|
-
|
|
1171
|
-
// Load discovered modules based on strategy
|
|
1172
|
-
private async loadDiscoveredModules(
|
|
1173
|
-
modules: ModuleConfig[],
|
|
1174
|
-
config: ModuleDefaultsConfig['autoDiscovery']
|
|
1175
|
-
): Promise<void> {
|
|
1176
|
-
switch (config.loadingStrategy) {
|
|
1177
|
-
case 'eager':
|
|
1178
|
-
// Load all modules immediately
|
|
1179
|
-
for (const module of modules) {
|
|
1180
|
-
await this.loadModule(module);
|
|
1181
|
-
}
|
|
1182
|
-
break;
|
|
1183
|
-
|
|
1184
|
-
case 'lazy':
|
|
1185
|
-
// Register modules for lazy loading
|
|
1186
|
-
this.registerLazyModules(modules);
|
|
1187
|
-
break;
|
|
1188
|
-
|
|
1189
|
-
case 'conditional':
|
|
1190
|
-
// Load modules based on conditions
|
|
1191
|
-
await this.loadConditionalModules(modules);
|
|
1192
|
-
break;
|
|
1193
|
-
|
|
1194
|
-
default:
|
|
1195
|
-
// Default to eager loading
|
|
1196
|
-
for (const module of modules) {
|
|
1197
|
-
await this.loadModule(module);
|
|
1198
926
|
}
|
|
927
|
+
});
|
|
928
|
+
} catch {
|
|
929
|
+
// Modules directory doesn't exist, that's fine
|
|
1199
930
|
}
|
|
1200
931
|
}
|
|
1201
932
|
|
|
1202
|
-
// Register modules for lazy loading
|
|
1203
|
-
private registerLazyModules(modules: ModuleConfig[]): void {
|
|
1204
|
-
modules.forEach(module => {
|
|
1205
|
-
// Store module for lazy loading when first route is accessed
|
|
1206
|
-
this.lazyModules.set(module.name, module);
|
|
1207
|
-
|
|
1208
|
-
// Register placeholder routes that trigger lazy loading
|
|
1209
|
-
if (module.routes) {
|
|
1210
|
-
module.routes.forEach(route => {
|
|
1211
|
-
const basePath = `/api/v${module.version}/${module.name}`;
|
|
1212
|
-
const fullPath = `${basePath}${route.path}`;
|
|
1213
|
-
|
|
1214
|
-
// Note: Lazy loading will be implemented when route is accessed
|
|
1215
|
-
// For now, we'll store the module for later loading
|
|
1216
|
-
this.logger.debug(
|
|
1217
|
-
`Registered lazy route: ${route.method} ${fullPath}`,
|
|
1218
|
-
'ModuleDiscovery'
|
|
1219
|
-
);
|
|
1220
|
-
});
|
|
1221
|
-
}
|
|
1222
|
-
});
|
|
1223
|
-
|
|
1224
|
-
this.logger.info(`Registered ${modules.length} modules for lazy loading`, 'ModuleDiscovery');
|
|
1225
|
-
}
|
|
1226
|
-
|
|
1227
|
-
// Load modules conditionally based on environment or configuration
|
|
1228
|
-
private async loadConditionalModules(modules: ModuleConfig[]): Promise<void> {
|
|
1229
|
-
for (const module of modules) {
|
|
1230
|
-
const shouldLoad = this.shouldLoadModule(module);
|
|
1231
|
-
|
|
1232
|
-
if (shouldLoad) {
|
|
1233
|
-
await this.loadModule(module);
|
|
1234
|
-
} else {
|
|
1235
|
-
this.logger.debug(`Skipping module ${module.name} due to conditions`, 'ModuleDiscovery');
|
|
1236
|
-
}
|
|
1237
|
-
}
|
|
1238
|
-
}
|
|
1239
|
-
|
|
1240
|
-
// Determine if a module should be loaded based on conditions
|
|
1241
|
-
private shouldLoadModule(module: ModuleConfig): boolean {
|
|
1242
|
-
const moduleConfig = module.config as any;
|
|
1243
|
-
|
|
1244
|
-
// Check environment conditions
|
|
1245
|
-
if (moduleConfig?.conditions?.environment) {
|
|
1246
|
-
const requiredEnv = moduleConfig.conditions.environment;
|
|
1247
|
-
const currentEnv = process.env.NODE_ENV || 'development';
|
|
1248
|
-
|
|
1249
|
-
if (Array.isArray(requiredEnv)) {
|
|
1250
|
-
if (!requiredEnv.includes(currentEnv)) {
|
|
1251
|
-
return false;
|
|
1252
|
-
}
|
|
1253
|
-
} else if (requiredEnv !== currentEnv) {
|
|
1254
|
-
return false;
|
|
1255
|
-
}
|
|
1256
|
-
}
|
|
1257
|
-
|
|
1258
|
-
// Check feature flags
|
|
1259
|
-
if (moduleConfig?.conditions?.features) {
|
|
1260
|
-
const requiredFeatures = moduleConfig.conditions.features;
|
|
1261
|
-
|
|
1262
|
-
for (const feature of requiredFeatures) {
|
|
1263
|
-
if (!process.env[`FEATURE_${feature.toUpperCase()}`]) {
|
|
1264
|
-
return false;
|
|
1265
|
-
}
|
|
1266
|
-
}
|
|
1267
|
-
}
|
|
1268
|
-
|
|
1269
|
-
// Check custom conditions
|
|
1270
|
-
if (moduleConfig?.conditions?.custom) {
|
|
1271
|
-
const customCondition = moduleConfig.conditions.custom;
|
|
1272
|
-
|
|
1273
|
-
if (typeof customCondition === 'function') {
|
|
1274
|
-
return customCondition();
|
|
1275
|
-
}
|
|
1276
|
-
}
|
|
1277
|
-
|
|
1278
|
-
return true;
|
|
1279
|
-
}
|
|
1280
|
-
|
|
1281
|
-
// Handle module changes during development
|
|
1282
|
-
private async handleModuleChanges(modules: ModuleConfig[]): Promise<void> {
|
|
1283
|
-
this.logger.info('Module changes detected, reloading...', 'ModuleDiscovery');
|
|
1284
|
-
|
|
1285
|
-
// Unload existing modules (if supported)
|
|
1286
|
-
// For now, just log the change
|
|
1287
|
-
this.eventBus.emit('modules:changed', {
|
|
1288
|
-
modules: modules.map(m => ({ name: m.name, version: m.version })),
|
|
1289
|
-
timestamp: new Date(),
|
|
1290
|
-
});
|
|
1291
|
-
}
|
|
1292
|
-
|
|
1293
|
-
// Legacy method for backward compatibility
|
|
1294
|
-
private autoDiscoverModules(modulesPath: string) {
|
|
1295
|
-
// Redirect to new system
|
|
1296
|
-
this.initializeAutoDiscovery({
|
|
1297
|
-
autoDiscover: {
|
|
1298
|
-
enabled: true,
|
|
1299
|
-
paths: [modulesPath],
|
|
1300
|
-
},
|
|
1301
|
-
});
|
|
1302
|
-
}
|
|
1303
|
-
|
|
1304
933
|
private async importModule(modulePath: string): Promise<ModuleConfig> {
|
|
1305
934
|
const module = await import(modulePath);
|
|
1306
935
|
return module.default || module;
|
|
1307
936
|
}
|
|
1308
937
|
|
|
1309
|
-
|
|
1310
|
-
* Node.js Clustering Implementation
|
|
1311
|
-
* This clustering algorithm is based on published research and Node.js best practices.
|
|
1312
|
-
*
|
|
1313
|
-
* IPC (Inter-Process Communication) Considerations:
|
|
1314
|
-
* - Excessive workers create IPC bottlenecks (Source: BetterStack Node.js Guide)
|
|
1315
|
-
* - Round-robin scheduling provides better load distribution (Node.js Documentation)
|
|
1316
|
-
* - Message passing overhead increases significantly with worker count
|
|
1317
|
-
*
|
|
1318
|
-
* Memory Management:
|
|
1319
|
-
* - ~2GB per worker prevents memory pressure and GC overhead
|
|
1320
|
-
* - Conservative heap limits reduce memory fragmentation
|
|
1321
|
-
*
|
|
1322
|
-
* References:
|
|
1323
|
-
* - Node.js Cluster Documentation: https://nodejs.org/api/cluster.html
|
|
1324
|
-
* - BetterStack Node.js Clustering: https://betterstack.com/community/guides/scaling-nodejs/node-clustering/
|
|
1325
|
-
*/
|
|
938
|
+
// Clustering support for massive performance gains with proper cleanup
|
|
1326
939
|
private clusterWorkers = new Map<number, any>();
|
|
1327
|
-
|
|
1328
940
|
private startWithClustering(port: number, host?: string, callback?: () => void): void {
|
|
1329
|
-
|
|
941
|
+
const cluster = require('cluster');
|
|
942
|
+
const os = require('os');
|
|
943
|
+
|
|
944
|
+
// Smart worker count calculation based on actual bottlenecks
|
|
1330
945
|
let workerCount = this.config.performance?.clustering?.workers || os.cpus().length;
|
|
1331
946
|
|
|
1332
|
-
//
|
|
1333
|
-
if (workerCount === 'auto') {
|
|
947
|
+
// Auto-optimize worker count based on system characteristics
|
|
948
|
+
if (workerCount === 'auto' || workerCount > 8) {
|
|
949
|
+
// For high-core machines, limit workers to prevent IPC/memory bottlenecks
|
|
1334
950
|
const cpuCount = os.cpus().length;
|
|
1335
951
|
const totalMemoryGB = os.totalmem() / (1024 * 1024 * 1024);
|
|
1336
952
|
|
|
1337
|
-
//
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
953
|
+
// Optimal worker count formula based on research
|
|
954
|
+
if (cpuCount >= 16) {
|
|
955
|
+
// High-core machines: focus on memory/IPC efficiency
|
|
956
|
+
workerCount = Math.min(Math.ceil(totalMemoryGB / 2), 4); // 2GB per worker max, cap at 4
|
|
957
|
+
} else if (cpuCount >= 8) {
|
|
958
|
+
// Mid-range machines: balanced approach
|
|
959
|
+
workerCount = Math.min(cpuCount / 2, 4);
|
|
960
|
+
} else {
|
|
961
|
+
// Low-core machines: use all cores
|
|
962
|
+
workerCount = cpuCount;
|
|
1344
963
|
}
|
|
1345
964
|
|
|
1346
|
-
// Conservative formula based on general guidelines:
|
|
1347
|
-
// - Don't exceed CPU cores
|
|
1348
|
-
// - Respect user's memory allocation preference
|
|
1349
|
-
// - Let the system resources determine the limit
|
|
1350
|
-
workerCount = Math.min(
|
|
1351
|
-
cpuCount, // Don't exceed CPU cores
|
|
1352
|
-
Math.floor(totalMemoryGB / memoryPerWorkerGB) // User-configurable memory per worker
|
|
1353
|
-
);
|
|
1354
|
-
|
|
1355
965
|
this.logger.info(
|
|
1356
|
-
`Auto-
|
|
966
|
+
`Auto-optimized workers: ${workerCount} (CPU: ${cpuCount}, RAM: ${totalMemoryGB.toFixed(1)}GB)`,
|
|
1357
967
|
'Cluster'
|
|
1358
968
|
);
|
|
1359
|
-
} else if (typeof workerCount === 'number') {
|
|
1360
|
-
// User specified a number - respect their choice
|
|
1361
|
-
this.logger.info(`Using user-specified worker count: ${workerCount}`, 'Cluster');
|
|
1362
969
|
}
|
|
1363
970
|
|
|
1364
971
|
if (cluster.isPrimary) {
|
|
1365
|
-
this.logger.info(
|
|
972
|
+
this.logger.info(`🚀 Starting ${workerCount} workers for maximum performance`, 'Cluster');
|
|
1366
973
|
|
|
1367
974
|
// Optimize cluster scheduling for high concurrency
|
|
1368
|
-
// Round-robin
|
|
1369
|
-
// Provides better load distribution than shared socket approach
|
|
1370
|
-
cluster.schedulingPolicy = cluster.SCHED_RR;
|
|
975
|
+
cluster.schedulingPolicy = cluster.SCHED_RR; // Round-robin scheduling
|
|
1371
976
|
|
|
1372
977
|
// Set cluster settings for better performance
|
|
1373
978
|
cluster.setupMaster({
|
|
1374
|
-
exec: process.argv[1]
|
|
979
|
+
exec: process.argv[1],
|
|
1375
980
|
args: process.argv.slice(2),
|
|
1376
981
|
silent: false,
|
|
1377
982
|
});
|
|
1378
983
|
|
|
1379
|
-
// IPC
|
|
1380
|
-
// Research shows excessive IPC can create bottlenecks in clustered applications
|
|
1381
|
-
// (Source: BetterStack - Node.js Clustering Guide)
|
|
984
|
+
// Optimize IPC to reduce communication overhead
|
|
1382
985
|
process.env.NODE_CLUSTER_SCHED_POLICY = 'rr'; // Ensure round-robin
|
|
1383
|
-
process.env.NODE_DISABLE_COLORS = '1'; // Reduce IPC message size
|
|
986
|
+
process.env.NODE_DISABLE_COLORS = '1'; // Reduce IPC message size
|
|
1384
987
|
|
|
1385
988
|
// Graceful shutdown handler
|
|
1386
989
|
const gracefulShutdown = () => {
|
|
@@ -1401,32 +1004,37 @@ export class Moro extends EventEmitter {
|
|
|
1401
1004
|
process.on('SIGINT', gracefulShutdown);
|
|
1402
1005
|
process.on('SIGTERM', gracefulShutdown);
|
|
1403
1006
|
|
|
1404
|
-
// Fork workers with
|
|
1007
|
+
// Fork workers with proper tracking and CPU affinity
|
|
1405
1008
|
for (let i = 0; i < workerCount; i++) {
|
|
1406
|
-
const worker = cluster.fork(
|
|
1009
|
+
const worker = cluster.fork({
|
|
1010
|
+
WORKER_ID: i,
|
|
1011
|
+
WORKER_CPU_AFFINITY: i % os.cpus().length, // Distribute workers across CPUs
|
|
1012
|
+
});
|
|
1407
1013
|
this.clusterWorkers.set(worker.process.pid!, worker);
|
|
1408
|
-
this.logger.info(
|
|
1014
|
+
this.logger.info(
|
|
1015
|
+
`Worker ${worker.process.pid} started (CPU ${i % os.cpus().length})`,
|
|
1016
|
+
'Cluster'
|
|
1017
|
+
);
|
|
1409
1018
|
|
|
1410
|
-
// Handle individual worker messages
|
|
1019
|
+
// Handle individual worker messages (reuse handler)
|
|
1411
1020
|
worker.on('message', this.handleWorkerMessage.bind(this));
|
|
1412
1021
|
}
|
|
1413
1022
|
|
|
1414
|
-
//
|
|
1023
|
+
// Handle worker exits with cleanup
|
|
1415
1024
|
cluster.on('exit', (worker: any, code: number, signal: string) => {
|
|
1416
|
-
|
|
1417
|
-
this.clusterWorkers.delete(pid);
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
}
|
|
1025
|
+
// Clean up worker tracking
|
|
1026
|
+
this.clusterWorkers.delete(worker.process.pid);
|
|
1027
|
+
|
|
1028
|
+
this.logger.warn(
|
|
1029
|
+
`Worker ${worker.process.pid} died (${signal || code}). Restarting...`,
|
|
1030
|
+
'Cluster'
|
|
1031
|
+
);
|
|
1032
|
+
|
|
1033
|
+
// Restart worker with proper tracking
|
|
1034
|
+
const newWorker = cluster.fork();
|
|
1035
|
+
this.clusterWorkers.set(newWorker.process.pid!, newWorker);
|
|
1036
|
+
newWorker.on('message', this.handleWorkerMessage.bind(this));
|
|
1037
|
+
this.logger.info(`Worker ${newWorker.process.pid} started`, 'Cluster');
|
|
1430
1038
|
});
|
|
1431
1039
|
|
|
1432
1040
|
// Master process callback
|
|
@@ -1439,28 +1047,13 @@ export class Moro extends EventEmitter {
|
|
|
1439
1047
|
process.env.UV_THREADPOOL_SIZE = '64';
|
|
1440
1048
|
|
|
1441
1049
|
// Reduce logging contention in workers (major bottleneck)
|
|
1442
|
-
// Multiple workers writing to same log files creates I/O contention
|
|
1443
1050
|
if (this.config.logging) {
|
|
1444
1051
|
// Workers log less frequently to reduce I/O contention
|
|
1445
|
-
|
|
1052
|
+
this.config.logging.level = 'warn'; // Only warnings and errors
|
|
1446
1053
|
}
|
|
1447
1054
|
|
|
1448
|
-
//
|
|
1449
|
-
|
|
1450
|
-
const workerCount = Object.keys(cluster.workers || {}).length || 1;
|
|
1451
|
-
|
|
1452
|
-
// Conservative memory allocation
|
|
1453
|
-
const heapSizePerWorkerMB = Math.min(
|
|
1454
|
-
Math.floor(((totalMemoryGB * 1024) / workerCount) * 0.8), // 80% of available memory
|
|
1455
|
-
1536 // Cap at 1.5GB (GC efficiency threshold from research)
|
|
1456
|
-
);
|
|
1457
|
-
|
|
1458
|
-
process.env.NODE_OPTIONS = `--max-old-space-size=${heapSizePerWorkerMB}`;
|
|
1459
|
-
|
|
1460
|
-
this.logger.debug(
|
|
1461
|
-
`Worker memory allocated: ${heapSizePerWorkerMB}MB heap (${workerCount} workers, ${totalMemoryGB.toFixed(1)}GB total)`,
|
|
1462
|
-
'Worker'
|
|
1463
|
-
);
|
|
1055
|
+
// Memory optimization for workers
|
|
1056
|
+
process.env.NODE_OPTIONS = '--max-old-space-size=1024'; // Limit memory per worker
|
|
1464
1057
|
|
|
1465
1058
|
// Optimize V8 flags for better performance (Rust-level optimizations)
|
|
1466
1059
|
if (process.env.NODE_ENV === 'production') {
|
|
@@ -1548,37 +1141,15 @@ export class Moro extends EventEmitter {
|
|
|
1548
1141
|
});
|
|
1549
1142
|
};
|
|
1550
1143
|
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
} else {
|
|
1557
|
-
this.coreFramework.listen(port, workerCallback);
|
|
1558
|
-
}
|
|
1559
|
-
})
|
|
1560
|
-
.catch(error => {
|
|
1561
|
-
this.logger.error('WebSocket initialization failed in worker', 'Worker', {
|
|
1562
|
-
error: error instanceof Error ? error.message : String(error),
|
|
1563
|
-
});
|
|
1564
|
-
// For WebSocket failures with queued registrations, error will propagate
|
|
1565
|
-
if (
|
|
1566
|
-
error instanceof Error &&
|
|
1567
|
-
error.message.includes('WebSocket features require a WebSocket adapter')
|
|
1568
|
-
) {
|
|
1569
|
-
throw error;
|
|
1570
|
-
}
|
|
1571
|
-
// Start anyway for other errors
|
|
1572
|
-
if (host) {
|
|
1573
|
-
this.coreFramework.listen(port, host, workerCallback);
|
|
1574
|
-
} else {
|
|
1575
|
-
this.coreFramework.listen(port, workerCallback);
|
|
1576
|
-
}
|
|
1577
|
-
});
|
|
1144
|
+
if (host) {
|
|
1145
|
+
this.coreFramework.listen(port, host, workerCallback);
|
|
1146
|
+
} else {
|
|
1147
|
+
this.coreFramework.listen(port, workerCallback);
|
|
1148
|
+
}
|
|
1578
1149
|
}
|
|
1579
1150
|
}
|
|
1580
1151
|
|
|
1581
|
-
//
|
|
1152
|
+
// Reusable worker message handler (avoids creating new functions)
|
|
1582
1153
|
private handleWorkerMessage(message: any): void {
|
|
1583
1154
|
// Handle inter-worker communication if needed
|
|
1584
1155
|
if (message.type === 'health-check') {
|
|
@@ -1589,58 +1160,6 @@ export class Moro extends EventEmitter {
|
|
|
1589
1160
|
// Log other worker messages
|
|
1590
1161
|
this.logger.debug(`Worker message: ${JSON.stringify(message)}`, 'Cluster');
|
|
1591
1162
|
}
|
|
1592
|
-
|
|
1593
|
-
/**
|
|
1594
|
-
* Gracefully close the application and clean up resources
|
|
1595
|
-
* This should be called in tests and during shutdown
|
|
1596
|
-
*/
|
|
1597
|
-
async close(): Promise<void> {
|
|
1598
|
-
this.logger.debug('Closing Moro application...');
|
|
1599
|
-
|
|
1600
|
-
// Flush logger buffer before shutdown
|
|
1601
|
-
try {
|
|
1602
|
-
// Use flushBuffer for immediate synchronous flush
|
|
1603
|
-
this.logger.flushBuffer();
|
|
1604
|
-
} catch (error) {
|
|
1605
|
-
// Ignore flush errors during shutdown
|
|
1606
|
-
}
|
|
1607
|
-
|
|
1608
|
-
// Close the core framework with timeout
|
|
1609
|
-
if (this.coreFramework && (this.coreFramework as any).httpServer) {
|
|
1610
|
-
try {
|
|
1611
|
-
await Promise.race([
|
|
1612
|
-
new Promise<void>(resolve => {
|
|
1613
|
-
(this.coreFramework as any).httpServer.close(() => {
|
|
1614
|
-
resolve();
|
|
1615
|
-
});
|
|
1616
|
-
}),
|
|
1617
|
-
new Promise<void>(resolve => setTimeout(resolve, 2000)), // 2 second timeout
|
|
1618
|
-
]);
|
|
1619
|
-
} catch (error) {
|
|
1620
|
-
// Force close if graceful close fails
|
|
1621
|
-
this.logger.warn('Force closing HTTP server due to timeout');
|
|
1622
|
-
}
|
|
1623
|
-
}
|
|
1624
|
-
|
|
1625
|
-
// Clean up module discovery watchers
|
|
1626
|
-
if (this.moduleDiscovery && typeof this.moduleDiscovery.cleanup === 'function') {
|
|
1627
|
-
try {
|
|
1628
|
-
this.moduleDiscovery.cleanup();
|
|
1629
|
-
} catch (error) {
|
|
1630
|
-
// Ignore cleanup errors
|
|
1631
|
-
}
|
|
1632
|
-
}
|
|
1633
|
-
|
|
1634
|
-
// Clean up event listeners
|
|
1635
|
-
try {
|
|
1636
|
-
this.eventBus.removeAllListeners();
|
|
1637
|
-
this.removeAllListeners();
|
|
1638
|
-
} catch (error) {
|
|
1639
|
-
// Ignore cleanup errors
|
|
1640
|
-
}
|
|
1641
|
-
|
|
1642
|
-
this.logger.debug('Moro application closed successfully');
|
|
1643
|
-
}
|
|
1644
1163
|
}
|
|
1645
1164
|
|
|
1646
1165
|
// Export convenience function
|