@morojs/moro 1.6.2 → 1.6.4

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 (366) hide show
  1. package/dist/core/http/http-server.js +12 -9
  2. package/dist/core/http/http-server.js.map +1 -1
  3. package/dist/core/http/uws-http-server.js +1 -1
  4. package/dist/core/http/uws-http-server.js.map +1 -1
  5. package/dist/core/middleware/built-in/auth/core.d.ts +78 -0
  6. package/dist/core/middleware/built-in/auth/core.js +358 -0
  7. package/dist/core/middleware/built-in/auth/core.js.map +1 -0
  8. package/dist/core/middleware/built-in/{auth-helpers.js → auth/helpers.js} +1 -1
  9. package/dist/core/middleware/built-in/auth/helpers.js.map +1 -0
  10. package/dist/core/middleware/built-in/auth/hook.d.ts +30 -0
  11. package/dist/core/middleware/built-in/auth/hook.js +99 -0
  12. package/dist/core/middleware/built-in/auth/hook.js.map +1 -0
  13. package/dist/core/middleware/built-in/auth/index.d.ts +7 -0
  14. package/dist/core/middleware/built-in/auth/index.js +15 -0
  15. package/dist/core/middleware/built-in/auth/index.js.map +1 -0
  16. package/dist/core/middleware/built-in/{jwt-helpers.js → auth/jwt-helpers.js} +1 -1
  17. package/dist/core/middleware/built-in/auth/jwt-helpers.js.map +1 -0
  18. package/dist/core/middleware/built-in/auth/middleware.d.ts +23 -0
  19. package/dist/core/middleware/built-in/auth/middleware.js +71 -0
  20. package/dist/core/middleware/built-in/auth/middleware.js.map +1 -0
  21. package/dist/core/middleware/built-in/{auth-providers.d.ts → auth/providers.d.ts} +1 -1
  22. package/dist/core/middleware/built-in/{auth-providers.js → auth/providers.js} +1 -1
  23. package/dist/core/middleware/built-in/auth/providers.js.map +1 -0
  24. package/dist/core/middleware/built-in/{adapters → cache/adapters}/cache/file.d.ts +1 -1
  25. package/dist/core/middleware/built-in/{adapters → cache/adapters}/cache/file.js +1 -1
  26. package/dist/core/middleware/built-in/cache/adapters/cache/file.js.map +1 -0
  27. package/dist/core/middleware/built-in/{adapters → cache/adapters}/cache/index.d.ts +1 -1
  28. package/dist/core/middleware/built-in/cache/adapters/cache/index.js.map +1 -0
  29. package/dist/core/middleware/built-in/{adapters → cache/adapters}/cache/memory.d.ts +1 -1
  30. package/dist/core/middleware/built-in/{adapters → cache/adapters}/cache/memory.js +1 -1
  31. package/dist/core/middleware/built-in/cache/adapters/cache/memory.js.map +1 -0
  32. package/dist/core/middleware/built-in/{adapters → cache/adapters}/cache/redis.d.ts +1 -1
  33. package/dist/core/middleware/built-in/{adapters → cache/adapters}/cache/redis.js +2 -2
  34. package/dist/core/middleware/built-in/cache/adapters/cache/redis.js.map +1 -0
  35. package/dist/core/middleware/built-in/{adapters → cache/adapters}/index.d.ts +0 -2
  36. package/{src/core/middleware/built-in/adapters/index.ts → dist/core/middleware/built-in/cache/adapters/index.js} +1 -3
  37. package/dist/core/middleware/built-in/cache/adapters/index.js.map +1 -0
  38. package/dist/core/middleware/built-in/cache/core.d.ts +37 -0
  39. package/dist/core/middleware/built-in/cache/core.js +87 -0
  40. package/dist/core/middleware/built-in/cache/core.js.map +1 -0
  41. package/dist/core/middleware/built-in/cache/hook.d.ts +20 -0
  42. package/dist/core/middleware/built-in/{cache.js → cache/hook.js} +22 -5
  43. package/dist/core/middleware/built-in/cache/hook.js.map +1 -0
  44. package/dist/core/middleware/built-in/cache/index.d.ts +3 -0
  45. package/dist/core/middleware/built-in/cache/index.js +9 -0
  46. package/dist/core/middleware/built-in/cache/index.js.map +1 -0
  47. package/dist/core/middleware/built-in/cache/middleware.d.ts +17 -0
  48. package/dist/core/middleware/built-in/cache/middleware.js +44 -0
  49. package/dist/core/middleware/built-in/cache/middleware.js.map +1 -0
  50. package/dist/core/middleware/built-in/{adapters → cdn/adapters}/cdn/azure.d.ts +1 -1
  51. package/dist/core/middleware/built-in/{adapters → cdn/adapters}/cdn/azure.js +1 -1
  52. package/dist/core/middleware/built-in/cdn/adapters/cdn/azure.js.map +1 -0
  53. package/dist/core/middleware/built-in/{adapters → cdn/adapters}/cdn/cloudflare.d.ts +1 -1
  54. package/dist/core/middleware/built-in/{adapters → cdn/adapters}/cdn/cloudflare.js +1 -1
  55. package/dist/core/middleware/built-in/cdn/adapters/cdn/cloudflare.js.map +1 -0
  56. package/dist/core/middleware/built-in/{adapters → cdn/adapters}/cdn/cloudfront.d.ts +1 -1
  57. package/dist/core/middleware/built-in/{adapters → cdn/adapters}/cdn/cloudfront.js +2 -2
  58. package/dist/core/middleware/built-in/cdn/adapters/cdn/cloudfront.js.map +1 -0
  59. package/dist/core/middleware/built-in/{adapters → cdn/adapters}/cdn/index.d.ts +1 -1
  60. package/dist/core/middleware/built-in/cdn/adapters/cdn/index.js.map +1 -0
  61. package/dist/core/middleware/built-in/cdn/adapters/index.d.ts +2 -0
  62. package/dist/core/middleware/built-in/{adapters → cdn/adapters}/index.js +0 -2
  63. package/dist/core/middleware/built-in/cdn/adapters/index.js.map +1 -0
  64. package/dist/core/middleware/built-in/cdn/core.d.ts +43 -0
  65. package/dist/core/middleware/built-in/cdn/core.js +144 -0
  66. package/dist/core/middleware/built-in/cdn/core.js.map +1 -0
  67. package/dist/core/middleware/built-in/cdn/hook.d.ts +22 -0
  68. package/dist/core/middleware/built-in/cdn/hook.js +70 -0
  69. package/dist/core/middleware/built-in/cdn/hook.js.map +1 -0
  70. package/dist/core/middleware/built-in/cdn/index.d.ts +5 -0
  71. package/dist/core/middleware/built-in/cdn/index.js +11 -0
  72. package/dist/core/middleware/built-in/cdn/index.js.map +1 -0
  73. package/dist/core/middleware/built-in/cdn/middleware.d.ts +21 -0
  74. package/dist/core/middleware/built-in/cdn/middleware.js +52 -0
  75. package/dist/core/middleware/built-in/cdn/middleware.js.map +1 -0
  76. package/dist/core/middleware/built-in/cookie/core.d.ts +37 -0
  77. package/dist/core/middleware/built-in/cookie/core.js +83 -0
  78. package/dist/core/middleware/built-in/cookie/core.js.map +1 -0
  79. package/dist/core/middleware/built-in/cookie/hook.d.ts +20 -0
  80. package/dist/core/middleware/built-in/cookie/hook.js +47 -0
  81. package/dist/core/middleware/built-in/cookie/hook.js.map +1 -0
  82. package/dist/core/middleware/built-in/cookie/index.d.ts +3 -0
  83. package/dist/core/middleware/built-in/cookie/index.js +9 -0
  84. package/dist/core/middleware/built-in/cookie/index.js.map +1 -0
  85. package/dist/core/middleware/built-in/cookie/middleware.d.ts +17 -0
  86. package/dist/core/middleware/built-in/cookie/middleware.js +36 -0
  87. package/dist/core/middleware/built-in/cookie/middleware.js.map +1 -0
  88. package/dist/core/middleware/built-in/cors/core.d.ts +23 -0
  89. package/dist/core/middleware/built-in/cors/core.js +51 -0
  90. package/dist/core/middleware/built-in/cors/core.js.map +1 -0
  91. package/dist/core/middleware/built-in/cors/hook.d.ts +17 -0
  92. package/dist/core/middleware/built-in/cors/hook.js +37 -0
  93. package/dist/core/middleware/built-in/cors/hook.js.map +1 -0
  94. package/dist/core/middleware/built-in/cors/index.d.ts +3 -0
  95. package/dist/core/middleware/built-in/cors/index.js +9 -0
  96. package/dist/core/middleware/built-in/cors/index.js.map +1 -0
  97. package/dist/core/middleware/built-in/cors/middleware.d.ts +16 -0
  98. package/dist/core/middleware/built-in/cors/middleware.js +22 -0
  99. package/dist/core/middleware/built-in/cors/middleware.js.map +1 -0
  100. package/dist/core/middleware/built-in/csp/core.d.ts +45 -0
  101. package/dist/core/middleware/built-in/csp/core.js +88 -0
  102. package/dist/core/middleware/built-in/csp/core.js.map +1 -0
  103. package/dist/core/middleware/built-in/csp/hook.d.ts +22 -0
  104. package/dist/core/middleware/built-in/csp/hook.js +47 -0
  105. package/dist/core/middleware/built-in/csp/hook.js.map +1 -0
  106. package/dist/core/middleware/built-in/csp/index.d.ts +3 -0
  107. package/dist/core/middleware/built-in/csp/index.js +9 -0
  108. package/dist/core/middleware/built-in/csp/index.js.map +1 -0
  109. package/dist/core/middleware/built-in/csp/middleware.d.ts +19 -0
  110. package/dist/core/middleware/built-in/csp/middleware.js +29 -0
  111. package/dist/core/middleware/built-in/csp/middleware.js.map +1 -0
  112. package/dist/core/middleware/built-in/csrf/core.d.ts +28 -0
  113. package/dist/core/middleware/built-in/csrf/core.js +69 -0
  114. package/dist/core/middleware/built-in/csrf/core.js.map +1 -0
  115. package/dist/core/middleware/built-in/csrf/hook.d.ts +17 -0
  116. package/dist/core/middleware/built-in/csrf/hook.js +45 -0
  117. package/dist/core/middleware/built-in/csrf/hook.js.map +1 -0
  118. package/dist/core/middleware/built-in/csrf/index.d.ts +3 -0
  119. package/dist/core/middleware/built-in/csrf/index.js +9 -0
  120. package/dist/core/middleware/built-in/csrf/index.js.map +1 -0
  121. package/dist/core/middleware/built-in/csrf/middleware.d.ts +16 -0
  122. package/dist/core/middleware/built-in/csrf/middleware.js +34 -0
  123. package/dist/core/middleware/built-in/csrf/middleware.js.map +1 -0
  124. package/dist/core/middleware/built-in/error-tracker/index.d.ts +1 -0
  125. package/dist/core/middleware/built-in/error-tracker/index.js +4 -0
  126. package/dist/core/middleware/built-in/error-tracker/index.js.map +1 -0
  127. package/dist/core/middleware/built-in/error-tracker/middleware.d.ts +12 -0
  128. package/dist/core/middleware/built-in/{error-tracker.js → error-tracker/middleware.js} +14 -3
  129. package/dist/core/middleware/built-in/error-tracker/middleware.js.map +1 -0
  130. package/dist/core/middleware/built-in/index.d.ts +25 -59
  131. package/dist/core/middleware/built-in/index.js +31 -31
  132. package/dist/core/middleware/built-in/index.js.map +1 -1
  133. package/dist/core/middleware/built-in/performance-monitor/index.d.ts +1 -0
  134. package/dist/core/middleware/built-in/performance-monitor/index.js +4 -0
  135. package/dist/core/middleware/built-in/performance-monitor/index.js.map +1 -0
  136. package/dist/core/middleware/built-in/performance-monitor/middleware.d.ts +12 -0
  137. package/dist/core/middleware/built-in/{performance-monitor.js → performance-monitor/middleware.js} +14 -3
  138. package/dist/core/middleware/built-in/performance-monitor/middleware.js.map +1 -0
  139. package/dist/core/middleware/built-in/rate-limit/core.d.ts +33 -0
  140. package/dist/core/middleware/built-in/rate-limit/core.js +86 -0
  141. package/dist/core/middleware/built-in/rate-limit/core.js.map +1 -0
  142. package/dist/core/middleware/built-in/rate-limit/hook.d.ts +20 -0
  143. package/dist/core/middleware/built-in/{rate-limit.js → rate-limit/hook.js} +22 -16
  144. package/dist/core/middleware/built-in/rate-limit/hook.js.map +1 -0
  145. package/dist/core/middleware/built-in/rate-limit/index.d.ts +3 -0
  146. package/dist/core/middleware/built-in/rate-limit/index.js +9 -0
  147. package/dist/core/middleware/built-in/rate-limit/index.js.map +1 -0
  148. package/dist/core/middleware/built-in/rate-limit/middleware.d.ts +16 -0
  149. package/dist/core/middleware/built-in/rate-limit/middleware.js +35 -0
  150. package/dist/core/middleware/built-in/rate-limit/middleware.js.map +1 -0
  151. package/dist/core/middleware/built-in/request-logger/index.d.ts +1 -0
  152. package/dist/core/middleware/built-in/request-logger/index.js +4 -0
  153. package/dist/core/middleware/built-in/request-logger/index.js.map +1 -0
  154. package/dist/core/middleware/built-in/request-logger/middleware.d.ts +12 -0
  155. package/dist/core/middleware/built-in/{request-logger.js → request-logger/middleware.js} +14 -3
  156. package/dist/core/middleware/built-in/request-logger/middleware.js.map +1 -0
  157. package/dist/core/middleware/built-in/session/core.d.ts +73 -0
  158. package/dist/core/middleware/built-in/session/core.js +227 -0
  159. package/dist/core/middleware/built-in/session/core.js.map +1 -0
  160. package/dist/core/middleware/built-in/session/hook.d.ts +17 -0
  161. package/dist/core/middleware/built-in/session/hook.js +53 -0
  162. package/dist/core/middleware/built-in/session/hook.js.map +1 -0
  163. package/dist/core/middleware/built-in/session/index.d.ts +3 -0
  164. package/dist/core/middleware/built-in/session/index.js +9 -0
  165. package/dist/core/middleware/built-in/session/index.js.map +1 -0
  166. package/dist/core/middleware/built-in/session/middleware.d.ts +17 -0
  167. package/dist/core/middleware/built-in/session/middleware.js +38 -0
  168. package/dist/core/middleware/built-in/session/middleware.js.map +1 -0
  169. package/dist/core/middleware/built-in/sse/core.d.ts +44 -0
  170. package/dist/core/middleware/built-in/sse/core.js +117 -0
  171. package/dist/core/middleware/built-in/sse/core.js.map +1 -0
  172. package/dist/core/middleware/built-in/sse/hook.d.ts +18 -0
  173. package/dist/core/middleware/built-in/sse/hook.js +60 -0
  174. package/dist/core/middleware/built-in/sse/hook.js.map +1 -0
  175. package/dist/core/middleware/built-in/sse/index.d.ts +3 -0
  176. package/dist/core/middleware/built-in/sse/index.js +9 -0
  177. package/dist/core/middleware/built-in/sse/index.js.map +1 -0
  178. package/dist/core/middleware/built-in/sse/middleware.d.ts +18 -0
  179. package/dist/core/middleware/built-in/sse/middleware.js +43 -0
  180. package/dist/core/middleware/built-in/sse/middleware.js.map +1 -0
  181. package/dist/core/middleware/built-in/validation/core.d.ts +23 -0
  182. package/dist/core/middleware/built-in/validation/core.js +93 -0
  183. package/dist/core/middleware/built-in/validation/core.js.map +1 -0
  184. package/dist/core/middleware/built-in/validation/hook.d.ts +13 -0
  185. package/dist/core/middleware/built-in/{validation.js → validation/hook.js} +14 -3
  186. package/dist/core/middleware/built-in/validation/hook.js.map +1 -0
  187. package/dist/core/middleware/built-in/validation/index.d.ts +3 -0
  188. package/dist/core/middleware/built-in/validation/index.js +9 -0
  189. package/dist/core/middleware/built-in/validation/index.js.map +1 -0
  190. package/dist/core/middleware/built-in/validation/middleware.d.ts +16 -0
  191. package/dist/core/middleware/built-in/validation/middleware.js +27 -0
  192. package/dist/core/middleware/built-in/validation/middleware.js.map +1 -0
  193. package/dist/core/middleware/index.js +6 -0
  194. package/dist/core/middleware/index.js.map +1 -1
  195. package/dist/core/routing/unified-router.d.ts +4 -20
  196. package/dist/core/routing/unified-router.js +61 -106
  197. package/dist/core/routing/unified-router.js.map +1 -1
  198. package/dist/index.d.ts +3 -2
  199. package/dist/index.js +3 -2
  200. package/dist/index.js.map +1 -1
  201. package/dist/moro.js +12 -18
  202. package/dist/moro.js.map +1 -1
  203. package/dist/types/hooks.d.ts +3 -0
  204. package/package.json +2 -6
  205. package/dist/core/middleware/built-in/adapters/cache/file.js.map +0 -1
  206. package/dist/core/middleware/built-in/adapters/cache/index.js.map +0 -1
  207. package/dist/core/middleware/built-in/adapters/cache/memory.js.map +0 -1
  208. package/dist/core/middleware/built-in/adapters/cache/redis.js.map +0 -1
  209. package/dist/core/middleware/built-in/adapters/cdn/azure.js.map +0 -1
  210. package/dist/core/middleware/built-in/adapters/cdn/cloudflare.js.map +0 -1
  211. package/dist/core/middleware/built-in/adapters/cdn/cloudfront.js.map +0 -1
  212. package/dist/core/middleware/built-in/adapters/cdn/index.js.map +0 -1
  213. package/dist/core/middleware/built-in/adapters/index.js.map +0 -1
  214. package/dist/core/middleware/built-in/auth-helpers.js.map +0 -1
  215. package/dist/core/middleware/built-in/auth-providers.js.map +0 -1
  216. package/dist/core/middleware/built-in/auth.d.ts +0 -30
  217. package/dist/core/middleware/built-in/auth.js +0 -348
  218. package/dist/core/middleware/built-in/auth.js.map +0 -1
  219. package/dist/core/middleware/built-in/cache.d.ts +0 -3
  220. package/dist/core/middleware/built-in/cache.js.map +0 -1
  221. package/dist/core/middleware/built-in/cdn.d.ts +0 -3
  222. package/dist/core/middleware/built-in/cdn.js +0 -109
  223. package/dist/core/middleware/built-in/cdn.js.map +0 -1
  224. package/dist/core/middleware/built-in/cookie.d.ts +0 -14
  225. package/dist/core/middleware/built-in/cookie.js +0 -64
  226. package/dist/core/middleware/built-in/cookie.js.map +0 -1
  227. package/dist/core/middleware/built-in/cors.d.ts +0 -2
  228. package/dist/core/middleware/built-in/cors.js +0 -25
  229. package/dist/core/middleware/built-in/cors.js.map +0 -1
  230. package/dist/core/middleware/built-in/csp.d.ts +0 -22
  231. package/dist/core/middleware/built-in/csp.js +0 -68
  232. package/dist/core/middleware/built-in/csp.js.map +0 -1
  233. package/dist/core/middleware/built-in/csrf.d.ts +0 -9
  234. package/dist/core/middleware/built-in/csrf.js +0 -60
  235. package/dist/core/middleware/built-in/csrf.js.map +0 -1
  236. package/dist/core/middleware/built-in/error-tracker.d.ts +0 -1
  237. package/dist/core/middleware/built-in/error-tracker.js.map +0 -1
  238. package/dist/core/middleware/built-in/jwt-helpers.js.map +0 -1
  239. package/dist/core/middleware/built-in/performance-monitor.d.ts +0 -1
  240. package/dist/core/middleware/built-in/performance-monitor.js.map +0 -1
  241. package/dist/core/middleware/built-in/rate-limit.d.ts +0 -6
  242. package/dist/core/middleware/built-in/rate-limit.js.map +0 -1
  243. package/dist/core/middleware/built-in/request-logger.d.ts +0 -1
  244. package/dist/core/middleware/built-in/request-logger.js.map +0 -1
  245. package/dist/core/middleware/built-in/session.d.ts +0 -41
  246. package/dist/core/middleware/built-in/session.js +0 -205
  247. package/dist/core/middleware/built-in/session.js.map +0 -1
  248. package/dist/core/middleware/built-in/sse.d.ts +0 -6
  249. package/dist/core/middleware/built-in/sse.js +0 -69
  250. package/dist/core/middleware/built-in/sse.js.map +0 -1
  251. package/dist/core/middleware/built-in/validation.d.ts +0 -2
  252. package/dist/core/middleware/built-in/validation.js.map +0 -1
  253. package/jest.config.mjs +0 -41
  254. package/src/core/auth/README.md +0 -339
  255. package/src/core/auth/morojs-adapter.ts +0 -415
  256. package/src/core/config/config-manager.ts +0 -133
  257. package/src/core/config/config-sources.ts +0 -600
  258. package/src/core/config/config-validator.ts +0 -1116
  259. package/src/core/config/file-loader.ts +0 -150
  260. package/src/core/config/index.ts +0 -109
  261. package/src/core/config/schema.ts +0 -164
  262. package/src/core/config/utils.ts +0 -244
  263. package/src/core/database/README.md +0 -238
  264. package/src/core/database/adapters/drizzle.ts +0 -415
  265. package/src/core/database/adapters/index.ts +0 -42
  266. package/src/core/database/adapters/mongodb.ts +0 -317
  267. package/src/core/database/adapters/mysql.ts +0 -235
  268. package/src/core/database/adapters/postgresql.ts +0 -226
  269. package/src/core/database/adapters/redis.ts +0 -379
  270. package/src/core/database/adapters/sqlite.ts +0 -263
  271. package/src/core/database/index.ts +0 -3
  272. package/src/core/docs/index.ts +0 -231
  273. package/src/core/docs/openapi-generator.ts +0 -576
  274. package/src/core/docs/schema-to-openapi.ts +0 -145
  275. package/src/core/docs/simple-docs.ts +0 -295
  276. package/src/core/docs/swagger-ui.ts +0 -354
  277. package/src/core/docs/zod-to-openapi.ts +0 -532
  278. package/src/core/events/event-bus.ts +0 -231
  279. package/src/core/events/index.ts +0 -12
  280. package/src/core/framework.ts +0 -885
  281. package/src/core/http/http-server.ts +0 -1847
  282. package/src/core/http/index.ts +0 -7
  283. package/src/core/http/uws-http-server.ts +0 -591
  284. package/src/core/logger/filters.ts +0 -153
  285. package/src/core/logger/index.ts +0 -21
  286. package/src/core/logger/logger.ts +0 -1033
  287. package/src/core/logger/outputs.ts +0 -132
  288. package/src/core/middleware/built-in/adapters/cache/file.ts +0 -104
  289. package/src/core/middleware/built-in/adapters/cache/index.ts +0 -23
  290. package/src/core/middleware/built-in/adapters/cache/memory.ts +0 -73
  291. package/src/core/middleware/built-in/adapters/cache/redis.ts +0 -114
  292. package/src/core/middleware/built-in/adapters/cdn/azure.ts +0 -60
  293. package/src/core/middleware/built-in/adapters/cdn/cloudflare.ts +0 -83
  294. package/src/core/middleware/built-in/adapters/cdn/cloudfront.ts +0 -94
  295. package/src/core/middleware/built-in/adapters/cdn/index.ts +0 -23
  296. package/src/core/middleware/built-in/auth-helpers.ts +0 -401
  297. package/src/core/middleware/built-in/auth-providers.ts +0 -480
  298. package/src/core/middleware/built-in/auth.ts +0 -410
  299. package/src/core/middleware/built-in/cache.ts +0 -213
  300. package/src/core/middleware/built-in/cdn.ts +0 -124
  301. package/src/core/middleware/built-in/cookie.ts +0 -85
  302. package/src/core/middleware/built-in/cors.ts +0 -38
  303. package/src/core/middleware/built-in/csp.ts +0 -101
  304. package/src/core/middleware/built-in/csrf.ts +0 -82
  305. package/src/core/middleware/built-in/error-tracker.ts +0 -16
  306. package/src/core/middleware/built-in/index.ts +0 -87
  307. package/src/core/middleware/built-in/jwt-helpers.ts +0 -243
  308. package/src/core/middleware/built-in/performance-monitor.ts +0 -25
  309. package/src/core/middleware/built-in/rate-limit.ts +0 -60
  310. package/src/core/middleware/built-in/request-logger.ts +0 -16
  311. package/src/core/middleware/built-in/session.ts +0 -287
  312. package/src/core/middleware/built-in/sse.ts +0 -88
  313. package/src/core/middleware/built-in/validation.ts +0 -33
  314. package/src/core/middleware/index.ts +0 -177
  315. package/src/core/modules/auto-discovery.ts +0 -726
  316. package/src/core/modules/index.ts +0 -3
  317. package/src/core/modules/modules.ts +0 -135
  318. package/src/core/networking/adapters/index.ts +0 -17
  319. package/src/core/networking/adapters/socketio-adapter.ts +0 -254
  320. package/src/core/networking/adapters/uws-adapter.ts +0 -619
  321. package/src/core/networking/adapters/ws-adapter.ts +0 -429
  322. package/src/core/networking/index.ts +0 -4
  323. package/src/core/networking/service-discovery.ts +0 -303
  324. package/src/core/networking/websocket-adapter.ts +0 -217
  325. package/src/core/networking/websocket-manager.ts +0 -308
  326. package/src/core/pooling/object-pool-manager.ts +0 -630
  327. package/src/core/routing/app-integration.ts +0 -164
  328. package/src/core/routing/index.ts +0 -261
  329. package/src/core/routing/path-matcher.ts +0 -222
  330. package/src/core/routing/router.ts +0 -97
  331. package/src/core/routing/unified-router.ts +0 -870
  332. package/src/core/runtime/aws-lambda-adapter.ts +0 -147
  333. package/src/core/runtime/base-adapter.ts +0 -130
  334. package/src/core/runtime/cloudflare-workers-adapter.ts +0 -152
  335. package/src/core/runtime/index.ts +0 -62
  336. package/src/core/runtime/node-adapter.ts +0 -202
  337. package/src/core/runtime/vercel-edge-adapter.ts +0 -114
  338. package/src/core/utilities/circuit-breaker.ts +0 -46
  339. package/src/core/utilities/container.ts +0 -736
  340. package/src/core/utilities/hooks.ts +0 -142
  341. package/src/core/utilities/index.ts +0 -17
  342. package/src/core/utilities/package-utils.ts +0 -59
  343. package/src/core/validation/adapters.ts +0 -147
  344. package/src/core/validation/index.ts +0 -258
  345. package/src/core/validation/schema-interface.ts +0 -100
  346. package/src/index.ts +0 -233
  347. package/src/moro.ts +0 -1728
  348. package/src/types/auth.ts +0 -440
  349. package/src/types/cache.ts +0 -38
  350. package/src/types/cdn.ts +0 -22
  351. package/src/types/config.ts +0 -229
  352. package/src/types/core.ts +0 -58
  353. package/src/types/database.ts +0 -32
  354. package/src/types/discovery.ts +0 -7
  355. package/src/types/events.ts +0 -82
  356. package/src/types/hooks.ts +0 -47
  357. package/src/types/http.ts +0 -89
  358. package/src/types/logger.ts +0 -102
  359. package/src/types/module.ts +0 -99
  360. package/src/types/runtime.ts +0 -76
  361. package/src/types/session.ts +0 -89
  362. package/tsconfig.json +0 -23
  363. /package/dist/core/middleware/built-in/{auth-helpers.d.ts → auth/helpers.d.ts} +0 -0
  364. /package/dist/core/middleware/built-in/{jwt-helpers.d.ts → auth/jwt-helpers.d.ts} +0 -0
  365. /package/dist/core/middleware/built-in/{adapters → cache/adapters}/cache/index.js +0 -0
  366. /package/dist/core/middleware/built-in/{adapters → cdn/adapters}/cdn/index.js +0 -0
package/src/moro.ts DELETED
@@ -1,1728 +0,0 @@
1
- // Moro Framework - Modern TypeScript API Framework
2
- // Built for developers who demand performance, elegance, and zero compromises
3
- // Event-driven • Modular • Enterprise-ready • Developer-first
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';
15
- import {
16
- UnifiedRouter,
17
- RouteBuilder as UnifiedRouteBuilder,
18
- } from './core/routing/unified-router.js';
19
- import { AppDocumentationManager, DocsConfig } from './core/docs/index.js';
20
- import { EventEmitter } from 'events';
21
- import cluster from 'cluster';
22
- import os from 'os';
23
- import { normalizeValidationError } from './core/validation/schema-interface.js';
24
- // Configuration System Integration
25
- import { initializeConfig, type AppConfig } from './core/config/index.js';
26
- // Runtime System Integration
27
- import { RuntimeAdapter, RuntimeType, createRuntimeAdapter } from './core/runtime/index.js';
28
-
29
- export class Moro extends EventEmitter {
30
- private coreFramework!: MoroCore;
31
- private routes: InternalRouteDefinition[] = [];
32
- private moduleCounter = 0;
33
- private loadedModules = new Set<string>();
34
- private lazyModules = new Map<string, ModuleConfig>();
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;
40
- // Enterprise event system integration
41
- private eventBus!: MoroEventBus;
42
- // Application logger
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;
48
- // Documentation system
49
- private documentation = new AppDocumentationManager();
50
- // Configuration system
51
- private config!: AppConfig;
52
- // Track if user explicitly set logger options (for worker log level handling)
53
- private userSetLogger = false;
54
- // Runtime system
55
- private runtimeAdapter!: RuntimeAdapter;
56
- private runtimeType!: RuntimeType;
57
- // Middleware system
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
- }> = [];
65
-
66
- constructor(options: MoroOptions = {}) {
67
- super(); // Call EventEmitter constructor
68
-
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)
74
- const envLogLevel = process.env.LOG_LEVEL || process.env.MORO_LOG_LEVEL;
75
- if (envLogLevel) {
76
- applyLoggingConfiguration({ level: envLogLevel }, undefined);
77
- }
78
-
79
- // 2. createApp logger options (highest precedence)
80
- if (options.logger !== undefined) {
81
- applyLoggingConfiguration(undefined, options.logger);
82
- }
83
-
84
- // Create logger AFTER initial configuration
85
- this.logger = createFrameworkLogger('App');
86
-
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');
96
- }
97
-
98
- // NOW initialize routing systems AFTER logger is configured
99
- this.unifiedRouter = UnifiedRouter.getInstance();
100
- this.intelligentRouting = new IntelligentRoutingManager();
101
-
102
- this.logger.info(
103
- `Configuration system initialized: ${process.env.NODE_ENV || 'development'}:${this.config.server.port}`
104
- );
105
-
106
- // Initialize runtime system
107
- this.runtimeType = options.runtime?.type || 'node';
108
- this.runtimeAdapter = options.runtime?.adapter || createRuntimeAdapter(this.runtimeType);
109
-
110
- this.logger.info(`Runtime system initialized: ${this.runtimeType}`, 'Runtime');
111
-
112
- // Pass configuration from config to framework
113
- const frameworkOptions: any = {
114
- ...options,
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,
122
- };
123
-
124
- this.coreFramework = new MoroCore(frameworkOptions);
125
-
126
- // Initialize middleware system
127
- this.middlewareManager = new MiddlewareManager();
128
-
129
- // Integrate hooks system with HTTP server
130
- const httpServer = (this.coreFramework as any).httpServer;
131
- if (httpServer && httpServer.setHookManager) {
132
- httpServer.setHookManager((this.middlewareManager as any).hooks);
133
- }
134
-
135
- // Configure HTTP server performance based on config
136
- if (httpServer && httpServer.configurePerformance) {
137
- const performanceConfig = this.config.performance;
138
- httpServer.configurePerformance({
139
- compression: performanceConfig?.compression || { enabled: true },
140
- minimal: performanceConfig?.compression?.enabled === false, // Enable minimal mode if compression disabled
141
- });
142
- }
143
-
144
- // Access enterprise event bus from core framework
145
- this.eventBus = (this.coreFramework as any).eventBus;
146
-
147
- // Setup default middleware if enabled - use config defaults with options override
148
- this.setupDefaultMiddleware({
149
- ...this.getDefaultOptionsFromConfig(),
150
- ...options,
151
- });
152
-
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;
157
-
158
- // Emit initialization event through enterprise event bus
159
- this.eventBus.emit('framework:initialized', {
160
- options,
161
- config: this.config,
162
- runtime: this.runtimeType,
163
- });
164
- }
165
-
166
- /**
167
- * Get configuration object
168
- */
169
- getConfig(): AppConfig {
170
- return this.config;
171
- }
172
-
173
- /**
174
- * Get runtime adapter
175
- */
176
- getRuntime(): RuntimeAdapter {
177
- return this.runtimeAdapter;
178
- }
179
-
180
- /**
181
- * Get runtime type
182
- */
183
- getRuntimeType(): RuntimeType {
184
- return this.runtimeType;
185
- }
186
-
187
- /**
188
- * Extract default options from configuration
189
- */
190
- private getDefaultOptionsFromConfig(): Partial<MoroOptions> {
191
- return {
192
- cors: this.config.security.cors.enabled,
193
- compression: this.config.performance.compression.enabled,
194
- helmet: this.config.security.helmet.enabled,
195
- };
196
- }
197
-
198
- // Intelligent route methods - chainable with automatic middleware ordering
199
- // Overloads for better TypeScript inference
200
- get(path: string): UnifiedRouteBuilder;
201
- get(path: string, handler: (req: HttpRequest, res: HttpResponse) => any, options?: any): this;
202
- get(
203
- path: string,
204
- handler?: (req: HttpRequest, res: HttpResponse) => any,
205
- options?: any
206
- ): UnifiedRouteBuilder | this {
207
- if (handler) {
208
- // Direct route registration
209
- return this.addRoute('GET', path, handler, options);
210
- }
211
- // Use unified router for chainable API
212
- return this.unifiedRouter.get(path);
213
- }
214
-
215
- post(path: string): UnifiedRouteBuilder;
216
- post(path: string, handler: (req: HttpRequest, res: HttpResponse) => any, options?: any): this;
217
- post(
218
- path: string,
219
- handler?: (req: HttpRequest, res: HttpResponse) => any,
220
- options?: any
221
- ): UnifiedRouteBuilder | this {
222
- if (handler) {
223
- // Direct route registration
224
- return this.addRoute('POST', path, handler, options);
225
- }
226
- // Use unified router for chainable API
227
- return this.unifiedRouter.post(path);
228
- }
229
-
230
- put(path: string): UnifiedRouteBuilder;
231
- put(path: string, handler: (req: HttpRequest, res: HttpResponse) => any, options?: any): this;
232
- put(
233
- path: string,
234
- handler?: (req: HttpRequest, res: HttpResponse) => any,
235
- options?: any
236
- ): UnifiedRouteBuilder | this {
237
- if (handler) {
238
- // Direct route registration
239
- return this.addRoute('PUT', path, handler, options);
240
- }
241
- // Use unified router for chainable API
242
- return this.unifiedRouter.put(path);
243
- }
244
-
245
- delete(path: string): UnifiedRouteBuilder;
246
- delete(path: string, handler: (req: HttpRequest, res: HttpResponse) => any, options?: any): this;
247
- delete(
248
- path: string,
249
- handler?: (req: HttpRequest, res: HttpResponse) => any,
250
- options?: any
251
- ): UnifiedRouteBuilder | this {
252
- if (handler) {
253
- // Direct route registration
254
- return this.addRoute('DELETE', path, handler, options);
255
- }
256
- // Use unified router for chainable API
257
- return this.unifiedRouter.delete(path);
258
- }
259
-
260
- patch(path: string): UnifiedRouteBuilder;
261
- patch(path: string, handler: (req: HttpRequest, res: HttpResponse) => any, options?: any): this;
262
- patch(
263
- path: string,
264
- handler?: (req: HttpRequest, res: HttpResponse) => any,
265
- options?: any
266
- ): UnifiedRouteBuilder | this {
267
- if (handler) {
268
- // Direct route registration
269
- return this.addRoute('PATCH', path, handler, options);
270
- }
271
- // Use unified router for chainable API
272
- return this.unifiedRouter.patch(path);
273
- }
274
-
275
- // Schema-first route method
276
- route(schema: RouteSchema): void {
277
- // Use unified router for schema-first registration
278
- this.unifiedRouter.route(schema);
279
- }
280
-
281
- // Enable automatic API documentation
282
- enableDocs(config: DocsConfig): void {
283
- this.documentation.enableDocs(config, this.intelligentRouting);
284
-
285
- this.logger.info(`API Documentation enabled at ${config.basePath || '/docs'}`, 'Documentation');
286
- this.eventBus.emit('docs:enabled', { config });
287
- }
288
-
289
- // Get OpenAPI specification
290
- getOpenAPISpec() {
291
- return this.documentation.getOpenAPISpec();
292
- }
293
-
294
- // Get documentation as JSON
295
- getDocsJSON(): string {
296
- return this.documentation.getDocsJSON();
297
- }
298
-
299
- // Get documentation as YAML
300
- getDocsYAML(): string {
301
- return this.documentation.getDocsYAML();
302
- }
303
-
304
- // Refresh documentation (useful after adding routes dynamically)
305
- refreshDocs(): void {
306
- this.documentation.refreshDocs();
307
- }
308
-
309
- // Universal middleware system - seamlessly handles standard and advanced middleware
310
- async use(middlewareOrFunction: any, config?: any) {
311
- // Standard middleware integration (req, res, next pattern)
312
- if (typeof middlewareOrFunction === 'function' && middlewareOrFunction.length >= 3) {
313
- this.coreFramework.addMiddleware(middlewareOrFunction);
314
- this.eventBus.emit('middleware:registered', {
315
- type: 'standard',
316
- middleware: middlewareOrFunction,
317
- });
318
- return this;
319
- }
320
-
321
- // Function-style middleware execution
322
- if (typeof middlewareOrFunction === 'function' && middlewareOrFunction.length <= 1) {
323
- await middlewareOrFunction(this);
324
- this.eventBus.emit('middleware:executed', {
325
- type: 'function',
326
- middleware: middlewareOrFunction,
327
- });
328
- return this;
329
- }
330
-
331
- // Advanced middleware pipeline integration - check if it's a MiddlewareInterface
332
- if (
333
- middlewareOrFunction &&
334
- typeof middlewareOrFunction === 'object' &&
335
- middlewareOrFunction.install &&
336
- middlewareOrFunction.metadata
337
- ) {
338
- // This is a MiddlewareInterface object - install it with the MiddlewareManager
339
- this.logger.debug(
340
- `Installing MiddlewareInterface: ${middlewareOrFunction.metadata.name}`,
341
- 'Middleware'
342
- );
343
- this.middlewareManager.install(middlewareOrFunction, config);
344
- return this;
345
- }
346
-
347
- // Fallback: emit event for unknown middleware types
348
- this.eventBus.emit('middleware:advanced', {
349
- middleware: middlewareOrFunction,
350
- config,
351
- });
352
- this.logger.debug(
353
- 'Advanced middleware integration - enhanced capabilities loading...',
354
- 'Middleware'
355
- );
356
- return this;
357
- }
358
-
359
- // Plugin compatibility layer - unified middleware interface
360
- async plugin(middleware: any, options?: any): Promise<this> {
361
- return this.use(middleware, options);
362
- }
363
-
364
- // Module loading with events
365
- async loadModule(moduleOrPath: ModuleConfig | string) {
366
- this.eventBus.emit('module:loading', {
367
- moduleId: typeof moduleOrPath === 'string' ? moduleOrPath : moduleOrPath.name,
368
- });
369
-
370
- if (typeof moduleOrPath === 'string') {
371
- const module = await this.importModule(moduleOrPath);
372
- await this.coreFramework.loadModule(module);
373
- this.loadedModules.add(moduleOrPath);
374
- this.eventBus.emit('module:loaded', {
375
- moduleId: module.name,
376
- version: module.version || '1.0.0',
377
- });
378
- } else {
379
- await this.coreFramework.loadModule(moduleOrPath);
380
- this.loadedModules.add(moduleOrPath.name);
381
- this.eventBus.emit('module:loaded', {
382
- moduleId: moduleOrPath.name,
383
- version: moduleOrPath.version || '1.0.0',
384
- });
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
-
393
- return this;
394
- }
395
-
396
- // Database helper with events
397
- database(adapter: any) {
398
- this.eventBus.emit('database:connected', {
399
- adapter: adapter.constructor.name,
400
- config: 'hidden',
401
- });
402
- this.coreFramework.registerDatabase(adapter);
403
- return this;
404
- }
405
-
406
- // WebSocket helper with events
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
413
- const adapter = this.coreFramework.getWebSocketAdapter();
414
- if (adapter && !registration.processed) {
415
- // Adapter is ready, process immediately
416
- this.processWebSocketRegistration(namespace, handlers, adapter);
417
- registration.processed = true;
418
- }
419
- // Otherwise, it will be processed when the server starts
420
-
421
- return this;
422
- }
423
-
424
- private processWebSocketRegistration(
425
- namespace: string,
426
- handlers: Record<string, Function>,
427
- adapter: any
428
- ) {
429
- this.emit('websocket:registering', { namespace, handlers });
430
-
431
- const ns = adapter.createNamespace(namespace);
432
-
433
- Object.entries(handlers).forEach(([event, handler]) => {
434
- ns.on('connection', (socket: any) => {
435
- this.emit('websocket:connection', { namespace, event, socket });
436
-
437
- socket.on(event, (data: any, callback: any) => {
438
- this.emit('websocket:event', { namespace, event, data });
439
-
440
- Promise.resolve(handler(socket, data))
441
- .then((result: any) => {
442
- this.emit('websocket:response', { namespace, event, result });
443
- if (callback) callback(result);
444
- else if (result) socket.emit(`${event}:response`, result);
445
- })
446
- .catch((error: any) => {
447
- this.emit('websocket:error', { namespace, event, error });
448
- const errorResponse = { success: false, error: error.message };
449
- if (callback) callback(errorResponse);
450
- else socket.emit('error', errorResponse);
451
- });
452
- });
453
- });
454
- });
455
-
456
- this.emit('websocket:registered', { namespace, handlers });
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
- }
491
- }
492
-
493
- // Start server with events (Node.js only)
494
- listen(callback?: () => void): void;
495
- listen(port: number, callback?: () => void): void;
496
- listen(port: number, host: string, callback?: () => void): void;
497
- listen(
498
- portOrCallback?: number | (() => void),
499
- hostOrCallback?: string | (() => void),
500
- callback?: () => void
501
- ) {
502
- // Only available for Node.js runtime
503
- if (this.runtimeType !== 'node') {
504
- throw new Error(
505
- `listen() is only available for Node.js runtime. Current runtime: ${this.runtimeType}. Use getHandler() for other runtimes.`
506
- );
507
- }
508
-
509
- // Handle overloaded parameters - supports:
510
- // listen(callback)
511
- // listen(port, callback)
512
- // listen(port, host, callback)
513
- let port: number;
514
- let host: string | undefined;
515
-
516
- if (typeof portOrCallback === 'function') {
517
- // listen(callback) - use port from config
518
- callback = portOrCallback;
519
- port = this.config.server.port;
520
- host = this.config.server.host;
521
- } else if (typeof portOrCallback === 'number') {
522
- // listen(port, ...) variants
523
- port = portOrCallback;
524
- if (typeof hostOrCallback === 'function') {
525
- // listen(port, callback)
526
- callback = hostOrCallback;
527
- host = undefined;
528
- } else {
529
- // listen(port, host, callback)
530
- host = hostOrCallback;
531
- }
532
- } else {
533
- // listen() - use config defaults
534
- port = this.config.server.port;
535
- host = this.config.server.host;
536
- }
537
-
538
- // Validate that we have a valid port
539
- if (!port || typeof port !== 'number') {
540
- throw new Error(
541
- 'Port not specified and not found in configuration. Please provide a port number or configure it in moro.config.js/ts'
542
- );
543
- }
544
-
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
-
549
- if (this.config.performance?.clustering?.enabled) {
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
- }
561
- }
562
- this.eventBus.emit('server:starting', { port, runtime: this.runtimeType });
563
-
564
- // Add documentation middleware first (if enabled)
565
- try {
566
- const docsMiddleware = this.documentation.getDocsMiddleware();
567
- this.coreFramework.addMiddleware(docsMiddleware);
568
- this.logger.debug('Documentation middleware added', 'Documentation');
569
- } catch (error) {
570
- // Documentation not enabled, that's fine
571
- this.logger.debug('Documentation not enabled', 'Documentation');
572
- }
573
-
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();
594
- }
595
- }
596
- });
597
-
598
- // Register legacy direct routes with the HTTP server (for backward compatibility)
599
- if (this.routes.length > 0) {
600
- this.registerDirectRoutes();
601
- }
602
-
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);
630
- }
631
- };
632
-
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
- },
705
- };
706
-
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
743
- }
744
- }
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
-
758
- // Get handler for non-Node.js runtimes
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
-
768
- // Create a unified request handler that works with the runtime adapter
769
- const handler = async (req: HttpRequest, res: HttpResponse) => {
770
- // Add documentation middleware first (if enabled)
771
- try {
772
- const docsMiddleware = this.documentation.getDocsMiddleware();
773
- await docsMiddleware(req, res, () => {});
774
- if (res.headersSent) return;
775
- } catch (error) {
776
- // Documentation not enabled, that's fine
777
- }
778
-
779
- // Try unified router first (handles all routes)
780
- const handled = await this.unifiedRouter.handleRequest(req, res);
781
- if (handled) return;
782
-
783
- // Handle legacy direct routes (backward compatibility)
784
- if (this.routes.length > 0) {
785
- await this.handleDirectRoutes(req, res);
786
- }
787
- };
788
-
789
- // Use the runtime adapter to create the appropriate handler
790
- return this.runtimeAdapter.createServer(handler);
791
- }
792
-
793
- // Handle direct routes for runtime adapters
794
- private async handleDirectRoutes(req: HttpRequest, res: HttpResponse) {
795
- // Find matching route
796
- const route = this.findMatchingRoute(req.method!, req.path);
797
- if (!route) {
798
- (res as any).status(404).json({ success: false, error: 'Not found' });
799
- return;
800
- }
801
-
802
- try {
803
- // Extract path parameters
804
- const matches = req.path.match(route.pattern);
805
- if (matches) {
806
- req.params = {};
807
- route.paramNames.forEach((name: string, index: number) => {
808
- req.params[name] = matches[index + 1];
809
- });
810
- }
811
-
812
- // Get handler function
813
- const handler = this.routeHandlers[route.handler];
814
- if (!handler) {
815
- (res as any).status(500).json({ success: false, error: 'Handler not found' });
816
- return;
817
- }
818
-
819
- // Execute validation if present
820
- if (route.validation) {
821
- try {
822
- const validated = route.validation.parse(req.body);
823
- req.body = validated;
824
- } catch (error: any) {
825
- if (error.issues) {
826
- (res as any).status(400).json({
827
- success: false,
828
- error: 'Validation failed',
829
- details: error.issues.map((issue: any) => ({
830
- field: issue.path.length > 0 ? issue.path.join('.') : 'body',
831
- message: issue.message,
832
- code: issue.code,
833
- })),
834
- });
835
- return;
836
- }
837
- throw error;
838
- }
839
- }
840
-
841
- // Execute rate limiting if present
842
- if (route.rateLimit) {
843
- const clientId = req.ip || 'unknown';
844
- const key = `${route.method}:${route.path}:${clientId}`;
845
-
846
- if (!this.checkRateLimit(key, route.rateLimit)) {
847
- (res as any).status(429).json({
848
- success: false,
849
- error: 'Rate limit exceeded',
850
- retryAfter: Math.ceil(route.rateLimit.window / 1000),
851
- });
852
- return;
853
- }
854
- }
855
-
856
- // Execute the handler
857
- const result = await handler(req, res);
858
- if (result && !(res as any).headersSent) {
859
- (res as any).json(result);
860
- }
861
- } catch (error) {
862
- if (!(res as any).headersSent) {
863
- (res as any).status(500).json({
864
- success: false,
865
- error: error instanceof Error ? error.message : 'Internal server error',
866
- });
867
- }
868
- }
869
- }
870
-
871
- // Advanced route matching with caching and optimization
872
- private routeCache = new Map<string, { pattern: RegExp; paramNames: string[] }>();
873
- private staticRouteMap = new Map<string, any>();
874
- private dynamicRoutesBySegments = new Map<number, any[]>();
875
-
876
- private findMatchingRoute(method: string, path: string) {
877
- // Phase 1: O(1) static route lookup
878
- const staticKey = `${method}:${path}`;
879
- const staticRoute = this.staticRouteMap.get(staticKey);
880
- if (staticRoute) {
881
- return {
882
- ...staticRoute,
883
- pattern: /^.*$/, // Dummy pattern for static routes
884
- paramNames: [],
885
- };
886
- }
887
-
888
- // Phase 2: Optimized dynamic route matching by segment count
889
- const segments = path.split('/').filter(s => s.length > 0);
890
- const segmentCount = segments.length;
891
- const candidateRoutes = this.dynamicRoutesBySegments.get(segmentCount) || [];
892
-
893
- for (const route of candidateRoutes) {
894
- if (route.method === method) {
895
- const cacheKey = `${method}:${route.path}`;
896
- let pattern = this.routeCache.get(cacheKey);
897
-
898
- if (!pattern) {
899
- pattern = this.pathToRegex(route.path);
900
- this.routeCache.set(cacheKey, pattern);
901
- }
902
-
903
- if (pattern.pattern.test(path)) {
904
- return {
905
- ...route,
906
- pattern: pattern.pattern,
907
- paramNames: pattern.paramNames,
908
- };
909
- }
910
- }
911
- }
912
-
913
- return null;
914
- }
915
-
916
- // Convert path to regex (simplified version)
917
- private pathToRegex(path: string): { pattern: RegExp; paramNames: string[] } {
918
- const paramNames: string[] = [];
919
- const regexPath = path.replace(/\//g, '\\/').replace(/:([^/]+)/g, (match, paramName) => {
920
- paramNames.push(paramName);
921
- return '([^/]+)';
922
- });
923
-
924
- return {
925
- pattern: new RegExp(`^${regexPath}$`),
926
- paramNames,
927
- };
928
- }
929
-
930
- // Access enterprise event system for advanced integrations
931
- get events() {
932
- return this.eventBus;
933
- }
934
-
935
- // Access to core framework for advanced usage
936
- get core() {
937
- return this.coreFramework;
938
- }
939
-
940
- // Force cleanup of pooled objects
941
- forceCleanup(): void {
942
- const httpServer = (this.coreFramework as any).httpServer;
943
- if (httpServer && httpServer.forceCleanup) {
944
- httpServer.forceCleanup();
945
- }
946
- }
947
-
948
- // Private methods
949
- private addRoute(method: string, path: string, handler: Function, options: any = {}) {
950
- // Register with unified router (primary routing system)
951
- this.unifiedRouter.addRoute(method as any, path, handler as any, options.middleware || []);
952
-
953
- // Also store in legacy routes array for backward compatibility
954
- const handlerName = `handler_${this.routes.length}`;
955
- const route = {
956
- method: method as any,
957
- path,
958
- handler: handlerName,
959
- validation: options.validation,
960
- rateLimit: options.rateLimit,
961
- cache: options.cache,
962
- middleware: options.middleware,
963
- };
964
-
965
- this.routes.push(route);
966
-
967
- // Organize routes for optimal lookup (legacy)
968
- this.organizeRouteForLookup(route);
969
-
970
- // Store handler for later module creation (legacy)
971
- this.routeHandlers[handlerName] = handler;
972
-
973
- return this;
974
- }
975
-
976
- private organizeRouteForLookup(route: any): void {
977
- if (!route.path.includes(':')) {
978
- // Static route - add to static map for O(1) lookup
979
- const staticKey = `${route.method}:${route.path}`;
980
- this.staticRouteMap.set(staticKey, route);
981
- } else {
982
- // Dynamic route - organize by segment count
983
- const segments = route.path.split('/').filter((s: string) => s.length > 0);
984
- const segmentCount = segments.length;
985
-
986
- if (!this.dynamicRoutesBySegments.has(segmentCount)) {
987
- this.dynamicRoutesBySegments.set(segmentCount, []);
988
- }
989
- this.dynamicRoutesBySegments.get(segmentCount)!.push(route);
990
- }
991
- }
992
-
993
- private registerDirectRoutes() {
994
- // Register routes directly with the HTTP server for optimal performance
995
- // This provides the intuitive developer experience users expect
996
- for (const route of this.routes) {
997
- const handler = this.routeHandlers[route.handler];
998
-
999
- // Get direct access to the HTTP server through the core framework
1000
- const httpServer = (this.coreFramework as any).httpServer;
1001
-
1002
- // Create a wrapper handler that handles validation, rate limiting, and return values
1003
- const wrappedHandler = async (req: any, res: any) => {
1004
- try {
1005
- // Enhance request with events property for direct routes
1006
- req.events = this.eventBus;
1007
-
1008
- // Universal validation middleware (works with any ValidationSchema)
1009
- if (route.validation) {
1010
- try {
1011
- const validated = await route.validation.parseAsync(req.body);
1012
- req.body = validated;
1013
- } catch (error: any) {
1014
- // Handle universal validation errors
1015
- const normalizedError = normalizeValidationError(error);
1016
- res.status(400).json({
1017
- success: false,
1018
- error: 'Validation failed',
1019
- details: normalizedError.issues.map((issue: any) => ({
1020
- field: issue.path.length > 0 ? issue.path.join('.') : 'body',
1021
- message: issue.message,
1022
- code: issue.code,
1023
- })),
1024
- requestId: req.requestId,
1025
- });
1026
- return;
1027
- }
1028
- }
1029
-
1030
- // Rate limiting middleware
1031
- if (route.rateLimit) {
1032
- const clientId = req.ip || req.connection.remoteAddress || 'unknown';
1033
- const key = `${route.method}:${route.path}:${clientId}`;
1034
-
1035
- if (!this.checkRateLimit(key, route.rateLimit)) {
1036
- res.status(429).json({
1037
- success: false,
1038
- error: 'Rate limit exceeded',
1039
- retryAfter: Math.ceil(route.rateLimit.window / 1000),
1040
- });
1041
- return;
1042
- }
1043
- }
1044
-
1045
- // Execute the actual handler
1046
- const result = await handler(req, res);
1047
- if (result && !res.headersSent) {
1048
- res.json(result);
1049
- }
1050
- } catch (error) {
1051
- if (!res.headersSent) {
1052
- res.status(500).json({
1053
- success: false,
1054
- error: error instanceof Error ? error.message : 'Internal server error',
1055
- });
1056
- }
1057
- }
1058
- };
1059
-
1060
- // Register with the appropriate HTTP method
1061
- const method = route.method.toLowerCase();
1062
- if (httpServer && httpServer[method]) {
1063
- httpServer[method](route.path, wrappedHandler);
1064
- }
1065
- }
1066
- }
1067
-
1068
- // Simple rate limiting for direct routes
1069
- private rateLimitStore = new Map<string, { count: number; resetTime: number }>();
1070
-
1071
- private checkRateLimit(key: string, config: { requests: number; window: number }): boolean {
1072
- const now = Date.now();
1073
- const bucket = this.rateLimitStore.get(key);
1074
-
1075
- if (!bucket || now > bucket.resetTime) {
1076
- // Create new bucket or reset expired bucket
1077
- this.rateLimitStore.set(key, {
1078
- count: 1,
1079
- resetTime: now + config.window,
1080
- });
1081
- return true;
1082
- }
1083
-
1084
- if (bucket.count >= config.requests) {
1085
- return false; // Rate limit exceeded
1086
- }
1087
-
1088
- bucket.count++;
1089
- return true;
1090
- }
1091
-
1092
- private setupDefaultMiddleware(options: MoroOptions) {
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
- : {};
1101
- this.use(middleware.cors(corsOptions));
1102
- }
1103
-
1104
- // Helmet - check config enabled property OR options.security.helmet.enabled === true
1105
- if (this.config.security.helmet.enabled || options.security?.helmet?.enabled === true) {
1106
- this.use(middleware.helmet());
1107
- }
1108
-
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
- : {};
1120
- this.use(middleware.compression(compressionOptions));
1121
- }
1122
-
1123
- // Body size limiting
1124
- this.use(middleware.bodySize({ limit: '10mb' }));
1125
- }
1126
-
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
-
1140
- try {
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);
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);
1216
- }
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
- }
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
- });
1337
- }
1338
-
1339
- private async importModule(modulePath: string): Promise<ModuleConfig> {
1340
- const module = await import(modulePath);
1341
- return module.default || module;
1342
- }
1343
-
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
- */
1361
- private clusterWorkers = new Map<number, any>();
1362
-
1363
- private startWithClustering(port: number, host?: string, callback?: () => void): void {
1364
- // Worker count calculation - respect user choice
1365
- let workerCount = this.config.performance?.clustering?.workers || os.cpus().length;
1366
-
1367
- // Only auto-optimize if user hasn't specified a number or set it to 'auto'
1368
- if (workerCount === 'auto') {
1369
- const cpuCount = os.cpus().length;
1370
- const totalMemoryGB = os.totalmem() / (1024 * 1024 * 1024);
1371
-
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));
1379
- }
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
-
1390
- this.logger.info(
1391
- `Auto-calculated worker count: ${workerCount} (CPU: ${cpuCount}, RAM: ${totalMemoryGB.toFixed(1)}GB, ${memoryPerWorkerGB}GB per worker)`,
1392
- 'Cluster'
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');
1397
- }
1398
-
1399
- if (cluster.isPrimary) {
1400
- this.logger.info(`Starting ${workerCount} workers`, 'Cluster');
1401
-
1402
- // Optimize cluster scheduling for high concurrency
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;
1406
-
1407
- // Set cluster settings for better performance
1408
- cluster.setupMaster({
1409
- exec: process.argv[1] || process.execPath,
1410
- args: process.argv.slice(2),
1411
- silent: false,
1412
- });
1413
-
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)
1417
- process.env.NODE_CLUSTER_SCHED_POLICY = 'rr'; // Ensure round-robin
1418
- process.env.NODE_DISABLE_COLORS = '1'; // Reduce IPC message size by disabling color codes
1419
-
1420
- // Graceful shutdown handler
1421
- const gracefulShutdown = () => {
1422
- this.logger.info('Gracefully shutting down cluster...', 'Cluster');
1423
-
1424
- // Clean up all workers
1425
- for (const [pid, worker] of this.clusterWorkers) {
1426
- worker.removeAllListeners();
1427
- worker.kill('SIGTERM');
1428
- }
1429
-
1430
- // Clean up cluster listeners
1431
- cluster.removeAllListeners();
1432
- process.exit(0);
1433
- };
1434
-
1435
- // Handle process signals for graceful shutdown
1436
- process.on('SIGINT', gracefulShutdown);
1437
- process.on('SIGTERM', gracefulShutdown);
1438
-
1439
- // Fork workers with basic tracking
1440
- for (let i = 0; i < workerCount; i++) {
1441
- const worker = cluster.fork();
1442
- this.clusterWorkers.set(worker.process.pid!, worker);
1443
- this.logger.info(`Worker ${worker.process.pid} started`, 'Cluster');
1444
-
1445
- // Handle individual worker messages
1446
- worker.on('message', this.handleWorkerMessage.bind(this));
1447
- }
1448
-
1449
- // Simple worker exit handling
1450
- cluster.on('exit', (worker: any, code: number, signal: string) => {
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
- }
1465
- });
1466
-
1467
- // Master process callback
1468
- if (callback) callback();
1469
- } else {
1470
- // Worker process - start the actual server with proper cleanup
1471
- this.logger.info(`Worker ${process.pid} initializing`, 'Worker');
1472
-
1473
- // Worker-specific optimizations for high concurrency
1474
- process.env.UV_THREADPOOL_SIZE = '64';
1475
-
1476
- // Reduce logging contention in workers (major bottleneck)
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
1482
- }
1483
-
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}`;
1495
-
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
1502
- if (process.env.NODE_ENV === 'production') {
1503
- // Aggressive V8 optimizations for maximum performance
1504
- const v8Flags = [
1505
- '--optimize-for-size', // Trade memory for speed
1506
- '--always-opt', // Always optimize functions
1507
- '--turbo-fast-api-calls', // Optimize API calls
1508
- '--turbo-escape-analysis', // Escape analysis optimization
1509
- '--turbo-inline-api-calls', // Inline API calls
1510
- '--max-old-space-size=1024', // Limit memory to prevent GC pressure
1511
- ];
1512
- process.env.NODE_OPTIONS = (process.env.NODE_OPTIONS || '') + ' ' + v8Flags.join(' ');
1513
- }
1514
-
1515
- // Optimize garbage collection for workers
1516
- // eslint-disable-next-line no-undef
1517
- if ((global as any).gc) {
1518
- setInterval(() => {
1519
- // eslint-disable-next-line no-undef
1520
- if ((global as any).gc) (global as any).gc();
1521
- }, 60000); // GC every 60 seconds (less frequent)
1522
- }
1523
-
1524
- // Graceful shutdown for worker
1525
- const workerShutdown = () => {
1526
- this.logger.info(`Worker ${process.pid} shutting down gracefully...`, 'Worker');
1527
-
1528
- // Clean up event listeners
1529
- this.eventBus.removeAllListeners();
1530
- this.removeAllListeners();
1531
-
1532
- // Close server gracefully
1533
- if (this.coreFramework) {
1534
- const server = (this.coreFramework as any).server;
1535
- if (server) {
1536
- server.close(() => {
1537
- process.exit(0);
1538
- });
1539
- }
1540
- }
1541
- };
1542
-
1543
- // Handle worker shutdown signals
1544
- process.on('SIGTERM', workerShutdown);
1545
- process.on('SIGINT', workerShutdown);
1546
-
1547
- // Continue with normal server startup for this worker
1548
- this.eventBus.emit('server:starting', {
1549
- port,
1550
- runtime: this.runtimeType,
1551
- worker: process.pid,
1552
- });
1553
-
1554
- // Add documentation middleware first (if enabled)
1555
- try {
1556
- const docsMiddleware = this.documentation.getDocsMiddleware();
1557
- this.coreFramework.addMiddleware(docsMiddleware);
1558
- } catch (error) {
1559
- // Documentation not enabled, that's fine
1560
- }
1561
-
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)) {
1581
- next();
1582
- }
1583
- }
1584
- });
1585
-
1586
- // Register legacy direct routes with the HTTP server (for backward compatibility)
1587
- if (this.routes.length > 0) {
1588
- this.registerDirectRoutes();
1589
- }
1590
-
1591
- const workerCallback = () => {
1592
- const displayHost = host || 'localhost';
1593
- this.logger.info(`Worker ${process.pid} ready on ${displayHost}:${port}`, 'Worker');
1594
- this.eventBus.emit('server:started', {
1595
- port,
1596
- runtime: this.runtimeType,
1597
- worker: process.pid,
1598
- });
1599
- };
1600
-
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
- });
1628
- }
1629
- }
1630
-
1631
- // Simple worker message handler
1632
- private handleWorkerMessage(message: any): void {
1633
- // Handle inter-worker communication if needed
1634
- if (message.type === 'health-check') {
1635
- // Worker health check response
1636
- return;
1637
- }
1638
-
1639
- // Log other worker messages
1640
- this.logger.debug(`Worker message: ${JSON.stringify(message)}`, 'Cluster');
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
- }
1694
- }
1695
-
1696
- // Export convenience function
1697
- export function createApp(options?: MoroOptions): Moro {
1698
- return new Moro(options);
1699
- }
1700
-
1701
- // Runtime-specific convenience functions
1702
- export function createAppNode(options?: Omit<MoroOptions, 'runtime'>): Moro {
1703
- return new Moro({
1704
- ...options,
1705
- runtime: { type: 'node' },
1706
- });
1707
- }
1708
-
1709
- export function createAppEdge(options?: Omit<MoroOptions, 'runtime'>): Moro {
1710
- return new Moro({
1711
- ...options,
1712
- runtime: { type: 'vercel-edge' },
1713
- });
1714
- }
1715
-
1716
- export function createAppLambda(options?: Omit<MoroOptions, 'runtime'>): Moro {
1717
- return new Moro({
1718
- ...options,
1719
- runtime: { type: 'aws-lambda' },
1720
- });
1721
- }
1722
-
1723
- export function createAppWorker(options?: Omit<MoroOptions, 'runtime'>): Moro {
1724
- return new Moro({
1725
- ...options,
1726
- runtime: { type: 'cloudflare-workers' },
1727
- });
1728
- }