@morojs/moro 1.6.1 → 1.6.3

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 (575) 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 +202 -185
  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/auth/core.d.ts +78 -0
  97. package/dist/core/middleware/built-in/auth/core.js +358 -0
  98. package/dist/core/middleware/built-in/auth/core.js.map +1 -0
  99. package/dist/core/middleware/built-in/{auth-helpers.js → auth/helpers.js} +12 -23
  100. package/dist/core/middleware/built-in/auth/helpers.js.map +1 -0
  101. package/dist/core/middleware/built-in/auth/hook.d.ts +30 -0
  102. package/dist/core/middleware/built-in/auth/hook.js +99 -0
  103. package/dist/core/middleware/built-in/auth/hook.js.map +1 -0
  104. package/dist/core/middleware/built-in/auth/index.d.ts +7 -0
  105. package/dist/core/middleware/built-in/auth/index.js +15 -0
  106. package/dist/core/middleware/built-in/auth/index.js.map +1 -0
  107. package/dist/core/middleware/built-in/auth/jwt-helpers.d.ts +118 -0
  108. package/dist/core/middleware/built-in/auth/jwt-helpers.js +218 -0
  109. package/dist/core/middleware/built-in/auth/jwt-helpers.js.map +1 -0
  110. package/dist/core/middleware/built-in/auth/middleware.d.ts +23 -0
  111. package/dist/core/middleware/built-in/auth/middleware.js +71 -0
  112. package/dist/core/middleware/built-in/auth/middleware.js.map +1 -0
  113. package/dist/core/middleware/built-in/{auth-providers.d.ts → auth/providers.d.ts} +1 -1
  114. package/dist/core/middleware/built-in/{auth-providers.js → auth/providers.js} +5 -10
  115. package/dist/core/middleware/built-in/auth/providers.js.map +1 -0
  116. package/dist/core/middleware/built-in/{adapters → cache/adapters}/cache/file.d.ts +1 -1
  117. package/dist/core/middleware/built-in/{adapters → cache/adapters}/cache/file.js +10 -47
  118. package/dist/core/middleware/built-in/cache/adapters/cache/file.js.map +1 -0
  119. package/dist/core/middleware/built-in/cache/adapters/cache/index.d.ts +5 -0
  120. package/dist/core/middleware/built-in/cache/adapters/cache/index.js +21 -0
  121. package/dist/core/middleware/built-in/cache/adapters/cache/index.js.map +1 -0
  122. package/dist/core/middleware/built-in/{adapters → cache/adapters}/cache/memory.d.ts +1 -1
  123. package/dist/core/middleware/built-in/{adapters → cache/adapters}/cache/memory.js +3 -7
  124. package/dist/core/middleware/built-in/cache/adapters/cache/memory.js.map +1 -0
  125. package/dist/core/middleware/built-in/{adapters → cache/adapters}/cache/redis.d.ts +3 -1
  126. package/dist/core/middleware/built-in/{adapters → cache/adapters}/cache/redis.js +11 -9
  127. package/dist/core/middleware/built-in/cache/adapters/cache/redis.js.map +1 -0
  128. package/dist/core/middleware/built-in/cache/adapters/index.d.ts +2 -0
  129. package/dist/core/middleware/built-in/cache/adapters/index.js +5 -0
  130. package/dist/core/middleware/built-in/cache/adapters/index.js.map +1 -0
  131. package/dist/core/middleware/built-in/cache/core.d.ts +37 -0
  132. package/dist/core/middleware/built-in/cache/core.js +87 -0
  133. package/dist/core/middleware/built-in/cache/core.js.map +1 -0
  134. package/dist/core/middleware/built-in/cache/hook.d.ts +20 -0
  135. package/dist/core/middleware/built-in/{cache.js → cache/hook.js} +30 -14
  136. package/dist/core/middleware/built-in/cache/hook.js.map +1 -0
  137. package/dist/core/middleware/built-in/cache/index.d.ts +3 -0
  138. package/dist/core/middleware/built-in/cache/index.js +9 -0
  139. package/dist/core/middleware/built-in/cache/index.js.map +1 -0
  140. package/dist/core/middleware/built-in/cache/middleware.d.ts +17 -0
  141. package/dist/core/middleware/built-in/cache/middleware.js +44 -0
  142. package/dist/core/middleware/built-in/cache/middleware.js.map +1 -0
  143. package/dist/core/middleware/built-in/{adapters → cdn/adapters}/cdn/azure.d.ts +1 -1
  144. package/dist/core/middleware/built-in/{adapters → cdn/adapters}/cdn/azure.js +3 -7
  145. package/dist/core/middleware/built-in/cdn/adapters/cdn/azure.js.map +1 -0
  146. package/dist/core/middleware/built-in/{adapters → cdn/adapters}/cdn/cloudflare.d.ts +1 -1
  147. package/dist/core/middleware/built-in/{adapters → cdn/adapters}/cdn/cloudflare.js +3 -7
  148. package/dist/core/middleware/built-in/cdn/adapters/cdn/cloudflare.js.map +1 -0
  149. package/dist/core/middleware/built-in/{adapters → cdn/adapters}/cdn/cloudfront.d.ts +3 -1
  150. package/dist/core/middleware/built-in/{adapters → cdn/adapters}/cdn/cloudfront.js +12 -10
  151. package/dist/core/middleware/built-in/cdn/adapters/cdn/cloudfront.js.map +1 -0
  152. package/dist/core/middleware/built-in/cdn/adapters/cdn/index.d.ts +5 -0
  153. package/dist/core/middleware/built-in/cdn/adapters/cdn/index.js +21 -0
  154. package/dist/core/middleware/built-in/cdn/adapters/cdn/index.js.map +1 -0
  155. package/dist/core/middleware/built-in/cdn/adapters/index.d.ts +2 -0
  156. package/dist/core/middleware/built-in/cdn/adapters/index.js +5 -0
  157. package/dist/core/middleware/built-in/cdn/adapters/index.js.map +1 -0
  158. package/dist/core/middleware/built-in/cdn/core.d.ts +43 -0
  159. package/dist/core/middleware/built-in/cdn/core.js +144 -0
  160. package/dist/core/middleware/built-in/cdn/core.js.map +1 -0
  161. package/dist/core/middleware/built-in/cdn/hook.d.ts +22 -0
  162. package/dist/core/middleware/built-in/cdn/hook.js +70 -0
  163. package/dist/core/middleware/built-in/cdn/hook.js.map +1 -0
  164. package/dist/core/middleware/built-in/cdn/index.d.ts +5 -0
  165. package/dist/core/middleware/built-in/cdn/index.js +11 -0
  166. package/dist/core/middleware/built-in/cdn/index.js.map +1 -0
  167. package/dist/core/middleware/built-in/cdn/middleware.d.ts +21 -0
  168. package/dist/core/middleware/built-in/cdn/middleware.js +52 -0
  169. package/dist/core/middleware/built-in/cdn/middleware.js.map +1 -0
  170. package/dist/core/middleware/built-in/cookie/core.d.ts +37 -0
  171. package/dist/core/middleware/built-in/cookie/core.js +83 -0
  172. package/dist/core/middleware/built-in/cookie/core.js.map +1 -0
  173. package/dist/core/middleware/built-in/cookie/hook.d.ts +20 -0
  174. package/dist/core/middleware/built-in/cookie/hook.js +47 -0
  175. package/dist/core/middleware/built-in/cookie/hook.js.map +1 -0
  176. package/dist/core/middleware/built-in/cookie/index.d.ts +3 -0
  177. package/dist/core/middleware/built-in/cookie/index.js +9 -0
  178. package/dist/core/middleware/built-in/cookie/index.js.map +1 -0
  179. package/dist/core/middleware/built-in/cookie/middleware.d.ts +17 -0
  180. package/dist/core/middleware/built-in/cookie/middleware.js +36 -0
  181. package/dist/core/middleware/built-in/cookie/middleware.js.map +1 -0
  182. package/dist/core/middleware/built-in/cors/core.d.ts +23 -0
  183. package/dist/core/middleware/built-in/cors/core.js +51 -0
  184. package/dist/core/middleware/built-in/cors/core.js.map +1 -0
  185. package/dist/core/middleware/built-in/cors/hook.d.ts +17 -0
  186. package/dist/core/middleware/built-in/cors/hook.js +37 -0
  187. package/dist/core/middleware/built-in/cors/hook.js.map +1 -0
  188. package/dist/core/middleware/built-in/cors/index.d.ts +3 -0
  189. package/dist/core/middleware/built-in/cors/index.js +9 -0
  190. package/dist/core/middleware/built-in/cors/index.js.map +1 -0
  191. package/dist/core/middleware/built-in/cors/middleware.d.ts +16 -0
  192. package/dist/core/middleware/built-in/cors/middleware.js +22 -0
  193. package/dist/core/middleware/built-in/cors/middleware.js.map +1 -0
  194. package/dist/core/middleware/built-in/csp/core.d.ts +45 -0
  195. package/dist/core/middleware/built-in/csp/core.js +88 -0
  196. package/dist/core/middleware/built-in/csp/core.js.map +1 -0
  197. package/dist/core/middleware/built-in/csp/hook.d.ts +22 -0
  198. package/dist/core/middleware/built-in/csp/hook.js +47 -0
  199. package/dist/core/middleware/built-in/csp/hook.js.map +1 -0
  200. package/dist/core/middleware/built-in/csp/index.d.ts +3 -0
  201. package/dist/core/middleware/built-in/csp/index.js +9 -0
  202. package/dist/core/middleware/built-in/csp/index.js.map +1 -0
  203. package/dist/core/middleware/built-in/csp/middleware.d.ts +19 -0
  204. package/dist/core/middleware/built-in/csp/middleware.js +29 -0
  205. package/dist/core/middleware/built-in/csp/middleware.js.map +1 -0
  206. package/dist/core/middleware/built-in/csrf/core.d.ts +28 -0
  207. package/dist/core/middleware/built-in/csrf/core.js +69 -0
  208. package/dist/core/middleware/built-in/csrf/core.js.map +1 -0
  209. package/dist/core/middleware/built-in/csrf/hook.d.ts +17 -0
  210. package/dist/core/middleware/built-in/csrf/hook.js +45 -0
  211. package/dist/core/middleware/built-in/csrf/hook.js.map +1 -0
  212. package/dist/core/middleware/built-in/csrf/index.d.ts +3 -0
  213. package/dist/core/middleware/built-in/csrf/index.js +9 -0
  214. package/dist/core/middleware/built-in/csrf/index.js.map +1 -0
  215. package/dist/core/middleware/built-in/csrf/middleware.d.ts +16 -0
  216. package/dist/core/middleware/built-in/csrf/middleware.js +34 -0
  217. package/dist/core/middleware/built-in/csrf/middleware.js.map +1 -0
  218. package/dist/core/middleware/built-in/error-tracker/index.d.ts +1 -0
  219. package/dist/core/middleware/built-in/error-tracker/index.js +4 -0
  220. package/dist/core/middleware/built-in/error-tracker/index.js.map +1 -0
  221. package/dist/core/middleware/built-in/error-tracker/middleware.d.ts +12 -0
  222. package/dist/core/middleware/built-in/error-tracker/middleware.js +26 -0
  223. package/dist/core/middleware/built-in/error-tracker/middleware.js.map +1 -0
  224. package/dist/core/middleware/built-in/index.d.ts +28 -61
  225. package/dist/core/middleware/built-in/index.js +48 -78
  226. package/dist/core/middleware/built-in/index.js.map +1 -1
  227. package/dist/core/middleware/built-in/performance-monitor/index.d.ts +1 -0
  228. package/dist/core/middleware/built-in/performance-monitor/index.js +4 -0
  229. package/dist/core/middleware/built-in/performance-monitor/index.js.map +1 -0
  230. package/dist/core/middleware/built-in/performance-monitor/middleware.d.ts +12 -0
  231. package/dist/core/middleware/built-in/performance-monitor/middleware.js +29 -0
  232. package/dist/core/middleware/built-in/performance-monitor/middleware.js.map +1 -0
  233. package/dist/core/middleware/built-in/rate-limit/core.d.ts +33 -0
  234. package/dist/core/middleware/built-in/rate-limit/core.js +86 -0
  235. package/dist/core/middleware/built-in/rate-limit/core.js.map +1 -0
  236. package/dist/core/middleware/built-in/rate-limit/hook.d.ts +20 -0
  237. package/dist/core/middleware/built-in/{rate-limit.js → rate-limit/hook.js} +24 -22
  238. package/dist/core/middleware/built-in/rate-limit/hook.js.map +1 -0
  239. package/dist/core/middleware/built-in/rate-limit/index.d.ts +3 -0
  240. package/dist/core/middleware/built-in/rate-limit/index.js +9 -0
  241. package/dist/core/middleware/built-in/rate-limit/index.js.map +1 -0
  242. package/dist/core/middleware/built-in/rate-limit/middleware.d.ts +16 -0
  243. package/dist/core/middleware/built-in/rate-limit/middleware.js +35 -0
  244. package/dist/core/middleware/built-in/rate-limit/middleware.js.map +1 -0
  245. package/dist/core/middleware/built-in/request-logger/index.d.ts +1 -0
  246. package/dist/core/middleware/built-in/request-logger/index.js +4 -0
  247. package/dist/core/middleware/built-in/request-logger/index.js.map +1 -0
  248. package/dist/core/middleware/built-in/request-logger/middleware.d.ts +12 -0
  249. package/dist/core/middleware/built-in/request-logger/middleware.js +24 -0
  250. package/dist/core/middleware/built-in/request-logger/middleware.js.map +1 -0
  251. package/dist/core/middleware/built-in/session/core.d.ts +73 -0
  252. package/dist/core/middleware/built-in/session/core.js +227 -0
  253. package/dist/core/middleware/built-in/session/core.js.map +1 -0
  254. package/dist/core/middleware/built-in/session/hook.d.ts +17 -0
  255. package/dist/core/middleware/built-in/session/hook.js +53 -0
  256. package/dist/core/middleware/built-in/session/hook.js.map +1 -0
  257. package/dist/core/middleware/built-in/session/index.d.ts +3 -0
  258. package/dist/core/middleware/built-in/session/index.js +9 -0
  259. package/dist/core/middleware/built-in/session/index.js.map +1 -0
  260. package/dist/core/middleware/built-in/session/middleware.d.ts +17 -0
  261. package/dist/core/middleware/built-in/session/middleware.js +38 -0
  262. package/dist/core/middleware/built-in/session/middleware.js.map +1 -0
  263. package/dist/core/middleware/built-in/sse/core.d.ts +44 -0
  264. package/dist/core/middleware/built-in/sse/core.js +117 -0
  265. package/dist/core/middleware/built-in/sse/core.js.map +1 -0
  266. package/dist/core/middleware/built-in/sse/hook.d.ts +18 -0
  267. package/dist/core/middleware/built-in/sse/hook.js +60 -0
  268. package/dist/core/middleware/built-in/sse/hook.js.map +1 -0
  269. package/dist/core/middleware/built-in/sse/index.d.ts +3 -0
  270. package/dist/core/middleware/built-in/sse/index.js +9 -0
  271. package/dist/core/middleware/built-in/sse/index.js.map +1 -0
  272. package/dist/core/middleware/built-in/sse/middleware.d.ts +18 -0
  273. package/dist/core/middleware/built-in/sse/middleware.js +43 -0
  274. package/dist/core/middleware/built-in/sse/middleware.js.map +1 -0
  275. package/dist/core/middleware/built-in/validation/core.d.ts +23 -0
  276. package/dist/core/middleware/built-in/validation/core.js +93 -0
  277. package/dist/core/middleware/built-in/validation/core.js.map +1 -0
  278. package/dist/core/middleware/built-in/validation/hook.d.ts +13 -0
  279. package/dist/core/middleware/built-in/{validation.js → validation/hook.js} +16 -9
  280. package/dist/core/middleware/built-in/validation/hook.js.map +1 -0
  281. package/dist/core/middleware/built-in/validation/index.d.ts +3 -0
  282. package/dist/core/middleware/built-in/validation/index.js +9 -0
  283. package/dist/core/middleware/built-in/validation/index.js.map +1 -0
  284. package/dist/core/middleware/built-in/validation/middleware.d.ts +16 -0
  285. package/dist/core/middleware/built-in/validation/middleware.js +27 -0
  286. package/dist/core/middleware/built-in/validation/middleware.js.map +1 -0
  287. package/dist/core/middleware/index.d.ts +4 -4
  288. package/dist/core/middleware/index.js +14 -28
  289. package/dist/core/middleware/index.js.map +1 -1
  290. package/dist/core/modules/auto-discovery.d.ts +19 -2
  291. package/dist/core/modules/auto-discovery.js +391 -74
  292. package/dist/core/modules/auto-discovery.js.map +1 -1
  293. package/dist/core/modules/index.d.ts +2 -2
  294. package/dist/core/modules/index.js +2 -9
  295. package/dist/core/modules/index.js.map +1 -1
  296. package/dist/core/modules/modules.d.ts +3 -3
  297. package/dist/core/modules/modules.js +23 -54
  298. package/dist/core/modules/modules.js.map +1 -1
  299. package/dist/core/networking/adapters/index.d.ts +4 -3
  300. package/dist/core/networking/adapters/index.js +3 -7
  301. package/dist/core/networking/adapters/index.js.map +1 -1
  302. package/dist/core/networking/adapters/socketio-adapter.d.ts +1 -1
  303. package/dist/core/networking/adapters/socketio-adapter.js +5 -40
  304. package/dist/core/networking/adapters/socketio-adapter.js.map +1 -1
  305. package/dist/core/networking/adapters/uws-adapter.d.ts +44 -0
  306. package/dist/core/networking/adapters/uws-adapter.js +513 -0
  307. package/dist/core/networking/adapters/uws-adapter.js.map +1 -0
  308. package/dist/core/networking/adapters/ws-adapter.d.ts +2 -2
  309. package/dist/core/networking/adapters/ws-adapter.js +8 -43
  310. package/dist/core/networking/adapters/ws-adapter.js.map +1 -1
  311. package/dist/core/networking/index.d.ts +3 -2
  312. package/dist/core/networking/index.js +2 -7
  313. package/dist/core/networking/index.js.map +1 -1
  314. package/dist/core/networking/service-discovery.js +8 -12
  315. package/dist/core/networking/service-discovery.js.map +1 -1
  316. package/dist/core/networking/websocket-adapter.js +1 -2
  317. package/dist/core/networking/websocket-adapter.js.map +1 -1
  318. package/dist/core/networking/websocket-manager.d.ts +3 -3
  319. package/dist/core/networking/websocket-manager.js +9 -11
  320. package/dist/core/networking/websocket-manager.js.map +1 -1
  321. package/dist/core/pooling/object-pool-manager.d.ts +140 -0
  322. package/dist/core/pooling/object-pool-manager.js +502 -0
  323. package/dist/core/pooling/object-pool-manager.js.map +1 -0
  324. package/dist/core/routing/app-integration.d.ts +14 -12
  325. package/dist/core/routing/app-integration.js +49 -85
  326. package/dist/core/routing/app-integration.js.map +1 -1
  327. package/dist/core/routing/index.d.ts +17 -11
  328. package/dist/core/routing/index.js +48 -237
  329. package/dist/core/routing/index.js.map +1 -1
  330. package/dist/core/routing/path-matcher.d.ts +67 -0
  331. package/dist/core/routing/path-matcher.js +182 -0
  332. package/dist/core/routing/path-matcher.js.map +1 -0
  333. package/dist/core/routing/router.d.ts +38 -0
  334. package/dist/core/routing/router.js +68 -0
  335. package/dist/core/routing/router.js.map +1 -0
  336. package/dist/core/routing/unified-router.d.ts +132 -0
  337. package/dist/core/routing/unified-router.js +639 -0
  338. package/dist/core/routing/unified-router.js.map +1 -0
  339. package/dist/core/runtime/aws-lambda-adapter.d.ts +3 -3
  340. package/dist/core/runtime/aws-lambda-adapter.js +2 -6
  341. package/dist/core/runtime/aws-lambda-adapter.js.map +1 -1
  342. package/dist/core/runtime/base-adapter.d.ts +2 -2
  343. package/dist/core/runtime/base-adapter.js +3 -7
  344. package/dist/core/runtime/base-adapter.js.map +1 -1
  345. package/dist/core/runtime/cloudflare-workers-adapter.d.ts +3 -3
  346. package/dist/core/runtime/cloudflare-workers-adapter.js +2 -6
  347. package/dist/core/runtime/cloudflare-workers-adapter.js.map +1 -1
  348. package/dist/core/runtime/index.d.ts +12 -12
  349. package/dist/core/runtime/index.js +22 -35
  350. package/dist/core/runtime/index.js.map +1 -1
  351. package/dist/core/runtime/node-adapter.d.ts +4 -4
  352. package/dist/core/runtime/node-adapter.js +18 -49
  353. package/dist/core/runtime/node-adapter.js.map +1 -1
  354. package/dist/core/runtime/vercel-edge-adapter.d.ts +3 -3
  355. package/dist/core/runtime/vercel-edge-adapter.js +2 -6
  356. package/dist/core/runtime/vercel-edge-adapter.js.map +1 -1
  357. package/dist/core/utilities/circuit-breaker.js +1 -5
  358. package/dist/core/utilities/circuit-breaker.js.map +1 -1
  359. package/dist/core/utilities/container.js +12 -22
  360. package/dist/core/utilities/container.js.map +1 -1
  361. package/dist/core/utilities/hooks.d.ts +2 -2
  362. package/dist/core/utilities/hooks.js +7 -12
  363. package/dist/core/utilities/hooks.js.map +1 -1
  364. package/dist/core/utilities/index.d.ts +5 -4
  365. package/dist/core/utilities/index.js +5 -19
  366. package/dist/core/utilities/index.js.map +1 -1
  367. package/dist/core/utilities/package-utils.d.ts +38 -0
  368. package/dist/core/utilities/package-utils.js +57 -0
  369. package/dist/core/utilities/package-utils.js.map +1 -0
  370. package/dist/core/validation/adapters.d.ts +1 -1
  371. package/dist/core/validation/adapters.js +15 -26
  372. package/dist/core/validation/adapters.js.map +1 -1
  373. package/dist/core/validation/index.d.ts +6 -4
  374. package/dist/core/validation/index.js +57 -28
  375. package/dist/core/validation/index.js.map +1 -1
  376. package/dist/core/validation/schema-interface.js +3 -9
  377. package/dist/core/validation/schema-interface.js.map +1 -1
  378. package/dist/index.d.ts +52 -52
  379. package/dist/index.js +24 -132
  380. package/dist/index.js.map +1 -1
  381. package/dist/moro.d.ts +70 -16
  382. package/dist/moro.js +650 -269
  383. package/dist/moro.js.map +1 -1
  384. package/dist/types/auth.js +3 -9
  385. package/dist/types/auth.js.map +1 -1
  386. package/dist/types/cache.js +1 -2
  387. package/dist/types/cdn.js +1 -2
  388. package/dist/types/config.d.ts +73 -2
  389. package/dist/types/config.js +1 -2
  390. package/dist/types/config.js.map +1 -1
  391. package/dist/types/core.d.ts +36 -42
  392. package/dist/types/core.js +1 -2
  393. package/dist/types/database.js +1 -2
  394. package/dist/types/discovery.js +1 -2
  395. package/dist/types/events.js +1 -2
  396. package/dist/types/hooks.d.ts +4 -1
  397. package/dist/types/hooks.js +1 -2
  398. package/dist/types/http.d.ts +16 -1
  399. package/dist/types/http.js +1 -2
  400. package/dist/types/logger.d.ts +7 -0
  401. package/dist/types/logger.js +1 -2
  402. package/dist/types/module.d.ts +11 -0
  403. package/dist/types/module.js +1 -2
  404. package/dist/types/runtime.d.ts +1 -1
  405. package/dist/types/runtime.js +1 -2
  406. package/dist/types/session.js +1 -2
  407. package/package.json +18 -55
  408. package/dist/core/config/loader.d.ts +0 -7
  409. package/dist/core/config/loader.js +0 -269
  410. package/dist/core/config/loader.js.map +0 -1
  411. package/dist/core/config/validation.d.ts +0 -17
  412. package/dist/core/config/validation.js +0 -131
  413. package/dist/core/config/validation.js.map +0 -1
  414. package/dist/core/http/router.d.ts +0 -14
  415. package/dist/core/http/router.js +0 -109
  416. package/dist/core/http/router.js.map +0 -1
  417. package/dist/core/middleware/built-in/adapters/cache/file.js.map +0 -1
  418. package/dist/core/middleware/built-in/adapters/cache/index.d.ts +0 -5
  419. package/dist/core/middleware/built-in/adapters/cache/index.js +0 -28
  420. package/dist/core/middleware/built-in/adapters/cache/index.js.map +0 -1
  421. package/dist/core/middleware/built-in/adapters/cache/memory.js.map +0 -1
  422. package/dist/core/middleware/built-in/adapters/cache/redis.js.map +0 -1
  423. package/dist/core/middleware/built-in/adapters/cdn/azure.js.map +0 -1
  424. package/dist/core/middleware/built-in/adapters/cdn/cloudflare.js.map +0 -1
  425. package/dist/core/middleware/built-in/adapters/cdn/cloudfront.js.map +0 -1
  426. package/dist/core/middleware/built-in/adapters/cdn/index.d.ts +0 -5
  427. package/dist/core/middleware/built-in/adapters/cdn/index.js +0 -28
  428. package/dist/core/middleware/built-in/adapters/cdn/index.js.map +0 -1
  429. package/dist/core/middleware/built-in/adapters/index.d.ts +0 -4
  430. package/dist/core/middleware/built-in/adapters/index.js +0 -26
  431. package/dist/core/middleware/built-in/adapters/index.js.map +0 -1
  432. package/dist/core/middleware/built-in/auth-helpers.js.map +0 -1
  433. package/dist/core/middleware/built-in/auth-providers.js.map +0 -1
  434. package/dist/core/middleware/built-in/auth.d.ts +0 -30
  435. package/dist/core/middleware/built-in/auth.js +0 -281
  436. package/dist/core/middleware/built-in/auth.js.map +0 -1
  437. package/dist/core/middleware/built-in/cache.d.ts +0 -3
  438. package/dist/core/middleware/built-in/cache.js.map +0 -1
  439. package/dist/core/middleware/built-in/cdn.d.ts +0 -3
  440. package/dist/core/middleware/built-in/cdn.js +0 -113
  441. package/dist/core/middleware/built-in/cdn.js.map +0 -1
  442. package/dist/core/middleware/built-in/cookie.d.ts +0 -14
  443. package/dist/core/middleware/built-in/cookie.js +0 -68
  444. package/dist/core/middleware/built-in/cookie.js.map +0 -1
  445. package/dist/core/middleware/built-in/cors.d.ts +0 -2
  446. package/dist/core/middleware/built-in/cors.js +0 -29
  447. package/dist/core/middleware/built-in/cors.js.map +0 -1
  448. package/dist/core/middleware/built-in/csp.d.ts +0 -22
  449. package/dist/core/middleware/built-in/csp.js +0 -71
  450. package/dist/core/middleware/built-in/csp.js.map +0 -1
  451. package/dist/core/middleware/built-in/csrf.d.ts +0 -9
  452. package/dist/core/middleware/built-in/csrf.js +0 -63
  453. package/dist/core/middleware/built-in/csrf.js.map +0 -1
  454. package/dist/core/middleware/built-in/error-tracker.d.ts +0 -1
  455. package/dist/core/middleware/built-in/error-tracker.js +0 -19
  456. package/dist/core/middleware/built-in/error-tracker.js.map +0 -1
  457. package/dist/core/middleware/built-in/performance-monitor.d.ts +0 -1
  458. package/dist/core/middleware/built-in/performance-monitor.js +0 -22
  459. package/dist/core/middleware/built-in/performance-monitor.js.map +0 -1
  460. package/dist/core/middleware/built-in/rate-limit.d.ts +0 -6
  461. package/dist/core/middleware/built-in/rate-limit.js.map +0 -1
  462. package/dist/core/middleware/built-in/request-logger.d.ts +0 -1
  463. package/dist/core/middleware/built-in/request-logger.js +0 -16
  464. package/dist/core/middleware/built-in/request-logger.js.map +0 -1
  465. package/dist/core/middleware/built-in/session.d.ts +0 -41
  466. package/dist/core/middleware/built-in/session.js +0 -209
  467. package/dist/core/middleware/built-in/session.js.map +0 -1
  468. package/dist/core/middleware/built-in/sse.d.ts +0 -6
  469. package/dist/core/middleware/built-in/sse.js +0 -71
  470. package/dist/core/middleware/built-in/sse.js.map +0 -1
  471. package/dist/core/middleware/built-in/validation.d.ts +0 -2
  472. package/dist/core/middleware/built-in/validation.js.map +0 -1
  473. package/src/core/auth/README.md +0 -339
  474. package/src/core/auth/morojs-adapter.ts +0 -410
  475. package/src/core/config/file-loader.ts +0 -407
  476. package/src/core/config/index.ts +0 -60
  477. package/src/core/config/loader.ts +0 -633
  478. package/src/core/config/schema.ts +0 -150
  479. package/src/core/config/utils.ts +0 -251
  480. package/src/core/config/validation.ts +0 -140
  481. package/src/core/database/README.md +0 -228
  482. package/src/core/database/adapters/drizzle.ts +0 -403
  483. package/src/core/database/adapters/index.ts +0 -42
  484. package/src/core/database/adapters/mongodb.ts +0 -269
  485. package/src/core/database/adapters/mysql.ts +0 -207
  486. package/src/core/database/adapters/postgresql.ts +0 -201
  487. package/src/core/database/adapters/redis.ts +0 -326
  488. package/src/core/database/adapters/sqlite.ts +0 -247
  489. package/src/core/database/index.ts +0 -3
  490. package/src/core/docs/index.ts +0 -231
  491. package/src/core/docs/openapi-generator.ts +0 -576
  492. package/src/core/docs/schema-to-openapi.ts +0 -148
  493. package/src/core/docs/simple-docs.ts +0 -295
  494. package/src/core/docs/swagger-ui.ts +0 -351
  495. package/src/core/docs/zod-to-openapi.ts +0 -532
  496. package/src/core/events/event-bus.ts +0 -231
  497. package/src/core/events/index.ts +0 -12
  498. package/src/core/framework.ts +0 -636
  499. package/src/core/http/http-server.ts +0 -1787
  500. package/src/core/http/index.ts +0 -6
  501. package/src/core/http/router.ts +0 -141
  502. package/src/core/logger/filters.ts +0 -145
  503. package/src/core/logger/index.ts +0 -20
  504. package/src/core/logger/logger.ts +0 -814
  505. package/src/core/logger/outputs.ts +0 -134
  506. package/src/core/middleware/built-in/adapters/cache/file.ts +0 -104
  507. package/src/core/middleware/built-in/adapters/cache/index.ts +0 -23
  508. package/src/core/middleware/built-in/adapters/cache/memory.ts +0 -73
  509. package/src/core/middleware/built-in/adapters/cache/redis.ts +0 -100
  510. package/src/core/middleware/built-in/adapters/cdn/azure.ts +0 -60
  511. package/src/core/middleware/built-in/adapters/cdn/cloudflare.ts +0 -83
  512. package/src/core/middleware/built-in/adapters/cdn/cloudfront.ts +0 -83
  513. package/src/core/middleware/built-in/adapters/cdn/index.ts +0 -23
  514. package/src/core/middleware/built-in/adapters/index.ts +0 -7
  515. package/src/core/middleware/built-in/auth-helpers.ts +0 -401
  516. package/src/core/middleware/built-in/auth-providers.ts +0 -480
  517. package/src/core/middleware/built-in/auth.ts +0 -329
  518. package/src/core/middleware/built-in/cache.ts +0 -211
  519. package/src/core/middleware/built-in/cdn.ts +0 -124
  520. package/src/core/middleware/built-in/cookie.ts +0 -85
  521. package/src/core/middleware/built-in/cors.ts +0 -38
  522. package/src/core/middleware/built-in/csp.ts +0 -101
  523. package/src/core/middleware/built-in/csrf.ts +0 -82
  524. package/src/core/middleware/built-in/error-tracker.ts +0 -16
  525. package/src/core/middleware/built-in/index.ts +0 -79
  526. package/src/core/middleware/built-in/performance-monitor.ts +0 -25
  527. package/src/core/middleware/built-in/rate-limit.ts +0 -60
  528. package/src/core/middleware/built-in/request-logger.ts +0 -14
  529. package/src/core/middleware/built-in/session.ts +0 -288
  530. package/src/core/middleware/built-in/sse.ts +0 -86
  531. package/src/core/middleware/built-in/validation.ts +0 -33
  532. package/src/core/middleware/index.ts +0 -177
  533. package/src/core/modules/auto-discovery.ts +0 -263
  534. package/src/core/modules/index.ts +0 -3
  535. package/src/core/modules/modules.ts +0 -124
  536. package/src/core/networking/adapters/index.ts +0 -16
  537. package/src/core/networking/adapters/socketio-adapter.ts +0 -252
  538. package/src/core/networking/adapters/ws-adapter.ts +0 -430
  539. package/src/core/networking/index.ts +0 -3
  540. package/src/core/networking/service-discovery.ts +0 -304
  541. package/src/core/networking/websocket-adapter.ts +0 -217
  542. package/src/core/networking/websocket-manager.ts +0 -308
  543. package/src/core/routing/app-integration.ts +0 -216
  544. package/src/core/routing/index.ts +0 -488
  545. package/src/core/runtime/aws-lambda-adapter.ts +0 -147
  546. package/src/core/runtime/base-adapter.ts +0 -130
  547. package/src/core/runtime/cloudflare-workers-adapter.ts +0 -152
  548. package/src/core/runtime/index.ts +0 -62
  549. package/src/core/runtime/node-adapter.ts +0 -196
  550. package/src/core/runtime/vercel-edge-adapter.ts +0 -114
  551. package/src/core/utilities/circuit-breaker.ts +0 -46
  552. package/src/core/utilities/container.ts +0 -736
  553. package/src/core/utilities/hooks.ts +0 -142
  554. package/src/core/utilities/index.ts +0 -16
  555. package/src/core/validation/adapters.ts +0 -147
  556. package/src/core/validation/index.ts +0 -206
  557. package/src/core/validation/schema-interface.ts +0 -100
  558. package/src/index.ts +0 -226
  559. package/src/moro.ts +0 -1197
  560. package/src/types/auth.ts +0 -440
  561. package/src/types/cache.ts +0 -38
  562. package/src/types/cdn.ts +0 -22
  563. package/src/types/config.ts +0 -157
  564. package/src/types/core.ts +0 -56
  565. package/src/types/database.ts +0 -32
  566. package/src/types/discovery.ts +0 -7
  567. package/src/types/events.ts +0 -82
  568. package/src/types/hooks.ts +0 -47
  569. package/src/types/http.ts +0 -67
  570. package/src/types/logger.ts +0 -93
  571. package/src/types/module.ts +0 -87
  572. package/src/types/runtime.ts +0 -76
  573. package/src/types/session.ts +0 -89
  574. package/tsconfig.json +0 -21
  575. /package/dist/core/middleware/built-in/{auth-helpers.d.ts → auth/helpers.d.ts} +0 -0
@@ -1,1787 +0,0 @@
1
- // src/core/http-server.ts
2
- import { IncomingMessage, ServerResponse, createServer, Server } from 'http';
3
- import * as zlib from 'zlib';
4
- import { promisify } from 'util';
5
- import { createFrameworkLogger } from '../logger';
6
- import { HttpRequest, HttpResponse, HttpHandler, Middleware, RouteEntry } from '../../types/http';
7
-
8
- const gzip = promisify(zlib.gzip);
9
- const deflate = promisify(zlib.deflate);
10
-
11
- export class MoroHttpServer {
12
- private server: Server;
13
- private routes: RouteEntry[] = [];
14
- private globalMiddleware: Middleware[] = [];
15
- private compressionEnabled = true;
16
- private compressionThreshold = 1024;
17
- private logger = createFrameworkLogger('HttpServer');
18
- private hookManager: any;
19
- private requestCounter = 0;
20
-
21
- // Efficient object pooling with minimal overhead
22
- private paramObjectPool: Record<string, string>[] = [];
23
- private bufferPool: Buffer[] = [];
24
- private readonly maxPoolSize = 50;
25
-
26
- // Request handler pooling to avoid function creation overhead
27
- private middlewareExecutionCache = new Map<string, Function>();
28
-
29
- // String interning for common values (massive memory savings)
30
- private static readonly INTERNED_METHODS = new Map([
31
- ['GET', 'GET'],
32
- ['POST', 'POST'],
33
- ['PUT', 'PUT'],
34
- ['DELETE', 'DELETE'],
35
- ['PATCH', 'PATCH'],
36
- ['HEAD', 'HEAD'],
37
- ['OPTIONS', 'OPTIONS'],
38
- ]);
39
-
40
- private static readonly INTERNED_HEADERS = new Map([
41
- ['content-type', 'content-type'],
42
- ['content-length', 'content-length'],
43
- ['authorization', 'authorization'],
44
- ['accept', 'accept'],
45
- ['user-agent', 'user-agent'],
46
- ['host', 'host'],
47
- ['connection', 'connection'],
48
- ['cache-control', 'cache-control'],
49
- ]);
50
-
51
- // Pre-compiled response templates for ultra-common responses
52
- private static readonly RESPONSE_TEMPLATES = {
53
- notFound: Buffer.from('{"success":false,"error":"Not found"}'),
54
- unauthorized: Buffer.from('{"success":false,"error":"Unauthorized"}'),
55
- forbidden: Buffer.from('{"success":false,"error":"Forbidden"}'),
56
- internalError: Buffer.from('{"success":false,"error":"Internal server error"}'),
57
- methodNotAllowed: Buffer.from('{"success":false,"error":"Method not allowed"}'),
58
- rateLimited: Buffer.from('{"success":false,"error":"Rate limit exceeded"}'),
59
- };
60
-
61
- // Ultra-fast buffer pool for zero-copy operations (Rust-level performance)
62
- private static readonly BUFFER_SIZES = [64, 256, 1024, 4096, 16384];
63
- private static readonly BUFFER_POOLS = new Map<number, Buffer[]>();
64
-
65
- static {
66
- // Pre-allocate buffer pools for zero-allocation responses
67
- for (const size of MoroHttpServer.BUFFER_SIZES) {
68
- MoroHttpServer.BUFFER_POOLS.set(size, []);
69
- for (let i = 0; i < 50; i++) {
70
- // 50 buffers per size
71
- MoroHttpServer.BUFFER_POOLS.get(size)!.push(Buffer.allocUnsafe(size));
72
- }
73
- }
74
- }
75
-
76
- private static getOptimalBuffer(size: number): Buffer {
77
- // Find the smallest buffer that fits
78
- for (const poolSize of MoroHttpServer.BUFFER_SIZES) {
79
- if (size <= poolSize) {
80
- const pool = MoroHttpServer.BUFFER_POOLS.get(poolSize)!;
81
- return pool.length > 0 ? pool.pop()! : Buffer.allocUnsafe(poolSize);
82
- }
83
- }
84
- return Buffer.allocUnsafe(size);
85
- }
86
-
87
- private static returnBuffer(buffer: Buffer): void {
88
- // Return buffer to appropriate pool
89
- const size = buffer.length;
90
- if (MoroHttpServer.BUFFER_POOLS.has(size)) {
91
- const pool = MoroHttpServer.BUFFER_POOLS.get(size)!;
92
- if (pool.length < 50) {
93
- // Don't let pools grow too large
94
- pool.push(buffer);
95
- }
96
- }
97
- }
98
-
99
- constructor() {
100
- this.server = createServer(this.handleRequest.bind(this));
101
-
102
- // Optimize server for high performance (conservative settings for compatibility)
103
- this.server.keepAliveTimeout = 5000; // 5 seconds
104
- this.server.headersTimeout = 6000; // 6 seconds
105
- this.server.timeout = 30000; // 30 seconds request timeout
106
- }
107
-
108
- // Configure server for maximum performance (can disable all overhead)
109
- configurePerformance(
110
- config: {
111
- compression?: { enabled: boolean; threshold?: number };
112
- minimal?: boolean;
113
- } = {}
114
- ) {
115
- if (config.compression !== undefined) {
116
- this.compressionEnabled = config.compression.enabled;
117
- if (config.compression.threshold !== undefined) {
118
- this.compressionThreshold = config.compression.threshold;
119
- }
120
- }
121
-
122
- // Minimal mode - disable ALL overhead for pure speed
123
- if (config.minimal) {
124
- this.compressionEnabled = false;
125
- this.compressionThreshold = Infinity; // Never compress
126
- }
127
- }
128
-
129
- // Middleware management
130
- use(middleware: Middleware): void {
131
- this.globalMiddleware.push(middleware);
132
- }
133
-
134
- // Set hooks manager for request processing
135
- setHookManager(hookManager: any): void {
136
- this.hookManager = hookManager;
137
- }
138
-
139
- // Routing methods
140
- get(path: string, ...handlers: (Middleware | HttpHandler)[]): void {
141
- this.addRoute('GET', path, handlers);
142
- }
143
-
144
- post(path: string, ...handlers: (Middleware | HttpHandler)[]): void {
145
- this.addRoute('POST', path, handlers);
146
- }
147
-
148
- put(path: string, ...handlers: (Middleware | HttpHandler)[]): void {
149
- this.addRoute('PUT', path, handlers);
150
- }
151
-
152
- delete(path: string, ...handlers: (Middleware | HttpHandler)[]): void {
153
- this.addRoute('DELETE', path, handlers);
154
- }
155
-
156
- patch(path: string, ...handlers: (Middleware | HttpHandler)[]): void {
157
- this.addRoute('PATCH', path, handlers);
158
- }
159
-
160
- private addRoute(method: string, path: string, handlers: (Middleware | HttpHandler)[]): void {
161
- const { pattern, paramNames } = this.pathToRegex(path);
162
- const handler = handlers.pop() as HttpHandler;
163
- const middleware = handlers as Middleware[];
164
-
165
- const route = {
166
- method,
167
- path,
168
- pattern,
169
- paramNames,
170
- handler,
171
- middleware,
172
- };
173
-
174
- this.routes.push(route);
175
-
176
- // Organize routes for optimal lookup
177
- if (paramNames.length === 0) {
178
- // Static route - O(1) lookup
179
- const staticKey = `${method}:${path}`;
180
- this.staticRoutes.set(staticKey, route);
181
- } else {
182
- // Dynamic route - organize by segment count for faster matching
183
- this.dynamicRoutes.push(route);
184
-
185
- const segments = path.split('/').filter(s => s.length > 0);
186
- const segmentCount = segments.length;
187
-
188
- if (!this.routesBySegmentCount.has(segmentCount)) {
189
- this.routesBySegmentCount.set(segmentCount, []);
190
- }
191
- this.routesBySegmentCount.get(segmentCount)!.push(route);
192
- }
193
- }
194
-
195
- private pathToRegex(path: string): { pattern: RegExp; paramNames: string[] } {
196
- const paramNames: string[] = [];
197
-
198
- // Convert parameterized routes to regex
199
- const regexPattern = path
200
- .replace(/\/:([^/]+)/g, (match, paramName) => {
201
- paramNames.push(paramName);
202
- return '/([^/]+)';
203
- })
204
- .replace(/\//g, '\\/');
205
-
206
- return {
207
- pattern: new RegExp(`^${regexPattern}$`),
208
- paramNames,
209
- };
210
- }
211
-
212
- private async handleRequest(req: IncomingMessage, res: ServerResponse): Promise<void> {
213
- const httpReq = this.enhanceRequest(req);
214
- const httpRes = this.enhanceResponse(res);
215
-
216
- // Store original params for efficient cleanup
217
- const originalParams = httpReq.params;
218
-
219
- try {
220
- // Optimized URL and query parsing
221
- const urlString = req.url!;
222
- const queryIndex = urlString.indexOf('?');
223
-
224
- if (queryIndex === -1) {
225
- // No query string - fast path
226
- httpReq.path = urlString;
227
- httpReq.query = {};
228
- } else {
229
- // Has query string - parse efficiently
230
- httpReq.path = urlString.substring(0, queryIndex);
231
- httpReq.query = this.parseQueryString(urlString.substring(queryIndex + 1));
232
- }
233
-
234
- // Ultra-fast method checking - avoid array includes
235
- const method = req.method!;
236
- if (method === 'POST' || method === 'PUT' || method === 'PATCH') {
237
- httpReq.body = await this.parseBody(req);
238
- }
239
-
240
- // Execute hooks before request processing
241
- if (this.hookManager) {
242
- await this.hookManager.execute('request', {
243
- request: httpReq,
244
- response: httpRes,
245
- });
246
- }
247
-
248
- // Execute global middleware first
249
- await this.executeMiddleware(this.globalMiddleware, httpReq, httpRes);
250
-
251
- // If middleware handled the request, don't continue
252
- if (httpRes.headersSent) {
253
- return;
254
- }
255
-
256
- // Find matching route
257
- const route = this.findRoute(req.method!, httpReq.path);
258
- if (!route) {
259
- // Ultra-fast 404 response with pre-compiled buffer
260
- httpRes.statusCode = 404;
261
- httpRes.setHeader('Content-Type', 'application/json; charset=utf-8');
262
- httpRes.setHeader('Content-Length', MoroHttpServer.RESPONSE_TEMPLATES.notFound.length);
263
- httpRes.end(MoroHttpServer.RESPONSE_TEMPLATES.notFound);
264
- return;
265
- }
266
-
267
- // Extract path parameters - optimized with object pooling
268
- const matches = httpReq.path.match(route.pattern);
269
- if (matches) {
270
- // Use pooled object for parameters
271
- httpReq.params = this.acquireParamObject();
272
- route.paramNames.forEach((name, index) => {
273
- httpReq.params[name] = matches[index + 1];
274
- });
275
- }
276
-
277
- // Execute middleware chain
278
- await this.executeMiddleware(route.middleware, httpReq, httpRes);
279
-
280
- // Execute handler
281
- await route.handler(httpReq, httpRes);
282
- } catch (error) {
283
- // Debug: Log the actual error and where it came from
284
- this.logger.debug('🚨 MoroJS Request Error Details:', 'RequestHandler');
285
- this.logger.debug(`📍 Error type: ${typeof error}`, 'RequestHandler');
286
- this.logger.debug(
287
- `📍 Error message: ${error instanceof Error ? error.message : String(error)}`,
288
- 'RequestHandler'
289
- );
290
- this.logger.debug(
291
- `📍 Error stack: ${error instanceof Error ? error.stack : 'No stack trace'}`,
292
- 'RequestHandler'
293
- );
294
- this.logger.debug(`📍 Request path: ${req.url}`, 'RequestHandler');
295
- this.logger.debug(`📍 Request method: ${req.method}`, 'RequestHandler');
296
-
297
- this.logger.error('Request error', 'RequestHandler', {
298
- error: error instanceof Error ? error.message : String(error),
299
- requestId: httpReq.requestId,
300
- method: req.method,
301
- path: req.url,
302
- });
303
-
304
- if (!httpRes.headersSent) {
305
- // Ensure response is properly enhanced before using custom methods
306
- if (typeof httpRes.status === 'function' && typeof httpRes.json === 'function') {
307
- httpRes.status(500).json({
308
- success: false,
309
- error: 'Internal server error',
310
- requestId: httpReq.requestId,
311
- });
312
- } else {
313
- // Ultra-defensive fallback - check each method individually
314
- if (typeof httpRes.setHeader === 'function') {
315
- httpRes.statusCode = 500;
316
- httpRes.setHeader('Content-Type', 'application/json');
317
- } else {
318
- // Even setHeader doesn't exist - object is completely wrong
319
- this.logger.error(
320
- '❌ Response object is not a proper ServerResponse:',
321
- 'RequestHandler',
322
- {
323
- responseType: typeof httpRes,
324
- responseKeys: Object.keys(httpRes),
325
- }
326
- );
327
- }
328
-
329
- if (typeof httpRes.end === 'function') {
330
- httpRes.end(
331
- JSON.stringify({
332
- success: false,
333
- error: 'Internal server error',
334
- requestId: httpReq.requestId,
335
- })
336
- );
337
- } else {
338
- this.logger.error(
339
- '❌ Cannot send error response - end() method missing',
340
- 'RequestHandler'
341
- );
342
- }
343
- }
344
- }
345
- } finally {
346
- // CRITICAL: Always release pooled objects back to the pool
347
- // This prevents memory leaks and ensures consistent performance
348
- if (originalParams && Object.keys(originalParams).length === 0) {
349
- this.releaseParamObject(originalParams);
350
- }
351
- if (
352
- httpReq.params &&
353
- httpReq.params !== originalParams &&
354
- Object.keys(httpReq.params).length === 0
355
- ) {
356
- this.releaseParamObject(httpReq.params);
357
- }
358
- }
359
-
360
- // Additional cleanup on response completion to ensure objects are returned to pool
361
- res.once('finish', () => {
362
- if (originalParams && Object.keys(originalParams).length === 0) {
363
- this.releaseParamObject(originalParams);
364
- }
365
- if (
366
- httpReq.params &&
367
- httpReq.params !== originalParams &&
368
- Object.keys(httpReq.params).length === 0
369
- ) {
370
- this.releaseParamObject(httpReq.params);
371
- }
372
- });
373
- }
374
-
375
- // Efficient object pooling for parameter objects with ES2022 optimizations
376
- private acquireParamObject(): Record<string, string> {
377
- const obj = this.paramObjectPool.pop();
378
- if (obj) {
379
- // ES2022: Use Object.hasOwn for safer property checks and faster clearing
380
- // Clear existing properties more efficiently
381
- for (const key in obj) {
382
- if (Object.hasOwn(obj, key)) {
383
- delete obj[key];
384
- }
385
- }
386
- return obj;
387
- }
388
- return {};
389
- }
390
-
391
- private releaseParamObject(params: Record<string, string>): void {
392
- if (this.paramObjectPool.length < this.maxPoolSize) {
393
- this.paramObjectPool.push(params);
394
- }
395
- }
396
-
397
- // Force cleanup of all pooled objects
398
- private forceCleanupPools(): void {
399
- // ES2022: More efficient array clearing
400
- this.paramObjectPool.splice(0);
401
- this.bufferPool.splice(0);
402
-
403
- // Force garbage collection if available
404
- // Use modern globalThis check with optional chaining
405
- if (globalThis?.gc) {
406
- globalThis.gc();
407
- }
408
- }
409
-
410
- private acquireBuffer(size: number): Buffer {
411
- // ES2022: Use findIndex for better performance than find + indexOf
412
- const index = this.bufferPool.findIndex(b => b.length >= size);
413
- if (index !== -1) {
414
- const buffer = this.bufferPool.splice(index, 1)[0];
415
- return buffer.subarray(0, size);
416
- }
417
- return Buffer.allocUnsafe(size);
418
- }
419
-
420
- private releaseBuffer(buffer: Buffer): void {
421
- if (this.bufferPool.length < this.maxPoolSize && buffer.length <= 8192) {
422
- this.bufferPool.push(buffer);
423
- }
424
- }
425
-
426
- private streamLargeResponse(res: any, data: any): void {
427
- res.setHeader('Content-Type', 'application/json; charset=utf-8');
428
- res.setHeader('Transfer-Encoding', 'chunked');
429
-
430
- // Stream the response in chunks
431
- const jsonString = JSON.stringify(data);
432
- const chunkSize = 8192; // 8KB chunks
433
-
434
- for (let i = 0; i < jsonString.length; i += chunkSize) {
435
- const chunk = jsonString.substring(i, i + chunkSize);
436
- res.write(chunk);
437
- }
438
- res.end();
439
- }
440
-
441
- private normalizePath(path: string): string {
442
- // Check cache first
443
- if (this.pathNormalizationCache.has(path)) {
444
- return this.pathNormalizationCache.get(path)!;
445
- }
446
-
447
- // Fast normalization: remove trailing slash (except root), decode once
448
- let normalized = path;
449
- if (normalized.length > 1 && normalized.endsWith('/')) {
450
- normalized = normalized.slice(0, -1);
451
- }
452
-
453
- // Cache result (limit cache size)
454
- if (this.pathNormalizationCache.size < 200) {
455
- this.pathNormalizationCache.set(path, normalized);
456
- }
457
-
458
- return normalized;
459
- }
460
-
461
- private enhanceRequest(req: IncomingMessage): HttpRequest {
462
- const httpReq = req as HttpRequest;
463
- httpReq.params = this.acquireParamObject();
464
- httpReq.query = {};
465
- httpReq.body = null;
466
- httpReq.path = '';
467
- httpReq.ip = req.socket.remoteAddress || '';
468
- // Faster request ID generation
469
- httpReq.requestId = Date.now().toString(36) + (++this.requestCounter).toString(36);
470
- httpReq.headers = req.headers as Record<string, string>;
471
-
472
- // Parse cookies
473
- httpReq.cookies = this.parseCookies(req.headers.cookie || '');
474
-
475
- return httpReq;
476
- }
477
-
478
- private parseCookies(cookieHeader: string): Record<string, string> {
479
- const cookies: Record<string, string> = {};
480
- if (!cookieHeader) return cookies;
481
-
482
- cookieHeader.split(';').forEach(cookie => {
483
- const [name, value] = cookie.trim().split('=');
484
- if (name && value) {
485
- cookies[name] = decodeURIComponent(value);
486
- }
487
- });
488
-
489
- return cookies;
490
- }
491
-
492
- private enhanceResponse(res: ServerResponse): HttpResponse {
493
- const httpRes = res as HttpResponse;
494
-
495
- // BULLETPROOF status method - always works
496
- httpRes.status = (code: number) => {
497
- httpRes.statusCode = code;
498
- return httpRes;
499
- };
500
-
501
- httpRes.json = async (data: any) => {
502
- if (httpRes.headersSent) return;
503
-
504
- // Ultra-fast JSON serialization with zero-copy buffers
505
- let jsonString: string;
506
-
507
- // Enhanced JSON optimization for common API patterns
508
- if (data && typeof data === 'object' && 'success' in data) {
509
- if ('data' in data && 'error' in data && !('total' in data)) {
510
- // {success, data, error} pattern
511
- jsonString = `{"success":${data.success},"data":${JSON.stringify(data.data)},"error":${JSON.stringify(data.error)}}`;
512
- } else if ('data' in data && 'total' in data && !('error' in data)) {
513
- // {success, data, total} pattern
514
- jsonString = `{"success":${data.success},"data":${JSON.stringify(data.data)},"total":${data.total}}`;
515
- } else if ('data' in data && !('error' in data) && !('total' in data)) {
516
- // {success, data} pattern
517
- jsonString = `{"success":${data.success},"data":${JSON.stringify(data.data)}}`;
518
- } else if ('error' in data && !('data' in data) && !('total' in data)) {
519
- // {success, error} pattern
520
- jsonString = `{"success":${data.success},"error":${JSON.stringify(data.error)}}`;
521
- } else {
522
- // Complex object - use standard JSON.stringify
523
- jsonString = JSON.stringify(data);
524
- }
525
- } else {
526
- jsonString = JSON.stringify(data);
527
- }
528
-
529
- // Use buffer pool for zero-allocation responses
530
- const estimatedSize = jsonString.length;
531
- if (estimatedSize > 32768) {
532
- // Large response - stream it
533
- return this.streamLargeResponse(httpRes, data);
534
- }
535
-
536
- const buffer = MoroHttpServer.getOptimalBuffer(estimatedSize);
537
- const actualLength = buffer.write(jsonString, 0, 'utf8');
538
-
539
- // Slice to actual size to avoid sending extra bytes
540
- const finalBuffer =
541
- actualLength === buffer.length ? buffer : buffer.subarray(0, actualLength);
542
-
543
- // Optimized header setting - set multiple headers at once when possible
544
- const headers: Record<string, string | number> = {
545
- 'Content-Type': 'application/json; charset=utf-8',
546
- };
547
-
548
- // Compression with buffer pool
549
- if (this.compressionEnabled && finalBuffer.length > this.compressionThreshold) {
550
- const acceptEncoding = httpRes.req.headers['accept-encoding'] || '';
551
-
552
- if (acceptEncoding.includes('gzip')) {
553
- const compressed = await gzip(finalBuffer);
554
- headers['Content-Encoding'] = 'gzip';
555
- headers['Content-Length'] = compressed.length;
556
-
557
- // Set all headers at once
558
- Object.entries(headers).forEach(([key, value]) => {
559
- httpRes.setHeader(key, value);
560
- });
561
-
562
- httpRes.end(compressed);
563
- // Return buffer to pool after response
564
- process.nextTick(() => MoroHttpServer.returnBuffer(buffer));
565
- return;
566
- } else if (acceptEncoding.includes('deflate')) {
567
- const compressed = await deflate(finalBuffer);
568
- headers['Content-Encoding'] = 'deflate';
569
- headers['Content-Length'] = compressed.length;
570
-
571
- Object.entries(headers).forEach(([key, value]) => {
572
- httpRes.setHeader(key, value);
573
- });
574
-
575
- httpRes.end(compressed);
576
- // Return buffer to pool after response
577
- process.nextTick(() => MoroHttpServer.returnBuffer(buffer));
578
- return;
579
- }
580
- }
581
-
582
- headers['Content-Length'] = finalBuffer.length;
583
-
584
- // Set all headers at once for better performance
585
- Object.entries(headers).forEach(([key, value]) => {
586
- httpRes.setHeader(key, value);
587
- });
588
-
589
- httpRes.end(finalBuffer);
590
- // Return buffer to pool after response (zero-copy achievement!)
591
- process.nextTick(() => MoroHttpServer.returnBuffer(buffer));
592
- };
593
-
594
- httpRes.send = (data: string | Buffer) => {
595
- if (httpRes.headersSent) return;
596
-
597
- // Auto-detect content type if not already set
598
- if (!httpRes.getHeader('Content-Type')) {
599
- if (typeof data === 'string') {
600
- // Check if it's JSON
601
- try {
602
- JSON.parse(data);
603
- httpRes.setHeader('Content-Type', 'application/json; charset=utf-8');
604
- } catch {
605
- // Default to plain text
606
- httpRes.setHeader('Content-Type', 'text/plain; charset=utf-8');
607
- }
608
- } else {
609
- // Buffer data - default to octet-stream
610
- httpRes.setHeader('Content-Type', 'application/octet-stream');
611
- }
612
- }
613
-
614
- httpRes.end(data);
615
- };
616
-
617
- httpRes.cookie = (name: string, value: string, options: any = {}) => {
618
- const cookieValue = encodeURIComponent(value);
619
- let cookieString = `${name}=${cookieValue}`;
620
-
621
- if (options.maxAge) cookieString += `; Max-Age=${options.maxAge}`;
622
- if (options.expires) cookieString += `; Expires=${options.expires.toUTCString()}`;
623
- if (options.httpOnly) cookieString += '; HttpOnly';
624
- if (options.secure) cookieString += '; Secure';
625
- if (options.sameSite) cookieString += `; SameSite=${options.sameSite}`;
626
- if (options.domain) cookieString += `; Domain=${options.domain}`;
627
- if (options.path) cookieString += `; Path=${options.path}`;
628
-
629
- const existingCookies = httpRes.getHeader('Set-Cookie') || [];
630
- const cookies = Array.isArray(existingCookies)
631
- ? [...existingCookies]
632
- : [existingCookies as string];
633
- cookies.push(cookieString);
634
- httpRes.setHeader('Set-Cookie', cookies);
635
-
636
- return httpRes;
637
- };
638
-
639
- httpRes.clearCookie = (name: string, options: any = {}) => {
640
- const clearOptions = { ...options, expires: new Date(0), maxAge: 0 };
641
- return httpRes.cookie(name, '', clearOptions);
642
- };
643
-
644
- httpRes.redirect = (url: string, status: number = 302) => {
645
- if (httpRes.headersSent) return;
646
- httpRes.statusCode = status;
647
- httpRes.setHeader('Location', url);
648
- httpRes.end();
649
- };
650
-
651
- httpRes.sendFile = async (filePath: string) => {
652
- if (httpRes.headersSent) return;
653
-
654
- try {
655
- const fs = await import('fs/promises');
656
- const path = await import('path');
657
- const extension = path.extname(filePath);
658
- const mime = await this.getMimeType(extension);
659
-
660
- const stats = await fs.stat(filePath);
661
- const data = await fs.readFile(filePath);
662
-
663
- // Add charset for text-based files
664
- const contentType = this.addCharsetIfNeeded(mime);
665
- httpRes.setHeader('Content-Type', contentType);
666
- httpRes.setHeader('Content-Length', stats.size);
667
-
668
- // Add security headers for file downloads
669
- httpRes.setHeader('X-Content-Type-Options', 'nosniff');
670
-
671
- // Add caching headers
672
- httpRes.setHeader('Last-Modified', stats.mtime.toUTCString());
673
- httpRes.setHeader('Cache-Control', 'public, max-age=31536000'); // 1 year for static files
674
-
675
- httpRes.end(data);
676
- } catch (error) {
677
- httpRes.status(404).json({ success: false, error: 'File not found' });
678
- }
679
- };
680
-
681
- return httpRes;
682
- }
683
-
684
- private async getMimeType(ext: string): Promise<string> {
685
- const mimeTypes: Record<string, string> = {
686
- '.html': 'text/html',
687
- '.css': 'text/css',
688
- '.js': 'application/javascript',
689
- '.json': 'application/json',
690
- '.png': 'image/png',
691
- '.jpg': 'image/jpeg',
692
- '.jpeg': 'image/jpeg',
693
- '.gif': 'image/gif',
694
- '.svg': 'image/svg+xml',
695
- '.ico': 'image/x-icon',
696
- '.pdf': 'application/pdf',
697
- '.txt': 'text/plain',
698
- '.xml': 'application/xml',
699
- };
700
-
701
- return mimeTypes[ext.toLowerCase()] || 'application/octet-stream';
702
- }
703
-
704
- private addCharsetIfNeeded(mimeType: string): string {
705
- // Add charset for text-based content types
706
- const textTypes = [
707
- 'text/',
708
- 'application/json',
709
- 'application/javascript',
710
- 'application/xml',
711
- 'image/svg+xml',
712
- ];
713
-
714
- const needsCharset = textTypes.some(type => mimeType.startsWith(type));
715
-
716
- if (needsCharset && !mimeType.includes('charset')) {
717
- return `${mimeType}; charset=utf-8`;
718
- }
719
-
720
- return mimeType;
721
- }
722
-
723
- private async parseBody(req: IncomingMessage): Promise<any> {
724
- return new Promise((resolve, reject) => {
725
- const chunks: Buffer[] = [];
726
- let totalLength = 0;
727
- const maxSize = 10 * 1024 * 1024; // 10MB limit
728
-
729
- req.on('data', (chunk: Buffer) => {
730
- totalLength += chunk.length;
731
- if (totalLength > maxSize) {
732
- reject(new Error('Request body too large'));
733
- return;
734
- }
735
- chunks.push(chunk);
736
- });
737
-
738
- req.on('end', () => {
739
- try {
740
- const body = Buffer.concat(chunks);
741
- const contentType = req.headers['content-type'] || '';
742
-
743
- if (contentType.includes('application/json')) {
744
- resolve(JSON.parse(body.toString()));
745
- } else if (contentType.includes('application/x-www-form-urlencoded')) {
746
- resolve(this.parseUrlEncoded(body.toString()));
747
- } else if (contentType.includes('multipart/form-data')) {
748
- resolve(this.parseMultipart(body, contentType));
749
- } else {
750
- resolve(body.toString());
751
- }
752
- } catch (error) {
753
- reject(error);
754
- }
755
- });
756
-
757
- req.on('error', reject);
758
- });
759
- }
760
-
761
- private parseMultipart(
762
- buffer: Buffer,
763
- contentType: string
764
- ): { fields: Record<string, string>; files: Record<string, any> } {
765
- const boundary = contentType.split('boundary=')[1];
766
- if (!boundary) {
767
- throw new Error('Invalid multipart boundary');
768
- }
769
-
770
- const parts = buffer.toString('binary').split('--' + boundary);
771
- const fields: Record<string, string> = {};
772
- const files: Record<string, any> = {};
773
-
774
- for (let i = 1; i < parts.length - 1; i++) {
775
- const part = parts[i];
776
- const [headers, content] = part.split('\r\n\r\n');
777
-
778
- if (!headers || content === undefined) continue;
779
-
780
- const nameMatch = headers.match(/name="([^"]+)"/);
781
- const filenameMatch = headers.match(/filename="([^"]+)"/);
782
- const contentTypeMatch = headers.match(/Content-Type: ([^\r\n]+)/);
783
-
784
- if (nameMatch) {
785
- const name = nameMatch[1];
786
-
787
- if (filenameMatch) {
788
- // This is a file
789
- const filename = filenameMatch[1];
790
- const mimeType = contentTypeMatch ? contentTypeMatch[1] : 'application/octet-stream';
791
- const fileContent = content.substring(0, content.length - 2); // Remove trailing \r\n
792
-
793
- files[name] = {
794
- filename,
795
- mimetype: mimeType,
796
- data: Buffer.from(fileContent, 'binary'),
797
- size: Buffer.byteLength(fileContent, 'binary'),
798
- };
799
- } else {
800
- // This is a regular field
801
- fields[name] = content.substring(0, content.length - 2); // Remove trailing \r\n
802
- }
803
- }
804
- }
805
-
806
- return { fields, files };
807
- }
808
-
809
- private parseUrlEncoded(body: string): Record<string, string> {
810
- const params = new URLSearchParams(body);
811
- const result: Record<string, string> = {};
812
- for (const [key, value] of params) {
813
- result[key] = value;
814
- }
815
- return result;
816
- }
817
-
818
- private parseQueryString(queryString: string): Record<string, string> {
819
- const result: Record<string, string> = {};
820
- if (!queryString) return result;
821
-
822
- const pairs = queryString.split('&');
823
- for (const pair of pairs) {
824
- const equalIndex = pair.indexOf('=');
825
- if (equalIndex === -1) {
826
- result[decodeURIComponent(pair)] = '';
827
- } else {
828
- const key = decodeURIComponent(pair.substring(0, equalIndex));
829
- const value = decodeURIComponent(pair.substring(equalIndex + 1));
830
- result[key] = value;
831
- }
832
- }
833
- return result;
834
- }
835
-
836
- // Advanced route optimization: cache + static routes + segment grouping
837
- private routeCache = new Map<string, RouteEntry | null>();
838
- private staticRoutes = new Map<string, RouteEntry>();
839
- private dynamicRoutes: RouteEntry[] = [];
840
- private routesBySegmentCount = new Map<number, RouteEntry[]>();
841
- private pathNormalizationCache = new Map<string, string>();
842
-
843
- // Ultra-fast CPU cache-friendly optimizations (Rust-level performance)
844
- private routeHitCount = new Map<string, number>(); // Track route popularity for cache optimization
845
- private static readonly HOT_ROUTE_THRESHOLD = 100; // Routes accessed 100+ times get hot path treatment
846
-
847
- private findRoute(method: string, path: string): RouteEntry | null {
848
- // Normalize path for consistent matching
849
- const normalizedPath = this.normalizePath(path);
850
- const cacheKey = `${method}:${normalizedPath}`;
851
-
852
- // Track route popularity for hot path optimization
853
- const hitCount = (this.routeHitCount.get(cacheKey) || 0) + 1;
854
- this.routeHitCount.set(cacheKey, hitCount);
855
-
856
- // Check cache first (hot path optimization)
857
- if (this.routeCache.has(cacheKey)) {
858
- const cachedRoute = this.routeCache.get(cacheKey)!;
859
-
860
- // Promote frequently accessed routes to front of cache (LRU-like)
861
- if (hitCount > MoroHttpServer.HOT_ROUTE_THRESHOLD && this.routeCache.size > 100) {
862
- this.routeCache.delete(cacheKey);
863
- this.routeCache.set(cacheKey, cachedRoute); // Move to end (most recent)
864
- }
865
-
866
- return cachedRoute;
867
- }
868
-
869
- // Phase 1: O(1) static route lookup
870
- const staticRoute = this.staticRoutes.get(cacheKey);
871
- if (staticRoute) {
872
- this.routeCache.set(cacheKey, staticRoute);
873
- return staticRoute;
874
- }
875
-
876
- // Phase 2: Optimized dynamic route matching by segment count
877
- let route: RouteEntry | null = null;
878
- if (this.dynamicRoutes.length > 0) {
879
- const segments = normalizedPath.split('/').filter(s => s.length > 0);
880
- const candidateRoutes = this.routesBySegmentCount.get(segments.length) || this.dynamicRoutes;
881
-
882
- // Only test routes with matching method and segment count
883
- for (const candidateRoute of candidateRoutes) {
884
- if (candidateRoute.method === method && candidateRoute.pattern.test(normalizedPath)) {
885
- route = candidateRoute;
886
- break;
887
- }
888
- }
889
- }
890
-
891
- // Cache result (limit cache size to prevent memory leaks)
892
- if (this.routeCache.size < 500) {
893
- this.routeCache.set(cacheKey, route);
894
- }
895
-
896
- return route;
897
- }
898
-
899
- private async executeMiddleware(
900
- middleware: Middleware[],
901
- req: HttpRequest,
902
- res: HttpResponse
903
- ): Promise<void> {
904
- for (const mw of middleware) {
905
- // Short-circuit if response already sent
906
- if (res.headersSent) return;
907
-
908
- await new Promise<void>((resolve, reject) => {
909
- let nextCalled = false;
910
-
911
- const next = () => {
912
- if (nextCalled) return;
913
- nextCalled = true;
914
- resolve();
915
- };
916
-
917
- try {
918
- const result = mw(req, res, next);
919
-
920
- // Handle async middleware
921
- if (result instanceof Promise) {
922
- result
923
- .then(() => {
924
- if (!nextCalled) next();
925
- })
926
- .catch(reject);
927
- }
928
- } catch (error) {
929
- reject(error);
930
- }
931
- });
932
- }
933
- }
934
-
935
- listen(port: number, callback?: () => void): void;
936
- listen(port: number, host: string, callback?: () => void): void;
937
- listen(port: number, host?: string | (() => void), callback?: () => void): void {
938
- // Handle overloaded parameters (port, callback) or (port, host, callback)
939
- if (typeof host === 'function') {
940
- callback = host;
941
- host = undefined;
942
- }
943
-
944
- if (host) {
945
- this.server.listen(port, host, callback);
946
- } else {
947
- this.server.listen(port, callback);
948
- }
949
- }
950
-
951
- close(): Promise<void> {
952
- return new Promise(resolve => {
953
- this.server.close(() => resolve());
954
- });
955
- }
956
-
957
- // Public method to force cleanup
958
- forceCleanup(): void {
959
- this.forceCleanupPools();
960
- }
961
-
962
- getServer(): Server {
963
- return this.server;
964
- }
965
- }
966
-
967
- // Built-in middleware
968
- export const middleware = {
969
- cors: (options: { origin?: string; credentials?: boolean } = {}): Middleware => {
970
- return (req, res, next) => {
971
- res.setHeader('Access-Control-Allow-Origin', options.origin || '*');
972
- res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
973
- res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization');
974
-
975
- if (options.credentials) {
976
- res.setHeader('Access-Control-Allow-Credentials', 'true');
977
- }
978
-
979
- if (req.method === 'OPTIONS') {
980
- res.status(200).send('');
981
- return;
982
- }
983
-
984
- next();
985
- };
986
- },
987
-
988
- helmet: (): Middleware => {
989
- return (req, res, next) => {
990
- res.setHeader('X-Content-Type-Options', 'nosniff');
991
- res.setHeader('X-Frame-Options', 'DENY');
992
- res.setHeader('X-XSS-Protection', '1; mode=block');
993
- res.setHeader('Referrer-Policy', 'strict-origin-when-cross-origin');
994
- res.setHeader('Strict-Transport-Security', 'max-age=31536000; includeSubDomains');
995
- res.setHeader('Content-Security-Policy', "default-src 'self'");
996
- next();
997
- };
998
- },
999
-
1000
- compression: (options: { threshold?: number; level?: number } = {}): Middleware => {
1001
- const zlib = require('zlib');
1002
- const threshold = options.threshold || 1024;
1003
- const level = options.level || 6;
1004
-
1005
- return (req, res, next) => {
1006
- const acceptEncoding = req.headers['accept-encoding'] || '';
1007
-
1008
- // Override res.json to compress responses
1009
- const originalJson = res.json;
1010
- const originalSend = res.send;
1011
-
1012
- const compressResponse = (data: any, isJson = false) => {
1013
- const content = isJson ? JSON.stringify(data) : data;
1014
- const buffer = Buffer.from(content);
1015
-
1016
- if (buffer.length < threshold) {
1017
- return isJson ? originalJson.call(res, data) : originalSend.call(res, data);
1018
- }
1019
-
1020
- if (acceptEncoding.includes('gzip')) {
1021
- res.setHeader('Content-Encoding', 'gzip');
1022
- zlib.gzip(buffer, { level }, (err: any, compressed: Buffer) => {
1023
- if (err) {
1024
- return isJson ? originalJson.call(res, data) : originalSend.call(res, data);
1025
- }
1026
- res.setHeader('Content-Length', compressed.length);
1027
- res.writeHead(res.statusCode || 200, res.getHeaders());
1028
- res.end(compressed);
1029
- });
1030
- } else if (acceptEncoding.includes('deflate')) {
1031
- res.setHeader('Content-Encoding', 'deflate');
1032
- zlib.deflate(buffer, { level }, (err: any, compressed: Buffer) => {
1033
- if (err) {
1034
- return isJson ? originalJson.call(res, data) : originalSend.call(res, data);
1035
- }
1036
- res.setHeader('Content-Length', compressed.length);
1037
- res.writeHead(res.statusCode || 200, res.getHeaders());
1038
- res.end(compressed);
1039
- });
1040
- } else {
1041
- return isJson ? originalJson.call(res, data) : originalSend.call(res, data);
1042
- }
1043
- };
1044
-
1045
- res.json = function (data: any) {
1046
- // Ensure charset is set for Safari compatibility
1047
- this.setHeader('Content-Type', 'application/json; charset=utf-8');
1048
- compressResponse(data, true);
1049
- return this;
1050
- };
1051
-
1052
- res.send = function (data: any) {
1053
- compressResponse(data, false);
1054
- return this;
1055
- };
1056
-
1057
- next();
1058
- };
1059
- },
1060
-
1061
- requestLogger: (): Middleware => {
1062
- return (req, res, next) => {
1063
- const start = Date.now();
1064
- res.on('finish', () => {
1065
- const duration = Date.now() - start;
1066
- // Request completed - logged by framework
1067
- });
1068
-
1069
- next();
1070
- };
1071
- },
1072
-
1073
- bodySize: (options: { limit?: string } = {}): Middleware => {
1074
- const limit = options.limit || '10mb';
1075
- const limitBytes = parseSize(limit);
1076
-
1077
- return (req, res, next) => {
1078
- const contentLength = parseInt(req.headers['content-length'] || '0');
1079
-
1080
- if (contentLength > limitBytes) {
1081
- res.status(413).json({
1082
- success: false,
1083
- error: 'Request entity too large',
1084
- limit: limit,
1085
- });
1086
- return;
1087
- }
1088
-
1089
- next();
1090
- };
1091
- },
1092
-
1093
- static: (options: {
1094
- root: string;
1095
- maxAge?: number;
1096
- index?: string[];
1097
- dotfiles?: 'allow' | 'deny' | 'ignore';
1098
- etag?: boolean;
1099
- }): Middleware => {
1100
- return async (req, res, next) => {
1101
- // Only handle GET and HEAD requests
1102
- if (req.method !== 'GET' && req.method !== 'HEAD') {
1103
- next();
1104
- return;
1105
- }
1106
-
1107
- try {
1108
- const fs = await import('fs/promises');
1109
- const path = await import('path');
1110
- const crypto = await import('crypto');
1111
-
1112
- let filePath = path.join(options.root, req.path);
1113
-
1114
- // Security: prevent directory traversal
1115
- if (!filePath.startsWith(path.resolve(options.root))) {
1116
- res.status(403).json({ success: false, error: 'Forbidden' });
1117
- return;
1118
- }
1119
-
1120
- // Handle dotfiles
1121
- const basename = path.basename(filePath);
1122
- if (basename.startsWith('.')) {
1123
- if (options.dotfiles === 'deny') {
1124
- res.status(403).json({ success: false, error: 'Forbidden' });
1125
- return;
1126
- } else if (options.dotfiles === 'ignore') {
1127
- next();
1128
- return;
1129
- }
1130
- }
1131
-
1132
- let stats;
1133
- try {
1134
- stats = await fs.stat(filePath);
1135
- } catch (error) {
1136
- next(); // File not found, let other middleware handle
1137
- return;
1138
- }
1139
-
1140
- // Handle directories
1141
- if (stats.isDirectory()) {
1142
- const indexFiles = options.index || ['index.html', 'index.htm'];
1143
- let indexFound = false;
1144
-
1145
- for (const indexFile of indexFiles) {
1146
- const indexPath = path.join(filePath, indexFile);
1147
- try {
1148
- const indexStats = await fs.stat(indexPath);
1149
- if (indexStats.isFile()) {
1150
- filePath = indexPath;
1151
- stats = indexStats;
1152
- indexFound = true;
1153
- break;
1154
- }
1155
- } catch (error) {
1156
- // Continue to next index file
1157
- }
1158
- }
1159
-
1160
- if (!indexFound) {
1161
- next();
1162
- return;
1163
- }
1164
- }
1165
-
1166
- // Set headers with proper mime type and charset
1167
- const ext = path.extname(filePath);
1168
- const mimeTypes: Record<string, string> = {
1169
- '.html': 'text/html',
1170
- '.css': 'text/css',
1171
- '.js': 'application/javascript',
1172
- '.json': 'application/json',
1173
- '.png': 'image/png',
1174
- '.jpg': 'image/jpeg',
1175
- '.jpeg': 'image/jpeg',
1176
- '.gif': 'image/gif',
1177
- '.svg': 'image/svg+xml',
1178
- '.ico': 'image/x-icon',
1179
- '.pdf': 'application/pdf',
1180
- '.txt': 'text/plain',
1181
- '.xml': 'application/xml',
1182
- };
1183
-
1184
- const baseMimeType = mimeTypes[ext.toLowerCase()] || 'application/octet-stream';
1185
-
1186
- // Add charset for text-based files
1187
- const textTypes = [
1188
- 'text/',
1189
- 'application/json',
1190
- 'application/javascript',
1191
- 'application/xml',
1192
- 'image/svg+xml',
1193
- ];
1194
- const needsCharset = textTypes.some(type => baseMimeType.startsWith(type));
1195
- const contentType = needsCharset ? `${baseMimeType}; charset=utf-8` : baseMimeType;
1196
-
1197
- res.setHeader('Content-Type', contentType);
1198
- res.setHeader('Content-Length', stats.size);
1199
-
1200
- // Cache headers
1201
- if (options.maxAge) {
1202
- res.setHeader('Cache-Control', `public, max-age=${options.maxAge}`);
1203
- }
1204
-
1205
- // ETag support
1206
- if (options.etag !== false) {
1207
- const etag = crypto
1208
- .createHash('md5')
1209
- .update(`${stats.mtime.getTime()}-${stats.size}`)
1210
- .digest('hex');
1211
- res.setHeader('ETag', `"${etag}"`);
1212
-
1213
- // Handle conditional requests
1214
- const ifNoneMatch = req.headers['if-none-match'];
1215
- if (ifNoneMatch === `"${etag}"`) {
1216
- res.statusCode = 304;
1217
- res.end();
1218
- return;
1219
- }
1220
- }
1221
-
1222
- // Handle HEAD requests
1223
- if (req.method === 'HEAD') {
1224
- res.end();
1225
- return;
1226
- }
1227
-
1228
- // Send file
1229
- const data = await fs.readFile(filePath);
1230
- res.end(data);
1231
- } catch (error) {
1232
- res.status(500).json({ success: false, error: 'Internal server error' });
1233
- }
1234
- };
1235
- },
1236
-
1237
- upload: (
1238
- options: {
1239
- dest?: string;
1240
- maxFileSize?: number;
1241
- maxFiles?: number;
1242
- allowedTypes?: string[];
1243
- } = {}
1244
- ): Middleware => {
1245
- return (req, res, next) => {
1246
- const contentType = req.headers['content-type'] || '';
1247
-
1248
- if (!contentType.includes('multipart/form-data')) {
1249
- next();
1250
- return;
1251
- }
1252
-
1253
- // File upload handling is now built into parseBody method
1254
- // This middleware can add additional validation
1255
- if (req.body && req.body.files) {
1256
- const files = req.body.files;
1257
- const maxFileSize = options.maxFileSize || 5 * 1024 * 1024; // 5MB default
1258
- const maxFiles = options.maxFiles || 10;
1259
- const allowedTypes = options.allowedTypes;
1260
-
1261
- // Validate file count
1262
- if (Object.keys(files).length > maxFiles) {
1263
- res.status(400).json({
1264
- success: false,
1265
- error: `Too many files. Maximum ${maxFiles} allowed.`,
1266
- });
1267
- return;
1268
- }
1269
-
1270
- // Validate each file
1271
- for (const [fieldName, file] of Object.entries(files)) {
1272
- const fileData = file as any;
1273
-
1274
- // Validate file size
1275
- if (fileData.size > maxFileSize) {
1276
- res.status(400).json({
1277
- success: false,
1278
- error: `File ${fileData.filename} is too large. Maximum ${maxFileSize} bytes allowed.`,
1279
- });
1280
- return;
1281
- }
1282
-
1283
- // Validate file type
1284
- if (allowedTypes && !allowedTypes.includes(fileData.mimetype)) {
1285
- res.status(400).json({
1286
- success: false,
1287
- error: `File type ${fileData.mimetype} not allowed.`,
1288
- });
1289
- return;
1290
- }
1291
- }
1292
-
1293
- // Store files in request for easy access
1294
- req.files = files;
1295
- }
1296
-
1297
- next();
1298
- };
1299
- },
1300
-
1301
- template: (options: {
1302
- views: string;
1303
- engine?: 'moro' | 'handlebars' | 'ejs';
1304
- cache?: boolean;
1305
- defaultLayout?: string;
1306
- }): Middleware => {
1307
- const templateCache = new Map<string, string>();
1308
-
1309
- return async (req, res, next) => {
1310
- // Add render method to response
1311
- res.render = async (template: string, data: any = {}) => {
1312
- try {
1313
- const fs = await import('fs/promises');
1314
- const path = await import('path');
1315
-
1316
- const templatePath = path.join(options.views, `${template}.html`);
1317
-
1318
- let templateContent: string;
1319
-
1320
- // Check cache first
1321
- if (options.cache && templateCache.has(templatePath)) {
1322
- templateContent = templateCache.get(templatePath)!;
1323
- } else {
1324
- templateContent = await fs.readFile(templatePath, 'utf-8');
1325
- if (options.cache) {
1326
- templateCache.set(templatePath, templateContent);
1327
- }
1328
- }
1329
-
1330
- // Simple template engine - replace {{variable}} with values
1331
- let rendered = templateContent;
1332
-
1333
- // Handle basic variable substitution
1334
- rendered = rendered.replace(/\{\{(\w+)\}\}/g, (match: string, key: string) => {
1335
- return data[key] !== undefined ? String(data[key]) : match;
1336
- });
1337
-
1338
- // Handle nested object properties like {{user.name}}
1339
- rendered = rendered.replace(/\{\{([\w.]+)\}\}/g, (match: string, key: string) => {
1340
- const value = key.split('.').reduce((obj: any, prop: string) => obj?.[prop], data);
1341
- return value !== undefined ? String(value) : match;
1342
- });
1343
-
1344
- // Handle loops: {{#each items}}{{name}}{{/each}}
1345
- rendered = rendered.replace(
1346
- /\{\{#each (\w+)\}\}(.*?)\{\{\/each\}\}/gs,
1347
- (match, arrayKey, template) => {
1348
- const array = data[arrayKey];
1349
- if (!Array.isArray(array)) return '';
1350
-
1351
- return array
1352
- .map(item => {
1353
- let itemTemplate = template;
1354
- // Replace variables in the loop template
1355
- itemTemplate = itemTemplate.replace(
1356
- /\{\{(\w+)\}\}/g,
1357
- (match: string, key: string) => {
1358
- return item[key] !== undefined ? String(item[key]) : match;
1359
- }
1360
- );
1361
- return itemTemplate;
1362
- })
1363
- .join('');
1364
- }
1365
- );
1366
-
1367
- // Handle conditionals: {{#if condition}}content{{/if}}
1368
- rendered = rendered.replace(
1369
- /\{\{#if (\w+)\}\}(.*?)\{\{\/if\}\}/gs,
1370
- (match, conditionKey, content) => {
1371
- const condition = data[conditionKey];
1372
- return condition ? content : '';
1373
- }
1374
- );
1375
-
1376
- // Handle layout
1377
- if (options.defaultLayout) {
1378
- const layoutPath = path.join(options.views, 'layouts', `${options.defaultLayout}.html`);
1379
- try {
1380
- let layoutContent: string;
1381
-
1382
- if (options.cache && templateCache.has(layoutPath)) {
1383
- layoutContent = templateCache.get(layoutPath)!;
1384
- } else {
1385
- layoutContent = await fs.readFile(layoutPath, 'utf-8');
1386
- if (options.cache) {
1387
- templateCache.set(layoutPath, layoutContent);
1388
- }
1389
- }
1390
-
1391
- rendered = layoutContent.replace(/\{\{body\}\}/, rendered);
1392
- } catch (error) {
1393
- // Layout not found, use template as-is
1394
- }
1395
- }
1396
-
1397
- res.setHeader('Content-Type', 'text/html');
1398
- res.end(rendered);
1399
- } catch (error) {
1400
- res.status(500).json({ success: false, error: 'Template rendering failed' });
1401
- }
1402
- };
1403
-
1404
- next();
1405
- };
1406
- },
1407
-
1408
- // HTTP/2 Server Push middleware
1409
- http2Push: (
1410
- options: {
1411
- resources?: Array<{ path: string; as: string; type?: string }>;
1412
- condition?: (req: any) => boolean;
1413
- } = {}
1414
- ): Middleware => {
1415
- return (req, res, next) => {
1416
- // Add HTTP/2 push capability to response
1417
- (res as any).push = (path: string, options: any = {}) => {
1418
- // Check if HTTP/2 is supported
1419
- if (req.httpVersion === '2.0' && (res as any).stream && (res as any).stream.pushAllowed) {
1420
- try {
1421
- const pushStream = (res as any).stream.pushStream({
1422
- ':method': 'GET',
1423
- ':path': path,
1424
- ...options.headers,
1425
- });
1426
-
1427
- if (pushStream) {
1428
- // Handle push stream
1429
- return pushStream;
1430
- }
1431
- } catch (error) {
1432
- // Push failed, continue normally
1433
- }
1434
- }
1435
- return null;
1436
- };
1437
-
1438
- // Auto-push configured resources
1439
- if (options.resources && (!options.condition || options.condition(req))) {
1440
- for (const resource of options.resources) {
1441
- (res as any).push?.(resource.path, {
1442
- headers: {
1443
- 'content-type': resource.type || 'text/plain',
1444
- },
1445
- });
1446
- }
1447
- }
1448
-
1449
- next();
1450
- };
1451
- },
1452
-
1453
- // Server-Sent Events middleware
1454
- sse: (
1455
- options: {
1456
- heartbeat?: number;
1457
- retry?: number;
1458
- cors?: boolean;
1459
- } = {}
1460
- ): Middleware => {
1461
- return (req, res, next) => {
1462
- // Only handle SSE requests
1463
- if (req.headers.accept?.includes('text/event-stream')) {
1464
- // Set SSE headers
1465
- res.writeHead(200, {
1466
- 'Content-Type': 'text/event-stream',
1467
- 'Cache-Control': 'no-cache',
1468
- Connection: 'keep-alive',
1469
- 'Access-Control-Allow-Origin': options.cors ? '*' : undefined,
1470
- 'Access-Control-Allow-Headers': options.cors ? 'Cache-Control' : undefined,
1471
- });
1472
-
1473
- // Add SSE methods to response
1474
- (res as any).sendEvent = (data: any, event?: string, id?: string) => {
1475
- if (id) res.write(`id: ${id}\n`);
1476
- if (event) res.write(`event: ${event}\n`);
1477
- res.write(`data: ${typeof data === 'string' ? data : JSON.stringify(data)}\n\n`);
1478
- };
1479
-
1480
- (res as any).sendComment = (comment: string) => {
1481
- res.write(`: ${comment}\n\n`);
1482
- };
1483
-
1484
- (res as any).sendRetry = (ms: number) => {
1485
- res.write(`retry: ${ms}\n\n`);
1486
- };
1487
-
1488
- // Set up heartbeat if configured
1489
- let heartbeatInterval: NodeJS.Timeout | null = null;
1490
- if (options.heartbeat) {
1491
- heartbeatInterval = setInterval(() => {
1492
- (res as any).sendComment('heartbeat');
1493
- }, options.heartbeat);
1494
- }
1495
-
1496
- // Set retry if configured
1497
- if (options.retry) {
1498
- (res as any).sendRetry(options.retry);
1499
- }
1500
-
1501
- // Clean up on close
1502
- req.on('close', () => {
1503
- if (heartbeatInterval) {
1504
- clearInterval(heartbeatInterval);
1505
- }
1506
- });
1507
-
1508
- // Don't call next() - this middleware handles the response
1509
- return;
1510
- }
1511
-
1512
- next();
1513
- };
1514
- },
1515
-
1516
- // Range request middleware for streaming
1517
- range: (
1518
- options: {
1519
- acceptRanges?: string;
1520
- maxRanges?: number;
1521
- } = {}
1522
- ): Middleware => {
1523
- return async (req, res, next) => {
1524
- // Add range support to response
1525
- (res as any).sendRange = async (filePath: string, stats?: any) => {
1526
- try {
1527
- const fs = await import('fs/promises');
1528
- const path = await import('path');
1529
-
1530
- if (!stats) {
1531
- stats = await fs.stat(filePath);
1532
- }
1533
-
1534
- const fileSize = stats.size;
1535
- const range = req.headers.range;
1536
-
1537
- // Set Accept-Ranges header
1538
- res.setHeader('Accept-Ranges', options.acceptRanges || 'bytes');
1539
-
1540
- if (!range) {
1541
- // No range requested, send entire file
1542
- res.setHeader('Content-Length', fileSize);
1543
- const data = await fs.readFile(filePath);
1544
- res.end(data);
1545
- return;
1546
- }
1547
-
1548
- // Parse range header
1549
- const ranges = range
1550
- .replace(/bytes=/, '')
1551
- .split(',')
1552
- .map(r => {
1553
- const [start, end] = r.split('-');
1554
- return {
1555
- start: start ? parseInt(start) : 0,
1556
- end: end ? parseInt(end) : fileSize - 1,
1557
- };
1558
- });
1559
-
1560
- // Validate ranges
1561
- if (options.maxRanges && ranges.length > options.maxRanges) {
1562
- res.status(416).json({ success: false, error: 'Too many ranges' });
1563
- return;
1564
- }
1565
-
1566
- if (ranges.length === 1) {
1567
- // Single range
1568
- const { start, end } = ranges[0];
1569
- const chunkSize = end - start + 1;
1570
-
1571
- if (start >= fileSize || end >= fileSize) {
1572
- res.status(416).setHeader('Content-Range', `bytes */${fileSize}`);
1573
- res.json({ success: false, error: 'Range not satisfiable' });
1574
- return;
1575
- }
1576
-
1577
- res.status(206);
1578
- res.setHeader('Content-Range', `bytes ${start}-${end}/${fileSize}`);
1579
- res.setHeader('Content-Length', chunkSize);
1580
-
1581
- // Stream the range
1582
- const stream = require('fs').createReadStream(filePath, {
1583
- start,
1584
- end,
1585
- });
1586
- stream.pipe(res);
1587
- } else {
1588
- // Multiple ranges - multipart response
1589
- const boundary = 'MULTIPART_BYTERANGES';
1590
- res.status(206);
1591
- res.setHeader('Content-Type', `multipart/byteranges; boundary=${boundary}`);
1592
-
1593
- for (const { start, end } of ranges) {
1594
- if (start >= fileSize || end >= fileSize) continue;
1595
-
1596
- const chunkSize = end - start + 1;
1597
- res.write(`\r\n--${boundary}\r\n`);
1598
- res.write(`Content-Range: bytes ${start}-${end}/${fileSize}\r\n\r\n`);
1599
-
1600
- const stream = require('fs').createReadStream(filePath, {
1601
- start,
1602
- end,
1603
- });
1604
- await new Promise(resolve => {
1605
- stream.on('end', resolve);
1606
- stream.pipe(res, { end: false });
1607
- });
1608
- }
1609
- res.write(`\r\n--${boundary}--\r\n`);
1610
- res.end();
1611
- }
1612
- } catch (error) {
1613
- res.status(500).json({ success: false, error: 'Range request failed' });
1614
- }
1615
- };
1616
-
1617
- next();
1618
- };
1619
- },
1620
-
1621
- // CSRF Protection middleware
1622
- csrf: (
1623
- options: {
1624
- secret?: string;
1625
- tokenLength?: number;
1626
- cookieName?: string;
1627
- headerName?: string;
1628
- ignoreMethods?: string[];
1629
- sameSite?: boolean;
1630
- } = {}
1631
- ): Middleware => {
1632
- const secret = options.secret || 'moro-csrf-secret';
1633
- const tokenLength = options.tokenLength || 32;
1634
- const cookieName = options.cookieName || '_csrf';
1635
- const headerName = options.headerName || 'x-csrf-token';
1636
- const ignoreMethods = options.ignoreMethods || ['GET', 'HEAD', 'OPTIONS'];
1637
-
1638
- const generateToken = () => {
1639
- const crypto = require('crypto');
1640
- return crypto.randomBytes(tokenLength).toString('hex');
1641
- };
1642
-
1643
- const verifyToken = (token: string, sessionToken: string) => {
1644
- return token && sessionToken && token === sessionToken;
1645
- };
1646
-
1647
- return (req, res, next) => {
1648
- // Add CSRF token generation method
1649
- (req as any).csrfToken = () => {
1650
- if (!(req as any)._csrfToken) {
1651
- (req as any)._csrfToken = generateToken();
1652
- // Set token in cookie
1653
- res.cookie(cookieName, (req as any)._csrfToken, {
1654
- httpOnly: true,
1655
- sameSite: options.sameSite !== false ? 'strict' : undefined,
1656
- secure: req.headers['x-forwarded-proto'] === 'https' || (req.socket as any).encrypted,
1657
- });
1658
- }
1659
- return (req as any)._csrfToken;
1660
- };
1661
-
1662
- // Skip verification for safe methods
1663
- if (ignoreMethods.includes(req.method!)) {
1664
- next();
1665
- return;
1666
- }
1667
-
1668
- // Get token from header or body
1669
- const token =
1670
- req.headers[headerName] || (req.body && req.body._csrf) || (req.query && req.query._csrf);
1671
-
1672
- // Get session token from cookie
1673
- const sessionToken = req.cookies?.[cookieName];
1674
-
1675
- if (!verifyToken(token as string, sessionToken || '')) {
1676
- res.status(403).json({
1677
- success: false,
1678
- error: 'Invalid CSRF token',
1679
- code: 'CSRF_TOKEN_MISMATCH',
1680
- });
1681
- return;
1682
- }
1683
-
1684
- next();
1685
- };
1686
- },
1687
-
1688
- // Content Security Policy middleware
1689
- csp: (
1690
- options: {
1691
- directives?: {
1692
- defaultSrc?: string[];
1693
- scriptSrc?: string[];
1694
- styleSrc?: string[];
1695
- imgSrc?: string[];
1696
- connectSrc?: string[];
1697
- fontSrc?: string[];
1698
- objectSrc?: string[];
1699
- mediaSrc?: string[];
1700
- frameSrc?: string[];
1701
- childSrc?: string[];
1702
- workerSrc?: string[];
1703
- formAction?: string[];
1704
- upgradeInsecureRequests?: boolean;
1705
- blockAllMixedContent?: boolean;
1706
- };
1707
- reportOnly?: boolean;
1708
- reportUri?: string;
1709
- nonce?: boolean;
1710
- } = {}
1711
- ): Middleware => {
1712
- return (req, res, next) => {
1713
- const directives = options.directives || {
1714
- defaultSrc: ["'self'"],
1715
- scriptSrc: ["'self'"],
1716
- styleSrc: ["'self'", "'unsafe-inline'"],
1717
- imgSrc: ["'self'", 'data:', 'https:'],
1718
- connectSrc: ["'self'"],
1719
- fontSrc: ["'self'"],
1720
- objectSrc: ["'none'"],
1721
- mediaSrc: ["'self'"],
1722
- frameSrc: ["'none'"],
1723
- };
1724
-
1725
- // Generate nonce if requested
1726
- let nonce: string | undefined;
1727
- if (options.nonce) {
1728
- const crypto = require('crypto');
1729
- nonce = crypto.randomBytes(16).toString('base64');
1730
- (req as any).cspNonce = nonce;
1731
- }
1732
-
1733
- // Build CSP header value
1734
- const cspParts: string[] = [];
1735
-
1736
- for (const [directive, sources] of Object.entries(directives)) {
1737
- if (directive === 'upgradeInsecureRequests' && sources === true) {
1738
- cspParts.push('upgrade-insecure-requests');
1739
- } else if (directive === 'blockAllMixedContent' && sources === true) {
1740
- cspParts.push('block-all-mixed-content');
1741
- } else if (Array.isArray(sources)) {
1742
- let sourceList = sources.join(' ');
1743
-
1744
- // Add nonce to script-src and style-src if enabled
1745
- if (nonce && (directive === 'scriptSrc' || directive === 'styleSrc')) {
1746
- sourceList += ` 'nonce-${nonce}'`;
1747
- }
1748
-
1749
- // Convert camelCase to kebab-case
1750
- const kebabDirective = directive.replace(/([A-Z])/g, '-$1').toLowerCase();
1751
- cspParts.push(`${kebabDirective} ${sourceList}`);
1752
- }
1753
- }
1754
-
1755
- // Add report-uri if specified
1756
- if (options.reportUri) {
1757
- cspParts.push(`report-uri ${options.reportUri}`);
1758
- }
1759
-
1760
- const cspValue = cspParts.join('; ');
1761
- const headerName = options.reportOnly
1762
- ? 'Content-Security-Policy-Report-Only'
1763
- : 'Content-Security-Policy';
1764
-
1765
- res.setHeader(headerName, cspValue);
1766
-
1767
- next();
1768
- };
1769
- },
1770
- };
1771
-
1772
- function parseSize(size: string): number {
1773
- const units: { [key: string]: number } = {
1774
- b: 1,
1775
- kb: 1024,
1776
- mb: 1024 * 1024,
1777
- gb: 1024 * 1024 * 1024,
1778
- };
1779
-
1780
- const match = size.toLowerCase().match(/^(\d+(?:\.\d+)?)\s*(b|kb|mb|gb)?$/);
1781
- if (!match) return 1024 * 1024; // Default 1MB
1782
-
1783
- const value = parseFloat(match[1]);
1784
- const unit = match[2] || 'b';
1785
-
1786
- return Math.round(value * units[unit]);
1787
- }