@morojs/moro 1.6.6 → 1.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (432) hide show
  1. package/README.md +22 -4
  2. package/dist/core/auth/morojs-adapter.js +33 -20
  3. package/dist/core/auth/morojs-adapter.js.map +1 -1
  4. package/dist/core/config/config-sources.js +71 -15
  5. package/dist/core/config/config-sources.js.map +1 -1
  6. package/dist/core/config/config-validator.js +201 -6
  7. package/dist/core/config/config-validator.js.map +1 -1
  8. package/dist/core/database/adapters/drizzle.js +5 -5
  9. package/dist/core/database/adapters/drizzle.js.map +1 -1
  10. package/dist/core/database/adapters/mongodb.js +5 -1
  11. package/dist/core/database/adapters/mongodb.js.map +1 -1
  12. package/dist/core/database/adapters/mysql.js +5 -1
  13. package/dist/core/database/adapters/mysql.js.map +1 -1
  14. package/dist/core/database/adapters/postgresql.js +1 -1
  15. package/dist/core/database/adapters/postgresql.js.map +1 -1
  16. package/dist/core/database/adapters/redis.js +2 -2
  17. package/dist/core/database/adapters/redis.js.map +1 -1
  18. package/dist/core/database/adapters/sqlite.js +5 -1
  19. package/dist/core/database/adapters/sqlite.js.map +1 -1
  20. package/dist/core/docs/index.js.map +1 -1
  21. package/dist/core/docs/openapi-generator.js +8 -9
  22. package/dist/core/docs/openapi-generator.js.map +1 -1
  23. package/dist/core/docs/simple-docs.js +2 -1
  24. package/dist/core/docs/simple-docs.js.map +1 -1
  25. package/dist/core/docs/swagger-ui.js +1 -0
  26. package/dist/core/docs/swagger-ui.js.map +1 -1
  27. package/dist/core/docs/zod-to-openapi.js +4 -0
  28. package/dist/core/docs/zod-to-openapi.js.map +1 -1
  29. package/dist/core/events/event-bus.d.ts +1 -1
  30. package/dist/core/events/event-bus.js +2 -1
  31. package/dist/core/events/event-bus.js.map +1 -1
  32. package/dist/core/framework.d.ts +5 -3
  33. package/dist/core/framework.js +28 -25
  34. package/dist/core/framework.js.map +1 -1
  35. package/dist/core/graphql/adapter.d.ts +73 -0
  36. package/dist/core/graphql/adapter.js +2 -0
  37. package/dist/core/graphql/adapter.js.map +1 -0
  38. package/dist/core/graphql/adapters/graphql-js-adapter.d.ts +26 -0
  39. package/dist/core/graphql/adapters/graphql-js-adapter.js +229 -0
  40. package/dist/core/graphql/adapters/graphql-js-adapter.js.map +1 -0
  41. package/dist/core/graphql/core.d.ts +60 -0
  42. package/dist/core/graphql/core.js +191 -0
  43. package/dist/core/graphql/core.js.map +1 -0
  44. package/dist/core/graphql/index.d.ts +4 -0
  45. package/dist/core/graphql/index.js +4 -0
  46. package/dist/core/graphql/index.js.map +1 -0
  47. package/dist/core/graphql/loader.d.ts +9 -0
  48. package/dist/core/graphql/loader.js +32 -0
  49. package/dist/core/graphql/loader.js.map +1 -0
  50. package/dist/core/graphql/types.d.ts +211 -0
  51. package/dist/core/graphql/types.js +2 -0
  52. package/dist/core/graphql/types.js.map +1 -0
  53. package/dist/core/grpc/adapters/grpc-js-adapter.d.ts +28 -0
  54. package/dist/core/grpc/adapters/grpc-js-adapter.js +449 -0
  55. package/dist/core/grpc/adapters/grpc-js-adapter.js.map +1 -0
  56. package/dist/core/grpc/adapters/index.d.ts +1 -0
  57. package/dist/core/grpc/adapters/index.js +6 -0
  58. package/dist/core/grpc/adapters/index.js.map +1 -0
  59. package/dist/core/grpc/grpc-adapter.d.ts +47 -0
  60. package/dist/core/grpc/grpc-adapter.js +4 -0
  61. package/dist/core/grpc/grpc-adapter.js.map +1 -0
  62. package/dist/core/grpc/grpc-manager.d.ts +59 -0
  63. package/dist/core/grpc/grpc-manager.js +218 -0
  64. package/dist/core/grpc/grpc-manager.js.map +1 -0
  65. package/dist/core/grpc/index.d.ts +7 -0
  66. package/dist/core/grpc/index.js +10 -0
  67. package/dist/core/grpc/index.js.map +1 -0
  68. package/dist/core/grpc/middleware/auth.d.ts +22 -0
  69. package/dist/core/grpc/middleware/auth.js +126 -0
  70. package/dist/core/grpc/middleware/auth.js.map +1 -0
  71. package/dist/core/grpc/middleware/logging.d.ts +19 -0
  72. package/dist/core/grpc/middleware/logging.js +57 -0
  73. package/dist/core/grpc/middleware/logging.js.map +1 -0
  74. package/dist/core/grpc/middleware/validation.d.ts +18 -0
  75. package/dist/core/grpc/middleware/validation.js +126 -0
  76. package/dist/core/grpc/middleware/validation.js.map +1 -0
  77. package/dist/core/grpc/types.d.ts +233 -0
  78. package/dist/core/grpc/types.js +36 -0
  79. package/dist/core/grpc/types.js.map +1 -0
  80. package/dist/core/http/http-server.d.ts +13 -84
  81. package/dist/core/http/http-server.js +216 -781
  82. package/dist/core/http/http-server.js.map +1 -1
  83. package/dist/core/http/http2-server.d.ts +131 -0
  84. package/dist/core/http/http2-server.js +803 -0
  85. package/dist/core/http/http2-server.js.map +1 -0
  86. package/dist/core/http/index.d.ts +3 -1
  87. package/dist/core/http/index.js +2 -1
  88. package/dist/core/http/index.js.map +1 -1
  89. package/dist/core/http/utils/uws-worker-clustering.d.ts +28 -0
  90. package/dist/core/http/utils/uws-worker-clustering.js +313 -0
  91. package/dist/core/http/utils/uws-worker-clustering.js.map +1 -0
  92. package/dist/core/http/uws-http-server.d.ts +3 -1
  93. package/dist/core/http/uws-http-server.js +58 -34
  94. package/dist/core/http/uws-http-server.js.map +1 -1
  95. package/dist/core/jobs/cron-parser.d.ts +62 -0
  96. package/dist/core/jobs/cron-parser.js +239 -0
  97. package/dist/core/jobs/cron-parser.js.map +1 -0
  98. package/dist/core/jobs/index.d.ts +12 -0
  99. package/dist/core/jobs/index.js +9 -0
  100. package/dist/core/jobs/index.js.map +1 -0
  101. package/dist/core/jobs/job-executor.d.ts +134 -0
  102. package/dist/core/jobs/job-executor.js +418 -0
  103. package/dist/core/jobs/job-executor.js.map +1 -0
  104. package/dist/core/jobs/job-scheduler.d.ts +214 -0
  105. package/dist/core/jobs/job-scheduler.js +554 -0
  106. package/dist/core/jobs/job-scheduler.js.map +1 -0
  107. package/dist/core/jobs/job-state-manager.d.ts +158 -0
  108. package/dist/core/jobs/job-state-manager.js +444 -0
  109. package/dist/core/jobs/job-state-manager.js.map +1 -0
  110. package/dist/core/jobs/leader-election.d.ts +124 -0
  111. package/dist/core/jobs/leader-election.js +482 -0
  112. package/dist/core/jobs/leader-election.js.map +1 -0
  113. package/dist/core/jobs/types.d.ts +151 -0
  114. package/dist/core/jobs/types.js +4 -0
  115. package/dist/core/jobs/types.js.map +1 -0
  116. package/dist/core/jobs/utils.d.ts +95 -0
  117. package/dist/core/jobs/utils.js +258 -0
  118. package/dist/core/jobs/utils.js.map +1 -0
  119. package/dist/core/logger/filters.js +2 -0
  120. package/dist/core/logger/filters.js.map +1 -1
  121. package/dist/core/logger/logger.js +48 -21
  122. package/dist/core/logger/logger.js.map +1 -1
  123. package/dist/core/logger/outputs.js +11 -3
  124. package/dist/core/logger/outputs.js.map +1 -1
  125. package/dist/core/mail/adapters/console-adapter.d.ts +14 -0
  126. package/dist/core/mail/adapters/console-adapter.js +89 -0
  127. package/dist/core/mail/adapters/console-adapter.js.map +1 -0
  128. package/dist/core/mail/adapters/index.d.ts +5 -0
  129. package/dist/core/mail/adapters/index.js +8 -0
  130. package/dist/core/mail/adapters/index.js.map +1 -0
  131. package/dist/core/mail/adapters/nodemailer-adapter.d.ts +18 -0
  132. package/dist/core/mail/adapters/nodemailer-adapter.js +188 -0
  133. package/dist/core/mail/adapters/nodemailer-adapter.js.map +1 -0
  134. package/dist/core/mail/adapters/resend-adapter.d.ts +18 -0
  135. package/dist/core/mail/adapters/resend-adapter.js +169 -0
  136. package/dist/core/mail/adapters/resend-adapter.js.map +1 -0
  137. package/dist/core/mail/adapters/sendgrid-adapter.d.ts +19 -0
  138. package/dist/core/mail/adapters/sendgrid-adapter.js +186 -0
  139. package/dist/core/mail/adapters/sendgrid-adapter.js.map +1 -0
  140. package/dist/core/mail/adapters/ses-adapter.d.ts +18 -0
  141. package/dist/core/mail/adapters/ses-adapter.js +167 -0
  142. package/dist/core/mail/adapters/ses-adapter.js.map +1 -0
  143. package/dist/core/mail/index.d.ts +5 -0
  144. package/dist/core/mail/index.js +8 -0
  145. package/dist/core/mail/index.js.map +1 -0
  146. package/dist/core/mail/mail-adapter.d.ts +62 -0
  147. package/dist/core/mail/mail-adapter.js +83 -0
  148. package/dist/core/mail/mail-adapter.js.map +1 -0
  149. package/dist/core/mail/mail-manager.d.ts +63 -0
  150. package/dist/core/mail/mail-manager.js +302 -0
  151. package/dist/core/mail/mail-manager.js.map +1 -0
  152. package/dist/core/mail/template-engine.d.ts +43 -0
  153. package/dist/core/mail/template-engine.js +239 -0
  154. package/dist/core/mail/template-engine.js.map +1 -0
  155. package/dist/core/mail/types.d.ts +237 -0
  156. package/dist/core/mail/types.js +4 -0
  157. package/dist/core/mail/types.js.map +1 -0
  158. package/dist/core/middleware/built-in/auth/helpers.js +1 -1
  159. package/dist/core/middleware/built-in/auth/helpers.js.map +1 -1
  160. package/dist/core/middleware/built-in/auth/jwt-helpers.js +1 -1
  161. package/dist/core/middleware/built-in/auth/jwt-helpers.js.map +1 -1
  162. package/dist/core/middleware/built-in/auth/providers.js +1 -1
  163. package/dist/core/middleware/built-in/auth/providers.js.map +1 -1
  164. package/dist/core/middleware/built-in/body-size/core.d.ts +12 -0
  165. package/dist/core/middleware/built-in/body-size/core.js +52 -0
  166. package/dist/core/middleware/built-in/body-size/core.js.map +1 -0
  167. package/dist/core/middleware/built-in/body-size/hook.d.ts +2 -0
  168. package/dist/core/middleware/built-in/body-size/hook.js +12 -0
  169. package/dist/core/middleware/built-in/body-size/hook.js.map +1 -0
  170. package/dist/core/middleware/built-in/body-size/index.d.ts +6 -0
  171. package/dist/core/middleware/built-in/body-size/index.js +7 -0
  172. package/dist/core/middleware/built-in/body-size/index.js.map +1 -0
  173. package/dist/core/middleware/built-in/body-size/middleware.d.ts +14 -0
  174. package/dist/core/middleware/built-in/body-size/middleware.js +22 -0
  175. package/dist/core/middleware/built-in/body-size/middleware.js.map +1 -0
  176. package/dist/core/middleware/built-in/cache/adapters/cache/file.js +3 -3
  177. package/dist/core/middleware/built-in/cache/adapters/cache/file.js.map +1 -1
  178. package/dist/core/middleware/built-in/cache/adapters/cache/memory.js +1 -0
  179. package/dist/core/middleware/built-in/cache/adapters/cache/memory.js.map +1 -1
  180. package/dist/core/middleware/built-in/cache/adapters/cache/redis.js +1 -1
  181. package/dist/core/middleware/built-in/cache/adapters/cache/redis.js.map +1 -1
  182. package/dist/core/middleware/built-in/cache/core.d.ts +20 -1
  183. package/dist/core/middleware/built-in/cache/core.js.map +1 -1
  184. package/dist/core/middleware/built-in/cache/hook.d.ts +38 -1
  185. package/dist/core/middleware/built-in/cache/hook.js +202 -16
  186. package/dist/core/middleware/built-in/cache/hook.js.map +1 -1
  187. package/dist/core/middleware/built-in/cache/index.js +1 -1
  188. package/dist/core/middleware/built-in/cache/index.js.map +1 -1
  189. package/dist/core/middleware/built-in/cdn/adapters/cdn/azure.d.ts +8 -0
  190. package/dist/core/middleware/built-in/cdn/adapters/cdn/azure.js +100 -7
  191. package/dist/core/middleware/built-in/cdn/adapters/cdn/azure.js.map +1 -1
  192. package/dist/core/middleware/built-in/cdn/adapters/cdn/cloudflare.d.ts +6 -0
  193. package/dist/core/middleware/built-in/cdn/adapters/cdn/cloudflare.js +97 -13
  194. package/dist/core/middleware/built-in/cdn/adapters/cdn/cloudflare.js.map +1 -1
  195. package/dist/core/middleware/built-in/cdn/adapters/cdn/cloudfront.js +1 -1
  196. package/dist/core/middleware/built-in/cdn/adapters/cdn/cloudfront.js.map +1 -1
  197. package/dist/core/middleware/built-in/compression/core.d.ts +16 -0
  198. package/dist/core/middleware/built-in/compression/core.js +75 -0
  199. package/dist/core/middleware/built-in/compression/core.js.map +1 -0
  200. package/dist/core/middleware/built-in/compression/hook.d.ts +2 -0
  201. package/dist/core/middleware/built-in/compression/hook.js +14 -0
  202. package/dist/core/middleware/built-in/compression/hook.js.map +1 -0
  203. package/dist/core/middleware/built-in/compression/index.d.ts +6 -0
  204. package/dist/core/middleware/built-in/compression/index.js +7 -0
  205. package/dist/core/middleware/built-in/compression/index.js.map +1 -0
  206. package/dist/core/middleware/built-in/compression/middleware.d.ts +20 -0
  207. package/dist/core/middleware/built-in/compression/middleware.js +28 -0
  208. package/dist/core/middleware/built-in/compression/middleware.js.map +1 -0
  209. package/dist/core/middleware/built-in/cookie/core.js +37 -9
  210. package/dist/core/middleware/built-in/cookie/core.js.map +1 -1
  211. package/dist/core/middleware/built-in/cookie/hook.d.ts +1 -1
  212. package/dist/core/middleware/built-in/cookie/hook.js +2 -2
  213. package/dist/core/middleware/built-in/cookie/hook.js.map +1 -1
  214. package/dist/core/middleware/built-in/csrf/core.js +1 -0
  215. package/dist/core/middleware/built-in/csrf/core.js.map +1 -1
  216. package/dist/core/middleware/built-in/graphql/core.d.ts +11 -0
  217. package/dist/core/middleware/built-in/graphql/core.js +24 -0
  218. package/dist/core/middleware/built-in/graphql/core.js.map +1 -0
  219. package/dist/core/middleware/built-in/graphql/helpers.d.ts +69 -0
  220. package/dist/core/middleware/built-in/graphql/helpers.js +187 -0
  221. package/dist/core/middleware/built-in/graphql/helpers.js.map +1 -0
  222. package/dist/core/middleware/built-in/graphql/hook.d.ts +7 -0
  223. package/dist/core/middleware/built-in/graphql/hook.js +78 -0
  224. package/dist/core/middleware/built-in/graphql/hook.js.map +1 -0
  225. package/dist/core/middleware/built-in/graphql/index.d.ts +5 -0
  226. package/dist/core/middleware/built-in/graphql/index.js +5 -0
  227. package/dist/core/middleware/built-in/graphql/index.js.map +1 -0
  228. package/dist/core/middleware/built-in/graphql/middleware.d.ts +7 -0
  229. package/dist/core/middleware/built-in/graphql/middleware.js +54 -0
  230. package/dist/core/middleware/built-in/graphql/middleware.js.map +1 -0
  231. package/dist/core/middleware/built-in/graphql/subscriptions.d.ts +20 -0
  232. package/dist/core/middleware/built-in/graphql/subscriptions.js +37 -0
  233. package/dist/core/middleware/built-in/graphql/subscriptions.js.map +1 -0
  234. package/dist/core/middleware/built-in/helmet/core.d.ts +19 -0
  235. package/dist/core/middleware/built-in/helmet/core.js +70 -0
  236. package/dist/core/middleware/built-in/helmet/core.js.map +1 -0
  237. package/dist/core/middleware/built-in/helmet/hook.d.ts +2 -0
  238. package/dist/core/middleware/built-in/helmet/hook.js +12 -0
  239. package/dist/core/middleware/built-in/helmet/hook.js.map +1 -0
  240. package/dist/core/middleware/built-in/helmet/index.d.ts +6 -0
  241. package/dist/core/middleware/built-in/helmet/index.js +7 -0
  242. package/dist/core/middleware/built-in/helmet/index.js.map +1 -0
  243. package/dist/core/middleware/built-in/helmet/middleware.d.ts +22 -0
  244. package/dist/core/middleware/built-in/helmet/middleware.js +28 -0
  245. package/dist/core/middleware/built-in/helmet/middleware.js.map +1 -0
  246. package/dist/core/middleware/built-in/http2/core.d.ts +35 -0
  247. package/dist/core/middleware/built-in/http2/core.js +128 -0
  248. package/dist/core/middleware/built-in/http2/core.js.map +1 -0
  249. package/dist/core/middleware/built-in/http2/hook.d.ts +5 -0
  250. package/dist/core/middleware/built-in/http2/hook.js +34 -0
  251. package/dist/core/middleware/built-in/http2/hook.js.map +1 -0
  252. package/dist/core/middleware/built-in/http2/index.d.ts +8 -0
  253. package/dist/core/middleware/built-in/http2/index.js +10 -0
  254. package/dist/core/middleware/built-in/http2/index.js.map +1 -0
  255. package/dist/core/middleware/built-in/http2/middleware.d.ts +20 -0
  256. package/dist/core/middleware/built-in/http2/middleware.js +31 -0
  257. package/dist/core/middleware/built-in/http2/middleware.js.map +1 -0
  258. package/dist/core/middleware/built-in/index.d.ts +20 -1
  259. package/dist/core/middleware/built-in/index.js +31 -0
  260. package/dist/core/middleware/built-in/index.js.map +1 -1
  261. package/dist/core/middleware/built-in/range/core.d.ts +16 -0
  262. package/dist/core/middleware/built-in/range/core.js +112 -0
  263. package/dist/core/middleware/built-in/range/core.js.map +1 -0
  264. package/dist/core/middleware/built-in/range/hook.d.ts +2 -0
  265. package/dist/core/middleware/built-in/range/hook.js +12 -0
  266. package/dist/core/middleware/built-in/range/hook.js.map +1 -0
  267. package/dist/core/middleware/built-in/range/index.d.ts +6 -0
  268. package/dist/core/middleware/built-in/range/index.js +7 -0
  269. package/dist/core/middleware/built-in/range/index.js.map +1 -0
  270. package/dist/core/middleware/built-in/range/middleware.d.ts +21 -0
  271. package/dist/core/middleware/built-in/range/middleware.js +27 -0
  272. package/dist/core/middleware/built-in/range/middleware.js.map +1 -0
  273. package/dist/core/middleware/built-in/session/core.js +14 -1
  274. package/dist/core/middleware/built-in/session/core.js.map +1 -1
  275. package/dist/core/middleware/built-in/static/core.d.ts +20 -0
  276. package/dist/core/middleware/built-in/static/core.js +143 -0
  277. package/dist/core/middleware/built-in/static/core.js.map +1 -0
  278. package/dist/core/middleware/built-in/static/hook.d.ts +2 -0
  279. package/dist/core/middleware/built-in/static/hook.js +12 -0
  280. package/dist/core/middleware/built-in/static/hook.js.map +1 -0
  281. package/dist/core/middleware/built-in/static/index.d.ts +6 -0
  282. package/dist/core/middleware/built-in/static/index.js +7 -0
  283. package/dist/core/middleware/built-in/static/index.js.map +1 -0
  284. package/dist/core/middleware/built-in/static/middleware.d.ts +18 -0
  285. package/dist/core/middleware/built-in/static/middleware.js +26 -0
  286. package/dist/core/middleware/built-in/static/middleware.js.map +1 -0
  287. package/dist/core/middleware/built-in/template/core.d.ts +19 -0
  288. package/dist/core/middleware/built-in/template/core.js +108 -0
  289. package/dist/core/middleware/built-in/template/core.js.map +1 -0
  290. package/dist/core/middleware/built-in/template/hook.d.ts +2 -0
  291. package/dist/core/middleware/built-in/template/hook.js +12 -0
  292. package/dist/core/middleware/built-in/template/hook.js.map +1 -0
  293. package/dist/core/middleware/built-in/template/index.d.ts +6 -0
  294. package/dist/core/middleware/built-in/template/index.js +7 -0
  295. package/dist/core/middleware/built-in/template/index.js.map +1 -0
  296. package/dist/core/middleware/built-in/template/middleware.d.ts +21 -0
  297. package/dist/core/middleware/built-in/template/middleware.js +27 -0
  298. package/dist/core/middleware/built-in/template/middleware.js.map +1 -0
  299. package/dist/core/middleware/built-in/upload/core.d.ts +29 -0
  300. package/dist/core/middleware/built-in/upload/core.js +66 -0
  301. package/dist/core/middleware/built-in/upload/core.js.map +1 -0
  302. package/dist/core/middleware/built-in/upload/hook.d.ts +2 -0
  303. package/dist/core/middleware/built-in/upload/hook.js +25 -0
  304. package/dist/core/middleware/built-in/upload/hook.js.map +1 -0
  305. package/dist/core/middleware/built-in/upload/index.d.ts +6 -0
  306. package/dist/core/middleware/built-in/upload/index.js +7 -0
  307. package/dist/core/middleware/built-in/upload/index.js.map +1 -0
  308. package/dist/core/middleware/built-in/upload/middleware.d.ts +18 -0
  309. package/dist/core/middleware/built-in/upload/middleware.js +41 -0
  310. package/dist/core/middleware/built-in/upload/middleware.js.map +1 -0
  311. package/dist/core/middleware/built-in/validation/core.js +4 -2
  312. package/dist/core/middleware/built-in/validation/core.js.map +1 -1
  313. package/dist/core/middleware/built-in/validation/middleware.js +2 -1
  314. package/dist/core/middleware/built-in/validation/middleware.js.map +1 -1
  315. package/dist/core/middleware/index.js +1 -0
  316. package/dist/core/middleware/index.js.map +1 -1
  317. package/dist/core/modules/auto-discovery.js +5 -4
  318. package/dist/core/modules/auto-discovery.js.map +1 -1
  319. package/dist/core/modules/modules.js.map +1 -1
  320. package/dist/core/networking/adapters/socketio-adapter.js +1 -1
  321. package/dist/core/networking/adapters/socketio-adapter.js.map +1 -1
  322. package/dist/core/networking/adapters/uws-adapter.js +61 -8
  323. package/dist/core/networking/adapters/uws-adapter.js.map +1 -1
  324. package/dist/core/networking/adapters/ws-adapter.js +61 -19
  325. package/dist/core/networking/adapters/ws-adapter.js.map +1 -1
  326. package/dist/core/networking/websocket-manager.js +2 -0
  327. package/dist/core/networking/websocket-manager.js.map +1 -1
  328. package/dist/core/pooling/object-pool-manager.js +12 -1
  329. package/dist/core/pooling/object-pool-manager.js.map +1 -1
  330. package/dist/core/queue/adapters/bull-adapter.d.ts +86 -0
  331. package/dist/core/queue/adapters/bull-adapter.js +330 -0
  332. package/dist/core/queue/adapters/bull-adapter.js.map +1 -0
  333. package/dist/core/queue/adapters/index.d.ts +9 -0
  334. package/dist/core/queue/adapters/index.js +10 -0
  335. package/dist/core/queue/adapters/index.js.map +1 -0
  336. package/dist/core/queue/adapters/kafka-adapter.d.ts +86 -0
  337. package/dist/core/queue/adapters/kafka-adapter.js +462 -0
  338. package/dist/core/queue/adapters/kafka-adapter.js.map +1 -0
  339. package/dist/core/queue/adapters/memory-adapter.d.ts +87 -0
  340. package/dist/core/queue/adapters/memory-adapter.js +415 -0
  341. package/dist/core/queue/adapters/memory-adapter.js.map +1 -0
  342. package/dist/core/queue/adapters/rabbitmq-adapter.d.ts +86 -0
  343. package/dist/core/queue/adapters/rabbitmq-adapter.js +436 -0
  344. package/dist/core/queue/adapters/rabbitmq-adapter.js.map +1 -0
  345. package/dist/core/queue/adapters/sqs-adapter.d.ts +102 -0
  346. package/dist/core/queue/adapters/sqs-adapter.js +522 -0
  347. package/dist/core/queue/adapters/sqs-adapter.js.map +1 -0
  348. package/dist/core/queue/index.d.ts +11 -0
  349. package/dist/core/queue/index.js +14 -0
  350. package/dist/core/queue/index.js.map +1 -0
  351. package/dist/core/queue/middleware/index.d.ts +7 -0
  352. package/dist/core/queue/middleware/index.js +8 -0
  353. package/dist/core/queue/middleware/index.js.map +1 -0
  354. package/dist/core/queue/middleware/monitoring.d.ts +84 -0
  355. package/dist/core/queue/middleware/monitoring.js +145 -0
  356. package/dist/core/queue/middleware/monitoring.js.map +1 -0
  357. package/dist/core/queue/middleware/priority.d.ts +61 -0
  358. package/dist/core/queue/middleware/priority.js +90 -0
  359. package/dist/core/queue/middleware/priority.js.map +1 -0
  360. package/dist/core/queue/middleware/rate-limit.d.ts +34 -0
  361. package/dist/core/queue/middleware/rate-limit.js +109 -0
  362. package/dist/core/queue/middleware/rate-limit.js.map +1 -0
  363. package/dist/core/queue/queue-adapter.d.ts +73 -0
  364. package/dist/core/queue/queue-adapter.js +20 -0
  365. package/dist/core/queue/queue-adapter.js.map +1 -0
  366. package/dist/core/queue/queue-manager.d.ts +92 -0
  367. package/dist/core/queue/queue-manager.js +327 -0
  368. package/dist/core/queue/queue-manager.js.map +1 -0
  369. package/dist/core/queue/types.d.ts +205 -0
  370. package/dist/core/queue/types.js +6 -0
  371. package/dist/core/queue/types.js.map +1 -0
  372. package/dist/core/routing/app-integration.d.ts +3 -3
  373. package/dist/core/routing/app-integration.js +1 -1
  374. package/dist/core/routing/app-integration.js.map +1 -1
  375. package/dist/core/routing/index.d.ts +1 -1
  376. package/dist/core/routing/index.js +42 -11
  377. package/dist/core/routing/index.js.map +1 -1
  378. package/dist/core/routing/radix-tree.d.ts +48 -0
  379. package/dist/core/routing/radix-tree.js +211 -0
  380. package/dist/core/routing/radix-tree.js.map +1 -0
  381. package/dist/core/routing/router.d.ts +10 -9
  382. package/dist/core/routing/router.js +3 -1
  383. package/dist/core/routing/router.js.map +1 -1
  384. package/dist/core/routing/unified-router.d.ts +18 -12
  385. package/dist/core/routing/unified-router.js +220 -163
  386. package/dist/core/routing/unified-router.js.map +1 -1
  387. package/dist/core/runtime/aws-lambda-adapter.js +21 -10
  388. package/dist/core/runtime/aws-lambda-adapter.js.map +1 -1
  389. package/dist/core/runtime/base-adapter.js +18 -8
  390. package/dist/core/runtime/base-adapter.js.map +1 -1
  391. package/dist/core/runtime/cloudflare-workers-adapter.js +36 -13
  392. package/dist/core/runtime/cloudflare-workers-adapter.js.map +1 -1
  393. package/dist/core/runtime/node-adapter.d.ts +1 -1
  394. package/dist/core/runtime/node-adapter.js +7 -4
  395. package/dist/core/runtime/node-adapter.js.map +1 -1
  396. package/dist/core/runtime/vercel-edge-adapter.js +17 -6
  397. package/dist/core/runtime/vercel-edge-adapter.js.map +1 -1
  398. package/dist/core/utilities/circuit-breaker.d.ts +9 -2
  399. package/dist/core/utilities/circuit-breaker.js +32 -3
  400. package/dist/core/utilities/circuit-breaker.js.map +1 -1
  401. package/dist/core/utilities/container.js +9 -1
  402. package/dist/core/utilities/container.js.map +1 -1
  403. package/dist/core/utilities/hooks.js +4 -0
  404. package/dist/core/utilities/hooks.js.map +1 -1
  405. package/dist/core/validation/index.js +6 -1
  406. package/dist/core/validation/index.js.map +1 -1
  407. package/dist/core/workers/facade.d.ts +74 -0
  408. package/dist/core/workers/facade.js +98 -0
  409. package/dist/core/workers/facade.js.map +1 -0
  410. package/dist/core/workers/index.d.ts +2 -0
  411. package/dist/core/workers/index.js +6 -0
  412. package/dist/core/workers/index.js.map +1 -0
  413. package/dist/core/workers/worker-manager.d.ts +124 -0
  414. package/dist/core/workers/worker-manager.js +299 -0
  415. package/dist/core/workers/worker-manager.js.map +1 -0
  416. package/dist/core/workers/worker.d.ts +1 -0
  417. package/dist/core/workers/worker.js +225 -0
  418. package/dist/core/workers/worker.js.map +1 -0
  419. package/dist/index.d.ts +14 -1
  420. package/dist/index.js +11 -1
  421. package/dist/index.js.map +1 -1
  422. package/dist/moro.d.ts +486 -1
  423. package/dist/moro.js +1194 -28
  424. package/dist/moro.js.map +1 -1
  425. package/dist/types/cache.d.ts +4 -0
  426. package/dist/types/config.d.ts +70 -0
  427. package/dist/types/core.d.ts +19 -1
  428. package/dist/types/events.d.ts +1 -1
  429. package/dist/types/events.js +1 -0
  430. package/dist/types/events.js.map +1 -1
  431. package/dist/types/module.d.ts +2 -2
  432. package/package.json +110 -16
@@ -1,8 +1,6 @@
1
1
  // src/core/http-server.ts
2
2
  import { createServer } from 'http';
3
3
  import * as zlib from 'zlib';
4
- import { createReadStream } from 'fs';
5
- import * as crypto from 'crypto';
6
4
  import { promisify } from 'util';
7
5
  import { createFrameworkLogger } from '../logger/index.js';
8
6
  import { PathMatcher } from '../routing/path-matcher.js';
@@ -38,40 +36,6 @@ export class MoroHttpServer {
38
36
  methodNotAllowed: Buffer.from('{"success":false,"error":"Method not allowed"}'),
39
37
  rateLimited: Buffer.from('{"success":false,"error":"Rate limit exceeded"}'),
40
38
  };
41
- // Buffer pool for zero-copy operations
42
- static BUFFER_SIZES = [64, 256, 1024, 4096, 16384];
43
- static BUFFER_POOLS = new Map();
44
- static {
45
- // Pre-allocate buffer pools for zero-allocation responses
46
- for (const size of MoroHttpServer.BUFFER_SIZES) {
47
- MoroHttpServer.BUFFER_POOLS.set(size, []);
48
- for (let i = 0; i < 50; i++) {
49
- // 50 buffers per size
50
- MoroHttpServer.BUFFER_POOLS.get(size).push(Buffer.allocUnsafe(size));
51
- }
52
- }
53
- }
54
- static getOptimalBuffer(size) {
55
- // Find the smallest buffer that fits
56
- for (const poolSize of MoroHttpServer.BUFFER_SIZES) {
57
- if (size <= poolSize) {
58
- const pool = MoroHttpServer.BUFFER_POOLS.get(poolSize);
59
- return pool.length > 0 ? pool.pop() : Buffer.allocUnsafe(poolSize);
60
- }
61
- }
62
- return Buffer.allocUnsafe(size);
63
- }
64
- static returnBuffer(buffer) {
65
- // Return buffer to appropriate pool
66
- const size = buffer.length;
67
- if (MoroHttpServer.BUFFER_POOLS.has(size)) {
68
- const pool = MoroHttpServer.BUFFER_POOLS.get(size);
69
- if (pool.length < 50) {
70
- // Don't let pools grow too large
71
- pool.push(buffer);
72
- }
73
- }
74
- }
75
39
  constructor() {
76
40
  this.server = createServer(this.handleRequest.bind(this));
77
41
  // Optimize server for high performance (conservative settings for compatibility)
@@ -147,6 +111,7 @@ export class MoroHttpServer {
147
111
  if (!this.routesBySegmentCount.has(segmentCount)) {
148
112
  this.routesBySegmentCount.set(segmentCount, []);
149
113
  }
114
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
150
115
  this.routesBySegmentCount.get(segmentCount).push(route);
151
116
  }
152
117
  }
@@ -165,6 +130,7 @@ export class MoroHttpServer {
165
130
  const originalParams = httpReq.params;
166
131
  try {
167
132
  // Optimized URL and query parsing with object pooling
133
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
168
134
  const urlString = req.url;
169
135
  const queryIndex = urlString.indexOf('?');
170
136
  if (queryIndex === -1) {
@@ -200,6 +166,7 @@ export class MoroHttpServer {
200
166
  return;
201
167
  }
202
168
  // Find matching route
169
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
203
170
  const route = this.findRoute(req.method, httpReq.path);
204
171
  if (!route) {
205
172
  // 404 response with pre-compiled buffer
@@ -282,11 +249,12 @@ export class MoroHttpServer {
282
249
  }
283
250
  }
284
251
  finally {
285
- // CRITICAL: Always release pooled objects back to the pool
252
+ // Always release pooled objects back to the pool
286
253
  // This prevents memory leaks and ensures consistent performance
287
- // Optimized: Check if object is empty without Object.keys()
254
+ // Check if object is empty without Object.keys()
288
255
  if (originalParams) {
289
256
  let isEmpty = true;
257
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
290
258
  for (const _key in originalParams) {
291
259
  isEmpty = false;
292
260
  break;
@@ -297,6 +265,7 @@ export class MoroHttpServer {
297
265
  }
298
266
  if (httpReq.params && httpReq.params !== originalParams) {
299
267
  let isEmpty = true;
268
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
300
269
  for (const _key in httpReq.params) {
301
270
  isEmpty = false;
302
271
  break;
@@ -308,9 +277,10 @@ export class MoroHttpServer {
308
277
  }
309
278
  // Additional cleanup on response completion to ensure objects are returned to pool
310
279
  res.once('finish', () => {
311
- // Optimized: Check if object is empty without Object.keys()
280
+ // Check if object is empty without Object.keys()
312
281
  if (originalParams) {
313
282
  let isEmpty = true;
283
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
314
284
  for (const _key in originalParams) {
315
285
  isEmpty = false;
316
286
  break;
@@ -321,6 +291,7 @@ export class MoroHttpServer {
321
291
  }
322
292
  if (httpReq.params && httpReq.params !== originalParams) {
323
293
  let isEmpty = true;
294
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
324
295
  for (const _key in httpReq.params) {
325
296
  isEmpty = false;
326
297
  break;
@@ -356,18 +327,45 @@ export class MoroHttpServer {
356
327
  streamLargeResponse(res, data) {
357
328
  res.setHeader('Content-Type', 'application/json; charset=utf-8');
358
329
  res.setHeader('Transfer-Encoding', 'chunked');
359
- // Stream the response in chunks
360
- const jsonString = JSON.stringify(data);
361
- const chunkSize = 8192; // 8KB chunks
362
- for (let i = 0; i < jsonString.length; i += chunkSize) {
363
- const chunk = jsonString.substring(i, i + chunkSize);
364
- res.write(chunk);
330
+ // Stream large JSON responses to prevent memory issues
331
+ if (Array.isArray(data) && data.length > 100) {
332
+ // Stream large arrays element by element
333
+ res.write('[');
334
+ // Stream each array element
335
+ let first = true;
336
+ for (const item of data) {
337
+ if (!first)
338
+ res.write(',');
339
+ res.write(JSON.stringify(item));
340
+ first = false;
341
+ }
342
+ // Write closing bracket and end
343
+ res.end(']');
344
+ }
345
+ else if (typeof data === 'object' && data !== null && Object.keys(data).length > 50) {
346
+ // For large objects, stream key-value pairs
347
+ res.write('{');
348
+ const keys = Object.keys(data);
349
+ let first = true;
350
+ for (const key of keys) {
351
+ if (!first)
352
+ res.write(',');
353
+ // Properly escape the key using JSON.stringify
354
+ res.write(`${JSON.stringify(key)}:${JSON.stringify(data[key])}`);
355
+ first = false;
356
+ }
357
+ res.end('}');
358
+ }
359
+ else {
360
+ // For smaller data, still avoid the old chunking approach
361
+ const jsonString = JSON.stringify(data);
362
+ res.end(jsonString);
365
363
  }
366
- res.end();
367
364
  }
368
365
  normalizePath(path) {
369
366
  // Check cache first
370
367
  if (this.pathNormalizationCache.has(path)) {
368
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
371
369
  return this.pathNormalizationCache.get(path);
372
370
  }
373
371
  // Normalization: remove trailing slash (except root), decode once
@@ -460,76 +458,21 @@ export class MoroHttpServer {
460
458
  httpRes.json = async (data) => {
461
459
  if (httpRes.headersSent)
462
460
  return;
463
- // JSON serialization with zero-copy buffers
464
- let jsonString;
465
- // Enhanced JSON optimization for common API patterns
466
- // Fast path for common 2-3 key objects without Object.keys() overhead
467
- if (data && typeof data === 'object' && 'success' in data) {
468
- // Check for common patterns using 'in' operator (faster than Object.keys for small objects)
469
- const hasData = 'data' in data;
470
- const hasError = 'error' in data;
471
- const hasTotal = 'total' in data;
472
- // Fast path: {success, data} - most common pattern
473
- if (hasData && !hasError && !hasTotal) {
474
- // Verify it's exactly 2 keys by checking no other common keys exist
475
- if (!('message' in data) && !('code' in data) && !('status' in data)) {
476
- jsonString = `{"success":${data.success},"data":${JSON.stringify(data.data)}}`;
477
- }
478
- else {
479
- jsonString = JSON.stringify(data);
480
- }
481
- }
482
- else if (hasError && !hasData && !hasTotal) {
483
- // Fast path: {success, error}
484
- if (!('message' in data) && !('code' in data) && !('status' in data)) {
485
- jsonString = `{"success":${data.success},"error":${JSON.stringify(data.error)}}`;
486
- }
487
- else {
488
- jsonString = JSON.stringify(data);
489
- }
490
- }
491
- else if (hasData && hasError && !hasTotal) {
492
- // Fast path: {success, data, error}
493
- if (!('message' in data) && !('code' in data) && !('status' in data)) {
494
- jsonString = `{"success":${data.success},"data":${JSON.stringify(data.data)},"error":${JSON.stringify(data.error)}}`;
495
- }
496
- else {
497
- jsonString = JSON.stringify(data);
498
- }
499
- }
500
- else if (hasData && hasTotal && !hasError) {
501
- // Fast path: {success, data, total}
502
- if (!('message' in data) && !('code' in data) && !('status' in data)) {
503
- jsonString = `{"success":${data.success},"data":${JSON.stringify(data.data)},"total":${data.total}}`;
504
- }
505
- else {
506
- jsonString = JSON.stringify(data);
507
- }
508
- }
509
- else {
510
- // Complex object - use standard JSON.stringify
511
- jsonString = JSON.stringify(data);
512
- }
513
- }
514
- else {
515
- jsonString = JSON.stringify(data);
516
- }
517
- // Use buffer pool for zero-allocation responses
518
- const estimatedSize = jsonString.length;
519
- if (estimatedSize > 32768) {
461
+ // Simple, optimized JSON serialization - let V8 handle the optimization
462
+ const jsonString = JSON.stringify(data);
463
+ // Large response check - stream if needed
464
+ if (jsonString.length > 32768) {
520
465
  // Large response - stream it
521
466
  return this.streamLargeResponse(httpRes, data);
522
467
  }
523
- const buffer = MoroHttpServer.getOptimalBuffer(estimatedSize);
524
- const actualLength = buffer.write(jsonString, 0, 'utf8');
525
- // Slice to actual size to avoid sending extra bytes
526
- const finalBuffer = actualLength === buffer.length ? buffer : buffer.subarray(0, actualLength);
468
+ // Use efficient buffer allocation - let Node.js handle optimization
469
+ const finalBuffer = Buffer.from(jsonString, 'utf8');
527
470
  // Optimized header setting - set multiple headers at once when possible
528
471
  const headers = {
529
472
  'Content-Type': 'application/json; charset=utf-8',
530
473
  };
531
474
  // Compression with buffer pool - EARLY EXIT if disabled or below threshold
532
- // CRITICAL: Only make this async if compression is actually happening
475
+ // Only make this async if compression is actually happening
533
476
  if (this.compressionEnabled && finalBuffer.length > this.compressionThreshold) {
534
477
  const acceptEncoding = httpRes.req.headers['accept-encoding'];
535
478
  if (acceptEncoding && acceptEncoding.includes('gzip')) {
@@ -540,8 +483,6 @@ export class MoroHttpServer {
540
483
  // Batch write all headers at once (50-100% faster)
541
484
  httpRes.writeHead(httpRes.statusCode || 200, headers);
542
485
  httpRes.end(compressed);
543
- // Return buffer to pool after response
544
- process.nextTick(() => MoroHttpServer.returnBuffer(buffer));
545
486
  });
546
487
  return;
547
488
  }
@@ -553,8 +494,6 @@ export class MoroHttpServer {
553
494
  // Batch write all headers at once
554
495
  httpRes.writeHead(httpRes.statusCode || 200, headers);
555
496
  httpRes.end(compressed);
556
- // Return buffer to pool after response
557
- process.nextTick(() => MoroHttpServer.returnBuffer(buffer));
558
497
  });
559
498
  return;
560
499
  }
@@ -564,8 +503,6 @@ export class MoroHttpServer {
564
503
  // Batch write all headers at once
565
504
  httpRes.writeHead(httpRes.statusCode || 200, headers);
566
505
  httpRes.end(finalBuffer);
567
- // Return buffer to pool after response (zero-copy achievement!)
568
- process.nextTick(() => MoroHttpServer.returnBuffer(buffer));
569
506
  };
570
507
  httpRes.send = (data) => {
571
508
  if (httpRes.headersSent)
@@ -626,15 +563,31 @@ export class MoroHttpServer {
626
563
  if (options.path)
627
564
  cookieString += `; Path=${options.path}`;
628
565
  const existingCookies = httpRes.getHeader('Set-Cookie') || [];
566
+ // Avoid spread operator - direct array manipulation
629
567
  const cookies = Array.isArray(existingCookies)
630
- ? [...existingCookies]
568
+ ? existingCookies
631
569
  : [existingCookies];
632
570
  cookies.push(cookieString);
633
571
  httpRes.setHeader('Set-Cookie', cookies);
634
572
  return httpRes;
635
573
  };
636
574
  httpRes.clearCookie = (name, options = {}) => {
637
- const clearOptions = { ...options, expires: new Date(0), maxAge: 0 };
575
+ // Avoid spread operator - manually set properties
576
+ const clearOptions = {
577
+ expires: new Date(0),
578
+ maxAge: 0,
579
+ };
580
+ // Copy other options manually
581
+ if (options.path !== undefined)
582
+ clearOptions.path = options.path;
583
+ if (options.domain !== undefined)
584
+ clearOptions.domain = options.domain;
585
+ if (options.httpOnly !== undefined)
586
+ clearOptions.httpOnly = options.httpOnly;
587
+ if (options.secure !== undefined)
588
+ clearOptions.secure = options.secure;
589
+ if (options.sameSite !== undefined)
590
+ clearOptions.sameSite = options.sameSite;
638
591
  return httpRes.cookie(name, '', clearOptions);
639
592
  };
640
593
  httpRes.redirect = (url, status = 302) => {
@@ -665,7 +618,7 @@ export class MoroHttpServer {
665
618
  httpRes.setHeader('Cache-Control', 'public, max-age=31536000'); // 1 year for static files
666
619
  httpRes.end(data);
667
620
  }
668
- catch (error) {
621
+ catch {
669
622
  httpRes.status(404).json({ success: false, error: 'File not found' });
670
623
  }
671
624
  };
@@ -676,15 +629,17 @@ export class MoroHttpServer {
676
629
  // Note: removeHeader is inherited from ServerResponse, we don't override it
677
630
  httpRes.setBulkHeaders = (headers) => {
678
631
  if (httpRes.headersSent) {
632
+ // Only enumerate keys for warning if headers were already sent
633
+ const attemptedHeaderKeys = [];
634
+ for (const key in headers) {
635
+ attemptedHeaderKeys.push(key);
636
+ }
679
637
  this.logger.warn('Cannot set headers - headers already sent', 'HeaderWarning', {
680
- attemptedHeaders: Object.keys(headers),
638
+ attemptedHeaders: attemptedHeaderKeys,
681
639
  });
682
640
  return httpRes;
683
641
  }
684
- const headerKeys = Object.keys(headers);
685
- const headerKeysLen = headerKeys.length;
686
- for (let i = 0; i < headerKeysLen; i++) {
687
- const key = headerKeys[i];
642
+ for (const key in headers) {
688
643
  httpRes.setHeader(key, headers[key]);
689
644
  }
690
645
  return httpRes;
@@ -753,10 +708,18 @@ export class MoroHttpServer {
753
708
  return mimeType;
754
709
  }
755
710
  async parseBody(req) {
711
+ const contentType = req.headers['content-type'] || '';
712
+ const contentLength = parseInt(req.headers['content-length'] || '0');
713
+ const maxSize = 10 * 1024 * 1024; // 10MB limit
714
+ // For very large payloads, return a streaming interface instead of buffering
715
+ if (contentLength > maxSize / 2) {
716
+ // Stream for payloads > 5MB
717
+ return this.createStreamingBodyParser(req, contentType, maxSize);
718
+ }
719
+ // Standard buffered parsing for smaller payloads
756
720
  return new Promise((resolve, reject) => {
757
721
  const chunks = [];
758
722
  let totalLength = 0;
759
- const maxSize = 10 * 1024 * 1024; // 10MB limit
760
723
  req.on('data', (chunk) => {
761
724
  totalLength += chunk.length;
762
725
  if (totalLength > maxSize) {
@@ -768,7 +731,6 @@ export class MoroHttpServer {
768
731
  req.on('end', () => {
769
732
  try {
770
733
  const body = Buffer.concat(chunks);
771
- const contentType = req.headers['content-type'] || '';
772
734
  if (contentType.includes('application/json')) {
773
735
  resolve(JSON.parse(body.toString()));
774
736
  }
@@ -789,6 +751,132 @@ export class MoroHttpServer {
789
751
  req.on('error', reject);
790
752
  });
791
753
  }
754
+ /**
755
+ * Create a streaming body parser for large payloads
756
+ * Returns a streaming interface instead of buffering
757
+ */
758
+ createStreamingBodyParser(req, contentType, maxSize) {
759
+ let totalLength = 0;
760
+ const chunks = [];
761
+ return new Promise((resolve, reject) => {
762
+ const streamParser = {
763
+ // Streaming JSON parser for large JSON payloads
764
+ json: () => this.streamJsonParse(req, maxSize),
765
+ // Streaming form data parser
766
+ form: () => this.streamFormParse(req, maxSize),
767
+ // Raw stream access
768
+ stream: () => ({
769
+ onData: (callback) => {
770
+ req.on('data', (chunk) => {
771
+ totalLength += chunk.length;
772
+ if (totalLength > maxSize) {
773
+ reject(new Error('Request body too large'));
774
+ return;
775
+ }
776
+ callback(chunk);
777
+ });
778
+ },
779
+ onEnd: (callback) => {
780
+ req.on('end', callback);
781
+ },
782
+ onError: (callback) => {
783
+ req.on('error', callback);
784
+ },
785
+ }),
786
+ // Traditional buffered parsing (fallback)
787
+ buffer: async () => {
788
+ return new Promise((resolveBuffer, rejectBuffer) => {
789
+ req.on('data', (chunk) => {
790
+ totalLength += chunk.length;
791
+ if (totalLength > maxSize) {
792
+ rejectBuffer(new Error('Request body too large'));
793
+ return;
794
+ }
795
+ chunks.push(chunk);
796
+ });
797
+ req.on('end', () => {
798
+ try {
799
+ const body = Buffer.concat(chunks);
800
+ if (contentType.includes('application/json')) {
801
+ resolveBuffer(JSON.parse(body.toString()));
802
+ }
803
+ else {
804
+ resolveBuffer(body.toString());
805
+ }
806
+ }
807
+ catch (error) {
808
+ rejectBuffer(error);
809
+ }
810
+ });
811
+ req.on('error', rejectBuffer);
812
+ });
813
+ },
814
+ };
815
+ // Auto-detect and return appropriate parser
816
+ if (contentType.includes('application/json')) {
817
+ resolve({ type: 'json', parser: streamParser.json });
818
+ }
819
+ else if (contentType.includes('application/x-www-form-urlencoded')) {
820
+ resolve({ type: 'form', parser: streamParser.form });
821
+ }
822
+ else {
823
+ resolve({ type: 'stream', parser: streamParser.stream });
824
+ }
825
+ });
826
+ }
827
+ /**
828
+ * Streaming JSON parser for large payloads
829
+ */
830
+ async streamJsonParse(req, maxSize) {
831
+ return new Promise((resolve, reject) => {
832
+ let jsonString = '';
833
+ let totalLength = 0;
834
+ req.on('data', (chunk) => {
835
+ totalLength += chunk.length;
836
+ if (totalLength > maxSize) {
837
+ reject(new Error('Request body too large'));
838
+ return;
839
+ }
840
+ jsonString += chunk.toString();
841
+ });
842
+ req.on('end', () => {
843
+ try {
844
+ // For very large JSON, consider streaming JSON parsing in the future
845
+ resolve(JSON.parse(jsonString));
846
+ }
847
+ catch (error) {
848
+ reject(error);
849
+ }
850
+ });
851
+ req.on('error', reject);
852
+ });
853
+ }
854
+ /**
855
+ * Streaming form data parser
856
+ */
857
+ async streamFormParse(req, maxSize) {
858
+ return new Promise((resolve, reject) => {
859
+ let formData = '';
860
+ let totalLength = 0;
861
+ req.on('data', (chunk) => {
862
+ totalLength += chunk.length;
863
+ if (totalLength > maxSize) {
864
+ reject(new Error('Request body too large'));
865
+ return;
866
+ }
867
+ formData += chunk.toString();
868
+ });
869
+ req.on('end', () => {
870
+ try {
871
+ resolve(this.parseUrlEncoded(formData));
872
+ }
873
+ catch (error) {
874
+ reject(error);
875
+ }
876
+ });
877
+ req.on('error', reject);
878
+ });
879
+ }
792
880
  parseMultipart(buffer, contentType) {
793
881
  const boundary = contentType.split('boundary=')[1];
794
882
  if (!boundary) {
@@ -875,6 +963,7 @@ export class MoroHttpServer {
875
963
  const cacheKey = `${method}:${path}`;
876
964
  // Check cache first (hot path optimization) - BEFORE any other work
877
965
  if (this.routeCache.has(cacheKey)) {
966
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
878
967
  return this.routeCache.get(cacheKey);
879
968
  }
880
969
  // Normalize path for consistent matching (only if not cached)
@@ -882,9 +971,10 @@ export class MoroHttpServer {
882
971
  const normalizedCacheKey = normalizedPath !== path ? `${method}:${normalizedPath}` : cacheKey;
883
972
  // Check cache again with normalized path
884
973
  if (normalizedPath !== path && this.routeCache.has(normalizedCacheKey)) {
974
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
885
975
  return this.routeCache.get(normalizedCacheKey);
886
976
  }
887
- // Phase 1: O(1) static route lookup
977
+ // O(1) static route lookup
888
978
  const staticRoute = this.staticRoutes.get(normalizedCacheKey);
889
979
  if (staticRoute) {
890
980
  this.routeCache.set(normalizedCacheKey, staticRoute);
@@ -893,7 +983,7 @@ export class MoroHttpServer {
893
983
  }
894
984
  return staticRoute;
895
985
  }
896
- // Phase 2: Optimized dynamic route matching by segment count
986
+ // Dynamic route matching by segment count
897
987
  let route = null;
898
988
  const dynamicRoutesLen = this.dynamicRoutes.length;
899
989
  if (dynamicRoutesLen > 0) {
@@ -997,659 +1087,4 @@ export class MoroHttpServer {
997
1087
  };
998
1088
  }
999
1089
  }
1000
- // Built-in middleware
1001
- export const middleware = {
1002
- cors: (options = {}) => {
1003
- return (req, res, next) => {
1004
- res.setHeader('Access-Control-Allow-Origin', options.origin || '*');
1005
- res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
1006
- res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization');
1007
- if (options.credentials) {
1008
- res.setHeader('Access-Control-Allow-Credentials', 'true');
1009
- }
1010
- if (req.method === 'OPTIONS') {
1011
- res.status(200).send('');
1012
- return;
1013
- }
1014
- next();
1015
- };
1016
- },
1017
- helmet: () => {
1018
- return (req, res, next) => {
1019
- res.setHeader('X-Content-Type-Options', 'nosniff');
1020
- res.setHeader('X-Frame-Options', 'DENY');
1021
- res.setHeader('X-XSS-Protection', '1; mode=block');
1022
- res.setHeader('Referrer-Policy', 'strict-origin-when-cross-origin');
1023
- res.setHeader('Strict-Transport-Security', 'max-age=31536000; includeSubDomains');
1024
- res.setHeader('Content-Security-Policy', "default-src 'self'");
1025
- next();
1026
- };
1027
- },
1028
- compression: (options = {}) => {
1029
- const threshold = options.threshold || 1024;
1030
- const level = options.level || 6;
1031
- return (req, res, next) => {
1032
- const acceptEncoding = req.headers['accept-encoding'] || '';
1033
- // Override res.json to compress responses
1034
- const originalJson = res.json;
1035
- const originalSend = res.send;
1036
- const compressResponse = (data, isJson = false) => {
1037
- const content = isJson ? JSON.stringify(data) : data;
1038
- const buffer = Buffer.from(content);
1039
- if (buffer.length < threshold) {
1040
- return isJson ? originalJson.call(res, data) : originalSend.call(res, data);
1041
- }
1042
- if (acceptEncoding.includes('gzip')) {
1043
- zlib.gzip(buffer, { level }, (err, compressed) => {
1044
- if (err) {
1045
- return isJson ? originalJson.call(res, data) : originalSend.call(res, data);
1046
- }
1047
- if (!res.headersSent) {
1048
- res.setHeader('Content-Encoding', 'gzip');
1049
- res.setHeader('Content-Length', compressed.length);
1050
- }
1051
- res.end(compressed);
1052
- });
1053
- }
1054
- else if (acceptEncoding.includes('deflate')) {
1055
- zlib.deflate(buffer, { level }, (err, compressed) => {
1056
- if (err) {
1057
- return isJson ? originalJson.call(res, data) : originalSend.call(res, data);
1058
- }
1059
- if (!res.headersSent) {
1060
- res.setHeader('Content-Encoding', 'deflate');
1061
- res.setHeader('Content-Length', compressed.length);
1062
- }
1063
- res.end(compressed);
1064
- });
1065
- }
1066
- else {
1067
- return isJson ? originalJson.call(res, data) : originalSend.call(res, data);
1068
- }
1069
- };
1070
- res.json = function (data) {
1071
- // Ensure charset is set for Safari compatibility
1072
- this.setHeader('Content-Type', 'application/json; charset=utf-8');
1073
- compressResponse(data, true);
1074
- return this;
1075
- };
1076
- res.send = function (data) {
1077
- compressResponse(data, false);
1078
- return this;
1079
- };
1080
- next();
1081
- };
1082
- },
1083
- requestLogger: () => {
1084
- return (req, res, next) => {
1085
- const start = Date.now();
1086
- res.on('finish', () => {
1087
- const duration = Date.now() - start;
1088
- // Request completed - logged by framework
1089
- });
1090
- next();
1091
- };
1092
- },
1093
- bodySize: (options = {}) => {
1094
- const limit = options.limit || '10mb';
1095
- const limitBytes = parseSize(limit);
1096
- return (req, res, next) => {
1097
- const contentLength = parseInt(req.headers['content-length'] || '0');
1098
- if (contentLength > limitBytes) {
1099
- res.status(413).json({
1100
- success: false,
1101
- error: 'Request entity too large',
1102
- limit: limit,
1103
- });
1104
- return;
1105
- }
1106
- next();
1107
- };
1108
- },
1109
- static: (options) => {
1110
- return async (req, res, next) => {
1111
- // Only handle GET and HEAD requests
1112
- if (req.method !== 'GET' && req.method !== 'HEAD') {
1113
- next();
1114
- return;
1115
- }
1116
- try {
1117
- const fs = await import('fs/promises');
1118
- const path = await import('path');
1119
- const crypto = await import('crypto');
1120
- let filePath = path.join(options.root, req.path);
1121
- // Security: prevent directory traversal
1122
- if (!filePath.startsWith(path.resolve(options.root))) {
1123
- res.status(403).json({ success: false, error: 'Forbidden' });
1124
- return;
1125
- }
1126
- // Handle dotfiles
1127
- const basename = path.basename(filePath);
1128
- if (basename.startsWith('.')) {
1129
- if (options.dotfiles === 'deny') {
1130
- res.status(403).json({ success: false, error: 'Forbidden' });
1131
- return;
1132
- }
1133
- else if (options.dotfiles === 'ignore') {
1134
- next();
1135
- return;
1136
- }
1137
- }
1138
- let stats;
1139
- try {
1140
- stats = await fs.stat(filePath);
1141
- }
1142
- catch (error) {
1143
- next(); // File not found, let other middleware handle
1144
- return;
1145
- }
1146
- // Handle directories
1147
- if (stats.isDirectory()) {
1148
- const indexFiles = options.index || ['index.html', 'index.htm'];
1149
- let indexFound = false;
1150
- for (const indexFile of indexFiles) {
1151
- const indexPath = path.join(filePath, indexFile);
1152
- try {
1153
- const indexStats = await fs.stat(indexPath);
1154
- if (indexStats.isFile()) {
1155
- filePath = indexPath;
1156
- stats = indexStats;
1157
- indexFound = true;
1158
- break;
1159
- }
1160
- }
1161
- catch (error) {
1162
- // Continue to next index file
1163
- }
1164
- }
1165
- if (!indexFound) {
1166
- next();
1167
- return;
1168
- }
1169
- }
1170
- // Set headers with proper mime type and charset
1171
- const ext = path.extname(filePath);
1172
- const mimeTypes = {
1173
- '.html': 'text/html',
1174
- '.css': 'text/css',
1175
- '.js': 'application/javascript',
1176
- '.json': 'application/json',
1177
- '.png': 'image/png',
1178
- '.jpg': 'image/jpeg',
1179
- '.jpeg': 'image/jpeg',
1180
- '.gif': 'image/gif',
1181
- '.svg': 'image/svg+xml',
1182
- '.ico': 'image/x-icon',
1183
- '.pdf': 'application/pdf',
1184
- '.txt': 'text/plain',
1185
- '.xml': 'application/xml',
1186
- };
1187
- const baseMimeType = mimeTypes[ext.toLowerCase()] || 'application/octet-stream';
1188
- // Add charset for text-based files
1189
- const textTypes = [
1190
- 'text/',
1191
- 'application/json',
1192
- 'application/javascript',
1193
- 'application/xml',
1194
- 'image/svg+xml',
1195
- ];
1196
- const needsCharset = textTypes.some(type => baseMimeType.startsWith(type));
1197
- const contentType = needsCharset ? `${baseMimeType}; charset=utf-8` : baseMimeType;
1198
- res.setHeader('Content-Type', contentType);
1199
- res.setHeader('Content-Length', stats.size);
1200
- // Cache headers
1201
- if (options.maxAge) {
1202
- res.setHeader('Cache-Control', `public, max-age=${options.maxAge}`);
1203
- }
1204
- // ETag support
1205
- if (options.etag !== false) {
1206
- const etag = crypto
1207
- .createHash('md5')
1208
- .update(`${stats.mtime.getTime()}-${stats.size}`)
1209
- .digest('hex');
1210
- res.setHeader('ETag', `"${etag}"`);
1211
- // Handle conditional requests
1212
- const ifNoneMatch = req.headers['if-none-match'];
1213
- if (ifNoneMatch === `"${etag}"`) {
1214
- res.statusCode = 304;
1215
- res.end();
1216
- return;
1217
- }
1218
- }
1219
- // Handle HEAD requests
1220
- if (req.method === 'HEAD') {
1221
- res.end();
1222
- return;
1223
- }
1224
- // Send file
1225
- const data = await fs.readFile(filePath);
1226
- res.end(data);
1227
- }
1228
- catch (error) {
1229
- res.status(500).json({ success: false, error: 'Internal server error' });
1230
- }
1231
- };
1232
- },
1233
- upload: (options = {}) => {
1234
- return (req, res, next) => {
1235
- const contentType = req.headers['content-type'] || '';
1236
- if (!contentType.includes('multipart/form-data')) {
1237
- next();
1238
- return;
1239
- }
1240
- // File upload handling is now built into parseBody method
1241
- // This middleware can add additional validation
1242
- if (req.body && req.body.files) {
1243
- const files = req.body.files;
1244
- const maxFileSize = options.maxFileSize || 5 * 1024 * 1024; // 5MB default
1245
- const maxFiles = options.maxFiles || 10;
1246
- const allowedTypes = options.allowedTypes;
1247
- // Validate file count
1248
- if (Object.keys(files).length > maxFiles) {
1249
- res.status(400).json({
1250
- success: false,
1251
- error: `Too many files. Maximum ${maxFiles} allowed.`,
1252
- });
1253
- return;
1254
- }
1255
- // Validate each file
1256
- for (const [fieldName, file] of Object.entries(files)) {
1257
- const fileData = file;
1258
- // Validate file size
1259
- if (fileData.size > maxFileSize) {
1260
- res.status(400).json({
1261
- success: false,
1262
- error: `File ${fileData.filename} is too large. Maximum ${maxFileSize} bytes allowed.`,
1263
- });
1264
- return;
1265
- }
1266
- // Validate file type
1267
- if (allowedTypes && !allowedTypes.includes(fileData.mimetype)) {
1268
- res.status(400).json({
1269
- success: false,
1270
- error: `File type ${fileData.mimetype} not allowed.`,
1271
- });
1272
- return;
1273
- }
1274
- }
1275
- // Store files in request for easy access
1276
- req.files = files;
1277
- }
1278
- next();
1279
- };
1280
- },
1281
- template: (options) => {
1282
- const templateCache = new Map();
1283
- return async (req, res, next) => {
1284
- // Add render method to response
1285
- res.render = async (template, data = {}) => {
1286
- try {
1287
- const fs = await import('fs/promises');
1288
- const path = await import('path');
1289
- const templatePath = path.join(options.views, `${template}.html`);
1290
- let templateContent;
1291
- // Check cache first
1292
- if (options.cache && templateCache.has(templatePath)) {
1293
- templateContent = templateCache.get(templatePath);
1294
- }
1295
- else {
1296
- templateContent = await fs.readFile(templatePath, 'utf-8');
1297
- if (options.cache) {
1298
- templateCache.set(templatePath, templateContent);
1299
- }
1300
- }
1301
- // Simple template engine - replace {{variable}} with values
1302
- let rendered = templateContent;
1303
- // Handle basic variable substitution
1304
- rendered = rendered.replace(/\{\{(\w+)\}\}/g, (match, key) => {
1305
- return data[key] !== undefined ? String(data[key]) : match;
1306
- });
1307
- // Handle nested object properties like {{user.name}}
1308
- rendered = rendered.replace(/\{\{([\w.]+)\}\}/g, (match, key) => {
1309
- const value = key.split('.').reduce((obj, prop) => obj?.[prop], data);
1310
- return value !== undefined ? String(value) : match;
1311
- });
1312
- // Handle loops: {{#each items}}{{name}}{{/each}}
1313
- rendered = rendered.replace(/\{\{#each (\w+)\}\}(.*?)\{\{\/each\}\}/gs, (match, arrayKey, template) => {
1314
- const array = data[arrayKey];
1315
- if (!Array.isArray(array))
1316
- return '';
1317
- return array
1318
- .map(item => {
1319
- let itemTemplate = template;
1320
- // Replace variables in the loop template
1321
- itemTemplate = itemTemplate.replace(/\{\{(\w+)\}\}/g, (match, key) => {
1322
- return item[key] !== undefined ? String(item[key]) : match;
1323
- });
1324
- return itemTemplate;
1325
- })
1326
- .join('');
1327
- });
1328
- // Handle conditionals: {{#if condition}}content{{/if}}
1329
- rendered = rendered.replace(/\{\{#if (\w+)\}\}(.*?)\{\{\/if\}\}/gs, (match, conditionKey, content) => {
1330
- const condition = data[conditionKey];
1331
- return condition ? content : '';
1332
- });
1333
- // Handle layout
1334
- if (options.defaultLayout) {
1335
- const layoutPath = path.join(options.views, 'layouts', `${options.defaultLayout}.html`);
1336
- try {
1337
- let layoutContent;
1338
- if (options.cache && templateCache.has(layoutPath)) {
1339
- layoutContent = templateCache.get(layoutPath);
1340
- }
1341
- else {
1342
- layoutContent = await fs.readFile(layoutPath, 'utf-8');
1343
- if (options.cache) {
1344
- templateCache.set(layoutPath, layoutContent);
1345
- }
1346
- }
1347
- rendered = layoutContent.replace(/\{\{body\}\}/, rendered);
1348
- }
1349
- catch (error) {
1350
- // Layout not found, use template as-is
1351
- }
1352
- }
1353
- res.setHeader('Content-Type', 'text/html');
1354
- res.end(rendered);
1355
- }
1356
- catch (error) {
1357
- res.status(500).json({ success: false, error: 'Template rendering failed' });
1358
- }
1359
- };
1360
- next();
1361
- };
1362
- },
1363
- // HTTP/2 Server Push middleware
1364
- http2Push: (options = {}) => {
1365
- return (req, res, next) => {
1366
- // Add HTTP/2 push capability to response
1367
- res.push = (path, options = {}) => {
1368
- // Check if HTTP/2 is supported
1369
- if (req.httpVersion === '2.0' && res.stream && res.stream.pushAllowed) {
1370
- try {
1371
- const pushStream = res.stream.pushStream({
1372
- ':method': 'GET',
1373
- ':path': path,
1374
- ...options.headers,
1375
- });
1376
- if (pushStream) {
1377
- // Handle push stream
1378
- return pushStream;
1379
- }
1380
- }
1381
- catch (error) {
1382
- // Push failed, continue normally
1383
- }
1384
- }
1385
- return null;
1386
- };
1387
- // Auto-push configured resources
1388
- if (options.resources && (!options.condition || options.condition(req))) {
1389
- for (const resource of options.resources) {
1390
- res.push?.(resource.path, {
1391
- headers: {
1392
- 'content-type': resource.type || 'text/plain',
1393
- },
1394
- });
1395
- }
1396
- }
1397
- next();
1398
- };
1399
- },
1400
- // Server-Sent Events middleware
1401
- sse: (options = {}) => {
1402
- return (req, res, next) => {
1403
- // Only handle SSE requests
1404
- if (req.headers.accept?.includes('text/event-stream')) {
1405
- // Set SSE headers
1406
- if (!res.headersSent) {
1407
- res.writeHead(200, {
1408
- 'Content-Type': 'text/event-stream',
1409
- 'Cache-Control': 'no-cache',
1410
- Connection: 'keep-alive',
1411
- 'Access-Control-Allow-Origin': options.cors ? '*' : undefined,
1412
- 'Access-Control-Allow-Headers': options.cors ? 'Cache-Control' : undefined,
1413
- });
1414
- }
1415
- // Add SSE methods to response
1416
- res.sendEvent = (data, event, id) => {
1417
- if (id)
1418
- res.write(`id: ${id}\n`);
1419
- if (event)
1420
- res.write(`event: ${event}\n`);
1421
- res.write(`data: ${typeof data === 'string' ? data : JSON.stringify(data)}\n\n`);
1422
- };
1423
- res.sendComment = (comment) => {
1424
- res.write(`: ${comment}\n\n`);
1425
- };
1426
- res.sendRetry = (ms) => {
1427
- res.write(`retry: ${ms}\n\n`);
1428
- };
1429
- // Set up heartbeat if configured
1430
- let heartbeatInterval = null;
1431
- if (options.heartbeat) {
1432
- heartbeatInterval = setInterval(() => {
1433
- res.sendComment('heartbeat');
1434
- }, options.heartbeat);
1435
- }
1436
- // Set retry if configured
1437
- if (options.retry) {
1438
- res.sendRetry(options.retry);
1439
- }
1440
- // Clean up on close
1441
- req.on('close', () => {
1442
- if (heartbeatInterval) {
1443
- clearInterval(heartbeatInterval);
1444
- }
1445
- });
1446
- // Don't call next() - this middleware handles the response
1447
- return;
1448
- }
1449
- next();
1450
- };
1451
- },
1452
- // Range request middleware for streaming
1453
- range: (options = {}) => {
1454
- return async (req, res, next) => {
1455
- // Add range support to response
1456
- res.sendRange = async (filePath, stats) => {
1457
- try {
1458
- const fs = await import('fs/promises');
1459
- const path = await import('path');
1460
- if (!stats) {
1461
- stats = await fs.stat(filePath);
1462
- }
1463
- const fileSize = stats.size;
1464
- const range = req.headers.range;
1465
- // Set Accept-Ranges header
1466
- res.setHeader('Accept-Ranges', options.acceptRanges || 'bytes');
1467
- if (!range) {
1468
- // No range requested, send entire file
1469
- res.setHeader('Content-Length', fileSize);
1470
- const data = await fs.readFile(filePath);
1471
- res.end(data);
1472
- return;
1473
- }
1474
- // Parse range header
1475
- const ranges = range
1476
- .replace(/bytes=/, '')
1477
- .split(',')
1478
- .map(r => {
1479
- const [start, end] = r.split('-');
1480
- return {
1481
- start: start ? parseInt(start) : 0,
1482
- end: end ? parseInt(end) : fileSize - 1,
1483
- };
1484
- });
1485
- // Validate ranges
1486
- if (options.maxRanges && ranges.length > options.maxRanges) {
1487
- res.status(416).json({ success: false, error: 'Too many ranges' });
1488
- return;
1489
- }
1490
- if (ranges.length === 1) {
1491
- // Single range
1492
- const { start, end } = ranges[0];
1493
- const chunkSize = end - start + 1;
1494
- if (start >= fileSize || end >= fileSize) {
1495
- res.status(416);
1496
- res.setHeader('Content-Range', `bytes */${fileSize}`);
1497
- res.json({ success: false, error: 'Range not satisfiable' });
1498
- return;
1499
- }
1500
- res.status(206);
1501
- res.setHeader('Content-Range', `bytes ${start}-${end}/${fileSize}`);
1502
- res.setHeader('Content-Length', chunkSize);
1503
- // Stream the range
1504
- const stream = createReadStream(filePath, {
1505
- start,
1506
- end,
1507
- });
1508
- stream.pipe(res);
1509
- }
1510
- else {
1511
- // Multiple ranges - multipart response
1512
- const boundary = 'MULTIPART_BYTERANGES';
1513
- res.status(206);
1514
- res.setHeader('Content-Type', `multipart/byteranges; boundary=${boundary}`);
1515
- for (const { start, end } of ranges) {
1516
- if (start >= fileSize || end >= fileSize)
1517
- continue;
1518
- const chunkSize = end - start + 1;
1519
- res.write(`\r\n--${boundary}\r\n`);
1520
- res.write(`Content-Range: bytes ${start}-${end}/${fileSize}\r\n\r\n`);
1521
- const stream = createReadStream(filePath, {
1522
- start,
1523
- end,
1524
- });
1525
- await new Promise(resolve => {
1526
- stream.on('end', () => resolve());
1527
- stream.pipe(res, { end: false });
1528
- });
1529
- }
1530
- res.write(`\r\n--${boundary}--\r\n`);
1531
- res.end();
1532
- }
1533
- }
1534
- catch (error) {
1535
- res.status(500).json({ success: false, error: 'Range request failed' });
1536
- }
1537
- };
1538
- next();
1539
- };
1540
- },
1541
- // CSRF Protection middleware
1542
- csrf: (options = {}) => {
1543
- const secret = options.secret || 'moro-csrf-secret';
1544
- const tokenLength = options.tokenLength || 32;
1545
- const cookieName = options.cookieName || '_csrf';
1546
- const headerName = options.headerName || 'x-csrf-token';
1547
- const ignoreMethods = options.ignoreMethods || ['GET', 'HEAD', 'OPTIONS'];
1548
- const generateToken = () => {
1549
- return crypto.randomBytes(tokenLength).toString('hex');
1550
- };
1551
- const verifyToken = (token, sessionToken) => {
1552
- return token && sessionToken && token === sessionToken;
1553
- };
1554
- return (req, res, next) => {
1555
- // Add CSRF token generation method
1556
- req.csrfToken = () => {
1557
- if (!req._csrfToken) {
1558
- req._csrfToken = generateToken();
1559
- // Set token in cookie
1560
- res.cookie(cookieName, req._csrfToken, {
1561
- httpOnly: true,
1562
- sameSite: options.sameSite !== false ? 'strict' : undefined,
1563
- secure: req.headers['x-forwarded-proto'] === 'https' || req.socket.encrypted,
1564
- });
1565
- }
1566
- return req._csrfToken;
1567
- };
1568
- // Skip verification for safe methods
1569
- if (ignoreMethods.includes(req.method)) {
1570
- next();
1571
- return;
1572
- }
1573
- // Get token from header or body
1574
- const token = req.headers[headerName] || (req.body && req.body._csrf) || (req.query && req.query._csrf);
1575
- // Get session token from cookie
1576
- const sessionToken = req.cookies?.[cookieName];
1577
- if (!verifyToken(token, sessionToken || '')) {
1578
- res.status(403).json({
1579
- success: false,
1580
- error: 'Invalid CSRF token',
1581
- code: 'CSRF_TOKEN_MISMATCH',
1582
- });
1583
- return;
1584
- }
1585
- next();
1586
- };
1587
- },
1588
- // Content Security Policy middleware
1589
- csp: (options = {}) => {
1590
- return (req, res, next) => {
1591
- const directives = options.directives || {
1592
- defaultSrc: ["'self'"],
1593
- scriptSrc: ["'self'"],
1594
- styleSrc: ["'self'", "'unsafe-inline'"],
1595
- imgSrc: ["'self'", 'data:', 'https:'],
1596
- connectSrc: ["'self'"],
1597
- fontSrc: ["'self'"],
1598
- objectSrc: ["'none'"],
1599
- mediaSrc: ["'self'"],
1600
- frameSrc: ["'none'"],
1601
- };
1602
- // Generate nonce if requested
1603
- let nonce;
1604
- if (options.nonce) {
1605
- nonce = crypto.randomBytes(16).toString('base64');
1606
- req.cspNonce = nonce;
1607
- }
1608
- // Build CSP header value
1609
- const cspParts = [];
1610
- for (const [directive, sources] of Object.entries(directives)) {
1611
- if (directive === 'upgradeInsecureRequests' && sources === true) {
1612
- cspParts.push('upgrade-insecure-requests');
1613
- }
1614
- else if (directive === 'blockAllMixedContent' && sources === true) {
1615
- cspParts.push('block-all-mixed-content');
1616
- }
1617
- else if (Array.isArray(sources)) {
1618
- let sourceList = sources.join(' ');
1619
- // Add nonce to script-src and style-src if enabled
1620
- if (nonce && (directive === 'scriptSrc' || directive === 'styleSrc')) {
1621
- sourceList += ` 'nonce-${nonce}'`;
1622
- }
1623
- // Convert camelCase to kebab-case
1624
- const kebabDirective = directive.replace(/([A-Z])/g, '-$1').toLowerCase();
1625
- cspParts.push(`${kebabDirective} ${sourceList}`);
1626
- }
1627
- }
1628
- // Add report-uri if specified
1629
- if (options.reportUri) {
1630
- cspParts.push(`report-uri ${options.reportUri}`);
1631
- }
1632
- const cspValue = cspParts.join('; ');
1633
- const headerName = options.reportOnly
1634
- ? 'Content-Security-Policy-Report-Only'
1635
- : 'Content-Security-Policy';
1636
- res.setHeader(headerName, cspValue);
1637
- next();
1638
- };
1639
- },
1640
- };
1641
- function parseSize(size) {
1642
- const units = {
1643
- b: 1,
1644
- kb: 1024,
1645
- mb: 1024 * 1024,
1646
- gb: 1024 * 1024 * 1024,
1647
- };
1648
- const match = size.toLowerCase().match(/^(\d+(?:\.\d+)?)\s*(b|kb|mb|gb)?$/);
1649
- if (!match)
1650
- return 1024 * 1024; // Default 1MB
1651
- const value = parseFloat(match[1]);
1652
- const unit = match[2] || 'b';
1653
- return Math.round(value * units[unit]);
1654
- }
1655
1090
  //# sourceMappingURL=http-server.js.map