@morojs/moro 1.7.30 → 1.8.1

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 (373) hide show
  1. package/README.md +89 -46
  2. package/dist/core/auth/morojs-adapter.js +95 -45
  3. package/dist/core/auth/morojs-adapter.js.map +1 -1
  4. package/dist/core/config/config-manager.js.map +1 -1
  5. package/dist/core/config/config-sources.js +119 -48
  6. package/dist/core/config/config-sources.js.map +1 -1
  7. package/dist/core/config/config-validator.js +189 -8
  8. package/dist/core/config/config-validator.js.map +1 -1
  9. package/dist/core/config/file-loader.js.map +1 -1
  10. package/dist/core/config/index.js.map +1 -1
  11. package/dist/core/config/schema.js +35 -4
  12. package/dist/core/config/schema.js.map +1 -1
  13. package/dist/core/config/utils.js.map +1 -1
  14. package/dist/core/database/adapters/drizzle.js +2 -2
  15. package/dist/core/database/adapters/drizzle.js.map +1 -1
  16. package/dist/core/database/adapters/index.js.map +1 -1
  17. package/dist/core/database/adapters/mongodb.d.ts +1 -0
  18. package/dist/core/database/adapters/mongodb.js +16 -0
  19. package/dist/core/database/adapters/mongodb.js.map +1 -1
  20. package/dist/core/database/adapters/mysql.d.ts +1 -0
  21. package/dist/core/database/adapters/mysql.js +13 -2
  22. package/dist/core/database/adapters/mysql.js.map +1 -1
  23. package/dist/core/database/adapters/postgresql.js +2 -2
  24. package/dist/core/database/adapters/postgresql.js.map +1 -1
  25. package/dist/core/database/adapters/redis.js.map +1 -1
  26. package/dist/core/database/adapters/sql-safety.js +5 -4
  27. package/dist/core/database/adapters/sql-safety.js.map +1 -1
  28. package/dist/core/database/adapters/sqlite.js.map +1 -1
  29. package/dist/core/database/index.js.map +1 -1
  30. package/dist/core/docs/index.js +11 -7
  31. package/dist/core/docs/index.js.map +1 -1
  32. package/dist/core/docs/openapi-generator.js +8 -6
  33. package/dist/core/docs/openapi-generator.js.map +1 -1
  34. package/dist/core/docs/schema-to-openapi.js +3 -3
  35. package/dist/core/docs/schema-to-openapi.js.map +1 -1
  36. package/dist/core/docs/simple-docs.js.map +1 -1
  37. package/dist/core/docs/swagger-ui.js.map +1 -1
  38. package/dist/core/docs/zod-to-openapi.js +2 -2
  39. package/dist/core/docs/zod-to-openapi.js.map +1 -1
  40. package/dist/core/events/event-bus.js +14 -5
  41. package/dist/core/events/event-bus.js.map +1 -1
  42. package/dist/core/events/index.js.map +1 -1
  43. package/dist/core/framework.d.ts +41 -3
  44. package/dist/core/framework.js +342 -95
  45. package/dist/core/framework.js.map +1 -1
  46. package/dist/core/graphql/adapter.js.map +1 -1
  47. package/dist/core/graphql/adapters/graphql-js-adapter.js.map +1 -1
  48. package/dist/core/graphql/core.js.map +1 -1
  49. package/dist/core/graphql/index.js.map +1 -1
  50. package/dist/core/graphql/loader.js.map +1 -1
  51. package/dist/core/graphql/types.js.map +1 -1
  52. package/dist/core/grpc/adapters/grpc-js-adapter.js +4 -0
  53. package/dist/core/grpc/adapters/grpc-js-adapter.js.map +1 -1
  54. package/dist/core/grpc/adapters/index.js.map +1 -1
  55. package/dist/core/grpc/grpc-adapter.js.map +1 -1
  56. package/dist/core/grpc/grpc-manager.js.map +1 -1
  57. package/dist/core/grpc/index.js.map +1 -1
  58. package/dist/core/grpc/middleware/auth.js +1 -1
  59. package/dist/core/grpc/middleware/auth.js.map +1 -1
  60. package/dist/core/grpc/middleware/logging.js.map +1 -1
  61. package/dist/core/grpc/middleware/validation.js +1 -1
  62. package/dist/core/grpc/middleware/validation.js.map +1 -1
  63. package/dist/core/grpc/types.d.ts +7 -7
  64. package/dist/core/grpc/types.js.map +1 -1
  65. package/dist/core/http/http-server.d.ts +17 -23
  66. package/dist/core/http/http-server.js +109 -249
  67. package/dist/core/http/http-server.js.map +1 -1
  68. package/dist/core/http/http2-server.d.ts +6 -16
  69. package/dist/core/http/http2-server.js +40 -83
  70. package/dist/core/http/http2-server.js.map +1 -1
  71. package/dist/core/http/index.d.ts +1 -0
  72. package/dist/core/http/index.js +3 -0
  73. package/dist/core/http/index.js.map +1 -1
  74. package/dist/core/http/moro-engine-server.d.ts +263 -0
  75. package/dist/core/http/moro-engine-server.js +1962 -0
  76. package/dist/core/http/moro-engine-server.js.map +1 -0
  77. package/dist/core/http/utils/compression.d.ts +47 -0
  78. package/dist/core/http/utils/compression.js +132 -0
  79. package/dist/core/http/utils/compression.js.map +1 -0
  80. package/dist/core/http/utils/lazy-event-emitter.d.ts +11 -0
  81. package/dist/core/http/utils/lazy-event-emitter.js +69 -0
  82. package/dist/core/http/utils/lazy-event-emitter.js.map +1 -0
  83. package/dist/core/http/utils/multipart-parser.d.ts +32 -0
  84. package/dist/core/http/utils/multipart-parser.js +215 -0
  85. package/dist/core/http/utils/multipart-parser.js.map +1 -0
  86. package/dist/core/http/utils/query-parser.d.ts +1 -0
  87. package/dist/core/http/utils/query-parser.js +44 -0
  88. package/dist/core/http/utils/query-parser.js.map +1 -0
  89. package/dist/core/http/utils/size.d.ts +33 -0
  90. package/dist/core/http/utils/size.js +37 -0
  91. package/dist/core/http/utils/size.js.map +1 -0
  92. package/dist/core/http/utils/ssl-config.d.ts +69 -0
  93. package/dist/core/http/utils/ssl-config.js +116 -0
  94. package/dist/core/http/utils/ssl-config.js.map +1 -0
  95. package/dist/core/http/utils/uws-worker-clustering.js +45 -32
  96. package/dist/core/http/utils/uws-worker-clustering.js.map +1 -1
  97. package/dist/core/http/uws-http-server.d.ts +25 -3
  98. package/dist/core/http/uws-http-server.js +465 -86
  99. package/dist/core/http/uws-http-server.js.map +1 -1
  100. package/dist/core/jobs/cron-parser.js +7 -5
  101. package/dist/core/jobs/cron-parser.js.map +1 -1
  102. package/dist/core/jobs/index.js.map +1 -1
  103. package/dist/core/jobs/job-executor.d.ts +1 -1
  104. package/dist/core/jobs/job-executor.js.map +1 -1
  105. package/dist/core/jobs/job-scheduler.d.ts +3 -3
  106. package/dist/core/jobs/job-scheduler.js.map +1 -1
  107. package/dist/core/jobs/job-state-manager.d.ts +3 -3
  108. package/dist/core/jobs/job-state-manager.js +25 -4
  109. package/dist/core/jobs/job-state-manager.js.map +1 -1
  110. package/dist/core/jobs/leader-election.d.ts +15 -0
  111. package/dist/core/jobs/leader-election.js +158 -69
  112. package/dist/core/jobs/leader-election.js.map +1 -1
  113. package/dist/core/jobs/types.d.ts +4 -4
  114. package/dist/core/jobs/types.js.map +1 -1
  115. package/dist/core/jobs/utils.d.ts +1 -1
  116. package/dist/core/jobs/utils.js +7 -3
  117. package/dist/core/jobs/utils.js.map +1 -1
  118. package/dist/core/logger/filters.js +3 -2
  119. package/dist/core/logger/filters.js.map +1 -1
  120. package/dist/core/logger/index.js.map +1 -1
  121. package/dist/core/logger/logger.js +56 -25
  122. package/dist/core/logger/logger.js.map +1 -1
  123. package/dist/core/logger/outputs.js.map +1 -1
  124. package/dist/core/mail/adapters/console-adapter.js.map +1 -1
  125. package/dist/core/mail/adapters/index.js.map +1 -1
  126. package/dist/core/mail/adapters/nodemailer-adapter.js.map +1 -1
  127. package/dist/core/mail/adapters/resend-adapter.js.map +1 -1
  128. package/dist/core/mail/adapters/sendgrid-adapter.js.map +1 -1
  129. package/dist/core/mail/adapters/ses-adapter.js.map +1 -1
  130. package/dist/core/mail/index.js.map +1 -1
  131. package/dist/core/mail/mail-adapter.js.map +1 -1
  132. package/dist/core/mail/mail-manager.d.ts +10 -0
  133. package/dist/core/mail/mail-manager.js +21 -5
  134. package/dist/core/mail/mail-manager.js.map +1 -1
  135. package/dist/core/mail/template-engine.js.map +1 -1
  136. package/dist/core/mail/types.js.map +1 -1
  137. package/dist/core/middleware/built-in/auth/core.js +3 -3
  138. package/dist/core/middleware/built-in/auth/core.js.map +1 -1
  139. package/dist/core/middleware/built-in/auth/helpers.js +4 -4
  140. package/dist/core/middleware/built-in/auth/helpers.js.map +1 -1
  141. package/dist/core/middleware/built-in/auth/hook.js.map +1 -1
  142. package/dist/core/middleware/built-in/auth/index.js.map +1 -1
  143. package/dist/core/middleware/built-in/auth/jwt-helpers.d.ts +4 -4
  144. package/dist/core/middleware/built-in/auth/jwt-helpers.js.map +1 -1
  145. package/dist/core/middleware/built-in/auth/middleware.js.map +1 -1
  146. package/dist/core/middleware/built-in/auth/providers.js.map +1 -1
  147. package/dist/core/middleware/built-in/body-parsers/index.js.map +1 -1
  148. package/dist/core/middleware/built-in/body-size/core.js +2 -2
  149. package/dist/core/middleware/built-in/body-size/core.js.map +1 -1
  150. package/dist/core/middleware/built-in/body-size/hook.js.map +1 -1
  151. package/dist/core/middleware/built-in/body-size/index.js.map +1 -1
  152. package/dist/core/middleware/built-in/body-size/middleware.js.map +1 -1
  153. package/dist/core/middleware/built-in/cache/adapters/cache/file.js +3 -1
  154. package/dist/core/middleware/built-in/cache/adapters/cache/file.js.map +1 -1
  155. package/dist/core/middleware/built-in/cache/adapters/cache/index.js.map +1 -1
  156. package/dist/core/middleware/built-in/cache/adapters/cache/memory.js.map +1 -1
  157. package/dist/core/middleware/built-in/cache/adapters/cache/redis.js.map +1 -1
  158. package/dist/core/middleware/built-in/cache/adapters/index.js.map +1 -1
  159. package/dist/core/middleware/built-in/cache/core.js.map +1 -1
  160. package/dist/core/middleware/built-in/cache/hook.js +3 -3
  161. package/dist/core/middleware/built-in/cache/hook.js.map +1 -1
  162. package/dist/core/middleware/built-in/cache/index.js.map +1 -1
  163. package/dist/core/middleware/built-in/cache/middleware.js.map +1 -1
  164. package/dist/core/middleware/built-in/cdn/adapters/cdn/azure.js.map +1 -1
  165. package/dist/core/middleware/built-in/cdn/adapters/cdn/cloudflare.js.map +1 -1
  166. package/dist/core/middleware/built-in/cdn/adapters/cdn/cloudfront.js.map +1 -1
  167. package/dist/core/middleware/built-in/cdn/adapters/cdn/index.js.map +1 -1
  168. package/dist/core/middleware/built-in/cdn/adapters/index.js.map +1 -1
  169. package/dist/core/middleware/built-in/cdn/core.js.map +1 -1
  170. package/dist/core/middleware/built-in/cdn/hook.js.map +1 -1
  171. package/dist/core/middleware/built-in/cdn/index.js.map +1 -1
  172. package/dist/core/middleware/built-in/cdn/middleware.js.map +1 -1
  173. package/dist/core/middleware/built-in/compression/core.d.ts +3 -0
  174. package/dist/core/middleware/built-in/compression/core.js +28 -34
  175. package/dist/core/middleware/built-in/compression/core.js.map +1 -1
  176. package/dist/core/middleware/built-in/compression/hook.js.map +1 -1
  177. package/dist/core/middleware/built-in/compression/index.js.map +1 -1
  178. package/dist/core/middleware/built-in/compression/middleware.js.map +1 -1
  179. package/dist/core/middleware/built-in/cookie/core.js.map +1 -1
  180. package/dist/core/middleware/built-in/cookie/hook.js.map +1 -1
  181. package/dist/core/middleware/built-in/cookie/index.js.map +1 -1
  182. package/dist/core/middleware/built-in/cookie/middleware.js.map +1 -1
  183. package/dist/core/middleware/built-in/cors/core.js.map +1 -1
  184. package/dist/core/middleware/built-in/cors/hook.js.map +1 -1
  185. package/dist/core/middleware/built-in/cors/index.js.map +1 -1
  186. package/dist/core/middleware/built-in/cors/middleware.js.map +1 -1
  187. package/dist/core/middleware/built-in/csp/core.js.map +1 -1
  188. package/dist/core/middleware/built-in/csp/hook.js.map +1 -1
  189. package/dist/core/middleware/built-in/csp/index.js.map +1 -1
  190. package/dist/core/middleware/built-in/csp/middleware.js.map +1 -1
  191. package/dist/core/middleware/built-in/csrf/core.js +1 -1
  192. package/dist/core/middleware/built-in/csrf/core.js.map +1 -1
  193. package/dist/core/middleware/built-in/csrf/hook.js.map +1 -1
  194. package/dist/core/middleware/built-in/csrf/index.js.map +1 -1
  195. package/dist/core/middleware/built-in/csrf/middleware.js.map +1 -1
  196. package/dist/core/middleware/built-in/error-tracker/index.js.map +1 -1
  197. package/dist/core/middleware/built-in/error-tracker/middleware.js.map +1 -1
  198. package/dist/core/middleware/built-in/graphql/core.js.map +1 -1
  199. package/dist/core/middleware/built-in/graphql/helpers.js.map +1 -1
  200. package/dist/core/middleware/built-in/graphql/hook.js.map +1 -1
  201. package/dist/core/middleware/built-in/graphql/index.js.map +1 -1
  202. package/dist/core/middleware/built-in/graphql/middleware.js.map +1 -1
  203. package/dist/core/middleware/built-in/graphql/subscriptions.js.map +1 -1
  204. package/dist/core/middleware/built-in/helmet/core.js.map +1 -1
  205. package/dist/core/middleware/built-in/helmet/hook.js.map +1 -1
  206. package/dist/core/middleware/built-in/helmet/index.js.map +1 -1
  207. package/dist/core/middleware/built-in/helmet/middleware.js.map +1 -1
  208. package/dist/core/middleware/built-in/http2/core.js.map +1 -1
  209. package/dist/core/middleware/built-in/http2/hook.js.map +1 -1
  210. package/dist/core/middleware/built-in/http2/index.js.map +1 -1
  211. package/dist/core/middleware/built-in/http2/middleware.js.map +1 -1
  212. package/dist/core/middleware/built-in/index.js.map +1 -1
  213. package/dist/core/middleware/built-in/performance-monitor/index.js.map +1 -1
  214. package/dist/core/middleware/built-in/performance-monitor/middleware.js.map +1 -1
  215. package/dist/core/middleware/built-in/range/core.js +4 -1
  216. package/dist/core/middleware/built-in/range/core.js.map +1 -1
  217. package/dist/core/middleware/built-in/range/hook.js.map +1 -1
  218. package/dist/core/middleware/built-in/range/index.js.map +1 -1
  219. package/dist/core/middleware/built-in/range/middleware.js.map +1 -1
  220. package/dist/core/middleware/built-in/rate-limit/core.js.map +1 -1
  221. package/dist/core/middleware/built-in/rate-limit/hook.js.map +1 -1
  222. package/dist/core/middleware/built-in/rate-limit/index.js.map +1 -1
  223. package/dist/core/middleware/built-in/rate-limit/middleware.js.map +1 -1
  224. package/dist/core/middleware/built-in/request-logger/index.js.map +1 -1
  225. package/dist/core/middleware/built-in/request-logger/middleware.js.map +1 -1
  226. package/dist/core/middleware/built-in/session/core.js +4 -4
  227. package/dist/core/middleware/built-in/session/core.js.map +1 -1
  228. package/dist/core/middleware/built-in/session/hook.js.map +1 -1
  229. package/dist/core/middleware/built-in/session/index.js.map +1 -1
  230. package/dist/core/middleware/built-in/session/middleware.js.map +1 -1
  231. package/dist/core/middleware/built-in/sse/core.js.map +1 -1
  232. package/dist/core/middleware/built-in/sse/hook.js.map +1 -1
  233. package/dist/core/middleware/built-in/sse/index.js.map +1 -1
  234. package/dist/core/middleware/built-in/sse/middleware.js.map +1 -1
  235. package/dist/core/middleware/built-in/static/core.js.map +1 -1
  236. package/dist/core/middleware/built-in/static/hook.js.map +1 -1
  237. package/dist/core/middleware/built-in/static/index.js.map +1 -1
  238. package/dist/core/middleware/built-in/static/middleware.js.map +1 -1
  239. package/dist/core/middleware/built-in/template/core.js +1 -1
  240. package/dist/core/middleware/built-in/template/core.js.map +1 -1
  241. package/dist/core/middleware/built-in/template/hook.js.map +1 -1
  242. package/dist/core/middleware/built-in/template/index.js.map +1 -1
  243. package/dist/core/middleware/built-in/template/middleware.js.map +1 -1
  244. package/dist/core/middleware/built-in/upload/core.js.map +1 -1
  245. package/dist/core/middleware/built-in/upload/hook.js.map +1 -1
  246. package/dist/core/middleware/built-in/upload/index.js.map +1 -1
  247. package/dist/core/middleware/built-in/upload/middleware.js.map +1 -1
  248. package/dist/core/middleware/built-in/validation/core.js.map +1 -1
  249. package/dist/core/middleware/built-in/validation/hook.js.map +1 -1
  250. package/dist/core/middleware/built-in/validation/index.js.map +1 -1
  251. package/dist/core/middleware/built-in/validation/middleware.js.map +1 -1
  252. package/dist/core/middleware/index.js +2 -2
  253. package/dist/core/middleware/index.js.map +1 -1
  254. package/dist/core/modules/auto-discovery.js +11 -7
  255. package/dist/core/modules/auto-discovery.js.map +1 -1
  256. package/dist/core/modules/index.js.map +1 -1
  257. package/dist/core/modules/modules.js +18 -13
  258. package/dist/core/modules/modules.js.map +1 -1
  259. package/dist/core/networking/adapters/engine-adapter.d.ts +23 -0
  260. package/dist/core/networking/adapters/engine-adapter.js +365 -0
  261. package/dist/core/networking/adapters/engine-adapter.js.map +1 -0
  262. package/dist/core/networking/adapters/index.d.ts +1 -0
  263. package/dist/core/networking/adapters/index.js +1 -0
  264. package/dist/core/networking/adapters/index.js.map +1 -1
  265. package/dist/core/networking/adapters/socketio-adapter.d.ts +1 -0
  266. package/dist/core/networking/adapters/socketio-adapter.js +12 -1
  267. package/dist/core/networking/adapters/socketio-adapter.js.map +1 -1
  268. package/dist/core/networking/adapters/uws-adapter.js +27 -16
  269. package/dist/core/networking/adapters/uws-adapter.js.map +1 -1
  270. package/dist/core/networking/adapters/ws-adapter.d.ts +15 -0
  271. package/dist/core/networking/adapters/ws-adapter.js +120 -6
  272. package/dist/core/networking/adapters/ws-adapter.js.map +1 -1
  273. package/dist/core/networking/index.js.map +1 -1
  274. package/dist/core/networking/service-discovery.js +7 -5
  275. package/dist/core/networking/service-discovery.js.map +1 -1
  276. package/dist/core/networking/websocket-adapter.d.ts +22 -3
  277. package/dist/core/networking/websocket-adapter.js.map +1 -1
  278. package/dist/core/networking/websocket-manager.js +70 -67
  279. package/dist/core/networking/websocket-manager.js.map +1 -1
  280. package/dist/core/pooling/object-pool-manager.js +6 -0
  281. package/dist/core/pooling/object-pool-manager.js.map +1 -1
  282. package/dist/core/queue/adapters/bull-adapter.js.map +1 -1
  283. package/dist/core/queue/adapters/index.js.map +1 -1
  284. package/dist/core/queue/adapters/kafka-adapter.js +2 -2
  285. package/dist/core/queue/adapters/kafka-adapter.js.map +1 -1
  286. package/dist/core/queue/adapters/memory-adapter.js +6 -6
  287. package/dist/core/queue/adapters/memory-adapter.js.map +1 -1
  288. package/dist/core/queue/adapters/rabbitmq-adapter.d.ts +50 -0
  289. package/dist/core/queue/adapters/rabbitmq-adapter.js +176 -20
  290. package/dist/core/queue/adapters/rabbitmq-adapter.js.map +1 -1
  291. package/dist/core/queue/adapters/sqs-adapter.d.ts +12 -2
  292. package/dist/core/queue/adapters/sqs-adapter.js +75 -24
  293. package/dist/core/queue/adapters/sqs-adapter.js.map +1 -1
  294. package/dist/core/queue/index.js.map +1 -1
  295. package/dist/core/queue/middleware/index.js.map +1 -1
  296. package/dist/core/queue/middleware/monitoring.d.ts +1 -1
  297. package/dist/core/queue/middleware/monitoring.js.map +1 -1
  298. package/dist/core/queue/middleware/priority.js +2 -1
  299. package/dist/core/queue/middleware/priority.js.map +1 -1
  300. package/dist/core/queue/middleware/rate-limit.js.map +1 -1
  301. package/dist/core/queue/queue-adapter.js.map +1 -1
  302. package/dist/core/queue/queue-manager.js.map +1 -1
  303. package/dist/core/queue/types.d.ts +8 -8
  304. package/dist/core/queue/types.js.map +1 -1
  305. package/dist/core/routing/app-integration.js.map +1 -1
  306. package/dist/core/routing/create-router.js.map +1 -1
  307. package/dist/core/routing/index.js.map +1 -1
  308. package/dist/core/routing/path-matcher.js.map +1 -1
  309. package/dist/core/routing/radix-tree.js.map +1 -1
  310. package/dist/core/routing/router.js.map +1 -1
  311. package/dist/core/routing/unified-router.js +12 -4
  312. package/dist/core/routing/unified-router.js.map +1 -1
  313. package/dist/core/runtime/aws-lambda-adapter.js.map +1 -1
  314. package/dist/core/runtime/base-adapter.js +2 -2
  315. package/dist/core/runtime/base-adapter.js.map +1 -1
  316. package/dist/core/runtime/cloudflare-workers-adapter.js +2 -0
  317. package/dist/core/runtime/cloudflare-workers-adapter.js.map +1 -1
  318. package/dist/core/runtime/index.js.map +1 -1
  319. package/dist/core/runtime/node-adapter.js +19 -17
  320. package/dist/core/runtime/node-adapter.js.map +1 -1
  321. package/dist/core/runtime/vercel-edge-adapter.js +2 -0
  322. package/dist/core/runtime/vercel-edge-adapter.js.map +1 -1
  323. package/dist/core/utilities/circuit-breaker.d.ts +1 -0
  324. package/dist/core/utilities/circuit-breaker.js +25 -2
  325. package/dist/core/utilities/circuit-breaker.js.map +1 -1
  326. package/dist/core/utilities/container.js +9 -9
  327. package/dist/core/utilities/container.js.map +1 -1
  328. package/dist/core/utilities/hooks.js +28 -6
  329. package/dist/core/utilities/hooks.js.map +1 -1
  330. package/dist/core/utilities/index.js.map +1 -1
  331. package/dist/core/utilities/module-path.js.map +1 -1
  332. package/dist/core/utilities/package-utils.d.ts +68 -0
  333. package/dist/core/utilities/package-utils.js +198 -1
  334. package/dist/core/utilities/package-utils.js.map +1 -1
  335. package/dist/core/utilities/response-helpers.js +1 -1
  336. package/dist/core/utilities/response-helpers.js.map +1 -1
  337. package/dist/core/validation/adapters.js.map +1 -1
  338. package/dist/core/validation/error-handler.js.map +1 -1
  339. package/dist/core/validation/index.js.map +1 -1
  340. package/dist/core/validation/schema-interface.js.map +1 -1
  341. package/dist/core/workers/facade.js.map +1 -1
  342. package/dist/core/workers/index.js.map +1 -1
  343. package/dist/core/workers/worker-entry.d.ts +1 -0
  344. package/dist/core/workers/worker-entry.js +12 -0
  345. package/dist/core/workers/worker-entry.js.map +1 -0
  346. package/dist/core/workers/worker-manager.d.ts +23 -2
  347. package/dist/core/workers/worker-manager.js +117 -17
  348. package/dist/core/workers/worker-manager.js.map +1 -1
  349. package/dist/core/workers/worker.js +8 -6
  350. package/dist/core/workers/worker.js.map +1 -1
  351. package/dist/index.d.ts +2 -2
  352. package/dist/index.js +5 -2
  353. package/dist/index.js.map +1 -1
  354. package/dist/moro.d.ts +20 -1
  355. package/dist/moro.js +230 -70
  356. package/dist/moro.js.map +1 -1
  357. package/dist/types/auth.d.ts +4 -4
  358. package/dist/types/auth.js.map +1 -1
  359. package/dist/types/cache.js.map +1 -1
  360. package/dist/types/cdn.js.map +1 -1
  361. package/dist/types/config.d.ts +100 -15
  362. package/dist/types/config.js.map +1 -1
  363. package/dist/types/core.js.map +1 -1
  364. package/dist/types/database.js.map +1 -1
  365. package/dist/types/discovery.js.map +1 -1
  366. package/dist/types/events.js.map +1 -1
  367. package/dist/types/hooks.js.map +1 -1
  368. package/dist/types/http.js.map +1 -1
  369. package/dist/types/logger.js.map +1 -1
  370. package/dist/types/module.js.map +1 -1
  371. package/dist/types/runtime.js.map +1 -1
  372. package/dist/types/session.js.map +1 -1
  373. package/package.json +32 -17
@@ -3,27 +3,13 @@
3
3
  import cluster from 'cluster';
4
4
  import { randomUUID } from 'crypto';
5
5
  import { createFrameworkLogger } from '../logger/index.js';
6
- function parseUwsQueryString(queryString) {
7
- const result = {};
8
- if (!queryString)
9
- return result;
10
- const pairs = queryString.split('&');
11
- for (let i = 0; i < pairs.length; i++) {
12
- const pair = pairs[i];
13
- const eqIdx = pair.indexOf('=');
14
- if (eqIdx > 0) {
15
- const key = pair.substring(0, eqIdx);
16
- const value = pair.substring(eqIdx + 1);
17
- try {
18
- result[decodeURIComponent(key)] = decodeURIComponent(value);
19
- }
20
- catch {
21
- result[key] = value;
22
- }
23
- }
24
- }
25
- return result;
26
- }
6
+ import { loadNativeEngine, getNativeEngineLoadErrors } from '../utilities/package-utils.js';
7
+ import { parseMultipart as parseMultipartBuffer, } from './utils/multipart-parser.js';
8
+ import { LazyEventEmitter } from './utils/lazy-event-emitter.js';
9
+ import { parseRawQueryString } from './utils/query-parser.js';
10
+ import { resolveCompressionSettings, compressBuffer, isCompressible, negotiateEncoding, } from './utils/compression.js';
11
+ // Back-compat alias (this parser is now the transport-neutral one in utils/)
12
+ const parseUwsQueryString = parseRawQueryString;
27
13
  /**
28
14
  * Prototype-based request object for the uWS adapter.
29
15
  *
@@ -33,7 +19,7 @@ function parseUwsQueryString(queryString) {
33
19
  * so `materialize()` MUST be called before any `await` - it snapshots the
34
20
  * headers and detaches the native reference.
35
21
  */
36
- export class UwsRequest {
22
+ export class UwsRequest extends LazyEventEmitter {
37
23
  method;
38
24
  path;
39
25
  url;
@@ -48,7 +34,9 @@ export class UwsRequest {
48
34
  _cookies = undefined;
49
35
  _context = undefined;
50
36
  _requestId = undefined;
37
+ _socket = undefined;
51
38
  constructor(server, uwsReq, method, url, queryString) {
39
+ super();
52
40
  this._server = server;
53
41
  this._uwsReq = uwsReq;
54
42
  this.method = method;
@@ -56,6 +44,35 @@ export class UwsRequest {
56
44
  this.url = url;
57
45
  this._queryString = queryString || null;
58
46
  }
47
+ // net.Socket-shaped shim so Node-style middleware (CSRF's req.socket.encrypted,
48
+ // rate-limiters' req.connection.remoteAddress) doesn't throw on this transport.
49
+ // uWS terminates plaintext HTTP here (TLS via its own listen options or a
50
+ // proxy); scheme detection should use x-forwarded-proto.
51
+ get socket() {
52
+ let s = this._socket;
53
+ if (s === undefined) {
54
+ const getIp = () => this.ip; // arrow captures this lexically (no this-alias)
55
+ s = {
56
+ get remoteAddress() {
57
+ return getIp() || undefined;
58
+ },
59
+ remotePort: undefined,
60
+ encrypted: false,
61
+ destroyed: false,
62
+ };
63
+ this._socket = s;
64
+ }
65
+ return s;
66
+ }
67
+ set socket(value) {
68
+ this._socket = value;
69
+ }
70
+ get connection() {
71
+ return this.socket;
72
+ }
73
+ set connection(value) {
74
+ this._socket = value;
75
+ }
59
76
  /** Snapshot uWS-backed data and detach the native request. Idempotent.
60
77
  * Must be called before returning control to the event loop if the
61
78
  * request object outlives the synchronous route callback. */
@@ -109,6 +126,8 @@ export class UwsRequest {
109
126
  const parts = header.split(';');
110
127
  for (let i = 0; i < parts.length; i++) {
111
128
  const part = parts[i];
129
+ if (part === undefined)
130
+ continue;
112
131
  const eq = part.indexOf('=');
113
132
  if (eq > 0) {
114
133
  const name = part.substring(0, eq).trim();
@@ -157,12 +176,12 @@ export class UwsRequest {
157
176
  // ==== Express-compatible request helpers (parity with the Node adapter) ====
158
177
  get hostname() {
159
178
  const host = this.headers.host || '';
160
- return host ? host.split(':')[0] : '';
179
+ return host ? (host.split(':')[0] ?? '') : '';
161
180
  }
162
181
  get protocol() {
163
182
  const forwardedProto = this.headers['x-forwarded-proto'];
164
183
  if (forwardedProto)
165
- return forwardedProto.split(',')[0].trim();
184
+ return (forwardedProto.split(',')[0] ?? '').trim();
166
185
  return this._server && this._server.isSsl ? 'https' : 'http';
167
186
  }
168
187
  get secure() {
@@ -203,7 +222,7 @@ export class UwsRequest {
203
222
  const ct = this.headers['content-type'] || '';
204
223
  if (!ct)
205
224
  return false;
206
- const mime = ct.split(';')[0].trim().toLowerCase();
225
+ const mime = (ct.split(';')[0] ?? '').trim().toLowerCase();
207
226
  const t = type.toLowerCase();
208
227
  if (t.indexOf('/') === -1) {
209
228
  return mime.endsWith(`/${t}`) || mime.endsWith(`+${t}`);
@@ -220,7 +239,7 @@ export class UwsRequest {
220
239
  const wanted = Array.isArray(types) ? types : [types];
221
240
  if (accept === '*/*' || accept === '')
222
241
  return wanted[0] || false;
223
- const acceptTypes = accept.split(',').map(s => s.split(';')[0].trim().toLowerCase());
242
+ const acceptTypes = accept.split(',').map(s => (s.split(';')[0] ?? '').trim().toLowerCase());
224
243
  for (const w of wanted) {
225
244
  const wl = w.toLowerCase();
226
245
  const wMime = wl.indexOf('/') === -1 ? `application/${wl}` : wl;
@@ -246,7 +265,7 @@ export class UwsRequest {
246
265
  .split(',')
247
266
  .map(s => {
248
267
  const parts = s.trim().split(';');
249
- const tag = parts[0].toLowerCase();
268
+ const tag = (parts[0] ?? '').toLowerCase();
250
269
  const qPart = parts.find(p => p.trim().startsWith('q='));
251
270
  const q = qPart ? parseFloat(qPart.trim().slice(2)) : 1;
252
271
  return { tag, q: isNaN(q) ? 0 : q };
@@ -270,6 +289,8 @@ export class UwsRequest {
270
289
  export class UWebSocketsHttpServer {
271
290
  app; // uWebSockets app instance
272
291
  uws; // uWebSockets module reference (stored to avoid re-importing)
292
+ _limits;
293
+ multipartLimits;
273
294
  listenSocket; // uWebSockets listen socket
274
295
  globalMiddleware = [];
275
296
  logger = createFrameworkLogger('UWSHttpServer');
@@ -327,6 +348,10 @@ export class UWebSocketsHttpServer {
327
348
  this.maxBodySize = options.maxBodySize;
328
349
  if (options.maxUploadSize)
329
350
  this.maxUploadSize = options.maxUploadSize;
351
+ if (options.limits)
352
+ this._limits = options.limits;
353
+ if (options.limits?.multipart)
354
+ this.multipartLimits = options.limits.multipart;
330
355
  this.initPromise = this.initialize(options);
331
356
  }
332
357
  // Direct router dispatch - called after global middleware without the
@@ -340,18 +365,34 @@ export class UWebSocketsHttpServer {
340
365
  // routes are included.
341
366
  setNativeRouteProvider(provider, onError) {
342
367
  this.nativeRouteProvider = provider;
343
- this.nativeErrorHandler = onError;
368
+ if (onError)
369
+ this.nativeErrorHandler = onError;
344
370
  }
345
371
  async initialize(options) {
346
372
  try {
347
- // Lazy load uWebSockets.js - only when explicitly configured
348
- // This ensures it's an optional dependency with graceful fallback
349
- const uwsModule = await import('uWebSockets.js');
373
+ // The framework preloads the engine synchronously (loadNativeEngine) and
374
+ // injects it; loading here directly covers standalone construction.
375
+ // This server is uWS-shaped only: never probe @morojs/engine, whose
376
+ // Moro-shaped module would pass a generic load but has no App().
377
+ const uwsModule = options.engineModule ?? loadNativeEngine({ candidates: ['uWebSockets.js'] })?.module;
378
+ if (!uwsModule) {
379
+ throw new Error('uWebSockets.js is not available: ' +
380
+ (getNativeEngineLoadErrors().join('; ') || 'not installed'));
381
+ }
350
382
  this.uws = uwsModule.default || uwsModule;
383
+ // A TLS config that was given only as inline PEM cannot be honored by
384
+ // uWS (it requires on-disk file paths). Surface it loudly rather than
385
+ // silently booting plain HTTP (the historical footgun).
386
+ if (options.sslInlineOnly) {
387
+ this.logger.error('uWebSockets.js requires TLS key/cert as FILE PATHS (keyFile/certFile ' +
388
+ 'or key_file_name/cert_file_name); inline PEM is not supported. ' +
389
+ 'Booting WITHOUT TLS - provide file paths or use engine: "moro"/"node".', 'Init');
390
+ }
351
391
  if (options.ssl && options.ssl.key_file_name && options.ssl.cert_file_name) {
352
392
  this.app = this.uws.SSLApp({
353
393
  key_file_name: options.ssl.key_file_name,
354
394
  cert_file_name: options.ssl.cert_file_name,
395
+ ...(options.ssl.ca_file_name && { ca_file_name: options.ssl.ca_file_name }),
355
396
  passphrase: options.ssl.passphrase,
356
397
  });
357
398
  this.isSsl = true;
@@ -361,15 +402,32 @@ export class UWebSocketsHttpServer {
361
402
  this.app = this.uws.App();
362
403
  this.logger.info('uWebSockets HTTP server created', 'Init');
363
404
  }
405
+ // uWS has no knobs for TCP timeouts/backlog/maxConnections/maxHeaderSize;
406
+ // if the user set any, say so once at startup rather than silently
407
+ // ignoring them (the "no arbitrary caps" contract cuts both ways).
408
+ const ignored = [];
409
+ const t = options.limits?.timeouts ?? {};
410
+ if (t.request || t.idle || t.keepAlive || t.headers)
411
+ ignored.push('timeouts');
412
+ if (options.limits?.maxConnections)
413
+ ignored.push('maxConnections');
414
+ if (options.limits?.backlog)
415
+ ignored.push('backlog');
416
+ if (options.limits?.maxHeaderSize || options.limits?.maxHeaders)
417
+ ignored.push('header limits');
418
+ if (ignored.length) {
419
+ this.logger.info(`uWebSockets.js does not support these server options; they are ignored: ${ignored.join(', ')}`, 'Init');
420
+ }
364
421
  // Setup generic route handler for all HTTP methods and paths
365
422
  this.setupRouteHandlers();
366
423
  }
367
424
  catch (error) {
368
425
  // Log helpful error message with installation instructions
369
- this.logger.error('Failed to load uWebSockets.js (optional dependency)\n' +
370
- 'To use uWebSockets, install it with:\n' +
371
- ' npm install --save-dev github:uNetworking/uWebSockets.js#v20.52.0\n' +
372
- 'Or set useUWebSockets: false in your config to use the standard HTTP server.\n' +
426
+ this.logger.error('Failed to load the native HTTP engine\n' +
427
+ 'Install it with:\n' +
428
+ ' npm install @morojs/engine\n' +
429
+ '(or the legacy engine: npm install --save-dev github:uNetworking/uWebSockets.js#v20.52.0)\n' +
430
+ "Or set engine: 'node' in your server config to use the standard HTTP server.\n" +
373
431
  'Error: ' +
374
432
  (error instanceof Error ? error.message : String(error)), 'Init');
375
433
  throw error; // Re-throw so framework.ts can catch and fallback
@@ -381,7 +439,7 @@ export class UWebSocketsHttpServer {
381
439
  // method-specific routes registered later take priority in uWS's router
382
440
  // (static > parameter > wildcard).
383
441
  this.app.any('/*', (res, req) => {
384
- this.handleRequest(req, res);
442
+ void this.handleRequest(req, res);
385
443
  });
386
444
  }
387
445
  // Register fast-path routes (no middleware/auth/validation) directly on
@@ -412,7 +470,7 @@ export class UWebSocketsHttpServer {
412
470
  if (this.globalMiddleware.length !== 0 ||
413
471
  (this.hookManager &&
414
472
  (this.hookManager.hasHooks === undefined || this.hookManager.hasHooks('request')))) {
415
- this.handleRequest(req, res);
473
+ void this.handleRequest(req, res);
416
474
  return;
417
475
  }
418
476
  this.handleNativeRoute(req, res, handler, methodInterned, paramNames, needsBody);
@@ -429,20 +487,28 @@ export class UWebSocketsHttpServer {
429
487
  if (paramNames) {
430
488
  const params = {};
431
489
  for (let i = 0; i < paramNames.length; i++) {
432
- params[paramNames[i]] = req.getParameter(i);
490
+ const paramName = paramNames[i];
491
+ if (paramName === undefined)
492
+ continue;
493
+ params[paramName] = req.getParameter(i);
433
494
  }
434
495
  httpReq.params = params;
435
496
  }
436
- const httpRes = new UWebSocketsHttpServer.ResponsePrototype().init(res, req, this.logger);
497
+ const httpRes = new UWebSocketsHttpServer.ResponsePrototype().init(res, req, this.logger, this._compression);
498
+ httpRes._moroReq = httpReq;
437
499
  if (needsBody) {
438
500
  // Body routes always go async: snapshot headers, arm the abort hook,
439
501
  // read the body, then run the handler
440
502
  httpReq.materialize();
503
+ res._abortArmed = true;
441
504
  res.onAborted(() => {
442
505
  res.aborted = true;
443
506
  const hook = res._abortHook;
444
507
  if (hook)
445
508
  hook();
509
+ const moroRes = res._moroRes;
510
+ if (moroRes)
511
+ moroRes._handleAbort();
446
512
  });
447
513
  const contentLength = req.getHeader('content-length');
448
514
  const bodyPromise = contentLength && parseInt(contentLength) > 0
@@ -461,6 +527,7 @@ export class UWebSocketsHttpServer {
461
527
  }
462
528
  if (result !== undefined && !httpRes.headersSent)
463
529
  httpRes.json(result);
530
+ return;
464
531
  })
465
532
  .catch(err => this.handleNativeRouteError(err, httpReq, httpRes, res));
466
533
  return;
@@ -471,11 +538,15 @@ export class UWebSocketsHttpServer {
471
538
  // Async handler: it is suspended at its first await - snapshot
472
539
  // uWS-backed data and arm abort tracking before returning to the loop
473
540
  httpReq.materialize();
541
+ res._abortArmed = true;
474
542
  res.onAborted(() => {
475
543
  res.aborted = true;
476
544
  const hook = res._abortHook;
477
545
  if (hook)
478
546
  hook();
547
+ const moroRes = res._moroRes;
548
+ if (moroRes)
549
+ moroRes._handleAbort();
479
550
  });
480
551
  result.then(r => {
481
552
  if (r !== undefined && !httpRes.headersSent && !res.aborted)
@@ -486,6 +557,23 @@ export class UWebSocketsHttpServer {
486
557
  // Fully synchronous: single corked write, no header copy, no promise
487
558
  httpRes.json(result);
488
559
  }
560
+ // A synchronous handler that returned with the response still open
561
+ // (e.g. SSE: writeHead + periodic write) is about to hand an unfinished
562
+ // uWS response back to the event loop - uWS forbids that without an
563
+ // abort handler, and without one client disconnects are never noticed.
564
+ if (!httpRes.finished && !res.aborted && !res._abortArmed) {
565
+ res._abortArmed = true;
566
+ httpReq.materialize();
567
+ res.onAborted(() => {
568
+ res.aborted = true;
569
+ const hook = res._abortHook;
570
+ if (hook)
571
+ hook();
572
+ const moroRes = res._moroRes;
573
+ if (moroRes)
574
+ moroRes._handleAbort();
575
+ });
576
+ }
489
577
  }
490
578
  catch (err) {
491
579
  this.handleNativeRouteError(err, httpReq, httpRes, res);
@@ -498,6 +586,12 @@ export class UWebSocketsHttpServer {
498
586
  httpRes.end('{"success":false,"error":"Request entity too large"}');
499
587
  return;
500
588
  }
589
+ if (err?.statusCode === 400 && !res.aborted && !httpRes.headersSent) {
590
+ httpRes.statusCode = 400;
591
+ httpRes.setHeader('Content-Type', 'application/json');
592
+ httpRes.end('{"success":false,"error":"Invalid request body"}');
593
+ return;
594
+ }
501
595
  const sendFallback = () => {
502
596
  if (!res.aborted && !httpRes.headersSent) {
503
597
  // Same shape as the unified router's fast-path error response
@@ -527,6 +621,10 @@ export class UWebSocketsHttpServer {
527
621
  const hook = res._abortHook;
528
622
  if (hook)
529
623
  hook();
624
+ // Fire 'close'/'aborted' on the moro wrappers (SSE/monitor cleanup)
625
+ const moroRes = res._moroRes;
626
+ if (moroRes)
627
+ moroRes._handleAbort();
530
628
  });
531
629
  let httpReq;
532
630
  let httpRes;
@@ -534,6 +632,7 @@ export class UWebSocketsHttpServer {
534
632
  // Create Moro-compatible request/response objects
535
633
  httpReq = this.createMoroRequest(req, res);
536
634
  httpRes = this.createMoroResponse(req, res);
635
+ httpRes._moroReq = httpReq;
537
636
  const method = httpReq.method;
538
637
  // Parse body only if there's actually a body (check content-length).
539
638
  // Check first char for early exit (all body methods start with 'P')
@@ -544,6 +643,12 @@ export class UWebSocketsHttpServer {
544
643
  // readBody awaits - snapshot uWS-backed data first
545
644
  httpReq.materialize();
546
645
  await this.readBody(res, httpReq);
646
+ // A client that disconnects mid-upload resolves readBody with a
647
+ // null/partial body; do not run hooks, middleware or the handler for
648
+ // a dead request (matches the native fast-path guard at ~line 625 and
649
+ // the Node backend, whose aborted stream rejects parseBody).
650
+ if (res.aborted)
651
+ return;
547
652
  }
548
653
  }
549
654
  // Execute hooks before request processing - skipped entirely when none registered
@@ -594,6 +699,13 @@ export class UWebSocketsHttpServer {
594
699
  httpRes.end('{"success":false,"error":"Request entity too large"}');
595
700
  return;
596
701
  }
702
+ // Malformed body: a client error (400), not a server error (500)
703
+ if (error?.statusCode === 400 && !res.aborted && httpRes && !httpRes.headersSent) {
704
+ httpRes.statusCode = 400;
705
+ httpRes.setHeader('Content-Type', 'application/json');
706
+ httpRes.end('{"success":false,"error":"Invalid request body"}');
707
+ return;
708
+ }
597
709
  this.logger.error(`Request handling error: ${error instanceof Error ? error.message : String(error)}`, 'RequestError');
598
710
  // Send error response if not already sent
599
711
  if (!res.aborted) {
@@ -620,12 +732,31 @@ export class UWebSocketsHttpServer {
620
732
  // materialize() snapshots them before the first await
621
733
  return new UwsRequest(this, req, method, url, queryString);
622
734
  }
735
+ // Strip CR/LF from a header value to prevent response-splitting / header
736
+ // injection, matching Node's setHeader ERR_INVALID_CHAR guarantee. Guarded
737
+ // with indexOf so the common (clean) path allocates nothing on the hot path.
738
+ static sanitizeHeaderValue(value) {
739
+ return value.indexOf('\r') === -1 && value.indexOf('\n') === -1
740
+ ? value
741
+ : value.replace(/[\r\n]/g, '');
742
+ }
623
743
  // Optimized helper to write headers
624
744
  static writeHeaders(res, headers) {
625
745
  // Performance: for...in is the fastest way to iterate response headers
626
746
  for (const key in headers) {
627
747
  const value = headers[key];
628
- res.writeHeader(key, Array.isArray(value) ? value.join(', ') : String(value));
748
+ if (Array.isArray(value)) {
749
+ // Emit one header line per element instead of comma-joining. RFC 6265
750
+ // forbids folding Set-Cookie, and browsers parse a folded
751
+ // `a=1; Path=/, b=2` as a single cookie, silently dropping the second
752
+ // (session + CSRF is the canonical breakage). uWS writes a separate
753
+ // line per writeHeader call with the same key.
754
+ for (let i = 0; i < value.length; i++) {
755
+ res.writeHeader(key, UWebSocketsHttpServer.sanitizeHeaderValue(String(value[i])));
756
+ }
757
+ continue;
758
+ }
759
+ res.writeHeader(key, UWebSocketsHttpServer.sanitizeHeaderValue(String(value)));
629
760
  }
630
761
  }
631
762
  // Helper to get status string (cached for performance)
@@ -633,14 +764,25 @@ export class UWebSocketsHttpServer {
633
764
  return UWebSocketsHttpServer.STATUS_STRINGS.get(code) || `${code} OK`;
634
765
  }
635
766
  // Pre-define methods on prototype instead of creating new closures for each request
636
- static ResponsePrototype = class {
767
+ static ResponsePrototype = class extends LazyEventEmitter {
637
768
  headersSent = false;
638
769
  statusCode = 200;
639
770
  responseHeaders = {};
640
771
  _res;
641
772
  _req;
642
773
  _logger;
643
- init(res, req, logger) {
774
+ // Terminal-write latch: headersSent means "head flushed" (true mid-stream
775
+ // after writeHead/write), _ended means the body is complete
776
+ _ended = false;
777
+ _drainArmed = false;
778
+ // Accept-Encoding captured eagerly (uWS req is valid only synchronously)
779
+ // and the server's compression settings, for buffered-response compression.
780
+ _acceptEncoding = undefined;
781
+ _compression = undefined;
782
+ // The UwsRequest wrapper, linked by the server so lifecycle events reach
783
+ // req.on('close') listeners (SSE cleanup, monitors)
784
+ _moroReq = null;
785
+ init(res, req, logger, compression) {
644
786
  this.headersSent = false;
645
787
  this.statusCode = 200;
646
788
  // Fresh object (a delete-loop reset would push the object into V8
@@ -649,8 +791,122 @@ export class UWebSocketsHttpServer {
649
791
  this._res = res;
650
792
  this._req = req;
651
793
  this._logger = logger;
794
+ this._ended = false;
795
+ this._drainArmed = false;
796
+ this._compression = compression;
797
+ // Capture Accept-Encoding now: the native req is valid only during the
798
+ // synchronous handler entry, but compression resolves asynchronously.
799
+ this._acceptEncoding =
800
+ compression?.enabled && req ? req.getHeader('accept-encoding') || undefined : undefined;
801
+ // Route client aborts to the wrapper so 'close' fires (see onAborted)
802
+ res._moroRes = this;
652
803
  return this;
653
804
  }
805
+ // Buffered-response compression for uWS (parity with the engine/Node
806
+ // paths). Returns true when it took over the write (async), false when the
807
+ // caller should proceed with its normal synchronous cork+end.
808
+ _tryCompressedEnd(body, contentType) {
809
+ const s = this._compression;
810
+ if (!s || !s.enabled)
811
+ return false;
812
+ const bytes = typeof body === 'string' ? Buffer.byteLength(body) : body.length;
813
+ if (bytes < s.threshold)
814
+ return false;
815
+ if ('content-encoding' in this.responseHeaders)
816
+ return false;
817
+ if (this.statusCode === 204 || this.statusCode === 304)
818
+ return false;
819
+ const ctHeader = this.responseHeaders['content-type'];
820
+ const ct = contentType ?? (Array.isArray(ctHeader) ? ctHeader[0] : ctHeader);
821
+ if (!isCompressible(ct))
822
+ return false;
823
+ const encoding = negotiateEncoding(this._acceptEncoding, s.encodings);
824
+ if (!encoding)
825
+ return false;
826
+ void compressBuffer(body, encoding, s.level)
827
+ .then(compressed => {
828
+ if (this._ended || this._res.aborted)
829
+ return;
830
+ this.responseHeaders['content-encoding'] = encoding;
831
+ const vary = this.responseHeaders['vary'];
832
+ this.responseHeaders['vary'] = vary ? `${vary}, Accept-Encoding` : 'Accept-Encoding';
833
+ this._res.cork(() => {
834
+ this._res.writeStatus(UWebSocketsHttpServer.getStatusString(this.statusCode));
835
+ UWebSocketsHttpServer.writeHeaders(this._res, this.responseHeaders);
836
+ this._res.end(compressed);
837
+ });
838
+ this.headersSent = true;
839
+ this._ended = true;
840
+ this._emitDone();
841
+ })
842
+ .catch((err) => {
843
+ this._logger?.error?.(`uWS response compression failed: ${err instanceof Error ? err.message : String(err)}`, 'Response');
844
+ if (this._ended || this._res.aborted)
845
+ return;
846
+ this._res.cork(() => {
847
+ this._res.writeStatus(UWebSocketsHttpServer.getStatusString(this.statusCode));
848
+ UWebSocketsHttpServer.writeHeaders(this._res, this.responseHeaders);
849
+ this._res.end(body);
850
+ });
851
+ this.headersSent = true;
852
+ this._ended = true;
853
+ this._emitDone();
854
+ });
855
+ return true;
856
+ }
857
+ get writableEnded() {
858
+ return this._ended;
859
+ }
860
+ get writableFinished() {
861
+ return this._ended;
862
+ }
863
+ get finished() {
864
+ return this._ended;
865
+ }
866
+ // Node ServerResponse lifecycle: 'finish' then 'close' after the terminal
867
+ // write; the request wrapper's 'close' fires too (IncomingMessage parity).
868
+ // No listeners -> two null checks, nothing else.
869
+ _emitDone() {
870
+ this._ended = true;
871
+ if (this._events) {
872
+ this.emit('finish');
873
+ this.emit('close');
874
+ }
875
+ const req = this._moroReq;
876
+ if (req && req._events)
877
+ req.emit('close');
878
+ }
879
+ // Client aborted: 'close' without 'finish' (Node semantics), plus
880
+ // 'aborted'/'close' on the request wrapper
881
+ _handleAbort() {
882
+ if (this._ended)
883
+ return;
884
+ this._ended = true;
885
+ if (this._events)
886
+ this.emit('close');
887
+ const req = this._moroReq;
888
+ if (req && req._events) {
889
+ req.emit('aborted');
890
+ req.emit('close');
891
+ }
892
+ }
893
+ // Backpressure: surface uWS's onWritable as the 'drain' event pipe()
894
+ // and manual streaming loops wait on
895
+ _armDrain() {
896
+ if (this._drainArmed)
897
+ return;
898
+ this._drainArmed = true;
899
+ try {
900
+ this._res.onWritable(() => {
901
+ this._drainArmed = false;
902
+ this.emit('drain');
903
+ return true;
904
+ });
905
+ }
906
+ catch {
907
+ this._drainArmed = false;
908
+ }
909
+ }
654
910
  status(code) {
655
911
  this.statusCode = code;
656
912
  return this;
@@ -678,7 +934,12 @@ export class UWebSocketsHttpServer {
678
934
  return;
679
935
  // Fast-path JSON serialization for common API patterns
680
936
  let body;
681
- if (data && typeof data === 'object' && 'success' in data) {
937
+ // The interpolated fast path is only valid when `success` is a real
938
+ // boolean (a string/number would produce invalid JSON like
939
+ // {"success":yes,...}), and each interpolated field must be defined
940
+ // (JSON.stringify(undefined) yields the literal token "undefined", which
941
+ // would corrupt the body). Otherwise fall through to JSON.stringify.
942
+ if (data && typeof data === 'object' && typeof data.success === 'boolean') {
682
943
  let keyCount = 0;
683
944
  let hasData = false;
684
945
  let hasError = false;
@@ -694,16 +955,24 @@ export class UWebSocketsHttpServer {
694
955
  hasTotal = true;
695
956
  }
696
957
  }
697
- if (keyCount === 3 && hasData && hasError) {
958
+ if (keyCount === 3 &&
959
+ hasData &&
960
+ hasError &&
961
+ data.data !== undefined &&
962
+ data.error !== undefined) {
698
963
  body = `{"success":${data.success},"data":${JSON.stringify(data.data)},"error":${JSON.stringify(data.error)}}`;
699
964
  }
700
- else if (keyCount === 3 && hasData && hasTotal) {
965
+ else if (keyCount === 3 &&
966
+ hasData &&
967
+ hasTotal &&
968
+ data.data !== undefined &&
969
+ typeof data.total === 'number') {
701
970
  body = `{"success":${data.success},"data":${JSON.stringify(data.data)},"total":${data.total}}`;
702
971
  }
703
- else if (keyCount === 2 && hasData) {
972
+ else if (keyCount === 2 && hasData && data.data !== undefined) {
704
973
  body = `{"success":${data.success},"data":${JSON.stringify(data.data)}}`;
705
974
  }
706
- else if (keyCount === 2 && hasError) {
975
+ else if (keyCount === 2 && hasError && data.error !== undefined) {
707
976
  body = `{"success":${data.success},"error":${JSON.stringify(data.error)}}`;
708
977
  }
709
978
  else {
@@ -717,12 +986,17 @@ export class UWebSocketsHttpServer {
717
986
  this.responseHeaders['content-type'] = 'application/json';
718
987
  }
719
988
  try {
989
+ // Compression (when enabled + compressible + accepted) takes over the
990
+ // write asynchronously; otherwise the synchronous corked path runs.
991
+ if (this._tryCompressedEnd(body, 'application/json'))
992
+ return;
720
993
  this._res.cork(() => {
721
994
  this._res.writeStatus(UWebSocketsHttpServer.getStatusString(this.statusCode));
722
995
  UWebSocketsHttpServer.writeHeaders(this._res, this.responseHeaders);
723
996
  this._res.end(body);
724
997
  });
725
998
  this.headersSent = true;
999
+ this._emitDone();
726
1000
  }
727
1001
  catch {
728
1002
  this._logger.error('Failed to send JSON response', 'ResponseError');
@@ -733,30 +1007,103 @@ export class UWebSocketsHttpServer {
733
1007
  return;
734
1008
  const body = typeof data === 'string' ? data : data.toString();
735
1009
  try {
1010
+ if (this._tryCompressedEnd(body, undefined))
1011
+ return;
736
1012
  this._res.cork(() => {
737
1013
  this._res.writeStatus(UWebSocketsHttpServer.getStatusString(this.statusCode));
738
1014
  UWebSocketsHttpServer.writeHeaders(this._res, this.responseHeaders);
739
1015
  this._res.end(body);
740
1016
  });
741
1017
  this.headersSent = true;
1018
+ this._emitDone();
742
1019
  }
743
1020
  catch {
744
1021
  this._logger.error('Failed to send response', 'ResponseError');
745
1022
  }
746
1023
  }
1024
+ // Flush status + headers without ending the response (Node streaming
1025
+ // entry point - SSE and manual chunked responses start here). Supports
1026
+ // both (code, headers) and (code, statusMessage, headers) signatures;
1027
+ // uWS derives the reason phrase from the status string, so a custom
1028
+ // message is folded into it.
1029
+ writeHead(statusCode, reasonOrHeaders, maybeHeaders) {
1030
+ if (this.headersSent || this._res.aborted)
1031
+ return this;
1032
+ this.statusCode = statusCode;
1033
+ const headers = reasonOrHeaders && typeof reasonOrHeaders === 'object' ? reasonOrHeaders : maybeHeaders;
1034
+ if (headers) {
1035
+ for (const key of Object.keys(headers)) {
1036
+ this.setHeader(key, headers[key]);
1037
+ }
1038
+ }
1039
+ try {
1040
+ this._res.cork(() => {
1041
+ const status = typeof reasonOrHeaders === 'string'
1042
+ ? `${statusCode} ${reasonOrHeaders}`
1043
+ : UWebSocketsHttpServer.getStatusString(statusCode);
1044
+ this._res.writeStatus(status);
1045
+ UWebSocketsHttpServer.writeHeaders(this._res, this.responseHeaders);
1046
+ });
1047
+ this.headersSent = true;
1048
+ }
1049
+ catch {
1050
+ this._logger.error('Failed to write response head', 'ResponseError');
1051
+ }
1052
+ return this;
1053
+ }
1054
+ // Stream a body chunk, flushing the head first if needed. Returns uWS's
1055
+ // backpressure signal like Node's Writable.write; 'drain' fires when the
1056
+ // socket can accept more.
1057
+ write(chunk, encoding, callback) {
1058
+ if (typeof encoding === 'function') {
1059
+ callback = encoding;
1060
+ encoding = undefined;
1061
+ }
1062
+ if (this._ended || this._res.aborted)
1063
+ return false;
1064
+ let ok = true;
1065
+ try {
1066
+ this._res.cork(() => {
1067
+ if (!this.headersSent) {
1068
+ this._res.writeStatus(UWebSocketsHttpServer.getStatusString(this.statusCode));
1069
+ UWebSocketsHttpServer.writeHeaders(this._res, this.responseHeaders);
1070
+ this.headersSent = true;
1071
+ }
1072
+ ok = this._res.write(chunk);
1073
+ });
1074
+ }
1075
+ catch {
1076
+ this._logger.error('Failed to write response chunk', 'ResponseError');
1077
+ return false;
1078
+ }
1079
+ if (!ok)
1080
+ this._armDrain();
1081
+ if (typeof callback === 'function')
1082
+ callback();
1083
+ return ok;
1084
+ }
747
1085
  end(data, encoding, callback) {
748
- if (this.headersSent || this._res.aborted) {
1086
+ if (typeof encoding === 'function') {
1087
+ callback = encoding;
1088
+ encoding = undefined;
1089
+ }
1090
+ // Guard on _ended, not headersSent: in streaming mode (writeHead/write)
1091
+ // the head is already flushed and end() must still complete the body
1092
+ if (this._ended || this._res.aborted) {
749
1093
  if (typeof callback === 'function')
750
1094
  callback();
751
1095
  return this;
752
1096
  }
753
1097
  try {
754
1098
  this._res.cork(() => {
755
- this._res.writeStatus(UWebSocketsHttpServer.getStatusString(this.statusCode));
756
- UWebSocketsHttpServer.writeHeaders(this._res, this.responseHeaders);
1099
+ if (!this.headersSent) {
1100
+ this._res.writeStatus(UWebSocketsHttpServer.getStatusString(this.statusCode));
1101
+ UWebSocketsHttpServer.writeHeaders(this._res, this.responseHeaders);
1102
+ this.headersSent = true;
1103
+ }
757
1104
  this._res.end(data || '');
758
1105
  });
759
- this.headersSent = true;
1106
+ this._emitDone();
760
1107
  if (typeof callback === 'function')
761
1108
  callback();
762
1109
  }
@@ -775,11 +1122,12 @@ export class UWebSocketsHttpServer {
775
1122
  try {
776
1123
  this._res.cork(() => {
777
1124
  this._res.writeStatus(UWebSocketsHttpServer.getStatusString(redirectCode));
778
- this._res.writeHeader('Location', url);
1125
+ this._res.writeHeader('Location', UWebSocketsHttpServer.sanitizeHeaderValue(url));
779
1126
  UWebSocketsHttpServer.writeHeaders(this._res, this.responseHeaders);
780
1127
  this._res.end();
781
1128
  });
782
1129
  this.headersSent = true;
1130
+ this._emitDone();
783
1131
  }
784
1132
  catch {
785
1133
  this._logger.error('Failed to send redirect', 'ResponseError');
@@ -918,32 +1266,25 @@ export class UWebSocketsHttpServer {
918
1266
  },
919
1267
  });
920
1268
  }
921
- // EventEmitter stubs for middleware compatibility
922
- on(_event, _callback) {
923
- return this;
924
- }
925
- once(_event, _callback) {
926
- return this;
927
- }
928
- emit(_event, ..._args) {
929
- return true;
930
- }
931
- removeListener(_event, _callback) {
932
- return this;
933
- }
934
1269
  cookie(name, value, options) {
935
- // Build cookie string with array join for better performance
936
- const parts = [name, '=', value];
1270
+ // Percent-encode the value (matches the Node path and Express) so a value
1271
+ // containing ';'/',' can't inject cookie attributes and control chars
1272
+ // don't corrupt the Set-Cookie header.
1273
+ const parts = [name, '=', encodeURIComponent(value)];
937
1274
  if (options) {
938
- if (options.maxAge) {
1275
+ // maxAge: 0 is meaningful (immediate expiry - clearCookie relies on it)
1276
+ if (options.maxAge !== undefined && options.maxAge !== null) {
939
1277
  parts.push('; Max-Age=', String(options.maxAge));
940
1278
  }
1279
+ if (options.expires) {
1280
+ const expires = options.expires instanceof Date
1281
+ ? options.expires.toUTCString()
1282
+ : String(options.expires);
1283
+ parts.push('; Expires=', expires);
1284
+ }
941
1285
  if (options.domain) {
942
1286
  parts.push('; Domain=', options.domain);
943
1287
  }
944
- if (options.path) {
945
- parts.push('; Path=', options.path);
946
- }
947
1288
  if (options.secure) {
948
1289
  parts.push('; Secure');
949
1290
  }
@@ -954,6 +1295,9 @@ export class UWebSocketsHttpServer {
954
1295
  parts.push('; SameSite=', options.sameSite);
955
1296
  }
956
1297
  }
1298
+ // Path defaults to '/' (Express behavior) so clearCookie() from a nested
1299
+ // route can clear a cookie originally set at the site root.
1300
+ parts.push('; Path=', options?.path ?? '/');
957
1301
  const cookie = parts.join('');
958
1302
  const lowerKey = 'set-cookie';
959
1303
  const existing = this.responseHeaders[lowerKey];
@@ -1109,8 +1453,8 @@ export class UWebSocketsHttpServer {
1109
1453
  headersSent: this.headersSent,
1110
1454
  statusCode: this.statusCode,
1111
1455
  headers: this.responseHeaders,
1112
- finished: this.headersSent,
1113
- writable: !this.headersSent,
1456
+ finished: this._ended,
1457
+ writable: !this._ended,
1114
1458
  };
1115
1459
  }
1116
1460
  };
@@ -1181,15 +1525,34 @@ export class UWebSocketsHttpServer {
1181
1525
  });
1182
1526
  httpReq.body = body;
1183
1527
  }
1528
+ else if (isMultipart) {
1529
+ // Shared parser (same as the Node http server) - stringifying
1530
+ // multipart would corrupt binary uploads and lose fields/files.
1531
+ // parseMultipart retains slices of the payload as file Buffers;
1532
+ // the single-chunk fast path (buffer === view) aliases uWS-owned
1533
+ // memory that is recycled after this callback returns, so copy
1534
+ // it first. Concatenated multi-chunk buffers are already stable.
1535
+ const stable = buffer === view ? Buffer.from(buffer) : buffer;
1536
+ httpReq.body = parseMultipartBuffer(stable, contentType, this.multipartLimits);
1537
+ }
1184
1538
  else {
1185
1539
  httpReq.body = buffer.toString('utf-8');
1186
1540
  }
1187
1541
  finish(resolve);
1188
1542
  }
1189
- catch {
1190
- this.logger.error('Failed to parse request body', 'BodyParseError');
1191
- httpReq.body = null;
1192
- finish(resolve);
1543
+ catch (parseError) {
1544
+ // A limit error (multipart maxParts/maxFiles/maxFileSize) carries its
1545
+ // own 413 - preserve it rather than masking it as a generic 400.
1546
+ if (parseError && typeof parseError.statusCode === 'number') {
1547
+ finish(() => reject(parseError));
1548
+ return;
1549
+ }
1550
+ // A malformed body is a client error: reject with 400 (parity with
1551
+ // the engine and Node transports) rather than silently handing the
1552
+ // handler a null body it may treat as "no input".
1553
+ const error = new Error(`Invalid request body: ${parseError instanceof Error ? parseError.message : String(parseError)}`);
1554
+ error.statusCode = 400;
1555
+ finish(() => reject(error));
1193
1556
  }
1194
1557
  });
1195
1558
  // Chain onto the request-lifetime abort hook (registered in handleRequest
@@ -1250,10 +1613,14 @@ export class UWebSocketsHttpServer {
1250
1613
  setHookManager(hookManager) {
1251
1614
  this.hookManager = hookManager;
1252
1615
  }
1253
- configurePerformance(_config) {
1254
- // uWebSockets is already highly optimized
1255
- // This method exists for API compatibility
1256
- this.logger.debug('Performance configuration noted (uWebSockets is pre-optimized)', 'Config');
1616
+ /** @internal buffered-response compression settings (read at init). */
1617
+ _compression = resolveCompressionSettings();
1618
+ configurePerformance(config) {
1619
+ // uWS batches the request hot path natively; response compression is the
1620
+ // one thing it doesn't do itself, so wire that here (parity with Node/
1621
+ // engine). Buffered responses only - streaming stays raw.
1622
+ this._compression = resolveCompressionSettings(config);
1623
+ this.logger.debug(`Performance configured (compression ${this._compression.enabled ? 'on' : 'off'})`, 'Config');
1257
1624
  }
1258
1625
  listen(port, hostOrCallback, callback) {
1259
1626
  // Wrap in async to await init
@@ -1280,24 +1647,36 @@ export class UWebSocketsHttpServer {
1280
1647
  // uWebSockets.js automatically enables SO_REUSEPORT when in cluster mode
1281
1648
  // Do NOT pass listenOptions in cluster mode - let uWS handle it automatically
1282
1649
  // In non-cluster mode, we don't need SO_REUSEPORT
1283
- this.app.listen(port, (token) => {
1650
+ const onListen = (token) => {
1284
1651
  if (token) {
1285
1652
  this.listenSocket = token;
1286
1653
  this.isListening = true;
1287
1654
  const clusterInfo = isClusterWorker ? ` (worker ${process.pid})` : '';
1288
- this.logger.info(`uWebSockets HTTP server listening on 0.0.0.0:${port}${clusterInfo}`, 'Listen');
1655
+ this.logger.info(`uWebSockets HTTP server listening on ${host}:${port}${clusterInfo}`, 'Listen');
1289
1656
  if (cb)
1290
1657
  cb();
1291
1658
  }
1292
1659
  else {
1293
1660
  const clusterInfo = isClusterWorker ? ` (worker ${process.pid})` : '';
1294
- this.logger.error(`Failed to listen on port ${port}${clusterInfo}`, 'Listen');
1661
+ this.logger.error(`Failed to listen on ${host}:${port}${clusterInfo}`, 'Listen');
1295
1662
  // Don't throw in cluster workers - let them fail gracefully
1296
1663
  if (!isClusterWorker) {
1297
- throw new Error(`Failed to bind to port ${port}`);
1664
+ throw new Error(`Failed to bind to ${host}:${port}`);
1298
1665
  }
1299
1666
  }
1300
- });
1667
+ };
1668
+ // Honor the requested bind host. For the all-interfaces default (and
1669
+ // cluster mode, where uWS auto-enables SO_REUSEPORT) keep the 2-arg
1670
+ // form so that behavior is unchanged; when a specific host is given
1671
+ // (e.g. 127.0.0.1 for an internal/admin service behind a proxy) use
1672
+ // uWS's (host, port, cb) overload so we don't silently bind 0.0.0.0.
1673
+ const bindAllInterfaces = host === '0.0.0.0' || host === '::';
1674
+ if (bindAllInterfaces) {
1675
+ this.app.listen(port, onListen);
1676
+ }
1677
+ else {
1678
+ this.app.listen(host, port, onListen);
1679
+ }
1301
1680
  })
1302
1681
  .catch(error => {
1303
1682
  this.logger.error('Failed to initialize server before listen', 'Listen', {