@morojs/moro 1.0.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 (345) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +233 -0
  3. package/dist/core/config/index.d.ts +19 -0
  4. package/dist/core/config/index.js +59 -0
  5. package/dist/core/config/index.js.map +1 -0
  6. package/dist/core/config/loader.d.ts +6 -0
  7. package/dist/core/config/loader.js +288 -0
  8. package/dist/core/config/loader.js.map +1 -0
  9. package/dist/core/config/schema.d.ts +335 -0
  10. package/dist/core/config/schema.js +286 -0
  11. package/dist/core/config/schema.js.map +1 -0
  12. package/dist/core/config/utils.d.ts +50 -0
  13. package/dist/core/config/utils.js +185 -0
  14. package/dist/core/config/utils.js.map +1 -0
  15. package/dist/core/database/adapters/drizzle.d.ts +29 -0
  16. package/dist/core/database/adapters/drizzle.js +366 -0
  17. package/dist/core/database/adapters/drizzle.js.map +1 -0
  18. package/dist/core/database/adapters/index.d.ts +8 -0
  19. package/dist/core/database/adapters/index.js +48 -0
  20. package/dist/core/database/adapters/index.js.map +1 -0
  21. package/dist/core/database/adapters/mongodb.d.ts +35 -0
  22. package/dist/core/database/adapters/mongodb.js +215 -0
  23. package/dist/core/database/adapters/mongodb.js.map +1 -0
  24. package/dist/core/database/adapters/mysql.d.ts +23 -0
  25. package/dist/core/database/adapters/mysql.js +149 -0
  26. package/dist/core/database/adapters/mysql.js.map +1 -0
  27. package/dist/core/database/adapters/postgresql.d.ts +24 -0
  28. package/dist/core/database/adapters/postgresql.js +160 -0
  29. package/dist/core/database/adapters/postgresql.js.map +1 -0
  30. package/dist/core/database/adapters/redis.d.ts +50 -0
  31. package/dist/core/database/adapters/redis.js +266 -0
  32. package/dist/core/database/adapters/redis.js.map +1 -0
  33. package/dist/core/database/adapters/sqlite.d.ts +23 -0
  34. package/dist/core/database/adapters/sqlite.js +194 -0
  35. package/dist/core/database/adapters/sqlite.js.map +1 -0
  36. package/dist/core/database/index.d.ts +2 -0
  37. package/dist/core/database/index.js +20 -0
  38. package/dist/core/database/index.js.map +1 -0
  39. package/dist/core/docs/index.d.ts +63 -0
  40. package/dist/core/docs/index.js +170 -0
  41. package/dist/core/docs/index.js.map +1 -0
  42. package/dist/core/docs/openapi-generator.d.ts +124 -0
  43. package/dist/core/docs/openapi-generator.js +413 -0
  44. package/dist/core/docs/openapi-generator.js.map +1 -0
  45. package/dist/core/docs/simple-docs.d.ts +21 -0
  46. package/dist/core/docs/simple-docs.js +268 -0
  47. package/dist/core/docs/simple-docs.js.map +1 -0
  48. package/dist/core/docs/swagger-ui.d.ts +28 -0
  49. package/dist/core/docs/swagger-ui.js +317 -0
  50. package/dist/core/docs/swagger-ui.js.map +1 -0
  51. package/dist/core/docs/zod-to-openapi.d.ts +29 -0
  52. package/dist/core/docs/zod-to-openapi.js +414 -0
  53. package/dist/core/docs/zod-to-openapi.js.map +1 -0
  54. package/dist/core/events/event-bus.d.ts +27 -0
  55. package/dist/core/events/event-bus.js +193 -0
  56. package/dist/core/events/event-bus.js.map +1 -0
  57. package/dist/core/events/index.d.ts +2 -0
  58. package/dist/core/events/index.js +7 -0
  59. package/dist/core/events/index.js.map +1 -0
  60. package/dist/core/framework.d.ts +57 -0
  61. package/dist/core/framework.js +432 -0
  62. package/dist/core/framework.js.map +1 -0
  63. package/dist/core/http/http-server.d.ts +114 -0
  64. package/dist/core/http/http-server.js +1154 -0
  65. package/dist/core/http/http-server.js.map +1 -0
  66. package/dist/core/http/index.d.ts +3 -0
  67. package/dist/core/http/index.js +10 -0
  68. package/dist/core/http/index.js.map +1 -0
  69. package/dist/core/http/router.d.ts +14 -0
  70. package/dist/core/http/router.js +113 -0
  71. package/dist/core/http/router.js.map +1 -0
  72. package/dist/core/logger/filters.d.ts +9 -0
  73. package/dist/core/logger/filters.js +134 -0
  74. package/dist/core/logger/filters.js.map +1 -0
  75. package/dist/core/logger/index.d.ts +3 -0
  76. package/dist/core/logger/index.js +26 -0
  77. package/dist/core/logger/index.js.map +1 -0
  78. package/dist/core/logger/logger.d.ts +49 -0
  79. package/dist/core/logger/logger.js +332 -0
  80. package/dist/core/logger/logger.js.map +1 -0
  81. package/dist/core/logger/outputs.d.ts +42 -0
  82. package/dist/core/logger/outputs.js +110 -0
  83. package/dist/core/logger/outputs.js.map +1 -0
  84. package/dist/core/middleware/built-in/adapters/cache/file.d.ts +15 -0
  85. package/dist/core/middleware/built-in/adapters/cache/file.js +128 -0
  86. package/dist/core/middleware/built-in/adapters/cache/file.js.map +1 -0
  87. package/dist/core/middleware/built-in/adapters/cache/index.d.ts +5 -0
  88. package/dist/core/middleware/built-in/adapters/cache/index.js +28 -0
  89. package/dist/core/middleware/built-in/adapters/cache/index.js.map +1 -0
  90. package/dist/core/middleware/built-in/adapters/cache/memory.d.ts +11 -0
  91. package/dist/core/middleware/built-in/adapters/cache/memory.js +65 -0
  92. package/dist/core/middleware/built-in/adapters/cache/memory.js.map +1 -0
  93. package/dist/core/middleware/built-in/adapters/cache/redis.d.ts +17 -0
  94. package/dist/core/middleware/built-in/adapters/cache/redis.js +91 -0
  95. package/dist/core/middleware/built-in/adapters/cache/redis.js.map +1 -0
  96. package/dist/core/middleware/built-in/adapters/cdn/azure.d.ts +21 -0
  97. package/dist/core/middleware/built-in/adapters/cdn/azure.js +40 -0
  98. package/dist/core/middleware/built-in/adapters/cdn/azure.js.map +1 -0
  99. package/dist/core/middleware/built-in/adapters/cdn/cloudflare.d.ts +14 -0
  100. package/dist/core/middleware/built-in/adapters/cdn/cloudflare.js +77 -0
  101. package/dist/core/middleware/built-in/adapters/cdn/cloudflare.js.map +1 -0
  102. package/dist/core/middleware/built-in/adapters/cdn/cloudfront.d.ts +15 -0
  103. package/dist/core/middleware/built-in/adapters/cdn/cloudfront.js +73 -0
  104. package/dist/core/middleware/built-in/adapters/cdn/cloudfront.js.map +1 -0
  105. package/dist/core/middleware/built-in/adapters/cdn/index.d.ts +5 -0
  106. package/dist/core/middleware/built-in/adapters/cdn/index.js +28 -0
  107. package/dist/core/middleware/built-in/adapters/cdn/index.js.map +1 -0
  108. package/dist/core/middleware/built-in/adapters/index.d.ts +4 -0
  109. package/dist/core/middleware/built-in/adapters/index.js +26 -0
  110. package/dist/core/middleware/built-in/adapters/index.js.map +1 -0
  111. package/dist/core/middleware/built-in/auth.d.ts +2 -0
  112. package/dist/core/middleware/built-in/auth.js +38 -0
  113. package/dist/core/middleware/built-in/auth.js.map +1 -0
  114. package/dist/core/middleware/built-in/cache.d.ts +3 -0
  115. package/dist/core/middleware/built-in/cache.js +188 -0
  116. package/dist/core/middleware/built-in/cache.js.map +1 -0
  117. package/dist/core/middleware/built-in/cdn.d.ts +3 -0
  118. package/dist/core/middleware/built-in/cdn.js +115 -0
  119. package/dist/core/middleware/built-in/cdn.js.map +1 -0
  120. package/dist/core/middleware/built-in/cookie.d.ts +14 -0
  121. package/dist/core/middleware/built-in/cookie.js +68 -0
  122. package/dist/core/middleware/built-in/cookie.js.map +1 -0
  123. package/dist/core/middleware/built-in/cors.d.ts +2 -0
  124. package/dist/core/middleware/built-in/cors.js +29 -0
  125. package/dist/core/middleware/built-in/cors.js.map +1 -0
  126. package/dist/core/middleware/built-in/csp.d.ts +22 -0
  127. package/dist/core/middleware/built-in/csp.js +74 -0
  128. package/dist/core/middleware/built-in/csp.js.map +1 -0
  129. package/dist/core/middleware/built-in/csrf.d.ts +9 -0
  130. package/dist/core/middleware/built-in/csrf.js +66 -0
  131. package/dist/core/middleware/built-in/csrf.js.map +1 -0
  132. package/dist/core/middleware/built-in/error-tracker.d.ts +1 -0
  133. package/dist/core/middleware/built-in/error-tracker.js +19 -0
  134. package/dist/core/middleware/built-in/error-tracker.js.map +1 -0
  135. package/dist/core/middleware/built-in/index.d.ts +70 -0
  136. package/dist/core/middleware/built-in/index.js +70 -0
  137. package/dist/core/middleware/built-in/index.js.map +1 -0
  138. package/dist/core/middleware/built-in/performance-monitor.d.ts +1 -0
  139. package/dist/core/middleware/built-in/performance-monitor.js +22 -0
  140. package/dist/core/middleware/built-in/performance-monitor.js.map +1 -0
  141. package/dist/core/middleware/built-in/rate-limit.d.ts +6 -0
  142. package/dist/core/middleware/built-in/rate-limit.js +47 -0
  143. package/dist/core/middleware/built-in/rate-limit.js.map +1 -0
  144. package/dist/core/middleware/built-in/request-logger.d.ts +1 -0
  145. package/dist/core/middleware/built-in/request-logger.js +15 -0
  146. package/dist/core/middleware/built-in/request-logger.js.map +1 -0
  147. package/dist/core/middleware/built-in/session.d.ts +41 -0
  148. package/dist/core/middleware/built-in/session.js +209 -0
  149. package/dist/core/middleware/built-in/session.js.map +1 -0
  150. package/dist/core/middleware/built-in/sse.d.ts +6 -0
  151. package/dist/core/middleware/built-in/sse.js +73 -0
  152. package/dist/core/middleware/built-in/sse.js.map +1 -0
  153. package/dist/core/middleware/built-in/validation.d.ts +2 -0
  154. package/dist/core/middleware/built-in/validation.js +31 -0
  155. package/dist/core/middleware/built-in/validation.js.map +1 -0
  156. package/dist/core/middleware/index.d.ts +21 -0
  157. package/dist/core/middleware/index.js +152 -0
  158. package/dist/core/middleware/index.js.map +1 -0
  159. package/dist/core/modules/auto-discovery.d.ts +27 -0
  160. package/dist/core/modules/auto-discovery.js +255 -0
  161. package/dist/core/modules/auto-discovery.js.map +1 -0
  162. package/dist/core/modules/index.d.ts +2 -0
  163. package/dist/core/modules/index.js +11 -0
  164. package/dist/core/modules/index.js.map +1 -0
  165. package/dist/core/modules/modules.d.ts +10 -0
  166. package/dist/core/modules/modules.js +137 -0
  167. package/dist/core/modules/modules.js.map +1 -0
  168. package/dist/core/networking/index.d.ts +2 -0
  169. package/dist/core/networking/index.js +9 -0
  170. package/dist/core/networking/index.js.map +1 -0
  171. package/dist/core/networking/service-discovery.d.ts +38 -0
  172. package/dist/core/networking/service-discovery.js +233 -0
  173. package/dist/core/networking/service-discovery.js.map +1 -0
  174. package/dist/core/networking/websocket-manager.d.ts +27 -0
  175. package/dist/core/networking/websocket-manager.js +211 -0
  176. package/dist/core/networking/websocket-manager.js.map +1 -0
  177. package/dist/core/routing/app-integration.d.ts +42 -0
  178. package/dist/core/routing/app-integration.js +152 -0
  179. package/dist/core/routing/app-integration.js.map +1 -0
  180. package/dist/core/routing/index.d.ts +106 -0
  181. package/dist/core/routing/index.js +343 -0
  182. package/dist/core/routing/index.js.map +1 -0
  183. package/dist/core/runtime/aws-lambda-adapter.d.ts +43 -0
  184. package/dist/core/runtime/aws-lambda-adapter.js +108 -0
  185. package/dist/core/runtime/aws-lambda-adapter.js.map +1 -0
  186. package/dist/core/runtime/base-adapter.d.ts +16 -0
  187. package/dist/core/runtime/base-adapter.js +105 -0
  188. package/dist/core/runtime/base-adapter.js.map +1 -0
  189. package/dist/core/runtime/cloudflare-workers-adapter.d.ts +18 -0
  190. package/dist/core/runtime/cloudflare-workers-adapter.js +131 -0
  191. package/dist/core/runtime/cloudflare-workers-adapter.js.map +1 -0
  192. package/dist/core/runtime/index.d.ts +14 -0
  193. package/dist/core/runtime/index.js +56 -0
  194. package/dist/core/runtime/index.js.map +1 -0
  195. package/dist/core/runtime/node-adapter.d.ts +15 -0
  196. package/dist/core/runtime/node-adapter.js +204 -0
  197. package/dist/core/runtime/node-adapter.js.map +1 -0
  198. package/dist/core/runtime/vercel-edge-adapter.d.ts +10 -0
  199. package/dist/core/runtime/vercel-edge-adapter.js +106 -0
  200. package/dist/core/runtime/vercel-edge-adapter.js.map +1 -0
  201. package/dist/core/utilities/circuit-breaker.d.ts +14 -0
  202. package/dist/core/utilities/circuit-breaker.js +42 -0
  203. package/dist/core/utilities/circuit-breaker.js.map +1 -0
  204. package/dist/core/utilities/container.d.ts +116 -0
  205. package/dist/core/utilities/container.js +529 -0
  206. package/dist/core/utilities/container.js.map +1 -0
  207. package/dist/core/utilities/hooks.d.ts +24 -0
  208. package/dist/core/utilities/hooks.js +131 -0
  209. package/dist/core/utilities/hooks.js.map +1 -0
  210. package/dist/core/utilities/index.d.ts +4 -0
  211. package/dist/core/utilities/index.js +22 -0
  212. package/dist/core/utilities/index.js.map +1 -0
  213. package/dist/core/validation/index.d.ts +30 -0
  214. package/dist/core/validation/index.js +144 -0
  215. package/dist/core/validation/index.js.map +1 -0
  216. package/dist/index.d.ts +30 -0
  217. package/dist/index.js +72 -0
  218. package/dist/index.js.map +1 -0
  219. package/dist/moro.d.ts +82 -0
  220. package/dist/moro.js +679 -0
  221. package/dist/moro.js.map +1 -0
  222. package/dist/types/cache.d.ts +34 -0
  223. package/dist/types/cache.js +3 -0
  224. package/dist/types/cache.js.map +1 -0
  225. package/dist/types/cdn.d.ts +19 -0
  226. package/dist/types/cdn.js +3 -0
  227. package/dist/types/cdn.js.map +1 -0
  228. package/dist/types/core.d.ts +13 -0
  229. package/dist/types/core.js +3 -0
  230. package/dist/types/core.js.map +1 -0
  231. package/dist/types/database.d.ts +29 -0
  232. package/dist/types/database.js +3 -0
  233. package/dist/types/database.js.map +1 -0
  234. package/dist/types/discovery.d.ts +6 -0
  235. package/dist/types/discovery.js +3 -0
  236. package/dist/types/discovery.js.map +1 -0
  237. package/dist/types/events.d.ts +116 -0
  238. package/dist/types/events.js +3 -0
  239. package/dist/types/events.js.map +1 -0
  240. package/dist/types/hooks.d.ts +38 -0
  241. package/dist/types/hooks.js +3 -0
  242. package/dist/types/hooks.js.map +1 -0
  243. package/dist/types/http.d.ts +51 -0
  244. package/dist/types/http.js +3 -0
  245. package/dist/types/http.js.map +1 -0
  246. package/dist/types/logger.d.ts +77 -0
  247. package/dist/types/logger.js +3 -0
  248. package/dist/types/logger.js.map +1 -0
  249. package/dist/types/module.d.ts +91 -0
  250. package/dist/types/module.js +3 -0
  251. package/dist/types/module.js.map +1 -0
  252. package/dist/types/runtime.d.ts +48 -0
  253. package/dist/types/runtime.js +3 -0
  254. package/dist/types/runtime.js.map +1 -0
  255. package/dist/types/session.d.ts +66 -0
  256. package/dist/types/session.js +3 -0
  257. package/dist/types/session.js.map +1 -0
  258. package/package.json +176 -0
  259. package/src/core/config/index.ts +47 -0
  260. package/src/core/config/loader.ts +366 -0
  261. package/src/core/config/schema.ts +346 -0
  262. package/src/core/config/utils.ts +220 -0
  263. package/src/core/database/README.md +228 -0
  264. package/src/core/database/adapters/drizzle.ts +425 -0
  265. package/src/core/database/adapters/index.ts +45 -0
  266. package/src/core/database/adapters/mongodb.ts +292 -0
  267. package/src/core/database/adapters/mysql.ts +217 -0
  268. package/src/core/database/adapters/postgresql.ts +211 -0
  269. package/src/core/database/adapters/redis.ts +331 -0
  270. package/src/core/database/adapters/sqlite.ts +255 -0
  271. package/src/core/database/index.ts +3 -0
  272. package/src/core/docs/index.ts +245 -0
  273. package/src/core/docs/openapi-generator.ts +588 -0
  274. package/src/core/docs/simple-docs.ts +305 -0
  275. package/src/core/docs/swagger-ui.ts +370 -0
  276. package/src/core/docs/zod-to-openapi.ts +532 -0
  277. package/src/core/events/event-bus.ts +249 -0
  278. package/src/core/events/index.ts +12 -0
  279. package/src/core/framework.ts +621 -0
  280. package/src/core/http/http-server.ts +1421 -0
  281. package/src/core/http/index.ts +11 -0
  282. package/src/core/http/router.ts +153 -0
  283. package/src/core/logger/filters.ts +148 -0
  284. package/src/core/logger/index.ts +20 -0
  285. package/src/core/logger/logger.ts +434 -0
  286. package/src/core/logger/outputs.ts +136 -0
  287. package/src/core/middleware/built-in/adapters/cache/file.ts +106 -0
  288. package/src/core/middleware/built-in/adapters/cache/index.ts +26 -0
  289. package/src/core/middleware/built-in/adapters/cache/memory.ts +73 -0
  290. package/src/core/middleware/built-in/adapters/cache/redis.ts +103 -0
  291. package/src/core/middleware/built-in/adapters/cdn/azure.ts +68 -0
  292. package/src/core/middleware/built-in/adapters/cdn/cloudflare.ts +100 -0
  293. package/src/core/middleware/built-in/adapters/cdn/cloudfront.ts +92 -0
  294. package/src/core/middleware/built-in/adapters/cdn/index.ts +23 -0
  295. package/src/core/middleware/built-in/adapters/index.ts +7 -0
  296. package/src/core/middleware/built-in/auth.ts +39 -0
  297. package/src/core/middleware/built-in/cache.ts +228 -0
  298. package/src/core/middleware/built-in/cdn.ts +151 -0
  299. package/src/core/middleware/built-in/cookie.ts +90 -0
  300. package/src/core/middleware/built-in/cors.ts +38 -0
  301. package/src/core/middleware/built-in/csp.ts +107 -0
  302. package/src/core/middleware/built-in/csrf.ts +87 -0
  303. package/src/core/middleware/built-in/error-tracker.ts +16 -0
  304. package/src/core/middleware/built-in/index.ts +57 -0
  305. package/src/core/middleware/built-in/performance-monitor.ts +25 -0
  306. package/src/core/middleware/built-in/rate-limit.ts +60 -0
  307. package/src/core/middleware/built-in/request-logger.ts +14 -0
  308. package/src/core/middleware/built-in/session.ts +311 -0
  309. package/src/core/middleware/built-in/sse.ts +91 -0
  310. package/src/core/middleware/built-in/validation.ts +33 -0
  311. package/src/core/middleware/index.ts +188 -0
  312. package/src/core/modules/auto-discovery.ts +265 -0
  313. package/src/core/modules/index.ts +6 -0
  314. package/src/core/modules/modules.ts +125 -0
  315. package/src/core/networking/index.ts +7 -0
  316. package/src/core/networking/service-discovery.ts +309 -0
  317. package/src/core/networking/websocket-manager.ts +259 -0
  318. package/src/core/routing/app-integration.ts +229 -0
  319. package/src/core/routing/index.ts +519 -0
  320. package/src/core/runtime/aws-lambda-adapter.ts +157 -0
  321. package/src/core/runtime/base-adapter.ts +140 -0
  322. package/src/core/runtime/cloudflare-workers-adapter.ts +166 -0
  323. package/src/core/runtime/index.ts +74 -0
  324. package/src/core/runtime/node-adapter.ts +210 -0
  325. package/src/core/runtime/vercel-edge-adapter.ts +125 -0
  326. package/src/core/utilities/circuit-breaker.ts +46 -0
  327. package/src/core/utilities/container.ts +760 -0
  328. package/src/core/utilities/hooks.ts +148 -0
  329. package/src/core/utilities/index.ts +16 -0
  330. package/src/core/validation/index.ts +216 -0
  331. package/src/index.ts +120 -0
  332. package/src/moro.ts +842 -0
  333. package/src/types/cache.ts +38 -0
  334. package/src/types/cdn.ts +22 -0
  335. package/src/types/core.ts +17 -0
  336. package/src/types/database.ts +40 -0
  337. package/src/types/discovery.ts +7 -0
  338. package/src/types/events.ts +90 -0
  339. package/src/types/hooks.ts +47 -0
  340. package/src/types/http.ts +70 -0
  341. package/src/types/logger.ts +109 -0
  342. package/src/types/module.ts +87 -0
  343. package/src/types/runtime.ts +91 -0
  344. package/src/types/session.ts +89 -0
  345. package/tsconfig.json +21 -0
@@ -0,0 +1,1154 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.middleware = exports.MoroHttpServer = void 0;
37
+ // src/core/http-server.ts
38
+ const http_1 = require("http");
39
+ const url_1 = require("url");
40
+ const zlib = __importStar(require("zlib"));
41
+ const util_1 = require("util");
42
+ const logger_1 = require("../logger");
43
+ const gzip = (0, util_1.promisify)(zlib.gzip);
44
+ const deflate = (0, util_1.promisify)(zlib.deflate);
45
+ class MoroHttpServer {
46
+ constructor() {
47
+ this.routes = [];
48
+ this.globalMiddleware = [];
49
+ this.compressionEnabled = true;
50
+ this.compressionThreshold = 1024;
51
+ this.logger = (0, logger_1.createFrameworkLogger)("HttpServer");
52
+ this.server = (0, http_1.createServer)(this.handleRequest.bind(this));
53
+ }
54
+ // Middleware management
55
+ use(middleware) {
56
+ this.globalMiddleware.push(middleware);
57
+ }
58
+ // Routing methods
59
+ get(path, ...handlers) {
60
+ this.addRoute("GET", path, handlers);
61
+ }
62
+ post(path, ...handlers) {
63
+ this.addRoute("POST", path, handlers);
64
+ }
65
+ put(path, ...handlers) {
66
+ this.addRoute("PUT", path, handlers);
67
+ }
68
+ delete(path, ...handlers) {
69
+ this.addRoute("DELETE", path, handlers);
70
+ }
71
+ patch(path, ...handlers) {
72
+ this.addRoute("PATCH", path, handlers);
73
+ }
74
+ addRoute(method, path, handlers) {
75
+ const { pattern, paramNames } = this.pathToRegex(path);
76
+ const handler = handlers.pop();
77
+ const middleware = handlers;
78
+ this.routes.push({
79
+ method,
80
+ path,
81
+ pattern,
82
+ paramNames,
83
+ handler,
84
+ middleware,
85
+ });
86
+ }
87
+ pathToRegex(path) {
88
+ const paramNames = [];
89
+ // Convert parameterized routes to regex
90
+ const regexPattern = path
91
+ .replace(/\/:([^/]+)/g, (match, paramName) => {
92
+ paramNames.push(paramName);
93
+ return "/([^/]+)";
94
+ })
95
+ .replace(/\//g, "\\/");
96
+ return {
97
+ pattern: new RegExp(`^${regexPattern}$`),
98
+ paramNames,
99
+ };
100
+ }
101
+ async handleRequest(req, res) {
102
+ const httpReq = this.enhanceRequest(req);
103
+ const httpRes = this.enhanceResponse(res);
104
+ try {
105
+ // Parse URL and query parameters
106
+ const url = new url_1.URL(req.url, `http://${req.headers.host}`);
107
+ httpReq.path = url.pathname;
108
+ httpReq.query = Object.fromEntries(url.searchParams);
109
+ // Parse body for POST/PUT/PATCH requests
110
+ if (["POST", "PUT", "PATCH"].includes(req.method)) {
111
+ httpReq.body = await this.parseBody(req);
112
+ }
113
+ // Execute global middleware first
114
+ await this.executeMiddleware(this.globalMiddleware, httpReq, httpRes);
115
+ // If middleware handled the request, don't continue
116
+ if (httpRes.headersSent) {
117
+ return;
118
+ }
119
+ // Find matching route
120
+ const route = this.findRoute(req.method, httpReq.path);
121
+ if (!route) {
122
+ httpRes.status(404).json({ success: false, error: "Not found" });
123
+ return;
124
+ }
125
+ // Extract path parameters
126
+ const matches = httpReq.path.match(route.pattern);
127
+ if (matches) {
128
+ httpReq.params = {};
129
+ route.paramNames.forEach((name, index) => {
130
+ httpReq.params[name] = matches[index + 1];
131
+ });
132
+ }
133
+ // Execute middleware chain
134
+ await this.executeMiddleware(route.middleware, httpReq, httpRes);
135
+ // Execute handler
136
+ await route.handler(httpReq, httpRes);
137
+ }
138
+ catch (error) {
139
+ this.logger.error("Request error", "RequestHandler", {
140
+ error: error instanceof Error ? error.message : String(error),
141
+ requestId: httpReq.requestId,
142
+ method: req.method,
143
+ path: req.url,
144
+ });
145
+ if (!httpRes.headersSent) {
146
+ httpRes.status(500).json({
147
+ success: false,
148
+ error: "Internal server error",
149
+ requestId: httpReq.requestId,
150
+ });
151
+ }
152
+ }
153
+ }
154
+ enhanceRequest(req) {
155
+ const httpReq = req;
156
+ httpReq.params = {};
157
+ httpReq.query = {};
158
+ httpReq.body = null;
159
+ httpReq.path = "";
160
+ httpReq.ip = req.socket.remoteAddress || "";
161
+ httpReq.requestId = Math.random().toString(36).substring(7);
162
+ httpReq.headers = req.headers;
163
+ // Parse cookies
164
+ httpReq.cookies = this.parseCookies(req.headers.cookie || "");
165
+ return httpReq;
166
+ }
167
+ parseCookies(cookieHeader) {
168
+ const cookies = {};
169
+ if (!cookieHeader)
170
+ return cookies;
171
+ cookieHeader.split(";").forEach((cookie) => {
172
+ const [name, value] = cookie.trim().split("=");
173
+ if (name && value) {
174
+ cookies[name] = decodeURIComponent(value);
175
+ }
176
+ });
177
+ return cookies;
178
+ }
179
+ enhanceResponse(res) {
180
+ const httpRes = res;
181
+ httpRes.json = async (data) => {
182
+ if (httpRes.headersSent)
183
+ return;
184
+ const jsonString = JSON.stringify(data);
185
+ const buffer = Buffer.from(jsonString);
186
+ httpRes.setHeader("Content-Type", "application/json; charset=utf-8");
187
+ // Compression
188
+ if (this.compressionEnabled &&
189
+ buffer.length > this.compressionThreshold) {
190
+ const acceptEncoding = httpRes.req.headers["accept-encoding"] || "";
191
+ if (acceptEncoding.includes("gzip")) {
192
+ const compressed = await gzip(buffer);
193
+ httpRes.setHeader("Content-Encoding", "gzip");
194
+ httpRes.setHeader("Content-Length", compressed.length);
195
+ httpRes.end(compressed);
196
+ return;
197
+ }
198
+ else if (acceptEncoding.includes("deflate")) {
199
+ const compressed = await deflate(buffer);
200
+ httpRes.setHeader("Content-Encoding", "deflate");
201
+ httpRes.setHeader("Content-Length", compressed.length);
202
+ httpRes.end(compressed);
203
+ return;
204
+ }
205
+ }
206
+ httpRes.setHeader("Content-Length", buffer.length);
207
+ httpRes.end(buffer);
208
+ };
209
+ httpRes.status = (code) => {
210
+ httpRes.statusCode = code;
211
+ return httpRes;
212
+ };
213
+ httpRes.send = (data) => {
214
+ if (httpRes.headersSent)
215
+ return;
216
+ // Auto-detect content type if not already set
217
+ if (!httpRes.getHeader("Content-Type")) {
218
+ if (typeof data === "string") {
219
+ // Check if it's JSON
220
+ try {
221
+ JSON.parse(data);
222
+ httpRes.setHeader("Content-Type", "application/json; charset=utf-8");
223
+ }
224
+ catch {
225
+ // Default to plain text
226
+ httpRes.setHeader("Content-Type", "text/plain; charset=utf-8");
227
+ }
228
+ }
229
+ else {
230
+ // Buffer data - default to octet-stream
231
+ httpRes.setHeader("Content-Type", "application/octet-stream");
232
+ }
233
+ }
234
+ httpRes.end(data);
235
+ };
236
+ httpRes.cookie = (name, value, options = {}) => {
237
+ const cookieValue = encodeURIComponent(value);
238
+ let cookieString = `${name}=${cookieValue}`;
239
+ if (options.maxAge)
240
+ cookieString += `; Max-Age=${options.maxAge}`;
241
+ if (options.expires)
242
+ cookieString += `; Expires=${options.expires.toUTCString()}`;
243
+ if (options.httpOnly)
244
+ cookieString += "; HttpOnly";
245
+ if (options.secure)
246
+ cookieString += "; Secure";
247
+ if (options.sameSite)
248
+ cookieString += `; SameSite=${options.sameSite}`;
249
+ if (options.domain)
250
+ cookieString += `; Domain=${options.domain}`;
251
+ if (options.path)
252
+ cookieString += `; Path=${options.path}`;
253
+ const existingCookies = httpRes.getHeader("Set-Cookie") || [];
254
+ const cookies = Array.isArray(existingCookies)
255
+ ? [...existingCookies]
256
+ : [existingCookies];
257
+ cookies.push(cookieString);
258
+ httpRes.setHeader("Set-Cookie", cookies);
259
+ return httpRes;
260
+ };
261
+ httpRes.clearCookie = (name, options = {}) => {
262
+ const clearOptions = { ...options, expires: new Date(0), maxAge: 0 };
263
+ return httpRes.cookie(name, "", clearOptions);
264
+ };
265
+ httpRes.redirect = (url, status = 302) => {
266
+ if (httpRes.headersSent)
267
+ return;
268
+ httpRes.statusCode = status;
269
+ httpRes.setHeader("Location", url);
270
+ httpRes.end();
271
+ };
272
+ httpRes.sendFile = async (filePath) => {
273
+ if (httpRes.headersSent)
274
+ return;
275
+ try {
276
+ const fs = await Promise.resolve().then(() => __importStar(require("fs/promises")));
277
+ const path = await Promise.resolve().then(() => __importStar(require("path")));
278
+ const extension = path.extname(filePath);
279
+ const mime = await this.getMimeType(extension);
280
+ const stats = await fs.stat(filePath);
281
+ const data = await fs.readFile(filePath);
282
+ // Add charset for text-based files
283
+ const contentType = this.addCharsetIfNeeded(mime);
284
+ httpRes.setHeader("Content-Type", contentType);
285
+ httpRes.setHeader("Content-Length", stats.size);
286
+ // Add security headers for file downloads
287
+ httpRes.setHeader("X-Content-Type-Options", "nosniff");
288
+ // Add caching headers
289
+ httpRes.setHeader("Last-Modified", stats.mtime.toUTCString());
290
+ httpRes.setHeader("Cache-Control", "public, max-age=31536000"); // 1 year for static files
291
+ httpRes.end(data);
292
+ }
293
+ catch (error) {
294
+ httpRes.status(404).json({ success: false, error: "File not found" });
295
+ }
296
+ };
297
+ return httpRes;
298
+ }
299
+ async getMimeType(ext) {
300
+ const mimeTypes = {
301
+ ".html": "text/html",
302
+ ".css": "text/css",
303
+ ".js": "application/javascript",
304
+ ".json": "application/json",
305
+ ".png": "image/png",
306
+ ".jpg": "image/jpeg",
307
+ ".jpeg": "image/jpeg",
308
+ ".gif": "image/gif",
309
+ ".svg": "image/svg+xml",
310
+ ".ico": "image/x-icon",
311
+ ".pdf": "application/pdf",
312
+ ".txt": "text/plain",
313
+ ".xml": "application/xml",
314
+ };
315
+ return mimeTypes[ext.toLowerCase()] || "application/octet-stream";
316
+ }
317
+ addCharsetIfNeeded(mimeType) {
318
+ // Add charset for text-based content types
319
+ const textTypes = [
320
+ "text/",
321
+ "application/json",
322
+ "application/javascript",
323
+ "application/xml",
324
+ "image/svg+xml",
325
+ ];
326
+ const needsCharset = textTypes.some((type) => mimeType.startsWith(type));
327
+ if (needsCharset && !mimeType.includes("charset")) {
328
+ return `${mimeType}; charset=utf-8`;
329
+ }
330
+ return mimeType;
331
+ }
332
+ async parseBody(req) {
333
+ return new Promise((resolve, reject) => {
334
+ const chunks = [];
335
+ let totalLength = 0;
336
+ const maxSize = 10 * 1024 * 1024; // 10MB limit
337
+ req.on("data", (chunk) => {
338
+ totalLength += chunk.length;
339
+ if (totalLength > maxSize) {
340
+ reject(new Error("Request body too large"));
341
+ return;
342
+ }
343
+ chunks.push(chunk);
344
+ });
345
+ req.on("end", () => {
346
+ try {
347
+ const body = Buffer.concat(chunks);
348
+ const contentType = req.headers["content-type"] || "";
349
+ if (contentType.includes("application/json")) {
350
+ resolve(JSON.parse(body.toString()));
351
+ }
352
+ else if (contentType.includes("application/x-www-form-urlencoded")) {
353
+ resolve(this.parseUrlEncoded(body.toString()));
354
+ }
355
+ else if (contentType.includes("multipart/form-data")) {
356
+ resolve(this.parseMultipart(body, contentType));
357
+ }
358
+ else {
359
+ resolve(body.toString());
360
+ }
361
+ }
362
+ catch (error) {
363
+ reject(error);
364
+ }
365
+ });
366
+ req.on("error", reject);
367
+ });
368
+ }
369
+ parseMultipart(buffer, contentType) {
370
+ const boundary = contentType.split("boundary=")[1];
371
+ if (!boundary) {
372
+ throw new Error("Invalid multipart boundary");
373
+ }
374
+ const parts = buffer.toString("binary").split("--" + boundary);
375
+ const fields = {};
376
+ const files = {};
377
+ for (let i = 1; i < parts.length - 1; i++) {
378
+ const part = parts[i];
379
+ const [headers, content] = part.split("\r\n\r\n");
380
+ if (!headers || content === undefined)
381
+ continue;
382
+ const nameMatch = headers.match(/name="([^"]+)"/);
383
+ const filenameMatch = headers.match(/filename="([^"]+)"/);
384
+ const contentTypeMatch = headers.match(/Content-Type: ([^\r\n]+)/);
385
+ if (nameMatch) {
386
+ const name = nameMatch[1];
387
+ if (filenameMatch) {
388
+ // This is a file
389
+ const filename = filenameMatch[1];
390
+ const mimeType = contentTypeMatch
391
+ ? contentTypeMatch[1]
392
+ : "application/octet-stream";
393
+ const fileContent = content.substring(0, content.length - 2); // Remove trailing \r\n
394
+ files[name] = {
395
+ filename,
396
+ mimetype: mimeType,
397
+ data: Buffer.from(fileContent, "binary"),
398
+ size: Buffer.byteLength(fileContent, "binary"),
399
+ };
400
+ }
401
+ else {
402
+ // This is a regular field
403
+ fields[name] = content.substring(0, content.length - 2); // Remove trailing \r\n
404
+ }
405
+ }
406
+ }
407
+ return { fields, files };
408
+ }
409
+ parseUrlEncoded(body) {
410
+ const params = new URLSearchParams(body);
411
+ const result = {};
412
+ for (const [key, value] of params) {
413
+ result[key] = value;
414
+ }
415
+ return result;
416
+ }
417
+ findRoute(method, path) {
418
+ return (this.routes.find((route) => route.method === method && route.pattern.test(path)) || null);
419
+ }
420
+ async executeMiddleware(middleware, req, res) {
421
+ for (const mw of middleware) {
422
+ await new Promise((resolve, reject) => {
423
+ let nextCalled = false;
424
+ const next = () => {
425
+ if (nextCalled)
426
+ return;
427
+ nextCalled = true;
428
+ resolve();
429
+ };
430
+ try {
431
+ const result = mw(req, res, next);
432
+ // Handle async middleware
433
+ if (result instanceof Promise) {
434
+ result
435
+ .then(() => {
436
+ if (!nextCalled)
437
+ next();
438
+ })
439
+ .catch(reject);
440
+ }
441
+ }
442
+ catch (error) {
443
+ reject(error);
444
+ }
445
+ });
446
+ }
447
+ }
448
+ listen(port, host, callback) {
449
+ // Handle overloaded parameters (port, callback) or (port, host, callback)
450
+ if (typeof host === "function") {
451
+ callback = host;
452
+ host = undefined;
453
+ }
454
+ if (host) {
455
+ this.server.listen(port, host, callback);
456
+ }
457
+ else {
458
+ this.server.listen(port, callback);
459
+ }
460
+ }
461
+ close() {
462
+ return new Promise((resolve) => {
463
+ this.server.close(() => resolve());
464
+ });
465
+ }
466
+ getServer() {
467
+ return this.server;
468
+ }
469
+ }
470
+ exports.MoroHttpServer = MoroHttpServer;
471
+ // Built-in middleware
472
+ exports.middleware = {
473
+ cors: (options = {}) => {
474
+ return (req, res, next) => {
475
+ res.setHeader("Access-Control-Allow-Origin", options.origin || "*");
476
+ res.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
477
+ res.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization");
478
+ if (options.credentials) {
479
+ res.setHeader("Access-Control-Allow-Credentials", "true");
480
+ }
481
+ if (req.method === "OPTIONS") {
482
+ res.status(200).send("");
483
+ return;
484
+ }
485
+ next();
486
+ };
487
+ },
488
+ helmet: () => {
489
+ return (req, res, next) => {
490
+ res.setHeader("X-Content-Type-Options", "nosniff");
491
+ res.setHeader("X-Frame-Options", "DENY");
492
+ res.setHeader("X-XSS-Protection", "1; mode=block");
493
+ res.setHeader("Referrer-Policy", "strict-origin-when-cross-origin");
494
+ res.setHeader("Strict-Transport-Security", "max-age=31536000; includeSubDomains");
495
+ res.setHeader("Content-Security-Policy", "default-src 'self'");
496
+ next();
497
+ };
498
+ },
499
+ compression: (options = {}) => {
500
+ const zlib = require("zlib");
501
+ const threshold = options.threshold || 1024;
502
+ const level = options.level || 6;
503
+ return (req, res, next) => {
504
+ const acceptEncoding = req.headers["accept-encoding"] || "";
505
+ // Override res.json to compress responses
506
+ const originalJson = res.json;
507
+ const originalSend = res.send;
508
+ const compressResponse = (data, isJson = false) => {
509
+ const content = isJson ? JSON.stringify(data) : data;
510
+ const buffer = Buffer.from(content);
511
+ if (buffer.length < threshold) {
512
+ return isJson
513
+ ? originalJson.call(res, data)
514
+ : originalSend.call(res, data);
515
+ }
516
+ if (acceptEncoding.includes("gzip")) {
517
+ res.setHeader("Content-Encoding", "gzip");
518
+ zlib.gzip(buffer, { level }, (err, compressed) => {
519
+ if (err) {
520
+ return isJson
521
+ ? originalJson.call(res, data)
522
+ : originalSend.call(res, data);
523
+ }
524
+ res.setHeader("Content-Length", compressed.length);
525
+ res.writeHead(res.statusCode || 200, res.getHeaders());
526
+ res.end(compressed);
527
+ });
528
+ }
529
+ else if (acceptEncoding.includes("deflate")) {
530
+ res.setHeader("Content-Encoding", "deflate");
531
+ zlib.deflate(buffer, { level }, (err, compressed) => {
532
+ if (err) {
533
+ return isJson
534
+ ? originalJson.call(res, data)
535
+ : originalSend.call(res, data);
536
+ }
537
+ res.setHeader("Content-Length", compressed.length);
538
+ res.writeHead(res.statusCode || 200, res.getHeaders());
539
+ res.end(compressed);
540
+ });
541
+ }
542
+ else {
543
+ return isJson
544
+ ? originalJson.call(res, data)
545
+ : originalSend.call(res, data);
546
+ }
547
+ };
548
+ res.json = function (data) {
549
+ // Ensure charset is set for Safari compatibility
550
+ this.setHeader("Content-Type", "application/json; charset=utf-8");
551
+ compressResponse(data, true);
552
+ return this;
553
+ };
554
+ res.send = function (data) {
555
+ compressResponse(data, false);
556
+ return this;
557
+ };
558
+ next();
559
+ };
560
+ },
561
+ requestLogger: () => {
562
+ return (req, res, next) => {
563
+ const start = Date.now();
564
+ res.on("finish", () => {
565
+ const duration = Date.now() - start;
566
+ // Request completed - logged by framework
567
+ });
568
+ next();
569
+ };
570
+ },
571
+ bodySize: (options = {}) => {
572
+ const limit = options.limit || "10mb";
573
+ const limitBytes = parseSize(limit);
574
+ return (req, res, next) => {
575
+ const contentLength = parseInt(req.headers["content-length"] || "0");
576
+ if (contentLength > limitBytes) {
577
+ res.status(413).json({
578
+ success: false,
579
+ error: "Request entity too large",
580
+ limit: limit,
581
+ });
582
+ return;
583
+ }
584
+ next();
585
+ };
586
+ },
587
+ static: (options) => {
588
+ return async (req, res, next) => {
589
+ // Only handle GET and HEAD requests
590
+ if (req.method !== "GET" && req.method !== "HEAD") {
591
+ next();
592
+ return;
593
+ }
594
+ try {
595
+ const fs = await Promise.resolve().then(() => __importStar(require("fs/promises")));
596
+ const path = await Promise.resolve().then(() => __importStar(require("path")));
597
+ const crypto = await Promise.resolve().then(() => __importStar(require("crypto")));
598
+ let filePath = path.join(options.root, req.path);
599
+ // Security: prevent directory traversal
600
+ if (!filePath.startsWith(path.resolve(options.root))) {
601
+ res.status(403).json({ success: false, error: "Forbidden" });
602
+ return;
603
+ }
604
+ // Handle dotfiles
605
+ const basename = path.basename(filePath);
606
+ if (basename.startsWith(".")) {
607
+ if (options.dotfiles === "deny") {
608
+ res.status(403).json({ success: false, error: "Forbidden" });
609
+ return;
610
+ }
611
+ else if (options.dotfiles === "ignore") {
612
+ next();
613
+ return;
614
+ }
615
+ }
616
+ let stats;
617
+ try {
618
+ stats = await fs.stat(filePath);
619
+ }
620
+ catch (error) {
621
+ next(); // File not found, let other middleware handle
622
+ return;
623
+ }
624
+ // Handle directories
625
+ if (stats.isDirectory()) {
626
+ const indexFiles = options.index || ["index.html", "index.htm"];
627
+ let indexFound = false;
628
+ for (const indexFile of indexFiles) {
629
+ const indexPath = path.join(filePath, indexFile);
630
+ try {
631
+ const indexStats = await fs.stat(indexPath);
632
+ if (indexStats.isFile()) {
633
+ filePath = indexPath;
634
+ stats = indexStats;
635
+ indexFound = true;
636
+ break;
637
+ }
638
+ }
639
+ catch (error) {
640
+ // Continue to next index file
641
+ }
642
+ }
643
+ if (!indexFound) {
644
+ next();
645
+ return;
646
+ }
647
+ }
648
+ // Set headers with proper mime type and charset
649
+ const ext = path.extname(filePath);
650
+ const mimeTypes = {
651
+ ".html": "text/html",
652
+ ".css": "text/css",
653
+ ".js": "application/javascript",
654
+ ".json": "application/json",
655
+ ".png": "image/png",
656
+ ".jpg": "image/jpeg",
657
+ ".jpeg": "image/jpeg",
658
+ ".gif": "image/gif",
659
+ ".svg": "image/svg+xml",
660
+ ".ico": "image/x-icon",
661
+ ".pdf": "application/pdf",
662
+ ".txt": "text/plain",
663
+ ".xml": "application/xml",
664
+ };
665
+ const baseMimeType = mimeTypes[ext.toLowerCase()] || "application/octet-stream";
666
+ // Add charset for text-based files
667
+ const textTypes = [
668
+ "text/",
669
+ "application/json",
670
+ "application/javascript",
671
+ "application/xml",
672
+ "image/svg+xml",
673
+ ];
674
+ const needsCharset = textTypes.some((type) => baseMimeType.startsWith(type));
675
+ const contentType = needsCharset
676
+ ? `${baseMimeType}; charset=utf-8`
677
+ : baseMimeType;
678
+ res.setHeader("Content-Type", contentType);
679
+ res.setHeader("Content-Length", stats.size);
680
+ // Cache headers
681
+ if (options.maxAge) {
682
+ res.setHeader("Cache-Control", `public, max-age=${options.maxAge}`);
683
+ }
684
+ // ETag support
685
+ if (options.etag !== false) {
686
+ const etag = crypto
687
+ .createHash("md5")
688
+ .update(`${stats.mtime.getTime()}-${stats.size}`)
689
+ .digest("hex");
690
+ res.setHeader("ETag", `"${etag}"`);
691
+ // Handle conditional requests
692
+ const ifNoneMatch = req.headers["if-none-match"];
693
+ if (ifNoneMatch === `"${etag}"`) {
694
+ res.statusCode = 304;
695
+ res.end();
696
+ return;
697
+ }
698
+ }
699
+ // Handle HEAD requests
700
+ if (req.method === "HEAD") {
701
+ res.end();
702
+ return;
703
+ }
704
+ // Send file
705
+ const data = await fs.readFile(filePath);
706
+ res.end(data);
707
+ }
708
+ catch (error) {
709
+ res
710
+ .status(500)
711
+ .json({ success: false, error: "Internal server error" });
712
+ }
713
+ };
714
+ },
715
+ upload: (options = {}) => {
716
+ return (req, res, next) => {
717
+ const contentType = req.headers["content-type"] || "";
718
+ if (!contentType.includes("multipart/form-data")) {
719
+ next();
720
+ return;
721
+ }
722
+ // File upload handling is now built into parseBody method
723
+ // This middleware can add additional validation
724
+ if (req.body && req.body.files) {
725
+ const files = req.body.files;
726
+ const maxFileSize = options.maxFileSize || 5 * 1024 * 1024; // 5MB default
727
+ const maxFiles = options.maxFiles || 10;
728
+ const allowedTypes = options.allowedTypes;
729
+ // Validate file count
730
+ if (Object.keys(files).length > maxFiles) {
731
+ res.status(400).json({
732
+ success: false,
733
+ error: `Too many files. Maximum ${maxFiles} allowed.`,
734
+ });
735
+ return;
736
+ }
737
+ // Validate each file
738
+ for (const [fieldName, file] of Object.entries(files)) {
739
+ const fileData = file;
740
+ // Validate file size
741
+ if (fileData.size > maxFileSize) {
742
+ res.status(400).json({
743
+ success: false,
744
+ error: `File ${fileData.filename} is too large. Maximum ${maxFileSize} bytes allowed.`,
745
+ });
746
+ return;
747
+ }
748
+ // Validate file type
749
+ if (allowedTypes && !allowedTypes.includes(fileData.mimetype)) {
750
+ res.status(400).json({
751
+ success: false,
752
+ error: `File type ${fileData.mimetype} not allowed.`,
753
+ });
754
+ return;
755
+ }
756
+ }
757
+ // Store files in request for easy access
758
+ req.files = files;
759
+ }
760
+ next();
761
+ };
762
+ },
763
+ template: (options) => {
764
+ const templateCache = new Map();
765
+ return async (req, res, next) => {
766
+ // Add render method to response
767
+ res.render = async (template, data = {}) => {
768
+ try {
769
+ const fs = await Promise.resolve().then(() => __importStar(require("fs/promises")));
770
+ const path = await Promise.resolve().then(() => __importStar(require("path")));
771
+ const templatePath = path.join(options.views, `${template}.html`);
772
+ let templateContent;
773
+ // Check cache first
774
+ if (options.cache && templateCache.has(templatePath)) {
775
+ templateContent = templateCache.get(templatePath);
776
+ }
777
+ else {
778
+ templateContent = await fs.readFile(templatePath, "utf-8");
779
+ if (options.cache) {
780
+ templateCache.set(templatePath, templateContent);
781
+ }
782
+ }
783
+ // Simple template engine - replace {{variable}} with values
784
+ let rendered = templateContent;
785
+ // Handle basic variable substitution
786
+ rendered = rendered.replace(/\{\{(\w+)\}\}/g, (match, key) => {
787
+ return data[key] !== undefined ? String(data[key]) : match;
788
+ });
789
+ // Handle nested object properties like {{user.name}}
790
+ rendered = rendered.replace(/\{\{([\w.]+)\}\}/g, (match, key) => {
791
+ const value = key
792
+ .split(".")
793
+ .reduce((obj, prop) => obj?.[prop], data);
794
+ return value !== undefined ? String(value) : match;
795
+ });
796
+ // Handle loops: {{#each items}}{{name}}{{/each}}
797
+ rendered = rendered.replace(/\{\{#each (\w+)\}\}(.*?)\{\{\/each\}\}/gs, (match, arrayKey, template) => {
798
+ const array = data[arrayKey];
799
+ if (!Array.isArray(array))
800
+ return "";
801
+ return array
802
+ .map((item) => {
803
+ let itemTemplate = template;
804
+ // Replace variables in the loop template
805
+ itemTemplate = itemTemplate.replace(/\{\{(\w+)\}\}/g, (match, key) => {
806
+ return item[key] !== undefined
807
+ ? String(item[key])
808
+ : match;
809
+ });
810
+ return itemTemplate;
811
+ })
812
+ .join("");
813
+ });
814
+ // Handle conditionals: {{#if condition}}content{{/if}}
815
+ rendered = rendered.replace(/\{\{#if (\w+)\}\}(.*?)\{\{\/if\}\}/gs, (match, conditionKey, content) => {
816
+ const condition = data[conditionKey];
817
+ return condition ? content : "";
818
+ });
819
+ // Handle layout
820
+ if (options.defaultLayout) {
821
+ const layoutPath = path.join(options.views, "layouts", `${options.defaultLayout}.html`);
822
+ try {
823
+ let layoutContent;
824
+ if (options.cache && templateCache.has(layoutPath)) {
825
+ layoutContent = templateCache.get(layoutPath);
826
+ }
827
+ else {
828
+ layoutContent = await fs.readFile(layoutPath, "utf-8");
829
+ if (options.cache) {
830
+ templateCache.set(layoutPath, layoutContent);
831
+ }
832
+ }
833
+ rendered = layoutContent.replace(/\{\{body\}\}/, rendered);
834
+ }
835
+ catch (error) {
836
+ // Layout not found, use template as-is
837
+ }
838
+ }
839
+ res.setHeader("Content-Type", "text/html");
840
+ res.end(rendered);
841
+ }
842
+ catch (error) {
843
+ res
844
+ .status(500)
845
+ .json({ success: false, error: "Template rendering failed" });
846
+ }
847
+ };
848
+ next();
849
+ };
850
+ },
851
+ // HTTP/2 Server Push middleware
852
+ http2Push: (options = {}) => {
853
+ return (req, res, next) => {
854
+ // Add HTTP/2 push capability to response
855
+ res.push = (path, options = {}) => {
856
+ // Check if HTTP/2 is supported
857
+ if (req.httpVersion === "2.0" &&
858
+ res.stream &&
859
+ res.stream.pushAllowed) {
860
+ try {
861
+ const pushStream = res.stream.pushStream({
862
+ ":method": "GET",
863
+ ":path": path,
864
+ ...options.headers,
865
+ });
866
+ if (pushStream) {
867
+ // Handle push stream
868
+ return pushStream;
869
+ }
870
+ }
871
+ catch (error) {
872
+ // Push failed, continue normally
873
+ }
874
+ }
875
+ return null;
876
+ };
877
+ // Auto-push configured resources
878
+ if (options.resources && (!options.condition || options.condition(req))) {
879
+ for (const resource of options.resources) {
880
+ res.push?.(resource.path, {
881
+ headers: {
882
+ "content-type": resource.type || "text/plain",
883
+ },
884
+ });
885
+ }
886
+ }
887
+ next();
888
+ };
889
+ },
890
+ // Server-Sent Events middleware
891
+ sse: (options = {}) => {
892
+ return (req, res, next) => {
893
+ // Only handle SSE requests
894
+ if (req.headers.accept?.includes("text/event-stream")) {
895
+ // Set SSE headers
896
+ res.writeHead(200, {
897
+ "Content-Type": "text/event-stream",
898
+ "Cache-Control": "no-cache",
899
+ Connection: "keep-alive",
900
+ "Access-Control-Allow-Origin": options.cors ? "*" : undefined,
901
+ "Access-Control-Allow-Headers": options.cors
902
+ ? "Cache-Control"
903
+ : undefined,
904
+ });
905
+ // Add SSE methods to response
906
+ res.sendEvent = (data, event, id) => {
907
+ if (id)
908
+ res.write(`id: ${id}\n`);
909
+ if (event)
910
+ res.write(`event: ${event}\n`);
911
+ res.write(`data: ${typeof data === "string" ? data : JSON.stringify(data)}\n\n`);
912
+ };
913
+ res.sendComment = (comment) => {
914
+ res.write(`: ${comment}\n\n`);
915
+ };
916
+ res.sendRetry = (ms) => {
917
+ res.write(`retry: ${ms}\n\n`);
918
+ };
919
+ // Set up heartbeat if configured
920
+ let heartbeatInterval = null;
921
+ if (options.heartbeat) {
922
+ heartbeatInterval = setInterval(() => {
923
+ res.sendComment("heartbeat");
924
+ }, options.heartbeat);
925
+ }
926
+ // Set retry if configured
927
+ if (options.retry) {
928
+ res.sendRetry(options.retry);
929
+ }
930
+ // Clean up on close
931
+ req.on("close", () => {
932
+ if (heartbeatInterval) {
933
+ clearInterval(heartbeatInterval);
934
+ }
935
+ });
936
+ // Don't call next() - this middleware handles the response
937
+ return;
938
+ }
939
+ next();
940
+ };
941
+ },
942
+ // Range request middleware for streaming
943
+ range: (options = {}) => {
944
+ return async (req, res, next) => {
945
+ // Add range support to response
946
+ res.sendRange = async (filePath, stats) => {
947
+ try {
948
+ const fs = await Promise.resolve().then(() => __importStar(require("fs/promises")));
949
+ const path = await Promise.resolve().then(() => __importStar(require("path")));
950
+ if (!stats) {
951
+ stats = await fs.stat(filePath);
952
+ }
953
+ const fileSize = stats.size;
954
+ const range = req.headers.range;
955
+ // Set Accept-Ranges header
956
+ res.setHeader("Accept-Ranges", options.acceptRanges || "bytes");
957
+ if (!range) {
958
+ // No range requested, send entire file
959
+ res.setHeader("Content-Length", fileSize);
960
+ const data = await fs.readFile(filePath);
961
+ res.end(data);
962
+ return;
963
+ }
964
+ // Parse range header
965
+ const ranges = range
966
+ .replace(/bytes=/, "")
967
+ .split(",")
968
+ .map((r) => {
969
+ const [start, end] = r.split("-");
970
+ return {
971
+ start: start ? parseInt(start) : 0,
972
+ end: end ? parseInt(end) : fileSize - 1,
973
+ };
974
+ });
975
+ // Validate ranges
976
+ if (options.maxRanges && ranges.length > options.maxRanges) {
977
+ res.status(416).json({ success: false, error: "Too many ranges" });
978
+ return;
979
+ }
980
+ if (ranges.length === 1) {
981
+ // Single range
982
+ const { start, end } = ranges[0];
983
+ const chunkSize = end - start + 1;
984
+ if (start >= fileSize || end >= fileSize) {
985
+ res.status(416).setHeader("Content-Range", `bytes */${fileSize}`);
986
+ res.json({ success: false, error: "Range not satisfiable" });
987
+ return;
988
+ }
989
+ res.status(206);
990
+ res.setHeader("Content-Range", `bytes ${start}-${end}/${fileSize}`);
991
+ res.setHeader("Content-Length", chunkSize);
992
+ // Stream the range
993
+ const stream = require("fs").createReadStream(filePath, {
994
+ start,
995
+ end,
996
+ });
997
+ stream.pipe(res);
998
+ }
999
+ else {
1000
+ // Multiple ranges - multipart response
1001
+ const boundary = "MULTIPART_BYTERANGES";
1002
+ res.status(206);
1003
+ res.setHeader("Content-Type", `multipart/byteranges; boundary=${boundary}`);
1004
+ for (const { start, end } of ranges) {
1005
+ if (start >= fileSize || end >= fileSize)
1006
+ continue;
1007
+ const chunkSize = end - start + 1;
1008
+ res.write(`\r\n--${boundary}\r\n`);
1009
+ res.write(`Content-Range: bytes ${start}-${end}/${fileSize}\r\n\r\n`);
1010
+ const stream = require("fs").createReadStream(filePath, {
1011
+ start,
1012
+ end,
1013
+ });
1014
+ await new Promise((resolve) => {
1015
+ stream.on("end", resolve);
1016
+ stream.pipe(res, { end: false });
1017
+ });
1018
+ }
1019
+ res.write(`\r\n--${boundary}--\r\n`);
1020
+ res.end();
1021
+ }
1022
+ }
1023
+ catch (error) {
1024
+ res
1025
+ .status(500)
1026
+ .json({ success: false, error: "Range request failed" });
1027
+ }
1028
+ };
1029
+ next();
1030
+ };
1031
+ },
1032
+ // CSRF Protection middleware
1033
+ csrf: (options = {}) => {
1034
+ const secret = options.secret || "moro-csrf-secret";
1035
+ const tokenLength = options.tokenLength || 32;
1036
+ const cookieName = options.cookieName || "_csrf";
1037
+ const headerName = options.headerName || "x-csrf-token";
1038
+ const ignoreMethods = options.ignoreMethods || ["GET", "HEAD", "OPTIONS"];
1039
+ const generateToken = () => {
1040
+ const crypto = require("crypto");
1041
+ return crypto.randomBytes(tokenLength).toString("hex");
1042
+ };
1043
+ const verifyToken = (token, sessionToken) => {
1044
+ return token && sessionToken && token === sessionToken;
1045
+ };
1046
+ return (req, res, next) => {
1047
+ // Add CSRF token generation method
1048
+ req.csrfToken = () => {
1049
+ if (!req._csrfToken) {
1050
+ req._csrfToken = generateToken();
1051
+ // Set token in cookie
1052
+ res.cookie(cookieName, req._csrfToken, {
1053
+ httpOnly: true,
1054
+ sameSite: options.sameSite !== false ? "strict" : undefined,
1055
+ secure: req.headers["x-forwarded-proto"] === "https" ||
1056
+ req.socket.encrypted,
1057
+ });
1058
+ }
1059
+ return req._csrfToken;
1060
+ };
1061
+ // Skip verification for safe methods
1062
+ if (ignoreMethods.includes(req.method)) {
1063
+ next();
1064
+ return;
1065
+ }
1066
+ // Get token from header or body
1067
+ const token = req.headers[headerName] ||
1068
+ (req.body && req.body._csrf) ||
1069
+ (req.query && req.query._csrf);
1070
+ // Get session token from cookie
1071
+ const sessionToken = req.cookies?.[cookieName];
1072
+ if (!verifyToken(token, sessionToken || "")) {
1073
+ res.status(403).json({
1074
+ success: false,
1075
+ error: "Invalid CSRF token",
1076
+ code: "CSRF_TOKEN_MISMATCH",
1077
+ });
1078
+ return;
1079
+ }
1080
+ next();
1081
+ };
1082
+ },
1083
+ // Content Security Policy middleware
1084
+ csp: (options = {}) => {
1085
+ return (req, res, next) => {
1086
+ const directives = options.directives || {
1087
+ defaultSrc: ["'self'"],
1088
+ scriptSrc: ["'self'"],
1089
+ styleSrc: ["'self'", "'unsafe-inline'"],
1090
+ imgSrc: ["'self'", "data:", "https:"],
1091
+ connectSrc: ["'self'"],
1092
+ fontSrc: ["'self'"],
1093
+ objectSrc: ["'none'"],
1094
+ mediaSrc: ["'self'"],
1095
+ frameSrc: ["'none'"],
1096
+ };
1097
+ // Generate nonce if requested
1098
+ let nonce;
1099
+ if (options.nonce) {
1100
+ const crypto = require("crypto");
1101
+ nonce = crypto.randomBytes(16).toString("base64");
1102
+ req.cspNonce = nonce;
1103
+ }
1104
+ // Build CSP header value
1105
+ const cspParts = [];
1106
+ for (const [directive, sources] of Object.entries(directives)) {
1107
+ if (directive === "upgradeInsecureRequests" && sources === true) {
1108
+ cspParts.push("upgrade-insecure-requests");
1109
+ }
1110
+ else if (directive === "blockAllMixedContent" && sources === true) {
1111
+ cspParts.push("block-all-mixed-content");
1112
+ }
1113
+ else if (Array.isArray(sources)) {
1114
+ let sourceList = sources.join(" ");
1115
+ // Add nonce to script-src and style-src if enabled
1116
+ if (nonce &&
1117
+ (directive === "scriptSrc" || directive === "styleSrc")) {
1118
+ sourceList += ` 'nonce-${nonce}'`;
1119
+ }
1120
+ // Convert camelCase to kebab-case
1121
+ const kebabDirective = directive
1122
+ .replace(/([A-Z])/g, "-$1")
1123
+ .toLowerCase();
1124
+ cspParts.push(`${kebabDirective} ${sourceList}`);
1125
+ }
1126
+ }
1127
+ // Add report-uri if specified
1128
+ if (options.reportUri) {
1129
+ cspParts.push(`report-uri ${options.reportUri}`);
1130
+ }
1131
+ const cspValue = cspParts.join("; ");
1132
+ const headerName = options.reportOnly
1133
+ ? "Content-Security-Policy-Report-Only"
1134
+ : "Content-Security-Policy";
1135
+ res.setHeader(headerName, cspValue);
1136
+ next();
1137
+ };
1138
+ },
1139
+ };
1140
+ function parseSize(size) {
1141
+ const units = {
1142
+ b: 1,
1143
+ kb: 1024,
1144
+ mb: 1024 * 1024,
1145
+ gb: 1024 * 1024 * 1024,
1146
+ };
1147
+ const match = size.toLowerCase().match(/^(\d+(?:\.\d+)?)\s*(b|kb|mb|gb)?$/);
1148
+ if (!match)
1149
+ return 1024 * 1024; // Default 1MB
1150
+ const value = parseFloat(match[1]);
1151
+ const unit = match[2] || "b";
1152
+ return Math.round(value * units[unit]);
1153
+ }
1154
+ //# sourceMappingURL=http-server.js.map