@morojs/moro 1.6.1 → 1.6.2

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 (404) hide show
  1. package/README.md +74 -256
  2. package/dist/core/auth/morojs-adapter.js +20 -20
  3. package/dist/core/auth/morojs-adapter.js.map +1 -1
  4. package/dist/core/config/config-manager.d.ts +44 -0
  5. package/dist/core/config/config-manager.js +104 -0
  6. package/dist/core/config/config-manager.js.map +1 -0
  7. package/dist/core/config/config-sources.d.ts +21 -0
  8. package/dist/core/config/config-sources.js +503 -0
  9. package/dist/core/config/config-sources.js.map +1 -0
  10. package/dist/core/config/config-validator.d.ts +21 -0
  11. package/dist/core/config/config-validator.js +791 -0
  12. package/dist/core/config/config-validator.js.map +1 -0
  13. package/dist/core/config/file-loader.d.ts +1 -6
  14. package/dist/core/config/file-loader.js +21 -249
  15. package/dist/core/config/file-loader.js.map +1 -1
  16. package/dist/core/config/index.d.ts +41 -12
  17. package/dist/core/config/index.js +65 -54
  18. package/dist/core/config/index.js.map +1 -1
  19. package/dist/core/config/schema.d.ts +2 -2
  20. package/dist/core/config/schema.js +55 -44
  21. package/dist/core/config/schema.js.map +1 -1
  22. package/dist/core/config/utils.d.ts +10 -3
  23. package/dist/core/config/utils.js +31 -58
  24. package/dist/core/config/utils.js.map +1 -1
  25. package/dist/core/database/adapters/drizzle.d.ts +1 -1
  26. package/dist/core/database/adapters/drizzle.js +18 -11
  27. package/dist/core/database/adapters/drizzle.js.map +1 -1
  28. package/dist/core/database/adapters/index.d.ts +7 -7
  29. package/dist/core/database/adapters/index.js +19 -29
  30. package/dist/core/database/adapters/index.js.map +1 -1
  31. package/dist/core/database/adapters/mongodb.d.ts +13 -1
  32. package/dist/core/database/adapters/mongodb.js +46 -10
  33. package/dist/core/database/adapters/mongodb.js.map +1 -1
  34. package/dist/core/database/adapters/mysql.d.ts +14 -1
  35. package/dist/core/database/adapters/mysql.js +19 -9
  36. package/dist/core/database/adapters/mysql.js.map +1 -1
  37. package/dist/core/database/adapters/postgresql.d.ts +12 -2
  38. package/dist/core/database/adapters/postgresql.js +19 -9
  39. package/dist/core/database/adapters/postgresql.js.map +1 -1
  40. package/dist/core/database/adapters/redis.d.ts +12 -1
  41. package/dist/core/database/adapters/redis.js +48 -13
  42. package/dist/core/database/adapters/redis.js.map +1 -1
  43. package/dist/core/database/adapters/sqlite.d.ts +3 -1
  44. package/dist/core/database/adapters/sqlite.js +19 -8
  45. package/dist/core/database/adapters/sqlite.js.map +1 -1
  46. package/dist/core/database/index.d.ts +2 -2
  47. package/dist/core/database/index.js +2 -18
  48. package/dist/core/database/index.js.map +1 -1
  49. package/dist/core/docs/index.d.ts +9 -9
  50. package/dist/core/docs/index.js +14 -35
  51. package/dist/core/docs/index.js.map +1 -1
  52. package/dist/core/docs/openapi-generator.d.ts +2 -2
  53. package/dist/core/docs/openapi-generator.js +11 -16
  54. package/dist/core/docs/openapi-generator.js.map +1 -1
  55. package/dist/core/docs/schema-to-openapi.d.ts +2 -2
  56. package/dist/core/docs/schema-to-openapi.js +5 -11
  57. package/dist/core/docs/schema-to-openapi.js.map +1 -1
  58. package/dist/core/docs/simple-docs.d.ts +1 -1
  59. package/dist/core/docs/simple-docs.js +4 -9
  60. package/dist/core/docs/simple-docs.js.map +1 -1
  61. package/dist/core/docs/swagger-ui.d.ts +2 -2
  62. package/dist/core/docs/swagger-ui.js +26 -29
  63. package/dist/core/docs/swagger-ui.js.map +1 -1
  64. package/dist/core/docs/zod-to-openapi.js +31 -28
  65. package/dist/core/docs/zod-to-openapi.js.map +1 -1
  66. package/dist/core/events/event-bus.d.ts +1 -1
  67. package/dist/core/events/event-bus.js +7 -11
  68. package/dist/core/events/event-bus.js.map +1 -1
  69. package/dist/core/events/index.d.ts +2 -2
  70. package/dist/core/events/index.js +1 -5
  71. package/dist/core/events/index.js.map +1 -1
  72. package/dist/core/framework.d.ts +20 -13
  73. package/dist/core/framework.js +285 -102
  74. package/dist/core/framework.js.map +1 -1
  75. package/dist/core/http/http-server.d.ts +59 -7
  76. package/dist/core/http/http-server.js +190 -176
  77. package/dist/core/http/http-server.js.map +1 -1
  78. package/dist/core/http/index.d.ts +4 -3
  79. package/dist/core/http/index.js +3 -8
  80. package/dist/core/http/index.js.map +1 -1
  81. package/dist/core/http/uws-http-server.d.ts +46 -0
  82. package/dist/core/http/uws-http-server.js +523 -0
  83. package/dist/core/http/uws-http-server.js.map +1 -0
  84. package/dist/core/logger/filters.d.ts +1 -1
  85. package/dist/core/logger/filters.js +20 -23
  86. package/dist/core/logger/filters.js.map +1 -1
  87. package/dist/core/logger/index.d.ts +3 -3
  88. package/dist/core/logger/index.js +2 -24
  89. package/dist/core/logger/index.js.map +1 -1
  90. package/dist/core/logger/logger.d.ts +30 -14
  91. package/dist/core/logger/logger.js +398 -223
  92. package/dist/core/logger/logger.js.map +1 -1
  93. package/dist/core/logger/outputs.d.ts +1 -1
  94. package/dist/core/logger/outputs.js +8 -17
  95. package/dist/core/logger/outputs.js.map +1 -1
  96. package/dist/core/middleware/built-in/adapters/cache/file.d.ts +1 -1
  97. package/dist/core/middleware/built-in/adapters/cache/file.js +10 -47
  98. package/dist/core/middleware/built-in/adapters/cache/file.js.map +1 -1
  99. package/dist/core/middleware/built-in/adapters/cache/index.d.ts +4 -4
  100. package/dist/core/middleware/built-in/adapters/cache/index.js +10 -17
  101. package/dist/core/middleware/built-in/adapters/cache/index.js.map +1 -1
  102. package/dist/core/middleware/built-in/adapters/cache/memory.d.ts +1 -1
  103. package/dist/core/middleware/built-in/adapters/cache/memory.js +3 -7
  104. package/dist/core/middleware/built-in/adapters/cache/memory.js.map +1 -1
  105. package/dist/core/middleware/built-in/adapters/cache/redis.d.ts +3 -1
  106. package/dist/core/middleware/built-in/adapters/cache/redis.js +11 -9
  107. package/dist/core/middleware/built-in/adapters/cache/redis.js.map +1 -1
  108. package/dist/core/middleware/built-in/adapters/cdn/azure.d.ts +1 -1
  109. package/dist/core/middleware/built-in/adapters/cdn/azure.js +3 -7
  110. package/dist/core/middleware/built-in/adapters/cdn/azure.js.map +1 -1
  111. package/dist/core/middleware/built-in/adapters/cdn/cloudflare.d.ts +1 -1
  112. package/dist/core/middleware/built-in/adapters/cdn/cloudflare.js +3 -7
  113. package/dist/core/middleware/built-in/adapters/cdn/cloudflare.js.map +1 -1
  114. package/dist/core/middleware/built-in/adapters/cdn/cloudfront.d.ts +3 -1
  115. package/dist/core/middleware/built-in/adapters/cdn/cloudfront.js +12 -10
  116. package/dist/core/middleware/built-in/adapters/cdn/cloudfront.js.map +1 -1
  117. package/dist/core/middleware/built-in/adapters/cdn/index.d.ts +4 -4
  118. package/dist/core/middleware/built-in/adapters/cdn/index.js +10 -17
  119. package/dist/core/middleware/built-in/adapters/cdn/index.js.map +1 -1
  120. package/dist/core/middleware/built-in/adapters/index.d.ts +4 -4
  121. package/dist/core/middleware/built-in/adapters/index.js +4 -23
  122. package/dist/core/middleware/built-in/adapters/index.js.map +1 -1
  123. package/dist/core/middleware/built-in/auth-helpers.js +11 -22
  124. package/dist/core/middleware/built-in/auth-helpers.js.map +1 -1
  125. package/dist/core/middleware/built-in/auth-providers.d.ts +1 -1
  126. package/dist/core/middleware/built-in/auth-providers.js +4 -9
  127. package/dist/core/middleware/built-in/auth-providers.js.map +1 -1
  128. package/dist/core/middleware/built-in/auth.d.ts +2 -2
  129. package/dist/core/middleware/built-in/auth.js +93 -26
  130. package/dist/core/middleware/built-in/auth.js.map +1 -1
  131. package/dist/core/middleware/built-in/cache.d.ts +2 -2
  132. package/dist/core/middleware/built-in/cache.js +11 -12
  133. package/dist/core/middleware/built-in/cache.js.map +1 -1
  134. package/dist/core/middleware/built-in/cdn.d.ts +2 -2
  135. package/dist/core/middleware/built-in/cdn.js +5 -9
  136. package/dist/core/middleware/built-in/cdn.js.map +1 -1
  137. package/dist/core/middleware/built-in/cookie.d.ts +1 -1
  138. package/dist/core/middleware/built-in/cookie.js +3 -7
  139. package/dist/core/middleware/built-in/cookie.js.map +1 -1
  140. package/dist/core/middleware/built-in/cors.d.ts +1 -1
  141. package/dist/core/middleware/built-in/cors.js +3 -7
  142. package/dist/core/middleware/built-in/cors.js.map +1 -1
  143. package/dist/core/middleware/built-in/csp.d.ts +1 -1
  144. package/dist/core/middleware/built-in/csp.js +5 -8
  145. package/dist/core/middleware/built-in/csp.js.map +1 -1
  146. package/dist/core/middleware/built-in/csrf.d.ts +1 -1
  147. package/dist/core/middleware/built-in/csrf.js +5 -8
  148. package/dist/core/middleware/built-in/csrf.js.map +1 -1
  149. package/dist/core/middleware/built-in/error-tracker.js +3 -7
  150. package/dist/core/middleware/built-in/error-tracker.js.map +1 -1
  151. package/dist/core/middleware/built-in/index.d.ts +28 -27
  152. package/dist/core/middleware/built-in/index.js +48 -78
  153. package/dist/core/middleware/built-in/index.js.map +1 -1
  154. package/dist/core/middleware/built-in/jwt-helpers.d.ts +118 -0
  155. package/dist/core/middleware/built-in/jwt-helpers.js +218 -0
  156. package/dist/core/middleware/built-in/jwt-helpers.js.map +1 -0
  157. package/dist/core/middleware/built-in/performance-monitor.js +3 -7
  158. package/dist/core/middleware/built-in/performance-monitor.js.map +1 -1
  159. package/dist/core/middleware/built-in/rate-limit.d.ts +1 -1
  160. package/dist/core/middleware/built-in/rate-limit.js +3 -7
  161. package/dist/core/middleware/built-in/rate-limit.js.map +1 -1
  162. package/dist/core/middleware/built-in/request-logger.js +5 -8
  163. package/dist/core/middleware/built-in/request-logger.js.map +1 -1
  164. package/dist/core/middleware/built-in/session.d.ts +2 -2
  165. package/dist/core/middleware/built-in/session.js +11 -15
  166. package/dist/core/middleware/built-in/session.js.map +1 -1
  167. package/dist/core/middleware/built-in/sse.d.ts +1 -1
  168. package/dist/core/middleware/built-in/sse.js +12 -14
  169. package/dist/core/middleware/built-in/sse.js.map +1 -1
  170. package/dist/core/middleware/built-in/validation.d.ts +1 -1
  171. package/dist/core/middleware/built-in/validation.js +3 -7
  172. package/dist/core/middleware/built-in/validation.js.map +1 -1
  173. package/dist/core/middleware/index.d.ts +4 -4
  174. package/dist/core/middleware/index.js +8 -28
  175. package/dist/core/middleware/index.js.map +1 -1
  176. package/dist/core/modules/auto-discovery.d.ts +19 -2
  177. package/dist/core/modules/auto-discovery.js +391 -74
  178. package/dist/core/modules/auto-discovery.js.map +1 -1
  179. package/dist/core/modules/index.d.ts +2 -2
  180. package/dist/core/modules/index.js +2 -9
  181. package/dist/core/modules/index.js.map +1 -1
  182. package/dist/core/modules/modules.d.ts +3 -3
  183. package/dist/core/modules/modules.js +23 -54
  184. package/dist/core/modules/modules.js.map +1 -1
  185. package/dist/core/networking/adapters/index.d.ts +4 -3
  186. package/dist/core/networking/adapters/index.js +3 -7
  187. package/dist/core/networking/adapters/index.js.map +1 -1
  188. package/dist/core/networking/adapters/socketio-adapter.d.ts +1 -1
  189. package/dist/core/networking/adapters/socketio-adapter.js +5 -40
  190. package/dist/core/networking/adapters/socketio-adapter.js.map +1 -1
  191. package/dist/core/networking/adapters/uws-adapter.d.ts +44 -0
  192. package/dist/core/networking/adapters/uws-adapter.js +513 -0
  193. package/dist/core/networking/adapters/uws-adapter.js.map +1 -0
  194. package/dist/core/networking/adapters/ws-adapter.d.ts +2 -2
  195. package/dist/core/networking/adapters/ws-adapter.js +8 -43
  196. package/dist/core/networking/adapters/ws-adapter.js.map +1 -1
  197. package/dist/core/networking/index.d.ts +3 -2
  198. package/dist/core/networking/index.js +2 -7
  199. package/dist/core/networking/index.js.map +1 -1
  200. package/dist/core/networking/service-discovery.js +8 -12
  201. package/dist/core/networking/service-discovery.js.map +1 -1
  202. package/dist/core/networking/websocket-adapter.js +1 -2
  203. package/dist/core/networking/websocket-adapter.js.map +1 -1
  204. package/dist/core/networking/websocket-manager.d.ts +3 -3
  205. package/dist/core/networking/websocket-manager.js +9 -11
  206. package/dist/core/networking/websocket-manager.js.map +1 -1
  207. package/dist/core/pooling/object-pool-manager.d.ts +140 -0
  208. package/dist/core/pooling/object-pool-manager.js +502 -0
  209. package/dist/core/pooling/object-pool-manager.js.map +1 -0
  210. package/dist/core/routing/app-integration.d.ts +14 -12
  211. package/dist/core/routing/app-integration.js +49 -85
  212. package/dist/core/routing/app-integration.js.map +1 -1
  213. package/dist/core/routing/index.d.ts +17 -11
  214. package/dist/core/routing/index.js +48 -237
  215. package/dist/core/routing/index.js.map +1 -1
  216. package/dist/core/routing/path-matcher.d.ts +67 -0
  217. package/dist/core/routing/path-matcher.js +182 -0
  218. package/dist/core/routing/path-matcher.js.map +1 -0
  219. package/dist/core/routing/router.d.ts +38 -0
  220. package/dist/core/routing/router.js +68 -0
  221. package/dist/core/routing/router.js.map +1 -0
  222. package/dist/core/routing/unified-router.d.ts +148 -0
  223. package/dist/core/routing/unified-router.js +684 -0
  224. package/dist/core/routing/unified-router.js.map +1 -0
  225. package/dist/core/runtime/aws-lambda-adapter.d.ts +3 -3
  226. package/dist/core/runtime/aws-lambda-adapter.js +2 -6
  227. package/dist/core/runtime/aws-lambda-adapter.js.map +1 -1
  228. package/dist/core/runtime/base-adapter.d.ts +2 -2
  229. package/dist/core/runtime/base-adapter.js +3 -7
  230. package/dist/core/runtime/base-adapter.js.map +1 -1
  231. package/dist/core/runtime/cloudflare-workers-adapter.d.ts +3 -3
  232. package/dist/core/runtime/cloudflare-workers-adapter.js +2 -6
  233. package/dist/core/runtime/cloudflare-workers-adapter.js.map +1 -1
  234. package/dist/core/runtime/index.d.ts +12 -12
  235. package/dist/core/runtime/index.js +22 -35
  236. package/dist/core/runtime/index.js.map +1 -1
  237. package/dist/core/runtime/node-adapter.d.ts +4 -4
  238. package/dist/core/runtime/node-adapter.js +18 -49
  239. package/dist/core/runtime/node-adapter.js.map +1 -1
  240. package/dist/core/runtime/vercel-edge-adapter.d.ts +3 -3
  241. package/dist/core/runtime/vercel-edge-adapter.js +2 -6
  242. package/dist/core/runtime/vercel-edge-adapter.js.map +1 -1
  243. package/dist/core/utilities/circuit-breaker.js +1 -5
  244. package/dist/core/utilities/circuit-breaker.js.map +1 -1
  245. package/dist/core/utilities/container.js +12 -22
  246. package/dist/core/utilities/container.js.map +1 -1
  247. package/dist/core/utilities/hooks.d.ts +2 -2
  248. package/dist/core/utilities/hooks.js +7 -12
  249. package/dist/core/utilities/hooks.js.map +1 -1
  250. package/dist/core/utilities/index.d.ts +5 -4
  251. package/dist/core/utilities/index.js +5 -19
  252. package/dist/core/utilities/index.js.map +1 -1
  253. package/dist/core/utilities/package-utils.d.ts +38 -0
  254. package/dist/core/utilities/package-utils.js +57 -0
  255. package/dist/core/utilities/package-utils.js.map +1 -0
  256. package/dist/core/validation/adapters.d.ts +1 -1
  257. package/dist/core/validation/adapters.js +15 -26
  258. package/dist/core/validation/adapters.js.map +1 -1
  259. package/dist/core/validation/index.d.ts +6 -4
  260. package/dist/core/validation/index.js +57 -28
  261. package/dist/core/validation/index.js.map +1 -1
  262. package/dist/core/validation/schema-interface.js +3 -9
  263. package/dist/core/validation/schema-interface.js.map +1 -1
  264. package/dist/index.d.ts +51 -52
  265. package/dist/index.js +23 -132
  266. package/dist/index.js.map +1 -1
  267. package/dist/moro.d.ts +70 -16
  268. package/dist/moro.js +658 -271
  269. package/dist/moro.js.map +1 -1
  270. package/dist/types/auth.js +3 -9
  271. package/dist/types/auth.js.map +1 -1
  272. package/dist/types/cache.js +1 -2
  273. package/dist/types/cdn.js +1 -2
  274. package/dist/types/config.d.ts +73 -2
  275. package/dist/types/config.js +1 -2
  276. package/dist/types/config.js.map +1 -1
  277. package/dist/types/core.d.ts +36 -42
  278. package/dist/types/core.js +1 -2
  279. package/dist/types/database.js +1 -2
  280. package/dist/types/discovery.js +1 -2
  281. package/dist/types/events.js +1 -2
  282. package/dist/types/hooks.d.ts +1 -1
  283. package/dist/types/hooks.js +1 -2
  284. package/dist/types/http.d.ts +16 -1
  285. package/dist/types/http.js +1 -2
  286. package/dist/types/logger.d.ts +7 -0
  287. package/dist/types/logger.js +1 -2
  288. package/dist/types/module.d.ts +11 -0
  289. package/dist/types/module.js +1 -2
  290. package/dist/types/runtime.d.ts +1 -1
  291. package/dist/types/runtime.js +1 -2
  292. package/dist/types/session.js +1 -2
  293. package/jest.config.mjs +41 -0
  294. package/package.json +19 -52
  295. package/src/core/auth/morojs-adapter.ts +18 -13
  296. package/src/core/config/config-manager.ts +133 -0
  297. package/src/core/config/config-sources.ts +600 -0
  298. package/src/core/config/config-validator.ts +1116 -0
  299. package/src/core/config/file-loader.ts +16 -273
  300. package/src/core/config/index.ts +83 -34
  301. package/src/core/config/schema.ts +47 -33
  302. package/src/core/config/utils.ts +24 -31
  303. package/src/core/database/README.md +26 -16
  304. package/src/core/database/adapters/drizzle.ts +18 -6
  305. package/src/core/database/adapters/index.ts +13 -13
  306. package/src/core/database/adapters/mongodb.ts +53 -5
  307. package/src/core/database/adapters/mysql.ts +32 -4
  308. package/src/core/database/adapters/postgresql.ts +30 -5
  309. package/src/core/database/adapters/redis.ts +61 -8
  310. package/src/core/database/adapters/sqlite.ts +19 -3
  311. package/src/core/database/index.ts +2 -2
  312. package/src/core/docs/index.ts +8 -8
  313. package/src/core/docs/openapi-generator.ts +4 -4
  314. package/src/core/docs/schema-to-openapi.ts +3 -6
  315. package/src/core/docs/simple-docs.ts +2 -2
  316. package/src/core/docs/swagger-ui.ts +19 -16
  317. package/src/core/docs/zod-to-openapi.ts +34 -34
  318. package/src/core/events/event-bus.ts +3 -3
  319. package/src/core/events/index.ts +2 -2
  320. package/src/core/framework.ts +320 -71
  321. package/src/core/http/http-server.ts +203 -143
  322. package/src/core/http/index.ts +4 -3
  323. package/src/core/http/uws-http-server.ts +591 -0
  324. package/src/core/logger/filters.ts +13 -5
  325. package/src/core/logger/index.ts +4 -3
  326. package/src/core/logger/logger.ts +435 -216
  327. package/src/core/logger/outputs.ts +1 -3
  328. package/src/core/middleware/built-in/adapters/cache/file.ts +3 -3
  329. package/src/core/middleware/built-in/adapters/cache/index.ts +7 -7
  330. package/src/core/middleware/built-in/adapters/cache/memory.ts +2 -2
  331. package/src/core/middleware/built-in/adapters/cache/redis.ts +18 -4
  332. package/src/core/middleware/built-in/adapters/cdn/azure.ts +2 -2
  333. package/src/core/middleware/built-in/adapters/cdn/cloudflare.ts +2 -2
  334. package/src/core/middleware/built-in/adapters/cdn/cloudfront.ts +16 -5
  335. package/src/core/middleware/built-in/adapters/cdn/index.ts +7 -7
  336. package/src/core/middleware/built-in/adapters/index.ts +4 -4
  337. package/src/core/middleware/built-in/auth-helpers.ts +1 -1
  338. package/src/core/middleware/built-in/auth-providers.ts +1 -1
  339. package/src/core/middleware/built-in/auth.ts +102 -21
  340. package/src/core/middleware/built-in/cache.ts +8 -6
  341. package/src/core/middleware/built-in/cdn.ts +4 -4
  342. package/src/core/middleware/built-in/cookie.ts +2 -2
  343. package/src/core/middleware/built-in/cors.ts +2 -2
  344. package/src/core/middleware/built-in/csp.ts +3 -3
  345. package/src/core/middleware/built-in/csrf.ts +3 -3
  346. package/src/core/middleware/built-in/error-tracker.ts +1 -1
  347. package/src/core/middleware/built-in/index.ts +38 -30
  348. package/src/core/middleware/built-in/jwt-helpers.ts +243 -0
  349. package/src/core/middleware/built-in/performance-monitor.ts +1 -1
  350. package/src/core/middleware/built-in/rate-limit.ts +2 -2
  351. package/src/core/middleware/built-in/request-logger.ts +3 -1
  352. package/src/core/middleware/built-in/session.ts +7 -8
  353. package/src/core/middleware/built-in/sse.ts +11 -9
  354. package/src/core/middleware/built-in/validation.ts +2 -2
  355. package/src/core/middleware/index.ts +6 -6
  356. package/src/core/modules/auto-discovery.ts +478 -15
  357. package/src/core/modules/index.ts +2 -2
  358. package/src/core/modules/modules.ts +23 -12
  359. package/src/core/networking/adapters/index.ts +4 -3
  360. package/src/core/networking/adapters/socketio-adapter.ts +5 -3
  361. package/src/core/networking/adapters/uws-adapter.ts +619 -0
  362. package/src/core/networking/adapters/ws-adapter.ts +8 -9
  363. package/src/core/networking/index.ts +3 -2
  364. package/src/core/networking/service-discovery.ts +6 -7
  365. package/src/core/networking/websocket-manager.ts +7 -7
  366. package/src/core/pooling/object-pool-manager.ts +630 -0
  367. package/src/core/routing/app-integration.ts +60 -112
  368. package/src/core/routing/index.ts +66 -293
  369. package/src/core/routing/path-matcher.ts +222 -0
  370. package/src/core/routing/router.ts +97 -0
  371. package/src/core/routing/unified-router.ts +870 -0
  372. package/src/core/runtime/aws-lambda-adapter.ts +3 -3
  373. package/src/core/runtime/base-adapter.ts +2 -2
  374. package/src/core/runtime/cloudflare-workers-adapter.ts +3 -3
  375. package/src/core/runtime/index.ts +13 -13
  376. package/src/core/runtime/node-adapter.ts +16 -10
  377. package/src/core/runtime/vercel-edge-adapter.ts +3 -3
  378. package/src/core/utilities/hooks.ts +3 -3
  379. package/src/core/utilities/index.ts +5 -4
  380. package/src/core/utilities/package-utils.ts +59 -0
  381. package/src/core/validation/adapters.ts +1 -1
  382. package/src/core/validation/index.ts +68 -16
  383. package/src/index.ts +73 -66
  384. package/src/moro.ts +784 -253
  385. package/src/types/config.ts +74 -2
  386. package/src/types/core.ts +49 -47
  387. package/src/types/hooks.ts +1 -1
  388. package/src/types/http.ts +23 -1
  389. package/src/types/logger.ts +9 -0
  390. package/src/types/module.ts +12 -0
  391. package/src/types/runtime.ts +1 -1
  392. package/tsconfig.json +4 -2
  393. package/dist/core/config/loader.d.ts +0 -7
  394. package/dist/core/config/loader.js +0 -269
  395. package/dist/core/config/loader.js.map +0 -1
  396. package/dist/core/config/validation.d.ts +0 -17
  397. package/dist/core/config/validation.js +0 -131
  398. package/dist/core/config/validation.js.map +0 -1
  399. package/dist/core/http/router.d.ts +0 -14
  400. package/dist/core/http/router.js +0 -109
  401. package/dist/core/http/router.js.map +0 -1
  402. package/src/core/config/loader.ts +0 -633
  403. package/src/core/config/validation.ts +0 -140
  404. package/src/core/http/router.ts +0 -141
package/src/moro.ts CHANGED
@@ -1,124 +1,106 @@
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 { MoroEventBus } from './core/events';
4
+ import { Moro as MoroCore } from './core/framework.js';
5
+ import { HttpRequest, HttpResponse, middleware } from './core/http/index.js';
6
+ import { ModuleConfig, InternalRouteDefinition } from './types/module.js';
7
+ import { MoroOptions } from './types/core.js';
8
+ import { ModuleDefaultsConfig } from './types/config.js';
9
+ import { MoroEventBus } from './core/events/index.js';
10
+ import { createFrameworkLogger, applyLoggingConfiguration } from './core/logger/index.js';
11
+ import { Logger } from './types/logger.js';
12
+ import { MiddlewareManager } from './core/middleware/index.js';
13
+ import { IntelligentRoutingManager } from './core/routing/app-integration.js';
14
+ import { RouteSchema } from './core/routing/index.js';
9
15
  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
+ UnifiedRouter,
17
+ RouteBuilder as UnifiedRouteBuilder,
18
+ } from './core/routing/unified-router.js';
19
+ import { AppDocumentationManager, DocsConfig } from './core/docs/index.js';
20
20
  import { EventEmitter } from 'events';
21
+ import cluster from 'cluster';
22
+ import os from 'os';
23
+ import { normalizeValidationError } from './core/validation/schema-interface.js';
21
24
  // Configuration System Integration
22
- import { initializeConfig, getGlobalConfig, type AppConfig } from './core/config';
25
+ import { initializeConfig, type AppConfig } from './core/config/index.js';
23
26
  // Runtime System Integration
24
- import {
25
- RuntimeAdapter,
26
- RuntimeType,
27
- createRuntimeAdapter,
28
- NodeRuntimeAdapter,
29
- } from './core/runtime';
27
+ import { RuntimeAdapter, RuntimeType, createRuntimeAdapter } from './core/runtime/index.js';
30
28
 
31
29
  export class Moro extends EventEmitter {
32
- private coreFramework: MoroCore;
30
+ private coreFramework!: MoroCore;
33
31
  private routes: InternalRouteDefinition[] = [];
34
32
  private moduleCounter = 0;
35
33
  private loadedModules = new Set<string>();
34
+ private lazyModules = new Map<string, ModuleConfig>();
36
35
  private routeHandlers: Record<string, Function> = {};
36
+ private moduleDiscovery?: any; // Store for cleanup
37
+ private autoDiscoveryOptions: MoroOptions | null = null;
38
+ private autoDiscoveryInitialized = false;
39
+ private autoDiscoveryPromise: Promise<void> | null = null;
37
40
  // Enterprise event system integration
38
- private eventBus: MoroEventBus;
41
+ private eventBus!: MoroEventBus;
39
42
  // Application logger
40
- private logger = createFrameworkLogger('App');
41
- // Intelligent routing system
42
- private intelligentRouting = new IntelligentRoutingManager();
43
+ private logger!: Logger;
44
+ // Unified routing system (singleton - shared across all routers)
45
+ private unifiedRouter!: UnifiedRouter;
46
+ // Legacy intelligent routing (kept for backward compatibility, now a facade)
47
+ private intelligentRouting!: IntelligentRoutingManager;
43
48
  // Documentation system
44
49
  private documentation = new AppDocumentationManager();
45
50
  // Configuration system
46
- private config: AppConfig;
51
+ private config!: AppConfig;
52
+ // Track if user explicitly set logger options (for worker log level handling)
53
+ private userSetLogger = false;
47
54
  // Runtime system
48
- private runtimeAdapter: RuntimeAdapter;
49
- private runtimeType: RuntimeType;
55
+ private runtimeAdapter!: RuntimeAdapter;
56
+ private runtimeType!: RuntimeType;
50
57
  // Middleware system
51
- private middlewareManager: MiddlewareManager;
58
+ private middlewareManager!: MiddlewareManager;
59
+ // Queued WebSocket registrations (for async adapter detection)
60
+ private queuedWebSocketRegistrations: Array<{
61
+ namespace: string;
62
+ handlers: Record<string, Function>;
63
+ processed: boolean;
64
+ }> = [];
52
65
 
53
66
  constructor(options: MoroOptions = {}) {
54
67
  super(); // Call EventEmitter constructor
55
68
 
56
- // Configure logger from environment variables BEFORE config system initialization
57
- // This ensures the config loading process respects the log level
69
+ // Track if user explicitly set logger/logging options
70
+ this.userSetLogger = !!(options.logger || options.logging);
71
+
72
+ // Apply logging configuration BEFORE config loading to avoid DEBUG spam
73
+ // 1. Environment variables (base level)
58
74
  const envLogLevel = process.env.LOG_LEVEL || process.env.MORO_LOG_LEVEL;
59
75
  if (envLogLevel) {
60
76
  applyLoggingConfiguration({ level: envLogLevel }, undefined);
61
77
  }
62
78
 
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)
79
+ // 2. createApp logger options (highest precedence)
72
80
  if (options.logger !== undefined) {
73
81
  applyLoggingConfiguration(undefined, options.logger);
74
82
  }
75
83
 
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
- }
84
+ // Create logger AFTER initial configuration
85
+ this.logger = createFrameworkLogger('App');
97
86
 
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
- }
87
+ // Use simplified global configuration system
88
+ this.config = initializeConfig(options);
89
+
90
+ // Apply final config logging (this includes normalized logger → logging conversion)
91
+ // Always apply this as it's the authoritative merged config
92
+ if (this.config.logging) {
93
+ applyLoggingConfiguration(this.config.logging, undefined);
94
+ // Recreate logger with updated config
95
+ this.logger = createFrameworkLogger('App');
118
96
  }
119
97
 
98
+ // NOW initialize routing systems AFTER logger is configured
99
+ this.unifiedRouter = UnifiedRouter.getInstance();
100
+ this.intelligentRouting = new IntelligentRoutingManager();
101
+
120
102
  this.logger.info(
121
- `Configuration system initialized: ${this.config.server.environment}:${this.config.server.port}`
103
+ `Configuration system initialized: ${process.env.NODE_ENV || 'development'}:${this.config.server.port}`
122
104
  );
123
105
 
124
106
  // Initialize runtime system
@@ -127,10 +109,16 @@ export class Moro extends EventEmitter {
127
109
 
128
110
  this.logger.info(`Runtime system initialized: ${this.runtimeType}`, 'Runtime');
129
111
 
130
- // Pass logging configuration from config to framework
112
+ // Pass configuration from config to framework
131
113
  const frameworkOptions: any = {
132
114
  ...options,
133
115
  logger: this.config.logging,
116
+ // Enable websockets if either config has it enabled OR user passed websocket options
117
+ websocket:
118
+ this.config.websocket.enabled || options.websocket
119
+ ? options.websocket || this.config.websocket || {}
120
+ : false,
121
+ config: this.config,
134
122
  };
135
123
 
136
124
  this.coreFramework = new MoroCore(frameworkOptions);
@@ -162,10 +150,10 @@ export class Moro extends EventEmitter {
162
150
  ...options,
163
151
  });
164
152
 
165
- // Auto-discover modules if enabled
166
- if (options.autoDiscover !== false) {
167
- this.autoDiscoverModules(options.modulesPath || './modules');
168
- }
153
+ // Store auto-discovery options for later initialization
154
+ // IMPORTANT: Auto-discovery is deferred to ensure user middleware (like auth)
155
+ // is registered before module middleware that might bypass it
156
+ this.autoDiscoveryOptions = options.autoDiscover !== false ? options : null;
169
157
 
170
158
  // Emit initialization event through enterprise event bus
171
159
  this.eventBus.emit('framework:initialized', {
@@ -209,84 +197,85 @@ export class Moro extends EventEmitter {
209
197
 
210
198
  // Intelligent route methods - chainable with automatic middleware ordering
211
199
  // Overloads for better TypeScript inference
212
- get(path: string): RouteBuilder;
200
+ get(path: string): UnifiedRouteBuilder;
213
201
  get(path: string, handler: (req: HttpRequest, res: HttpResponse) => any, options?: any): this;
214
202
  get(
215
203
  path: string,
216
204
  handler?: (req: HttpRequest, res: HttpResponse) => any,
217
205
  options?: any
218
- ): RouteBuilder | this {
206
+ ): UnifiedRouteBuilder | this {
219
207
  if (handler) {
220
208
  // Direct route registration
221
209
  return this.addRoute('GET', path, handler, options);
222
210
  }
223
- // Chainable route builder
224
- return this.intelligentRouting.get(path);
211
+ // Use unified router for chainable API
212
+ return this.unifiedRouter.get(path);
225
213
  }
226
214
 
227
- post(path: string): RouteBuilder;
215
+ post(path: string): UnifiedRouteBuilder;
228
216
  post(path: string, handler: (req: HttpRequest, res: HttpResponse) => any, options?: any): this;
229
217
  post(
230
218
  path: string,
231
219
  handler?: (req: HttpRequest, res: HttpResponse) => any,
232
220
  options?: any
233
- ): RouteBuilder | this {
221
+ ): UnifiedRouteBuilder | this {
234
222
  if (handler) {
235
223
  // Direct route registration
236
224
  return this.addRoute('POST', path, handler, options);
237
225
  }
238
- // Chainable route builder
239
- return this.intelligentRouting.post(path);
226
+ // Use unified router for chainable API
227
+ return this.unifiedRouter.post(path);
240
228
  }
241
229
 
242
- put(path: string): RouteBuilder;
230
+ put(path: string): UnifiedRouteBuilder;
243
231
  put(path: string, handler: (req: HttpRequest, res: HttpResponse) => any, options?: any): this;
244
232
  put(
245
233
  path: string,
246
234
  handler?: (req: HttpRequest, res: HttpResponse) => any,
247
235
  options?: any
248
- ): RouteBuilder | this {
236
+ ): UnifiedRouteBuilder | this {
249
237
  if (handler) {
250
238
  // Direct route registration
251
239
  return this.addRoute('PUT', path, handler, options);
252
240
  }
253
- // Chainable route builder
254
- return this.intelligentRouting.put(path);
241
+ // Use unified router for chainable API
242
+ return this.unifiedRouter.put(path);
255
243
  }
256
244
 
257
- delete(path: string): RouteBuilder;
245
+ delete(path: string): UnifiedRouteBuilder;
258
246
  delete(path: string, handler: (req: HttpRequest, res: HttpResponse) => any, options?: any): this;
259
247
  delete(
260
248
  path: string,
261
249
  handler?: (req: HttpRequest, res: HttpResponse) => any,
262
250
  options?: any
263
- ): RouteBuilder | this {
251
+ ): UnifiedRouteBuilder | this {
264
252
  if (handler) {
265
253
  // Direct route registration
266
254
  return this.addRoute('DELETE', path, handler, options);
267
255
  }
268
- // Chainable route builder
269
- return this.intelligentRouting.delete(path);
256
+ // Use unified router for chainable API
257
+ return this.unifiedRouter.delete(path);
270
258
  }
271
259
 
272
- patch(path: string): RouteBuilder;
260
+ patch(path: string): UnifiedRouteBuilder;
273
261
  patch(path: string, handler: (req: HttpRequest, res: HttpResponse) => any, options?: any): this;
274
262
  patch(
275
263
  path: string,
276
264
  handler?: (req: HttpRequest, res: HttpResponse) => any,
277
265
  options?: any
278
- ): RouteBuilder | this {
266
+ ): UnifiedRouteBuilder | this {
279
267
  if (handler) {
280
268
  // Direct route registration
281
269
  return this.addRoute('PATCH', path, handler, options);
282
270
  }
283
- // Chainable route builder
284
- return this.intelligentRouting.patch(path);
271
+ // Use unified router for chainable API
272
+ return this.unifiedRouter.patch(path);
285
273
  }
286
274
 
287
275
  // Schema-first route method
288
- route(schema: RouteSchema): CompiledRoute {
289
- return this.intelligentRouting.route(schema);
276
+ route(schema: RouteSchema): void {
277
+ // Use unified router for schema-first registration
278
+ this.unifiedRouter.route(schema);
290
279
  }
291
280
 
292
281
  // Enable automatic API documentation
@@ -394,6 +383,13 @@ export class Moro extends EventEmitter {
394
383
  version: moduleOrPath.version || '1.0.0',
395
384
  });
396
385
  }
386
+
387
+ // IMPORTANT: If modules are loaded manually after auto-discovery,
388
+ // ensure the final module handler is set up to maintain middleware order
389
+ if (this.autoDiscoveryInitialized) {
390
+ this.coreFramework.setupFinalModuleHandler();
391
+ }
392
+
397
393
  return this;
398
394
  }
399
395
 
@@ -409,34 +405,45 @@ export class Moro extends EventEmitter {
409
405
 
410
406
  // WebSocket helper with events
411
407
  websocket(namespace: string, handlers: Record<string, Function>) {
408
+ // Queue the registration to be processed after adapter initialization
409
+ const registration = { namespace, handlers, processed: false };
410
+ this.queuedWebSocketRegistrations.push(registration);
411
+
412
+ // Try to process immediately if adapter is already ready
412
413
  const adapter = this.coreFramework.getWebSocketAdapter();
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
- );
414
+ if (adapter && !registration.processed) {
415
+ // Adapter is ready, process immediately
416
+ this.processWebSocketRegistration(namespace, handlers, adapter);
417
+ registration.processed = true;
420
418
  }
419
+ // Otherwise, it will be processed when the server starts
421
420
 
421
+ return this;
422
+ }
423
+
424
+ private processWebSocketRegistration(
425
+ namespace: string,
426
+ handlers: Record<string, Function>,
427
+ adapter: any
428
+ ) {
422
429
  this.emit('websocket:registering', { namespace, handlers });
423
430
 
424
431
  const ns = adapter.createNamespace(namespace);
425
432
 
426
433
  Object.entries(handlers).forEach(([event, handler]) => {
427
- ns.on('connection', socket => {
434
+ ns.on('connection', (socket: any) => {
428
435
  this.emit('websocket:connection', { namespace, event, socket });
429
436
 
430
- socket.on(event, (data, callback) => {
437
+ socket.on(event, (data: any, callback: any) => {
431
438
  this.emit('websocket:event', { namespace, event, data });
432
439
 
433
440
  Promise.resolve(handler(socket, data))
434
- .then(result => {
441
+ .then((result: any) => {
435
442
  this.emit('websocket:response', { namespace, event, result });
436
443
  if (callback) callback(result);
437
444
  else if (result) socket.emit(`${event}:response`, result);
438
445
  })
439
- .catch(error => {
446
+ .catch((error: any) => {
440
447
  this.emit('websocket:error', { namespace, event, error });
441
448
  const errorResponse = { success: false, error: error.message };
442
449
  if (callback) callback(errorResponse);
@@ -447,7 +454,40 @@ export class Moro extends EventEmitter {
447
454
  });
448
455
 
449
456
  this.emit('websocket:registered', { namespace, handlers });
450
- return this;
457
+ }
458
+
459
+ private async processQueuedWebSocketRegistrations() {
460
+ // Wait for WebSocket adapter to be ready
461
+ await this.coreFramework.ensureWebSocketReady();
462
+
463
+ const adapter = this.coreFramework.getWebSocketAdapter();
464
+
465
+ // Check if any unprocessed registrations exist
466
+ const unprocessedRegistrations = this.queuedWebSocketRegistrations.filter(r => !r.processed);
467
+
468
+ if (!adapter && unprocessedRegistrations.length > 0) {
469
+ throw new Error(
470
+ 'WebSocket features require a WebSocket adapter.\n\n' +
471
+ 'Option 1: Install socket.io (auto-detected):\n' +
472
+ ' npm install socket.io\n' +
473
+ ' const app = new Moro({ websocket: {} });\n\n' +
474
+ 'Option 2: Configure a specific adapter:\n' +
475
+ " import { SocketIOAdapter } from '@morojs/moro';\n" +
476
+ ' const app = new Moro({ websocket: { adapter: new SocketIOAdapter() } });\n\n' +
477
+ 'Option 3: Enable in config file (moro.config.js):\n' +
478
+ ' export default { websocket: { enabled: true } };'
479
+ );
480
+ }
481
+
482
+ if (adapter) {
483
+ // Process all unprocessed registrations
484
+ for (const registration of this.queuedWebSocketRegistrations) {
485
+ if (!registration.processed) {
486
+ this.processWebSocketRegistration(registration.namespace, registration.handlers, adapter);
487
+ registration.processed = true;
488
+ }
489
+ }
490
+ }
451
491
  }
452
492
 
453
493
  // Start server with events (Node.js only)
@@ -503,9 +543,21 @@ export class Moro extends EventEmitter {
503
543
  }
504
544
 
505
545
  // Check if clustering is enabled for massive performance gains
546
+ // NOTE: uWebSockets.js does NOT support Node.js clustering - it's single-threaded only
547
+ const usingUWebSockets = this.config.server?.useUWebSockets || false;
548
+
506
549
  if (this.config.performance?.clustering?.enabled) {
507
- this.startWithClustering(port, host as string, callback);
508
- return;
550
+ if (usingUWebSockets) {
551
+ this.logger.warn(
552
+ 'Clustering is not supported with uWebSockets.js - running in single-threaded mode. ' +
553
+ 'uWebSockets is so fast that single-threaded performance often exceeds multi-threaded Node.js!',
554
+ 'Cluster'
555
+ );
556
+ // Continue without clustering
557
+ } else {
558
+ this.startWithClustering(port, host as string, callback);
559
+ return;
560
+ }
509
561
  }
510
562
  this.eventBus.emit('server:starting', { port, runtime: this.runtimeType });
511
563
 
@@ -519,50 +571,200 @@ export class Moro extends EventEmitter {
519
571
  this.logger.debug('Documentation not enabled', 'Documentation');
520
572
  }
521
573
 
522
- // Add intelligent routing middleware to handle chainable routes
523
- this.coreFramework.addMiddleware(
524
- async (req: HttpRequest, res: HttpResponse, next: () => void) => {
525
- // Try intelligent routing first
526
- const handled = await this.intelligentRouting.handleIntelligentRoute(req, res);
527
- if (!handled) {
528
- next(); // Fall back to direct routes
574
+ // Add unified routing middleware (handles both chainable and direct routes)
575
+ // Optimized: call router without extra async wrapper when possible
576
+ this.coreFramework.addMiddleware((req: HttpRequest, res: HttpResponse, next: () => void) => {
577
+ // Try unified router first (handles all route types)
578
+ const handled = this.unifiedRouter.handleRequest(req, res);
579
+
580
+ // Check if it's a promise (async route) or sync
581
+ if (handled && typeof (handled as any).then === 'function') {
582
+ // Async - await the result
583
+ (handled as Promise<boolean>)
584
+ .then(isHandled => {
585
+ if (!isHandled) {
586
+ next(); // Fall back to legacy routes if any
587
+ }
588
+ })
589
+ .catch(() => next());
590
+ } else {
591
+ // Sync - check immediately
592
+ if (!(handled as boolean)) {
593
+ next();
529
594
  }
530
595
  }
531
- );
596
+ });
532
597
 
533
- // Register direct routes with the HTTP server
598
+ // Register legacy direct routes with the HTTP server (for backward compatibility)
534
599
  if (this.routes.length > 0) {
535
600
  this.registerDirectRoutes();
536
601
  }
537
602
 
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');
603
+ const startServer = () => {
604
+ const actualCallback = () => {
605
+ const displayHost = host || 'localhost';
606
+ this.logger.info('Moro Server Started', 'Server');
607
+ this.logger.info(`Runtime: ${this.runtimeType}`, 'Server');
608
+ this.logger.info(`HTTP API: http://${displayHost}:${port}`, 'Server');
609
+ if (this.config.websocket.enabled) {
610
+ this.logger.info(`WebSocket: ws://${displayHost}:${port}`, 'Server');
611
+ }
612
+ this.logger.info('Learn more at https://morojs.com', 'Server');
613
+
614
+ // Log unified router stats
615
+ const routeCount = this.unifiedRouter.getRouteCount();
616
+ if (routeCount > 0) {
617
+ this.logger.info(`Unified Router: ${routeCount} routes registered`, 'Server');
618
+ // Log performance stats
619
+ this.unifiedRouter.logPerformanceStats();
620
+ }
621
+
622
+ this.eventBus.emit('server:started', { port, runtime: this.runtimeType });
623
+ if (callback) callback();
624
+ };
625
+
626
+ if (host && typeof host === 'string') {
627
+ this.coreFramework.listen(port, host, actualCallback);
628
+ } else {
629
+ this.coreFramework.listen(port, actualCallback);
551
630
  }
631
+ };
552
632
 
553
- this.eventBus.emit('server:started', { port, runtime: this.runtimeType });
554
- if (callback) callback();
633
+ // Ensure auto-discovery and WebSocket setup is complete before starting server
634
+ Promise.all([this.ensureAutoDiscoveryComplete(), this.processQueuedWebSocketRegistrations()])
635
+ .then(() => {
636
+ startServer();
637
+ })
638
+ .catch(error => {
639
+ this.logger.error('Initialization failed during server start', 'Framework', {
640
+ error: error instanceof Error ? error.message : String(error),
641
+ });
642
+ // For auto-discovery failures, start server anyway
643
+ // For WebSocket failures with queued registrations, error will propagate
644
+ if (
645
+ error instanceof Error &&
646
+ error.message.includes('WebSocket features require a WebSocket adapter')
647
+ ) {
648
+ throw error;
649
+ }
650
+ startServer();
651
+ });
652
+ }
653
+
654
+ // Public method to manually initialize auto-discovery
655
+ // Useful for ensuring auth middleware is registered before auto-discovery
656
+ async initializeAutoDiscoveryNow(): Promise<void> {
657
+ return this.ensureAutoDiscoveryComplete();
658
+ }
659
+
660
+ // Public API: Initialize modules explicitly after middleware setup
661
+ // This provides users with explicit control over module loading timing
662
+ // IMPORTANT: This forces module loading even if autoDiscovery.enabled is false
663
+ // Usage: app.initModules() or app.initModules({ paths: ['./my-modules'] })
664
+ initModules(options?: {
665
+ paths?: string[];
666
+ patterns?: string[];
667
+ recursive?: boolean;
668
+ loadingStrategy?: 'eager' | 'lazy' | 'conditional';
669
+ watchForChanges?: boolean;
670
+ ignorePatterns?: string[];
671
+ loadOrder?: 'alphabetical' | 'dependency' | 'custom';
672
+ failOnError?: boolean;
673
+ maxDepth?: number;
674
+ }): void {
675
+ this.logger.info('User-requested module initialization', 'ModuleSystem');
676
+
677
+ // If already initialized, do nothing
678
+ if (this.autoDiscoveryInitialized) {
679
+ this.logger.debug('Auto-discovery already completed, skipping', 'ModuleSystem');
680
+ return;
681
+ }
682
+
683
+ // Store the options and mark that we want to force initialization
684
+ this.autoDiscoveryOptions = {
685
+ autoDiscover: {
686
+ enabled: true, // Force enabled regardless of original config
687
+ paths: options?.paths || ['./modules', './src/modules'],
688
+ patterns: options?.patterns || [
689
+ '**/*.module.{ts,js}',
690
+ '**/index.{ts,js}',
691
+ '**/*.config.{ts,js}',
692
+ ],
693
+ recursive: options?.recursive ?? true,
694
+ loadingStrategy: options?.loadingStrategy || ('eager' as const),
695
+ watchForChanges: options?.watchForChanges ?? false,
696
+ ignorePatterns: options?.ignorePatterns || [
697
+ '**/*.test.{ts,js}',
698
+ '**/*.spec.{ts,js}',
699
+ '**/node_modules/**',
700
+ ],
701
+ loadOrder: options?.loadOrder || ('dependency' as const),
702
+ failOnError: options?.failOnError ?? false,
703
+ maxDepth: options?.maxDepth ?? 5,
704
+ },
555
705
  };
556
706
 
557
- if (host && typeof host === 'string') {
558
- this.coreFramework.listen(port, host, actualCallback);
559
- } else {
560
- this.coreFramework.listen(port, actualCallback);
707
+ this.logger.debug(
708
+ 'Module initialization options stored, will execute on next listen/getHandler call',
709
+ 'ModuleSystem'
710
+ );
711
+ }
712
+
713
+ // Robust method to ensure auto-discovery is complete, handling race conditions
714
+ private async ensureAutoDiscoveryComplete(): Promise<void> {
715
+ // If already initialized, nothing to do
716
+ if (this.autoDiscoveryInitialized) {
717
+ return;
718
+ }
719
+
720
+ // If auto-discovery is disabled, mark as initialized
721
+ if (!this.autoDiscoveryOptions) {
722
+ this.autoDiscoveryInitialized = true;
723
+ return;
724
+ }
725
+
726
+ // If already in progress, wait for it to complete
727
+ if (this.autoDiscoveryPromise) {
728
+ return this.autoDiscoveryPromise;
729
+ }
730
+
731
+ // Start auto-discovery
732
+ this.autoDiscoveryPromise = this.performAutoDiscovery();
733
+
734
+ try {
735
+ await this.autoDiscoveryPromise;
736
+ this.autoDiscoveryInitialized = true;
737
+ } catch (error) {
738
+ // Reset promise on error so it can be retried
739
+ this.autoDiscoveryPromise = null;
740
+ throw error;
741
+ } finally {
742
+ this.autoDiscoveryOptions = null; // Clear after attempt
561
743
  }
562
744
  }
563
745
 
746
+ // Perform the actual auto-discovery work
747
+ private async performAutoDiscovery(optionsOverride?: MoroOptions): Promise<void> {
748
+ const optionsToUse = optionsOverride || this.autoDiscoveryOptions;
749
+ if (!optionsToUse) return;
750
+
751
+ this.logger.debug('Starting auto-discovery initialization', 'AutoDiscovery');
752
+
753
+ await this.initializeAutoDiscovery(optionsToUse);
754
+
755
+ this.logger.debug('Auto-discovery initialization completed', 'AutoDiscovery');
756
+ }
757
+
564
758
  // Get handler for non-Node.js runtimes
565
759
  getHandler() {
760
+ // Ensure auto-discovery is complete for non-Node.js runtimes
761
+ // This handles the case where users call getHandler() immediately after createApp()
762
+ this.ensureAutoDiscoveryComplete().catch(error => {
763
+ this.logger.error('Auto-discovery initialization failed for runtime handler', 'Framework', {
764
+ error: error instanceof Error ? error.message : String(error),
765
+ });
766
+ });
767
+
566
768
  // Create a unified request handler that works with the runtime adapter
567
769
  const handler = async (req: HttpRequest, res: HttpResponse) => {
568
770
  // Add documentation middleware first (if enabled)
@@ -574,11 +776,11 @@ export class Moro extends EventEmitter {
574
776
  // Documentation not enabled, that's fine
575
777
  }
576
778
 
577
- // Try intelligent routing first
578
- const handled = await this.intelligentRouting.handleIntelligentRoute(req, res);
779
+ // Try unified router first (handles all routes)
780
+ const handled = await this.unifiedRouter.handleRequest(req, res);
579
781
  if (handled) return;
580
782
 
581
- // Handle direct routes
783
+ // Handle legacy direct routes (backward compatibility)
582
784
  if (this.routes.length > 0) {
583
785
  await this.handleDirectRoutes(req, res);
584
786
  }
@@ -745,8 +947,11 @@ export class Moro extends EventEmitter {
745
947
 
746
948
  // Private methods
747
949
  private addRoute(method: string, path: string, handler: Function, options: any = {}) {
748
- const handlerName = `handler_${this.routes.length}`;
950
+ // Register with unified router (primary routing system)
951
+ this.unifiedRouter.addRoute(method as any, path, handler as any, options.middleware || []);
749
952
 
953
+ // Also store in legacy routes array for backward compatibility
954
+ const handlerName = `handler_${this.routes.length}`;
750
955
  const route = {
751
956
  method: method as any,
752
957
  path,
@@ -759,10 +964,10 @@ export class Moro extends EventEmitter {
759
964
 
760
965
  this.routes.push(route);
761
966
 
762
- // Organize routes for optimal lookup
967
+ // Organize routes for optimal lookup (legacy)
763
968
  this.organizeRouteForLookup(route);
764
969
 
765
- // Store handler for later module creation
970
+ // Store handler for later module creation (legacy)
766
971
  this.routeHandlers[handlerName] = handler;
767
972
 
768
973
  return this;
@@ -807,7 +1012,6 @@ export class Moro extends EventEmitter {
807
1012
  req.body = validated;
808
1013
  } catch (error: any) {
809
1014
  // Handle universal validation errors
810
- const { normalizeValidationError } = require('./core/validation/schema-interface');
811
1015
  const normalizedError = normalizeValidationError(error);
812
1016
  res.status(400).json({
813
1017
  success: false,
@@ -886,20 +1090,33 @@ export class Moro extends EventEmitter {
886
1090
  }
887
1091
 
888
1092
  private setupDefaultMiddleware(options: MoroOptions) {
889
- // CORS
890
- if (options.cors !== false) {
891
- const corsOptions = typeof options.cors === 'object' ? options.cors : {};
1093
+ // CORS - check config enabled property OR options.security.cors.enabled === true
1094
+ if (this.config.security.cors.enabled || options.security?.cors?.enabled === true) {
1095
+ const corsOptions =
1096
+ typeof options.cors === 'object'
1097
+ ? options.cors
1098
+ : this.config.security.cors
1099
+ ? this.config.security.cors
1100
+ : {};
892
1101
  this.use(middleware.cors(corsOptions));
893
1102
  }
894
1103
 
895
- // Helmet
896
- if (options.helmet !== false) {
1104
+ // Helmet - check config enabled property OR options.security.helmet.enabled === true
1105
+ if (this.config.security.helmet.enabled || options.security?.helmet?.enabled === true) {
897
1106
  this.use(middleware.helmet());
898
1107
  }
899
1108
 
900
- // Compression
901
- if (options.compression !== false) {
902
- const compressionOptions = typeof options.compression === 'object' ? options.compression : {};
1109
+ // Compression - check config enabled property OR options.performance.compression.enabled === true
1110
+ if (
1111
+ this.config.performance.compression.enabled ||
1112
+ options.performance?.compression?.enabled === true
1113
+ ) {
1114
+ const compressionOptions =
1115
+ typeof options.compression === 'object'
1116
+ ? options.compression
1117
+ : this.config.performance.compression
1118
+ ? this.config.performance.compression
1119
+ : {};
903
1120
  this.use(middleware.compression(compressionOptions));
904
1121
  }
905
1122
 
@@ -907,27 +1124,216 @@ export class Moro extends EventEmitter {
907
1124
  this.use(middleware.bodySize({ limit: '10mb' }));
908
1125
  }
909
1126
 
910
- private autoDiscoverModules(modulesPath: string) {
1127
+ // Enhanced auto-discovery initialization
1128
+ private async initializeAutoDiscovery(options: MoroOptions): Promise<void> {
1129
+ const { ModuleDiscovery } = await import('./core/modules/auto-discovery.js');
1130
+
1131
+ // Merge auto-discovery configuration
1132
+ const autoDiscoveryConfig = this.mergeAutoDiscoveryConfig(options);
1133
+
1134
+ if (!autoDiscoveryConfig.enabled) {
1135
+ return;
1136
+ }
1137
+
1138
+ this.moduleDiscovery = new ModuleDiscovery(process.cwd());
1139
+
911
1140
  try {
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
1141
+ // Discover modules based on configuration
1142
+ const modules = await this.moduleDiscovery.discoverModulesAdvanced(autoDiscoveryConfig);
1143
+
1144
+ // Load modules based on strategy
1145
+ await this.loadDiscoveredModules(modules, autoDiscoveryConfig);
1146
+
1147
+ // Setup final module handler to run after user middleware (like auth)
1148
+ this.coreFramework.setupFinalModuleHandler();
1149
+
1150
+ // Setup file watching if enabled
1151
+ if (autoDiscoveryConfig.watchForChanges) {
1152
+ this.moduleDiscovery.watchModulesAdvanced(
1153
+ autoDiscoveryConfig,
1154
+ async (updatedModules: ModuleConfig[]) => {
1155
+ await this.handleModuleChanges(updatedModules);
925
1156
  }
1157
+ );
1158
+ }
1159
+
1160
+ this.logger.info(
1161
+ `Auto-discovery completed: ${modules.length} modules loaded`,
1162
+ 'ModuleDiscovery'
1163
+ );
1164
+ } catch (error) {
1165
+ const errorMsg = error instanceof Error ? error.message : String(error);
1166
+
1167
+ if (autoDiscoveryConfig.failOnError) {
1168
+ throw new Error(`Module auto-discovery failed: ${errorMsg}`);
1169
+ } else {
1170
+ this.logger.warn(`Module auto-discovery failed: ${errorMsg}`, 'ModuleDiscovery');
1171
+ }
1172
+ }
1173
+ }
1174
+
1175
+ // Merge auto-discovery configuration from multiple sources
1176
+ private mergeAutoDiscoveryConfig(options: MoroOptions) {
1177
+ const defaultConfig = this.config.modules.autoDiscovery;
1178
+
1179
+ // Handle legacy modulesPath option
1180
+ if (options.modulesPath && !options.autoDiscover) {
1181
+ return {
1182
+ ...defaultConfig,
1183
+ paths: [options.modulesPath],
1184
+ };
1185
+ }
1186
+
1187
+ // Handle boolean autoDiscover option
1188
+ if (typeof options.autoDiscover === 'boolean') {
1189
+ return {
1190
+ ...defaultConfig,
1191
+ enabled: options.autoDiscover,
1192
+ };
1193
+ }
1194
+
1195
+ // Handle object autoDiscover option
1196
+ if (typeof options.autoDiscover === 'object') {
1197
+ return {
1198
+ ...defaultConfig,
1199
+ ...options.autoDiscover,
1200
+ };
1201
+ }
1202
+
1203
+ return defaultConfig;
1204
+ }
1205
+
1206
+ // Load discovered modules based on strategy
1207
+ private async loadDiscoveredModules(
1208
+ modules: ModuleConfig[],
1209
+ config: ModuleDefaultsConfig['autoDiscovery']
1210
+ ): Promise<void> {
1211
+ switch (config.loadingStrategy) {
1212
+ case 'eager':
1213
+ // Load all modules immediately
1214
+ for (const module of modules) {
1215
+ await this.loadModule(module);
926
1216
  }
927
- });
928
- } catch {
929
- // Modules directory doesn't exist, that's fine
1217
+ break;
1218
+
1219
+ case 'lazy':
1220
+ // Register modules for lazy loading
1221
+ this.registerLazyModules(modules);
1222
+ break;
1223
+
1224
+ case 'conditional':
1225
+ // Load modules based on conditions
1226
+ await this.loadConditionalModules(modules);
1227
+ break;
1228
+
1229
+ default:
1230
+ // Default to eager loading
1231
+ for (const module of modules) {
1232
+ await this.loadModule(module);
1233
+ }
1234
+ }
1235
+ }
1236
+
1237
+ // Register modules for lazy loading
1238
+ private registerLazyModules(modules: ModuleConfig[]): void {
1239
+ modules.forEach(module => {
1240
+ // Store module for lazy loading when first route is accessed
1241
+ this.lazyModules.set(module.name, module);
1242
+
1243
+ // Register placeholder routes that trigger lazy loading
1244
+ if (module.routes) {
1245
+ module.routes.forEach(route => {
1246
+ const basePath = `/api/v${module.version}/${module.name}`;
1247
+ const fullPath = `${basePath}${route.path}`;
1248
+
1249
+ // Note: Lazy loading will be implemented when route is accessed
1250
+ // For now, we'll store the module for later loading
1251
+ this.logger.debug(
1252
+ `Registered lazy route: ${route.method} ${fullPath}`,
1253
+ 'ModuleDiscovery'
1254
+ );
1255
+ });
1256
+ }
1257
+ });
1258
+
1259
+ this.logger.info(`Registered ${modules.length} modules for lazy loading`, 'ModuleDiscovery');
1260
+ }
1261
+
1262
+ // Load modules conditionally based on environment or configuration
1263
+ private async loadConditionalModules(modules: ModuleConfig[]): Promise<void> {
1264
+ for (const module of modules) {
1265
+ const shouldLoad = this.shouldLoadModule(module);
1266
+
1267
+ if (shouldLoad) {
1268
+ await this.loadModule(module);
1269
+ } else {
1270
+ this.logger.debug(`Skipping module ${module.name} due to conditions`, 'ModuleDiscovery');
1271
+ }
1272
+ }
1273
+ }
1274
+
1275
+ // Determine if a module should be loaded based on conditions
1276
+ private shouldLoadModule(module: ModuleConfig): boolean {
1277
+ const moduleConfig = module.config as any;
1278
+
1279
+ // Check environment conditions
1280
+ if (moduleConfig?.conditions?.environment) {
1281
+ const requiredEnv = moduleConfig.conditions.environment;
1282
+ const currentEnv = process.env.NODE_ENV || 'development';
1283
+
1284
+ if (Array.isArray(requiredEnv)) {
1285
+ if (!requiredEnv.includes(currentEnv)) {
1286
+ return false;
1287
+ }
1288
+ } else if (requiredEnv !== currentEnv) {
1289
+ return false;
1290
+ }
1291
+ }
1292
+
1293
+ // Check feature flags
1294
+ if (moduleConfig?.conditions?.features) {
1295
+ const requiredFeatures = moduleConfig.conditions.features;
1296
+
1297
+ for (const feature of requiredFeatures) {
1298
+ if (!process.env[`FEATURE_${feature.toUpperCase()}`]) {
1299
+ return false;
1300
+ }
1301
+ }
1302
+ }
1303
+
1304
+ // Check custom conditions
1305
+ if (moduleConfig?.conditions?.custom) {
1306
+ const customCondition = moduleConfig.conditions.custom;
1307
+
1308
+ if (typeof customCondition === 'function') {
1309
+ return customCondition();
1310
+ }
930
1311
  }
1312
+
1313
+ return true;
1314
+ }
1315
+
1316
+ // Handle module changes during development
1317
+ private async handleModuleChanges(modules: ModuleConfig[]): Promise<void> {
1318
+ this.logger.info('Module changes detected, reloading...', 'ModuleDiscovery');
1319
+
1320
+ // Unload existing modules (if supported)
1321
+ // For now, just log the change
1322
+ this.eventBus.emit('modules:changed', {
1323
+ modules: modules.map(m => ({ name: m.name, version: m.version })),
1324
+ timestamp: new Date(),
1325
+ });
1326
+ }
1327
+
1328
+ // Legacy method for backward compatibility
1329
+ private autoDiscoverModules(modulesPath: string) {
1330
+ // Redirect to new system
1331
+ this.initializeAutoDiscovery({
1332
+ autoDiscover: {
1333
+ enabled: true,
1334
+ paths: [modulesPath],
1335
+ },
1336
+ });
931
1337
  }
932
1338
 
933
1339
  private async importModule(modulePath: string): Promise<ModuleConfig> {
@@ -935,55 +1341,81 @@ export class Moro extends EventEmitter {
935
1341
  return module.default || module;
936
1342
  }
937
1343
 
938
- // Clustering support for massive performance gains with proper cleanup
1344
+ /**
1345
+ * Node.js Clustering Implementation
1346
+ * This clustering algorithm is based on published research and Node.js best practices.
1347
+ *
1348
+ * IPC (Inter-Process Communication) Considerations:
1349
+ * - Excessive workers create IPC bottlenecks (Source: BetterStack Node.js Guide)
1350
+ * - Round-robin scheduling provides better load distribution (Node.js Documentation)
1351
+ * - Message passing overhead increases significantly with worker count
1352
+ *
1353
+ * Memory Management:
1354
+ * - ~2GB per worker prevents memory pressure and GC overhead
1355
+ * - Conservative heap limits reduce memory fragmentation
1356
+ *
1357
+ * References:
1358
+ * - Node.js Cluster Documentation: https://nodejs.org/api/cluster.html
1359
+ * - BetterStack Node.js Clustering: https://betterstack.com/community/guides/scaling-nodejs/node-clustering/
1360
+ */
939
1361
  private clusterWorkers = new Map<number, any>();
940
- private startWithClustering(port: number, host?: string, callback?: () => void): void {
941
- const cluster = require('cluster');
942
- const os = require('os');
943
1362
 
944
- // Smart worker count calculation based on actual bottlenecks
1363
+ private startWithClustering(port: number, host?: string, callback?: () => void): void {
1364
+ // Worker count calculation - respect user choice
945
1365
  let workerCount = this.config.performance?.clustering?.workers || os.cpus().length;
946
1366
 
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
1367
+ // Only auto-optimize if user hasn't specified a number or set it to 'auto'
1368
+ if (workerCount === 'auto') {
950
1369
  const cpuCount = os.cpus().length;
951
1370
  const totalMemoryGB = os.totalmem() / (1024 * 1024 * 1024);
952
1371
 
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;
1372
+ // Get memory per worker from config - if not set by user, calculate dynamically
1373
+ let memoryPerWorkerGB = this.config.performance?.clustering?.memoryPerWorkerGB;
1374
+
1375
+ if (!memoryPerWorkerGB) {
1376
+ // Dynamic calculation: (Total RAM - 4GB headroom) / CPU cores
1377
+ const headroomGB = 4;
1378
+ memoryPerWorkerGB = Math.max(0.5, Math.floor((totalMemoryGB - headroomGB) / cpuCount));
963
1379
  }
964
1380
 
1381
+ // Conservative formula based on general guidelines:
1382
+ // - Don't exceed CPU cores
1383
+ // - Respect user's memory allocation preference
1384
+ // - Let the system resources determine the limit
1385
+ workerCount = Math.min(
1386
+ cpuCount, // Don't exceed CPU cores
1387
+ Math.floor(totalMemoryGB / memoryPerWorkerGB) // User-configurable memory per worker
1388
+ );
1389
+
965
1390
  this.logger.info(
966
- `Auto-optimized workers: ${workerCount} (CPU: ${cpuCount}, RAM: ${totalMemoryGB.toFixed(1)}GB)`,
1391
+ `Auto-calculated worker count: ${workerCount} (CPU: ${cpuCount}, RAM: ${totalMemoryGB.toFixed(1)}GB, ${memoryPerWorkerGB}GB per worker)`,
967
1392
  'Cluster'
968
1393
  );
1394
+ } else if (typeof workerCount === 'number') {
1395
+ // User specified a number - respect their choice
1396
+ this.logger.info(`Using user-specified worker count: ${workerCount}`, 'Cluster');
969
1397
  }
970
1398
 
971
1399
  if (cluster.isPrimary) {
972
- this.logger.info(`🚀 Starting ${workerCount} workers for maximum performance`, 'Cluster');
1400
+ this.logger.info(`Starting ${workerCount} workers`, 'Cluster');
973
1401
 
974
1402
  // Optimize cluster scheduling for high concurrency
975
- cluster.schedulingPolicy = cluster.SCHED_RR; // Round-robin scheduling
1403
+ // Round-robin is the default on all platforms except Windows (Node.js docs)
1404
+ // Provides better load distribution than shared socket approach
1405
+ cluster.schedulingPolicy = cluster.SCHED_RR;
976
1406
 
977
1407
  // Set cluster settings for better performance
978
1408
  cluster.setupMaster({
979
- exec: process.argv[1],
1409
+ exec: process.argv[1] || process.execPath,
980
1410
  args: process.argv.slice(2),
981
1411
  silent: false,
982
1412
  });
983
1413
 
984
- // Optimize IPC to reduce communication overhead
1414
+ // IPC Optimization: Reduce communication overhead between master and workers
1415
+ // Research shows excessive IPC can create bottlenecks in clustered applications
1416
+ // (Source: BetterStack - Node.js Clustering Guide)
985
1417
  process.env.NODE_CLUSTER_SCHED_POLICY = 'rr'; // Ensure round-robin
986
- process.env.NODE_DISABLE_COLORS = '1'; // Reduce IPC message size
1418
+ process.env.NODE_DISABLE_COLORS = '1'; // Reduce IPC message size by disabling color codes
987
1419
 
988
1420
  // Graceful shutdown handler
989
1421
  const gracefulShutdown = () => {
@@ -1004,37 +1436,32 @@ export class Moro extends EventEmitter {
1004
1436
  process.on('SIGINT', gracefulShutdown);
1005
1437
  process.on('SIGTERM', gracefulShutdown);
1006
1438
 
1007
- // Fork workers with proper tracking and CPU affinity
1439
+ // Fork workers with basic tracking
1008
1440
  for (let i = 0; i < workerCount; i++) {
1009
- const worker = cluster.fork({
1010
- WORKER_ID: i,
1011
- WORKER_CPU_AFFINITY: i % os.cpus().length, // Distribute workers across CPUs
1012
- });
1441
+ const worker = cluster.fork();
1013
1442
  this.clusterWorkers.set(worker.process.pid!, worker);
1014
- this.logger.info(
1015
- `Worker ${worker.process.pid} started (CPU ${i % os.cpus().length})`,
1016
- 'Cluster'
1017
- );
1443
+ this.logger.info(`Worker ${worker.process.pid} started`, 'Cluster');
1018
1444
 
1019
- // Handle individual worker messages (reuse handler)
1445
+ // Handle individual worker messages
1020
1446
  worker.on('message', this.handleWorkerMessage.bind(this));
1021
1447
  }
1022
1448
 
1023
- // Handle worker exits with cleanup
1449
+ // Simple worker exit handling
1024
1450
  cluster.on('exit', (worker: any, code: number, signal: string) => {
1025
- // Clean up worker tracking
1026
- this.clusterWorkers.delete(worker.process.pid);
1027
-
1028
- this.logger.warn(
1029
- `Worker ${worker.process.pid} died (${signal || code}). Restarting...`,
1030
- 'Cluster'
1031
- );
1032
-
1033
- // Restart worker with proper tracking
1034
- const newWorker = cluster.fork();
1035
- this.clusterWorkers.set(newWorker.process.pid!, newWorker);
1036
- newWorker.on('message', this.handleWorkerMessage.bind(this));
1037
- this.logger.info(`Worker ${newWorker.process.pid} started`, 'Cluster');
1451
+ const pid = worker.process.pid;
1452
+ this.clusterWorkers.delete(pid);
1453
+
1454
+ if (code !== 0 && !worker.exitedAfterDisconnect) {
1455
+ this.logger.warn(
1456
+ `Worker ${pid} died unexpectedly (${signal || code}). Restarting...`,
1457
+ 'Cluster'
1458
+ );
1459
+
1460
+ // Simple restart
1461
+ const newWorker = cluster.fork();
1462
+ this.clusterWorkers.set(newWorker.process.pid!, newWorker);
1463
+ this.logger.info(`Worker ${newWorker.process.pid} restarted`, 'Cluster');
1464
+ }
1038
1465
  });
1039
1466
 
1040
1467
  // Master process callback
@@ -1047,17 +1474,33 @@ export class Moro extends EventEmitter {
1047
1474
  process.env.UV_THREADPOOL_SIZE = '64';
1048
1475
 
1049
1476
  // Reduce logging contention in workers (major bottleneck)
1050
- if (this.config.logging) {
1051
- // Workers log less frequently to reduce I/O contention
1052
- this.config.logging.level = 'warn'; // Only warnings and errors
1477
+ // Multiple workers writing to same log files creates I/O contention
1478
+ // ONLY reduce log level if user didn't explicitly set one
1479
+ if (!this.userSetLogger) {
1480
+ // Workers log less frequently to reduce I/O contention (only if not explicitly configured)
1481
+ applyLoggingConfiguration(undefined, { level: 'warn' }); // Only warnings and errors
1053
1482
  }
1054
1483
 
1055
- // Memory optimization for workers
1056
- process.env.NODE_OPTIONS = '--max-old-space-size=1024'; // Limit memory per worker
1484
+ // Research-based memory optimization for workers
1485
+ const totalMemoryGB = os.totalmem() / (1024 * 1024 * 1024);
1486
+ const workerCount = Object.keys(cluster.workers || {}).length || 1;
1487
+
1488
+ // Conservative memory allocation
1489
+ const heapSizePerWorkerMB = Math.min(
1490
+ Math.floor(((totalMemoryGB * 1024) / workerCount) * 0.8), // 80% of available memory
1491
+ 1536 // Cap at 1.5GB (GC efficiency threshold from research)
1492
+ );
1493
+
1494
+ process.env.NODE_OPTIONS = `--max-old-space-size=${heapSizePerWorkerMB}`;
1057
1495
 
1058
- // Optimize V8 flags for better performance (Rust-level optimizations)
1496
+ this.logger.debug(
1497
+ `Worker memory allocated: ${heapSizePerWorkerMB}MB heap (${workerCount} workers, ${totalMemoryGB.toFixed(1)}GB total)`,
1498
+ 'Worker'
1499
+ );
1500
+
1501
+ // Optimize V8 flags for better performance
1059
1502
  if (process.env.NODE_ENV === 'production') {
1060
- // Ultra-aggressive V8 optimizations for maximum performance
1503
+ // Aggressive V8 optimizations for maximum performance
1061
1504
  const v8Flags = [
1062
1505
  '--optimize-for-size', // Trade memory for speed
1063
1506
  '--always-opt', // Always optimize functions
@@ -1116,17 +1559,31 @@ export class Moro extends EventEmitter {
1116
1559
  // Documentation not enabled, that's fine
1117
1560
  }
1118
1561
 
1119
- // Add intelligent routing middleware
1120
- this.coreFramework.addMiddleware(
1121
- async (req: HttpRequest, res: HttpResponse, next: () => void) => {
1122
- const handled = await this.intelligentRouting.handleIntelligentRoute(req, res);
1123
- if (!handled) {
1562
+ // Add unified routing middleware (handles both chainable and direct routes)
1563
+ // Optimized: call router without extra async wrapper when possible
1564
+ this.coreFramework.addMiddleware((req: HttpRequest, res: HttpResponse, next: () => void) => {
1565
+ // Try unified router first (handles all route types)
1566
+ const handled = this.unifiedRouter.handleRequest(req, res);
1567
+
1568
+ // Check if it's a promise (async route) or sync
1569
+ if (handled && typeof (handled as any).then === 'function') {
1570
+ // Async - await the result
1571
+ (handled as Promise<boolean>)
1572
+ .then(isHandled => {
1573
+ if (!isHandled) {
1574
+ next(); // Fall back to legacy routes if any
1575
+ }
1576
+ })
1577
+ .catch(() => next());
1578
+ } else {
1579
+ // Sync - check immediately
1580
+ if (!(handled as boolean)) {
1124
1581
  next();
1125
1582
  }
1126
1583
  }
1127
- );
1584
+ });
1128
1585
 
1129
- // Register direct routes
1586
+ // Register legacy direct routes with the HTTP server (for backward compatibility)
1130
1587
  if (this.routes.length > 0) {
1131
1588
  this.registerDirectRoutes();
1132
1589
  }
@@ -1141,15 +1598,37 @@ export class Moro extends EventEmitter {
1141
1598
  });
1142
1599
  };
1143
1600
 
1144
- if (host) {
1145
- this.coreFramework.listen(port, host, workerCallback);
1146
- } else {
1147
- this.coreFramework.listen(port, workerCallback);
1148
- }
1601
+ // Ensure WebSocket setup is complete before starting worker
1602
+ this.processQueuedWebSocketRegistrations()
1603
+ .then(() => {
1604
+ if (host) {
1605
+ this.coreFramework.listen(port, host, workerCallback);
1606
+ } else {
1607
+ this.coreFramework.listen(port, workerCallback);
1608
+ }
1609
+ })
1610
+ .catch(error => {
1611
+ this.logger.error('WebSocket initialization failed in worker', 'Worker', {
1612
+ error: error instanceof Error ? error.message : String(error),
1613
+ });
1614
+ // For WebSocket failures with queued registrations, error will propagate
1615
+ if (
1616
+ error instanceof Error &&
1617
+ error.message.includes('WebSocket features require a WebSocket adapter')
1618
+ ) {
1619
+ throw error;
1620
+ }
1621
+ // Start anyway for other errors
1622
+ if (host) {
1623
+ this.coreFramework.listen(port, host, workerCallback);
1624
+ } else {
1625
+ this.coreFramework.listen(port, workerCallback);
1626
+ }
1627
+ });
1149
1628
  }
1150
1629
  }
1151
1630
 
1152
- // Reusable worker message handler (avoids creating new functions)
1631
+ // Simple worker message handler
1153
1632
  private handleWorkerMessage(message: any): void {
1154
1633
  // Handle inter-worker communication if needed
1155
1634
  if (message.type === 'health-check') {
@@ -1160,6 +1639,58 @@ export class Moro extends EventEmitter {
1160
1639
  // Log other worker messages
1161
1640
  this.logger.debug(`Worker message: ${JSON.stringify(message)}`, 'Cluster');
1162
1641
  }
1642
+
1643
+ /**
1644
+ * Gracefully close the application and clean up resources
1645
+ * This should be called in tests and during shutdown
1646
+ */
1647
+ async close(): Promise<void> {
1648
+ this.logger.debug('Closing Moro application...');
1649
+
1650
+ // Flush logger buffer before shutdown
1651
+ try {
1652
+ // Use flushBuffer for immediate synchronous flush
1653
+ this.logger.flushBuffer();
1654
+ } catch (error) {
1655
+ // Ignore flush errors during shutdown
1656
+ }
1657
+
1658
+ // Close the core framework with timeout
1659
+ if (this.coreFramework && (this.coreFramework as any).httpServer) {
1660
+ try {
1661
+ await Promise.race([
1662
+ new Promise<void>(resolve => {
1663
+ (this.coreFramework as any).httpServer.close(() => {
1664
+ resolve();
1665
+ });
1666
+ }),
1667
+ new Promise<void>(resolve => setTimeout(resolve, 2000)), // 2 second timeout
1668
+ ]);
1669
+ } catch (error) {
1670
+ // Force close if graceful close fails
1671
+ this.logger.warn('Force closing HTTP server due to timeout');
1672
+ }
1673
+ }
1674
+
1675
+ // Clean up module discovery watchers
1676
+ if (this.moduleDiscovery && typeof this.moduleDiscovery.cleanup === 'function') {
1677
+ try {
1678
+ this.moduleDiscovery.cleanup();
1679
+ } catch (error) {
1680
+ // Ignore cleanup errors
1681
+ }
1682
+ }
1683
+
1684
+ // Clean up event listeners
1685
+ try {
1686
+ this.eventBus.removeAllListeners();
1687
+ this.removeAllListeners();
1688
+ } catch (error) {
1689
+ // Ignore cleanup errors
1690
+ }
1691
+
1692
+ this.logger.debug('Moro application closed successfully');
1693
+ }
1163
1694
  }
1164
1695
 
1165
1696
  // Export convenience function