exisjs 0.1.2

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 (495) hide show
  1. package/README.md +52 -0
  2. package/bin/exis.js +2 -0
  3. package/dist/adapters/aws-lambda.d.ts +21 -0
  4. package/dist/adapters/aws-lambda.d.ts.map +1 -0
  5. package/dist/adapters/aws-lambda.js +110 -0
  6. package/dist/adapters/aws-lambda.js.map +1 -0
  7. package/dist/adapters/bun.d.ts +9 -0
  8. package/dist/adapters/bun.d.ts.map +1 -0
  9. package/dist/adapters/bun.js +15 -0
  10. package/dist/adapters/bun.js.map +1 -0
  11. package/dist/adapters/cloudflare.d.ts +10 -0
  12. package/dist/adapters/cloudflare.d.ts.map +1 -0
  13. package/dist/adapters/cloudflare.js +14 -0
  14. package/dist/adapters/cloudflare.js.map +1 -0
  15. package/dist/adapters/deno.d.ts +7 -0
  16. package/dist/adapters/deno.d.ts.map +1 -0
  17. package/dist/adapters/deno.js +11 -0
  18. package/dist/adapters/deno.js.map +1 -0
  19. package/dist/adapters/fastly.d.ts +6 -0
  20. package/dist/adapters/fastly.d.ts.map +1 -0
  21. package/dist/adapters/fastly.js +12 -0
  22. package/dist/adapters/fastly.js.map +1 -0
  23. package/dist/adapters/fetch.d.ts +30 -0
  24. package/dist/adapters/fetch.d.ts.map +1 -0
  25. package/dist/adapters/fetch.js +119 -0
  26. package/dist/adapters/fetch.js.map +1 -0
  27. package/dist/adapters/index.d.ts +9 -0
  28. package/dist/adapters/index.d.ts.map +1 -0
  29. package/dist/adapters/index.js +28 -0
  30. package/dist/adapters/index.js.map +1 -0
  31. package/dist/adapters/netlify.d.ts +8 -0
  32. package/dist/adapters/netlify.d.ts.map +1 -0
  33. package/dist/adapters/netlify.js +12 -0
  34. package/dist/adapters/netlify.js.map +1 -0
  35. package/dist/adapters/vercel.d.ts +14 -0
  36. package/dist/adapters/vercel.d.ts.map +1 -0
  37. package/dist/adapters/vercel.js +20 -0
  38. package/dist/adapters/vercel.js.map +1 -0
  39. package/dist/auth/jwt.d.ts +20 -0
  40. package/dist/auth/jwt.d.ts.map +1 -0
  41. package/dist/auth/jwt.js +79 -0
  42. package/dist/auth/jwt.js.map +1 -0
  43. package/dist/auth/password.d.ts +25 -0
  44. package/dist/auth/password.d.ts.map +1 -0
  45. package/dist/auth/password.js +93 -0
  46. package/dist/auth/password.js.map +1 -0
  47. package/dist/auth/rbac.d.ts +10 -0
  48. package/dist/auth/rbac.d.ts.map +1 -0
  49. package/dist/auth/rbac.js +34 -0
  50. package/dist/auth/rbac.js.map +1 -0
  51. package/dist/auth/session.d.ts +23 -0
  52. package/dist/auth/session.d.ts.map +1 -0
  53. package/dist/auth/session.js +105 -0
  54. package/dist/auth/session.js.map +1 -0
  55. package/dist/cache/index.d.ts +2 -0
  56. package/dist/cache/index.d.ts.map +1 -0
  57. package/dist/cache/index.js +9 -0
  58. package/dist/cache/index.js.map +1 -0
  59. package/dist/cache/store.d.ts +48 -0
  60. package/dist/cache/store.d.ts.map +1 -0
  61. package/dist/cache/store.js +211 -0
  62. package/dist/cache/store.js.map +1 -0
  63. package/dist/cli/commands/build.d.ts +7 -0
  64. package/dist/cli/commands/build.d.ts.map +1 -0
  65. package/dist/cli/commands/build.js +183 -0
  66. package/dist/cli/commands/build.js.map +1 -0
  67. package/dist/cli/commands/dev.d.ts +9 -0
  68. package/dist/cli/commands/dev.d.ts.map +1 -0
  69. package/dist/cli/commands/dev.js +290 -0
  70. package/dist/cli/commands/dev.js.map +1 -0
  71. package/dist/cli/commands/exports.d.ts +2 -0
  72. package/dist/cli/commands/exports.d.ts.map +1 -0
  73. package/dist/cli/commands/exports.js +76 -0
  74. package/dist/cli/commands/exports.js.map +1 -0
  75. package/dist/cli/commands/generate.d.ts +5 -0
  76. package/dist/cli/commands/generate.d.ts.map +1 -0
  77. package/dist/cli/commands/generate.js +149 -0
  78. package/dist/cli/commands/generate.js.map +1 -0
  79. package/dist/cli/commands/init.d.ts +2 -0
  80. package/dist/cli/commands/init.d.ts.map +1 -0
  81. package/dist/cli/commands/init.js +37 -0
  82. package/dist/cli/commands/init.js.map +1 -0
  83. package/dist/cli/commands/repl.d.ts +6 -0
  84. package/dist/cli/commands/repl.d.ts.map +1 -0
  85. package/dist/cli/commands/repl.js +75 -0
  86. package/dist/cli/commands/repl.js.map +1 -0
  87. package/dist/cli/commands/start.d.ts +7 -0
  88. package/dist/cli/commands/start.d.ts.map +1 -0
  89. package/dist/cli/commands/start.js +111 -0
  90. package/dist/cli/commands/start.js.map +1 -0
  91. package/dist/cli/commands/test.d.ts +7 -0
  92. package/dist/cli/commands/test.d.ts.map +1 -0
  93. package/dist/cli/commands/test.js +170 -0
  94. package/dist/cli/commands/test.js.map +1 -0
  95. package/dist/cli/index.d.ts +3 -0
  96. package/dist/cli/index.d.ts.map +1 -0
  97. package/dist/cli/index.js +183 -0
  98. package/dist/cli/index.js.map +1 -0
  99. package/dist/cli/manifest.d.ts +2 -0
  100. package/dist/cli/manifest.d.ts.map +1 -0
  101. package/dist/cli/manifest.js +160 -0
  102. package/dist/cli/manifest.js.map +1 -0
  103. package/dist/cli/optimizers/aot-routes.d.ts +2 -0
  104. package/dist/cli/optimizers/aot-routes.d.ts.map +1 -0
  105. package/dist/cli/optimizers/aot-routes.js +59 -0
  106. package/dist/cli/optimizers/aot-routes.js.map +1 -0
  107. package/dist/cli/optimizers/precompile-serializers.d.ts +2 -0
  108. package/dist/cli/optimizers/precompile-serializers.d.ts.map +1 -0
  109. package/dist/cli/optimizers/precompile-serializers.js +68 -0
  110. package/dist/cli/optimizers/precompile-serializers.js.map +1 -0
  111. package/dist/cli/optimizers/tree-shake.d.ts +2 -0
  112. package/dist/cli/optimizers/tree-shake.d.ts.map +1 -0
  113. package/dist/cli/optimizers/tree-shake.js +64 -0
  114. package/dist/cli/optimizers/tree-shake.js.map +1 -0
  115. package/dist/cli/routes.d.ts +2 -0
  116. package/dist/cli/routes.d.ts.map +1 -0
  117. package/dist/cli/routes.js +158 -0
  118. package/dist/cli/routes.js.map +1 -0
  119. package/dist/cli/utils.d.ts +24 -0
  120. package/dist/cli/utils.d.ts.map +1 -0
  121. package/dist/cli/utils.js +54 -0
  122. package/dist/cli/utils.js.map +1 -0
  123. package/dist/config/constants.d.ts +11 -0
  124. package/dist/config/constants.d.ts.map +1 -0
  125. package/dist/config/constants.js +14 -0
  126. package/dist/config/constants.js.map +1 -0
  127. package/dist/cron/parser.d.ts +2 -0
  128. package/dist/cron/parser.d.ts.map +1 -0
  129. package/dist/cron/parser.js +52 -0
  130. package/dist/cron/parser.js.map +1 -0
  131. package/dist/cron/scheduler.d.ts +22 -0
  132. package/dist/cron/scheduler.d.ts.map +1 -0
  133. package/dist/cron/scheduler.js +102 -0
  134. package/dist/cron/scheduler.js.map +1 -0
  135. package/dist/dataloader/dataloader.d.ts +23 -0
  136. package/dist/dataloader/dataloader.d.ts.map +1 -0
  137. package/dist/dataloader/dataloader.js +98 -0
  138. package/dist/dataloader/dataloader.js.map +1 -0
  139. package/dist/dataloader/registry.d.ts +17 -0
  140. package/dist/dataloader/registry.d.ts.map +1 -0
  141. package/dist/dataloader/registry.js +47 -0
  142. package/dist/dataloader/registry.js.map +1 -0
  143. package/dist/decorators/index.d.ts +63 -0
  144. package/dist/decorators/index.d.ts.map +1 -0
  145. package/dist/decorators/index.js +460 -0
  146. package/dist/decorators/index.js.map +1 -0
  147. package/dist/di/container.d.ts +21 -0
  148. package/dist/di/container.d.ts.map +1 -0
  149. package/dist/di/container.js +81 -0
  150. package/dist/di/container.js.map +1 -0
  151. package/dist/di/inject.d.ts +3 -0
  152. package/dist/di/inject.d.ts.map +1 -0
  153. package/dist/di/inject.js +12 -0
  154. package/dist/di/inject.js.map +1 -0
  155. package/dist/exports/auth.d.ts +6 -0
  156. package/dist/exports/auth.d.ts.map +1 -0
  157. package/dist/exports/auth.js +46 -0
  158. package/dist/exports/auth.js.map +1 -0
  159. package/dist/exports/cache.d.ts +4 -0
  160. package/dist/exports/cache.d.ts.map +1 -0
  161. package/dist/exports/cache.js +20 -0
  162. package/dist/exports/cache.js.map +1 -0
  163. package/dist/exports/config.d.ts +4 -0
  164. package/dist/exports/config.d.ts.map +1 -0
  165. package/dist/exports/config.js +23 -0
  166. package/dist/exports/config.js.map +1 -0
  167. package/dist/exports/dataloader.d.ts +3 -0
  168. package/dist/exports/dataloader.d.ts.map +1 -0
  169. package/dist/exports/dataloader.js +19 -0
  170. package/dist/exports/dataloader.js.map +1 -0
  171. package/dist/exports/decorators.d.ts +2 -0
  172. package/dist/exports/decorators.d.ts.map +1 -0
  173. package/dist/exports/decorators.js +18 -0
  174. package/dist/exports/decorators.js.map +1 -0
  175. package/dist/exports/di.d.ts +4 -0
  176. package/dist/exports/di.d.ts.map +1 -0
  177. package/dist/exports/di.js +8 -0
  178. package/dist/exports/di.js.map +1 -0
  179. package/dist/exports/error.d.ts +2 -0
  180. package/dist/exports/error.d.ts.map +1 -0
  181. package/dist/exports/error.js +16 -0
  182. package/dist/exports/error.js.map +1 -0
  183. package/dist/exports/module.d.ts +3 -0
  184. package/dist/exports/module.d.ts.map +1 -0
  185. package/dist/exports/module.js +6 -0
  186. package/dist/exports/module.js.map +1 -0
  187. package/dist/exports/plugin.d.ts +3 -0
  188. package/dist/exports/plugin.d.ts.map +1 -0
  189. package/dist/exports/plugin.js +6 -0
  190. package/dist/exports/plugin.js.map +1 -0
  191. package/dist/exports/queue.d.ts +9 -0
  192. package/dist/exports/queue.d.ts.map +1 -0
  193. package/dist/exports/queue.js +16 -0
  194. package/dist/exports/queue.js.map +1 -0
  195. package/dist/exports/response.d.ts +2 -0
  196. package/dist/exports/response.d.ts.map +1 -0
  197. package/dist/exports/response.js +18 -0
  198. package/dist/exports/response.js.map +1 -0
  199. package/dist/exports/route.d.ts +14 -0
  200. package/dist/exports/route.d.ts.map +1 -0
  201. package/dist/exports/route.js +25 -0
  202. package/dist/exports/route.js.map +1 -0
  203. package/dist/exports/security.d.ts +3 -0
  204. package/dist/exports/security.d.ts.map +1 -0
  205. package/dist/exports/security.js +13 -0
  206. package/dist/exports/security.js.map +1 -0
  207. package/dist/exports/swagger.d.ts +3 -0
  208. package/dist/exports/swagger.d.ts.map +1 -0
  209. package/dist/exports/swagger.js +6 -0
  210. package/dist/exports/swagger.js.map +1 -0
  211. package/dist/exports/validation.d.ts +2 -0
  212. package/dist/exports/validation.d.ts.map +1 -0
  213. package/dist/exports/validation.js +8 -0
  214. package/dist/exports/validation.js.map +1 -0
  215. package/dist/index.d.ts +11 -0
  216. package/dist/index.d.ts.map +1 -0
  217. package/dist/index.js +71 -0
  218. package/dist/index.js.map +1 -0
  219. package/dist/integrations/drizzle.d.ts +10 -0
  220. package/dist/integrations/drizzle.d.ts.map +1 -0
  221. package/dist/integrations/drizzle.js +34 -0
  222. package/dist/integrations/drizzle.js.map +1 -0
  223. package/dist/integrations/jwt.d.ts +12 -0
  224. package/dist/integrations/jwt.d.ts.map +1 -0
  225. package/dist/integrations/jwt.js +27 -0
  226. package/dist/integrations/jwt.js.map +1 -0
  227. package/dist/integrations/mongodb.d.ts +13 -0
  228. package/dist/integrations/mongodb.d.ts.map +1 -0
  229. package/dist/integrations/mongodb.js +48 -0
  230. package/dist/integrations/mongodb.js.map +1 -0
  231. package/dist/integrations/mongoose.d.ts +13 -0
  232. package/dist/integrations/mongoose.d.ts.map +1 -0
  233. package/dist/integrations/mongoose.js +58 -0
  234. package/dist/integrations/mongoose.js.map +1 -0
  235. package/dist/integrations/openai.d.ts +12 -0
  236. package/dist/integrations/openai.d.ts.map +1 -0
  237. package/dist/integrations/openai.js +47 -0
  238. package/dist/integrations/openai.js.map +1 -0
  239. package/dist/integrations/postgres.d.ts +13 -0
  240. package/dist/integrations/postgres.d.ts.map +1 -0
  241. package/dist/integrations/postgres.js +55 -0
  242. package/dist/integrations/postgres.js.map +1 -0
  243. package/dist/integrations/posthog.d.ts +13 -0
  244. package/dist/integrations/posthog.d.ts.map +1 -0
  245. package/dist/integrations/posthog.js +49 -0
  246. package/dist/integrations/posthog.js.map +1 -0
  247. package/dist/integrations/prisma.d.ts +10 -0
  248. package/dist/integrations/prisma.d.ts.map +1 -0
  249. package/dist/integrations/prisma.js +27 -0
  250. package/dist/integrations/prisma.js.map +1 -0
  251. package/dist/integrations/redis.d.ts +12 -0
  252. package/dist/integrations/redis.d.ts.map +1 -0
  253. package/dist/integrations/redis.js +48 -0
  254. package/dist/integrations/redis.js.map +1 -0
  255. package/dist/integrations/resend.d.ts +12 -0
  256. package/dist/integrations/resend.d.ts.map +1 -0
  257. package/dist/integrations/resend.js +47 -0
  258. package/dist/integrations/resend.js.map +1 -0
  259. package/dist/integrations/s3.d.ts +15 -0
  260. package/dist/integrations/s3.d.ts.map +1 -0
  261. package/dist/integrations/s3.js +60 -0
  262. package/dist/integrations/s3.js.map +1 -0
  263. package/dist/integrations/supabase.d.ts +13 -0
  264. package/dist/integrations/supabase.d.ts.map +1 -0
  265. package/dist/integrations/supabase.js +51 -0
  266. package/dist/integrations/supabase.js.map +1 -0
  267. package/dist/lib/start-repl.d.ts +2 -0
  268. package/dist/lib/start-repl.d.ts.map +1 -0
  269. package/dist/lib/start-repl.js +122 -0
  270. package/dist/lib/start-repl.js.map +1 -0
  271. package/dist/lib/start-server.d.ts +2 -0
  272. package/dist/lib/start-server.d.ts.map +1 -0
  273. package/dist/lib/start-server.js +103 -0
  274. package/dist/lib/start-server.js.map +1 -0
  275. package/dist/middleware/backpressure.d.ts +11 -0
  276. package/dist/middleware/backpressure.d.ts.map +1 -0
  277. package/dist/middleware/backpressure.js +55 -0
  278. package/dist/middleware/backpressure.js.map +1 -0
  279. package/dist/middleware/cache.d.ts +10 -0
  280. package/dist/middleware/cache.d.ts.map +1 -0
  281. package/dist/middleware/cache.js +97 -0
  282. package/dist/middleware/cache.js.map +1 -0
  283. package/dist/middleware/compression.d.ts +3 -0
  284. package/dist/middleware/compression.d.ts.map +1 -0
  285. package/dist/middleware/compression.js +132 -0
  286. package/dist/middleware/compression.js.map +1 -0
  287. package/dist/middleware/dedupe.d.ts +10 -0
  288. package/dist/middleware/dedupe.d.ts.map +1 -0
  289. package/dist/middleware/dedupe.js +79 -0
  290. package/dist/middleware/dedupe.js.map +1 -0
  291. package/dist/middleware/exception-filter.d.ts +9 -0
  292. package/dist/middleware/exception-filter.d.ts.map +1 -0
  293. package/dist/middleware/exception-filter.js +26 -0
  294. package/dist/middleware/exception-filter.js.map +1 -0
  295. package/dist/middleware/guard.d.ts +11 -0
  296. package/dist/middleware/guard.d.ts.map +1 -0
  297. package/dist/middleware/guard.js +26 -0
  298. package/dist/middleware/guard.js.map +1 -0
  299. package/dist/middleware/interceptor.d.ts +4 -0
  300. package/dist/middleware/interceptor.d.ts.map +1 -0
  301. package/dist/middleware/interceptor.js +32 -0
  302. package/dist/middleware/interceptor.js.map +1 -0
  303. package/dist/middleware/ip-filter.d.ts +11 -0
  304. package/dist/middleware/ip-filter.d.ts.map +1 -0
  305. package/dist/middleware/ip-filter.js +27 -0
  306. package/dist/middleware/ip-filter.js.map +1 -0
  307. package/dist/middleware/middleware.d.ts +26 -0
  308. package/dist/middleware/middleware.d.ts.map +1 -0
  309. package/dist/middleware/middleware.js +271 -0
  310. package/dist/middleware/middleware.js.map +1 -0
  311. package/dist/middleware/pipe.d.ts +9 -0
  312. package/dist/middleware/pipe.d.ts.map +1 -0
  313. package/dist/middleware/pipe.js +24 -0
  314. package/dist/middleware/pipe.js.map +1 -0
  315. package/dist/middleware/security.d.ts +53 -0
  316. package/dist/middleware/security.d.ts.map +1 -0
  317. package/dist/middleware/security.js +257 -0
  318. package/dist/middleware/security.js.map +1 -0
  319. package/dist/module/module.d.ts +17 -0
  320. package/dist/module/module.d.ts.map +1 -0
  321. package/dist/module/module.js +37 -0
  322. package/dist/module/module.js.map +1 -0
  323. package/dist/observability/health.d.ts +23 -0
  324. package/dist/observability/health.d.ts.map +1 -0
  325. package/dist/observability/health.js +49 -0
  326. package/dist/observability/health.js.map +1 -0
  327. package/dist/observability/index.d.ts +4 -0
  328. package/dist/observability/index.d.ts.map +1 -0
  329. package/dist/observability/index.js +10 -0
  330. package/dist/observability/index.js.map +1 -0
  331. package/dist/observability/otel.d.ts +27 -0
  332. package/dist/observability/otel.d.ts.map +1 -0
  333. package/dist/observability/otel.js +45 -0
  334. package/dist/observability/otel.js.map +1 -0
  335. package/dist/observability/prometheus.d.ts +30 -0
  336. package/dist/observability/prometheus.d.ts.map +1 -0
  337. package/dist/observability/prometheus.js +31 -0
  338. package/dist/observability/prometheus.js.map +1 -0
  339. package/dist/queue/client.d.ts +13 -0
  340. package/dist/queue/client.d.ts.map +1 -0
  341. package/dist/queue/client.js +64 -0
  342. package/dist/queue/client.js.map +1 -0
  343. package/dist/queue/drivers/MemoryDriver.d.ts +17 -0
  344. package/dist/queue/drivers/MemoryDriver.d.ts.map +1 -0
  345. package/dist/queue/drivers/MemoryDriver.js +109 -0
  346. package/dist/queue/drivers/MemoryDriver.js.map +1 -0
  347. package/dist/queue/drivers/RedisDriver.d.ts +18 -0
  348. package/dist/queue/drivers/RedisDriver.d.ts.map +1 -0
  349. package/dist/queue/drivers/RedisDriver.js +120 -0
  350. package/dist/queue/drivers/RedisDriver.js.map +1 -0
  351. package/dist/queue/index.d.ts +12 -0
  352. package/dist/queue/index.d.ts.map +1 -0
  353. package/dist/queue/index.js +27 -0
  354. package/dist/queue/index.js.map +1 -0
  355. package/dist/queue/types.d.ts +51 -0
  356. package/dist/queue/types.d.ts.map +1 -0
  357. package/dist/queue/types.js +3 -0
  358. package/dist/queue/types.js.map +1 -0
  359. package/dist/queue/worker.d.ts +18 -0
  360. package/dist/queue/worker.d.ts.map +1 -0
  361. package/dist/queue/worker.js +160 -0
  362. package/dist/queue/worker.js.map +1 -0
  363. package/dist/response/index.d.ts +16 -0
  364. package/dist/response/index.d.ts.map +1 -0
  365. package/dist/response/index.js +19 -0
  366. package/dist/response/index.js.map +1 -0
  367. package/dist/router/gateway.d.ts +64 -0
  368. package/dist/router/gateway.d.ts.map +1 -0
  369. package/dist/router/gateway.js +11 -0
  370. package/dist/router/gateway.js.map +1 -0
  371. package/dist/router/radix.d.ts +42 -0
  372. package/dist/router/radix.d.ts.map +1 -0
  373. package/dist/router/radix.js +523 -0
  374. package/dist/router/radix.js.map +1 -0
  375. package/dist/router/router.d.ts +59 -0
  376. package/dist/router/router.d.ts.map +1 -0
  377. package/dist/router/router.js +386 -0
  378. package/dist/router/router.js.map +1 -0
  379. package/dist/server/app.d.ts +206 -0
  380. package/dist/server/app.d.ts.map +1 -0
  381. package/dist/server/app.js +2191 -0
  382. package/dist/server/app.js.map +1 -0
  383. package/dist/server/cluster.d.ts +52 -0
  384. package/dist/server/cluster.d.ts.map +1 -0
  385. package/dist/server/cluster.js +165 -0
  386. package/dist/server/cluster.js.map +1 -0
  387. package/dist/server/context.d.ts +40 -0
  388. package/dist/server/context.d.ts.map +1 -0
  389. package/dist/server/context.js +74 -0
  390. package/dist/server/context.js.map +1 -0
  391. package/dist/server/define.d.ts +14 -0
  392. package/dist/server/define.d.ts.map +1 -0
  393. package/dist/server/define.js +22 -0
  394. package/dist/server/define.js.map +1 -0
  395. package/dist/server/dep-graph.d.ts +2 -0
  396. package/dist/server/dep-graph.d.ts.map +1 -0
  397. package/dist/server/dep-graph.js +109 -0
  398. package/dist/server/dep-graph.js.map +1 -0
  399. package/dist/server/dev-error-overlay.d.ts +3 -0
  400. package/dist/server/dev-error-overlay.d.ts.map +1 -0
  401. package/dist/server/dev-error-overlay.js +116 -0
  402. package/dist/server/dev-error-overlay.js.map +1 -0
  403. package/dist/server/hot-reload.d.ts +28 -0
  404. package/dist/server/hot-reload.d.ts.map +1 -0
  405. package/dist/server/hot-reload.js +253 -0
  406. package/dist/server/hot-reload.js.map +1 -0
  407. package/dist/server/request.d.ts +71 -0
  408. package/dist/server/request.d.ts.map +1 -0
  409. package/dist/server/request.js +408 -0
  410. package/dist/server/request.js.map +1 -0
  411. package/dist/server/response.d.ts +36 -0
  412. package/dist/server/response.d.ts.map +1 -0
  413. package/dist/server/response.js +254 -0
  414. package/dist/server/response.js.map +1 -0
  415. package/dist/server/route.d.ts +148 -0
  416. package/dist/server/route.d.ts.map +1 -0
  417. package/dist/server/route.js +56 -0
  418. package/dist/server/route.js.map +1 -0
  419. package/dist/server/sse.d.ts +16 -0
  420. package/dist/server/sse.d.ts.map +1 -0
  421. package/dist/server/sse.js +51 -0
  422. package/dist/server/sse.js.map +1 -0
  423. package/dist/server/uws-adapter.d.ts +101 -0
  424. package/dist/server/uws-adapter.d.ts.map +1 -0
  425. package/dist/server/uws-adapter.js +338 -0
  426. package/dist/server/uws-adapter.js.map +1 -0
  427. package/dist/swagger/index.d.ts +9 -0
  428. package/dist/swagger/index.d.ts.map +1 -0
  429. package/dist/swagger/index.js +147 -0
  430. package/dist/swagger/index.js.map +1 -0
  431. package/dist/testing/client.d.ts +44 -0
  432. package/dist/testing/client.d.ts.map +1 -0
  433. package/dist/testing/client.js +105 -0
  434. package/dist/testing/client.js.map +1 -0
  435. package/dist/testing/index.d.ts +7 -0
  436. package/dist/testing/index.d.ts.map +1 -0
  437. package/dist/testing/index.js +128 -0
  438. package/dist/testing/index.js.map +1 -0
  439. package/dist/testing/mocks.d.ts +25 -0
  440. package/dist/testing/mocks.d.ts.map +1 -0
  441. package/dist/testing/mocks.js +117 -0
  442. package/dist/testing/mocks.js.map +1 -0
  443. package/dist/testing/reporter.d.ts +13 -0
  444. package/dist/testing/reporter.d.ts.map +1 -0
  445. package/dist/testing/reporter.js +101 -0
  446. package/dist/testing/reporter.js.map +1 -0
  447. package/dist/threads/pool.d.ts +13 -0
  448. package/dist/threads/pool.d.ts.map +1 -0
  449. package/dist/threads/pool.js +93 -0
  450. package/dist/threads/pool.js.map +1 -0
  451. package/dist/threads/worker-runner.d.ts +2 -0
  452. package/dist/threads/worker-runner.d.ts.map +1 -0
  453. package/dist/threads/worker-runner.js +71 -0
  454. package/dist/threads/worker-runner.js.map +1 -0
  455. package/dist/types.d.ts +200 -0
  456. package/dist/types.d.ts.map +1 -0
  457. package/dist/types.js +3 -0
  458. package/dist/types.js.map +1 -0
  459. package/dist/utils/circuit-breaker.d.ts +30 -0
  460. package/dist/utils/circuit-breaker.d.ts.map +1 -0
  461. package/dist/utils/circuit-breaker.js +72 -0
  462. package/dist/utils/circuit-breaker.js.map +1 -0
  463. package/dist/utils/config.d.ts +9 -0
  464. package/dist/utils/config.d.ts.map +1 -0
  465. package/dist/utils/config.js +229 -0
  466. package/dist/utils/config.js.map +1 -0
  467. package/dist/utils/env.d.ts +25 -0
  468. package/dist/utils/env.d.ts.map +1 -0
  469. package/dist/utils/env.js +155 -0
  470. package/dist/utils/env.js.map +1 -0
  471. package/dist/utils/errors.d.ts +51 -0
  472. package/dist/utils/errors.d.ts.map +1 -0
  473. package/dist/utils/errors.js +233 -0
  474. package/dist/utils/errors.js.map +1 -0
  475. package/dist/utils/logger.d.ts +6 -0
  476. package/dist/utils/logger.d.ts.map +1 -0
  477. package/dist/utils/logger.js +70 -0
  478. package/dist/utils/logger.js.map +1 -0
  479. package/dist/utils/plugin.d.ts +7 -0
  480. package/dist/utils/plugin.d.ts.map +1 -0
  481. package/dist/utils/plugin.js +30 -0
  482. package/dist/utils/plugin.js.map +1 -0
  483. package/dist/utils/validator.d.ts +300 -0
  484. package/dist/utils/validator.d.ts.map +1 -0
  485. package/dist/utils/validator.js +719 -0
  486. package/dist/utils/validator.js.map +1 -0
  487. package/dist/websocket/server.d.ts +66 -0
  488. package/dist/websocket/server.d.ts.map +1 -0
  489. package/dist/websocket/server.js +156 -0
  490. package/dist/websocket/server.js.map +1 -0
  491. package/dist/websocket/socket.d.ts +119 -0
  492. package/dist/websocket/socket.d.ts.map +1 -0
  493. package/dist/websocket/socket.js +240 -0
  494. package/dist/websocket/socket.js.map +1 -0
  495. package/package.json +270 -0
@@ -0,0 +1,2191 @@
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.activeAppInstance = exports.app = exports.App = void 0;
37
+ exports.getActiveApp = getActiveApp;
38
+ const node_http_1 = require("node:http");
39
+ const node_https_1 = require("node:https");
40
+ const node_http2_1 = require("node:http2");
41
+ const ws_1 = require("ws");
42
+ const node_url_1 = require("node:url");
43
+ const validator_1 = require("../utils/validator");
44
+ const request_1 = require("./request");
45
+ const response_1 = require("./response");
46
+ const context_1 = require("./context");
47
+ const router_1 = require("../router/router");
48
+ const middleware_1 = require("../middleware/middleware");
49
+ const errors_1 = require("../utils/errors");
50
+ const server_1 = require("../websocket/server");
51
+ const socket_1 = require("../websocket/socket");
52
+ const config_1 = require("../utils/config");
53
+ const logger_1 = require("../utils/logger");
54
+ const hot_reload_1 = require("./hot-reload");
55
+ const dev_error_overlay_1 = require("./dev-error-overlay");
56
+ const uws_adapter_1 = require("./uws-adapter");
57
+ const client_1 = require("../queue/client");
58
+ const worker_1 = require("../queue/worker");
59
+ const scheduler_1 = require("../cron/scheduler");
60
+ const container_1 = require("../di/container");
61
+ // eslint-disable-next-line @typescript-eslint/no-empty-object-type
62
+ class App {
63
+ get queueClient() {
64
+ return this._queueClient;
65
+ }
66
+ get queueWorker() {
67
+ return this._queueWorker;
68
+ }
69
+ constructor(options = {}) {
70
+ this.redirectServer = null;
71
+ this.apiDir = null;
72
+ this.globalMiddleware = [];
73
+ this.errorHandlers = [];
74
+ this._configured = false;
75
+ this._routesMounted = false;
76
+ this._loggerCreated = false;
77
+ this.registeredPlugins = new Map();
78
+ this.routeMap = new Map();
79
+ this.hotReloader = null;
80
+ this.lazyRoutes = new Map();
81
+ this.wsServer = new server_1.ExisWebSocketServer();
82
+ this.rawWsServer = new ws_1.WebSocketServer({ noServer: true });
83
+ // ─── uWebSockets.js Backend ──────────────────────────────────────────────────
84
+ this._useUws = false;
85
+ this._uwsApp = null;
86
+ this._uwsListenToken = null;
87
+ // ─── Lifecycle Hooks Registry ───────────────────────────────────────────────
88
+ this.hooks = {
89
+ ready: [],
90
+ close: [],
91
+ request: [],
92
+ response: [],
93
+ error: [],
94
+ route: [],
95
+ };
96
+ // ─── Dataloaders Registry ───────────────────────────────────────────────────
97
+ this._dataloaders = new Map();
98
+ this.explicitOptions = {};
99
+ // ─── Dependency Injection ───────────────────────────────────────────────────
100
+ this.container = new container_1.Container();
101
+ // ─── Queue ──────────────────────────────────────────────────────────────────
102
+ this._queueClient = null;
103
+ this._queueWorker = null;
104
+ this._cronScheduler = null;
105
+ this._pendingQueueJobs = [];
106
+ this.shutdownHooks = [];
107
+ // ─── App Initialization ────────────────────────────────────────────────────────
108
+ this._configLoaded = false;
109
+ this.explicitOptions = options;
110
+ this.options = (0, config_1.mergeConfig)(config_1.defaultConfig, options);
111
+ this.router = new router_1.Router();
112
+ this.ensureLogger();
113
+ // Queue setup is now deferred to app.create() when the config is loaded
114
+ if (this.options.queue) {
115
+ this._initQueue(this.options.queue);
116
+ }
117
+ // Auto-detect uWebSockets.js backend
118
+ if (options.server === 'uws' ||
119
+ (options.server !== 'node' &&
120
+ (0, uws_adapter_1.isUwsAvailable)() &&
121
+ this.options.env !== 'test')) {
122
+ this._useUws = true;
123
+ // Don't init Node server — we'll create the uWS app at listen() time
124
+ }
125
+ else {
126
+ this._useUws = false;
127
+ this.server = this._initServer();
128
+ }
129
+ }
130
+ ensureLogger() {
131
+ if (this._loggerCreated)
132
+ return;
133
+ this._loggerCreated = true;
134
+ const loggerOptions = (0, logger_1.resolveLoggerConfig)(this.options.logger);
135
+ if (process.env.__EXIS_REPL || process.env.__EXIS_TEST) {
136
+ loggerOptions.level = 'warn';
137
+ }
138
+ this.log = (0, logger_1.createLogger)(loggerOptions);
139
+ }
140
+ _initServer() {
141
+ let server;
142
+ if (this.options.ssl) {
143
+ if (this.options.http2 !== false) {
144
+ server = (0, node_http2_1.createSecureServer)({ allowHTTP1: true, ...this.options.ssl }, this.handle.bind(this));
145
+ }
146
+ else {
147
+ server = (0, node_https_1.createServer)(this.options.ssl, this.handle.bind(this));
148
+ }
149
+ }
150
+ else {
151
+ server = (0, node_http_1.createServer)(this.handle.bind(this));
152
+ }
153
+ server.on('upgrade', this._handleUpgrade.bind(this));
154
+ const keepAlive = this.options.keepAlive;
155
+ if (keepAlive) {
156
+ const kaConfig = keepAlive === true ? {} : keepAlive;
157
+ if ('keepAliveTimeout' in server)
158
+ server.keepAliveTimeout = kaConfig.timeoutMs ?? 5000;
159
+ if ('headersTimeout' in server)
160
+ server.headersTimeout = kaConfig.headersTimeoutMs ?? 60000;
161
+ if (kaConfig.maxRequests !== undefined &&
162
+ 'maxRequestsPerSocket' in server) {
163
+ ;
164
+ server.maxRequestsPerSocket = kaConfig.maxRequests;
165
+ }
166
+ }
167
+ return server;
168
+ }
169
+ // ─── Lifecycle Hooks Registration ───────────────────────────────────────────
170
+ onReady(cb) {
171
+ this.hooks.ready.push(cb);
172
+ return this;
173
+ }
174
+ onClose(cb) {
175
+ this.hooks.close.push(cb);
176
+ return this;
177
+ }
178
+ onRequest(cb) {
179
+ this.hooks.request.push(cb);
180
+ return this;
181
+ }
182
+ onResponse(cb) {
183
+ this.hooks.response.push(cb);
184
+ return this;
185
+ }
186
+ onError(cb) {
187
+ this.hooks.error.push(cb);
188
+ return this;
189
+ }
190
+ onRoute(cb) {
191
+ this.hooks.route.push(cb);
192
+ return this;
193
+ }
194
+ // ─── Dataloaders ────────────────────────────────────────────────────────────
195
+ dataloader(name, batchFn, options) {
196
+ if (this._dataloaders.has(name)) {
197
+ throw new Error(`Dataloader '${name}' is already registered`);
198
+ }
199
+ this._dataloaders.set(name, { batchFn, options });
200
+ return this;
201
+ }
202
+ // ─── Introspection ──────────────────────────────────────────────────────────
203
+ getRoutes() {
204
+ return this.router.getRoutes();
205
+ }
206
+ // ─── Plugin System ──────────────────────────────────────────────────────────
207
+ hasPlugin(name) {
208
+ return this.registeredPlugins.has(name);
209
+ }
210
+ async register(pluginOrInstance, legacyOptions) {
211
+ let plugin;
212
+ let options;
213
+ if ('plugin' in pluginOrInstance && !('register' in pluginOrInstance)) {
214
+ plugin = pluginOrInstance.plugin;
215
+ options = pluginOrInstance.options;
216
+ }
217
+ else {
218
+ plugin = pluginOrInstance;
219
+ options = legacyOptions;
220
+ }
221
+ if (this.registeredPlugins.has(plugin.name)) {
222
+ throw new Error(`Plugin '${plugin.name}' is already registered`);
223
+ }
224
+ if (plugin.dependencies) {
225
+ for (const dep of plugin.dependencies) {
226
+ if (!this.registeredPlugins.has(dep)) {
227
+ throw new Error(`Plugin '${plugin.name}' requires dependency '${dep}' which is not registered`);
228
+ }
229
+ }
230
+ }
231
+ // eslint-disable-next-line @typescript-eslint/no-this-alias
232
+ let targetApp = this;
233
+ let pluginRouter;
234
+ if (plugin.encapsulate !== false) {
235
+ pluginRouter = new router_1.Router();
236
+ // Create a proxy that intercepts route and middleware registrations
237
+ targetApp = new Proxy(this, {
238
+ get(target, prop, receiver) {
239
+ if ([
240
+ 'get',
241
+ 'post',
242
+ 'put',
243
+ 'patch',
244
+ 'delete',
245
+ 'httpOptions',
246
+ 'head',
247
+ 'all',
248
+ 'ws',
249
+ 'query',
250
+ 'connect',
251
+ 'trace',
252
+ ].includes(prop)) {
253
+ return (path, ...handlers) => {
254
+ const routerMethod = prop === 'httpOptions' ? 'options' : prop;
255
+ pluginRouter[routerMethod](path, ...handlers);
256
+ return receiver;
257
+ };
258
+ }
259
+ if (prop === 'use') {
260
+ return (...handlers) => {
261
+ for (const h of handlers) {
262
+ if (h.length === 4) {
263
+ // error handlers stay global for now, or we could scope them
264
+ target.use(h);
265
+ }
266
+ else {
267
+ pluginRouter.use(h);
268
+ }
269
+ }
270
+ return receiver;
271
+ };
272
+ }
273
+ // Encapsulate lifecycle hooks by converting them to scoped middleware!
274
+ if (['onRequest', 'onResponse'].includes(prop)) {
275
+ return (cb) => {
276
+ if (prop === 'onRequest') {
277
+ pluginRouter.use(async (req, res, next) => {
278
+ try {
279
+ await cb(req, res);
280
+ next();
281
+ }
282
+ catch (err) {
283
+ next(err);
284
+ }
285
+ });
286
+ }
287
+ else if (prop === 'onResponse') {
288
+ pluginRouter.use((req, res, next) => {
289
+ res.raw.on('finish', () => {
290
+ cb(req, res).catch(() => {
291
+ /* ignore */
292
+ });
293
+ });
294
+ next();
295
+ });
296
+ }
297
+ return receiver;
298
+ };
299
+ }
300
+ const value = Reflect.get(target, prop, receiver);
301
+ return typeof value === 'function' ? value.bind(target) : value;
302
+ },
303
+ });
304
+ }
305
+ await plugin.register(targetApp, options);
306
+ if (pluginRouter) {
307
+ this.mount('/', pluginRouter);
308
+ }
309
+ this.registeredPlugins.set(plugin.name, plugin);
310
+ return this;
311
+ }
312
+ // ─── Middleware ─────────────────────────────────────────────────────────────
313
+ use(...handlers) {
314
+ for (const h of handlers) {
315
+ if (h.length === 4) {
316
+ this.errorHandlers.push(h);
317
+ }
318
+ else {
319
+ this.globalMiddleware.push(h);
320
+ }
321
+ }
322
+ return this;
323
+ }
324
+ get(path, ...handlers) {
325
+ this.router.get(path, ...handlers);
326
+ this.hooks.route.forEach((hook) => hook({ method: 'GET', path }));
327
+ return this;
328
+ }
329
+ post(path, ...handlers) {
330
+ this.router.post(path, ...handlers);
331
+ this.hooks.route.forEach((hook) => hook({ method: 'POST', path }));
332
+ return this;
333
+ }
334
+ put(path, ...handlers) {
335
+ this.router.put(path, ...handlers);
336
+ this.hooks.route.forEach((hook) => hook({ method: 'PUT', path }));
337
+ return this;
338
+ }
339
+ patch(path, ...handlers) {
340
+ this.router.patch(path, ...handlers);
341
+ this.hooks.route.forEach((hook) => hook({ method: 'PATCH', path }));
342
+ return this;
343
+ }
344
+ delete(path, ...handlers) {
345
+ this.router.delete(path, ...handlers);
346
+ this.hooks.route.forEach((hook) => hook({ method: 'DELETE', path }));
347
+ return this;
348
+ }
349
+ httpOptions(path, ...handlers) {
350
+ this.router.options(path, ...handlers);
351
+ this.hooks.route.forEach((hook) => hook({ method: 'OPTIONS', path }));
352
+ return this;
353
+ }
354
+ all(path, ...handlers) {
355
+ this.router.all(path, ...handlers);
356
+ this.hooks.route.forEach((hook) => hook({ method: 'ALL', path }));
357
+ return this;
358
+ }
359
+ head(path, ...handlers) {
360
+ this.router.head(path, ...handlers);
361
+ this.hooks.route.forEach((hook) => hook({ method: 'HEAD', path }));
362
+ return this;
363
+ }
364
+ connect(path, ...handlers) {
365
+ this.router.connect(path, ...handlers);
366
+ this.hooks.route.forEach((hook) => hook({ method: 'CONNECT', path }));
367
+ return this;
368
+ }
369
+ trace(path, ...handlers) {
370
+ this.router.trace(path, ...handlers);
371
+ this.hooks.route.forEach((hook) => hook({ method: 'TRACE', path }));
372
+ return this;
373
+ }
374
+ query(path, ...handlers) {
375
+ this.router.query(path, ...handlers);
376
+ this.hooks.route.forEach((hook) => hook({ method: 'QUERY', path }));
377
+ return this;
378
+ }
379
+ // ─── WebSocket Registration ──────────────────────────────────────────────────
380
+ ws(path, ...handlers) {
381
+ this.router.ws(path, ...handlers);
382
+ this.hooks.route.forEach((hook) => hook({ method: 'WS', path }));
383
+ return this;
384
+ }
385
+ publish(room, data) {
386
+ this.wsServer.publish(room, data);
387
+ }
388
+ // ─── Mount Sub-Router ─────────────────────────────────────────────────────────
389
+ mount(prefix, subRouter) {
390
+ // copy routes from sub-router with prefix prepended
391
+ for (const route of subRouter.getRoutes()) {
392
+ const newRouter = new router_1.Router();
393
+ const method = route.method.toLowerCase();
394
+ if (method === 'all') {
395
+ newRouter.all(prefix + route.path, ...route.handlers);
396
+ }
397
+ else {
398
+ ;
399
+ newRouter[method](prefix + route.path, ...route.handlers);
400
+ }
401
+ for (const r of newRouter.getRoutes()) {
402
+ this.router.addRawRoute(r);
403
+ }
404
+ this.hooks.route.forEach((hook) => hook({ method: route.method, path: prefix + route.path }));
405
+ }
406
+ return this;
407
+ }
408
+ // ─── Class-Based Controllers Registration ──────────────────────────────────
409
+ registerControllers(controllers, basePath = '', prefixMiddlewares = []) {
410
+ const ROUTE_REGISTRY = Symbol.for('exisjs:routes');
411
+ const CONTROLLER_PREFIX = Symbol.for('exisjs:controller_prefix');
412
+ const CONTROLLER_HOST = Symbol.for('exisjs:controller_host');
413
+ const MIDDLEWARE_REGISTRY = Symbol.for('exisjs:middlewares');
414
+ const ROUTE_METADATA = Symbol.for('exisjs:route_metadata');
415
+ const LIFECYCLE_METADATA = Symbol.for('exisjs:lifecycle_metadata');
416
+ const PARAM_METADATA = Symbol.for('exisjs:param_metadata');
417
+ for (const ControllerClass of controllers) {
418
+ const prefix = ControllerClass.prototype[CONTROLLER_PREFIX] || '';
419
+ const host = ControllerClass.prototype[CONTROLLER_HOST];
420
+ const routes = ControllerClass.prototype[ROUTE_REGISTRY] || [];
421
+ const classMiddlewares = [
422
+ ...(Array.isArray(ControllerClass.prototype[MIDDLEWARE_REGISTRY])
423
+ ? ControllerClass.prototype[MIDDLEWARE_REGISTRY]
424
+ : ControllerClass.prototype[MIDDLEWARE_REGISTRY]?._classMiddlewares ||
425
+ []),
426
+ ];
427
+ const methodMiddlewares = (!Array.isArray(ControllerClass.prototype[MIDDLEWARE_REGISTRY])
428
+ ? ControllerClass.prototype[MIDDLEWARE_REGISTRY]
429
+ : {}) || {};
430
+ const routeMetadataMap = ControllerClass.prototype[ROUTE_METADATA] || {};
431
+ const lifecycleMetadataMap = ControllerClass.prototype[LIFECYCLE_METADATA] || {};
432
+ const paramMetadataMap = ControllerClass.prototype[PARAM_METADATA] || {};
433
+ for (const route of routes) {
434
+ const method = route.method.toLowerCase();
435
+ const executeCore = async (req, res, next, streamOrSocket) => {
436
+ let instance = this.container.resolve(ControllerClass);
437
+ if (!instance) {
438
+ // Auto-instantiate if not provided via DI
439
+ instance = new ControllerClass();
440
+ this.container.provide(ControllerClass, { useValue: instance });
441
+ }
442
+ try {
443
+ // 1. Run Guards
444
+ const routeLifecycle = lifecycleMetadataMap[route.handlerName] || {};
445
+ const guards = routeLifecycle.guards || [];
446
+ for (const guard of guards) {
447
+ let allowed = false;
448
+ if (typeof guard === 'function') {
449
+ if (guard.prototype &&
450
+ typeof guard.prototype.canActivate === 'function') {
451
+ let guardInstance = this.container.resolve(guard);
452
+ if (!guardInstance) {
453
+ guardInstance = new guard();
454
+ }
455
+ allowed = await guardInstance.canActivate(req);
456
+ }
457
+ else {
458
+ allowed = await guard(req);
459
+ }
460
+ }
461
+ if (!allowed) {
462
+ if (res) {
463
+ res.status(403).json({
464
+ success: false,
465
+ error: { code: 'FORBIDDEN', message: 'Forbidden by Guard' },
466
+ });
467
+ }
468
+ return;
469
+ }
470
+ }
471
+ // 2. Resolve parameters
472
+ const paramMetadata = paramMetadataMap[route.handlerName] || [];
473
+ const args = [];
474
+ if (paramMetadata.length === 0) {
475
+ if (method === 'ws' || method === 'sse') {
476
+ args.push(req, streamOrSocket);
477
+ }
478
+ else {
479
+ args.push(req, res, next);
480
+ }
481
+ }
482
+ else {
483
+ for (const param of paramMetadata) {
484
+ if (!param) {
485
+ args.push(undefined);
486
+ continue;
487
+ }
488
+ let rawArg;
489
+ switch (param.type) {
490
+ case 'req':
491
+ rawArg = req;
492
+ break;
493
+ case 'res':
494
+ rawArg = res;
495
+ break;
496
+ case 'body':
497
+ if (req.body === undefined &&
498
+ ['POST', 'PUT', 'PATCH'].includes(req.method)) {
499
+ const contentType = req.headers['content-type'] || '';
500
+ if (contentType.includes('json')) {
501
+ await req.json().catch(() => {
502
+ /* noop */
503
+ });
504
+ }
505
+ else if (contentType.includes('form')) {
506
+ await req.formData().catch(() => {
507
+ /* noop */
508
+ });
509
+ }
510
+ }
511
+ rawArg = req.body;
512
+ break;
513
+ case 'param':
514
+ rawArg = param.name ? req.params[param.name] : req.params;
515
+ break;
516
+ case 'query':
517
+ rawArg = param.name ? req.query[param.name] : req.query;
518
+ break;
519
+ case 'header':
520
+ rawArg = param.name
521
+ ? req.headers[param.name.toLowerCase()]
522
+ : req.headers;
523
+ break;
524
+ case 'session':
525
+ rawArg = req.session;
526
+ break;
527
+ case 'next':
528
+ rawArg = next;
529
+ break;
530
+ case 'host':
531
+ rawArg = param.name
532
+ ? req.params[param.name]
533
+ : req.hostname;
534
+ break;
535
+ case 'socket':
536
+ case 'stream':
537
+ rawArg = streamOrSocket;
538
+ break;
539
+ case 'ip':
540
+ rawArg = req.ip;
541
+ break;
542
+ case 'uploadedFile':
543
+ case 'uploadedFiles':
544
+ if (req.body === undefined &&
545
+ ['POST', 'PUT', 'PATCH'].includes(req.method)) {
546
+ await req.formData().catch(() => {
547
+ /* noop */
548
+ });
549
+ }
550
+ if (req.files) {
551
+ const isMulti = param.type === 'uploadedFiles';
552
+ let files = [];
553
+ if (param.name) {
554
+ files = req.files.filter((f) => f.fieldname === param.name);
555
+ }
556
+ else {
557
+ files = req.files;
558
+ }
559
+ rawArg = isMulti ? files : files[0];
560
+ }
561
+ break;
562
+ default:
563
+ rawArg = undefined;
564
+ }
565
+ if (param.pipes && param.pipes.length > 0) {
566
+ for (const pipe of param.pipes) {
567
+ if (typeof pipe === 'function' &&
568
+ pipe.prototype?.transform) {
569
+ let pipeInstance = this.container.resolve(pipe);
570
+ if (!pipeInstance)
571
+ pipeInstance = new pipe();
572
+ rawArg = await pipeInstance.transform(rawArg, {
573
+ type: param.type,
574
+ name: param.name,
575
+ });
576
+ }
577
+ else if (typeof pipe === 'object' &&
578
+ typeof pipe.transform === 'function') {
579
+ rawArg = await pipe.transform(rawArg, {
580
+ type: param.type,
581
+ name: param.name,
582
+ });
583
+ }
584
+ else if (typeof pipe === 'object' &&
585
+ typeof pipe.parse === 'function') {
586
+ rawArg = await pipe.parse(rawArg);
587
+ }
588
+ else if (typeof pipe === 'function') {
589
+ rawArg = await pipe(rawArg);
590
+ }
591
+ }
592
+ }
593
+ args.push(rawArg);
594
+ }
595
+ }
596
+ // 3. Execute Custom Interceptors
597
+ const interceptors = routeLifecycle.interceptors || [];
598
+ for (const interceptor of interceptors) {
599
+ if (typeof interceptor === 'function') {
600
+ if (interceptor.prototype &&
601
+ typeof interceptor.prototype.intercept === 'function') {
602
+ let interceptorInstance = this.container.resolve(interceptor);
603
+ if (!interceptorInstance) {
604
+ interceptorInstance = new interceptor();
605
+ }
606
+ await interceptorInstance.intercept(req, res);
607
+ }
608
+ else {
609
+ await interceptor(req, res);
610
+ }
611
+ }
612
+ }
613
+ // 4. Invoke the method
614
+ const result = await instance[route.handlerName](...args);
615
+ if (res && !res.headersSent) {
616
+ const routeMeta = routeMetadataMap[route.handlerName] || {};
617
+ if (routeMeta.redirect) {
618
+ res.redirect(routeMeta.redirect.url, routeMeta.redirect.statusCode);
619
+ return;
620
+ }
621
+ // Apply custom headers
622
+ if (routeMeta.headers) {
623
+ for (const [name, val] of Object.entries(routeMeta.headers)) {
624
+ res.setHeader(name, val);
625
+ }
626
+ }
627
+ // Apply custom HTTP Code
628
+ if (routeMeta.httpCode) {
629
+ res.status(routeMeta.httpCode);
630
+ }
631
+ if (result !== undefined) {
632
+ res.json(result);
633
+ }
634
+ }
635
+ }
636
+ catch (err) {
637
+ let handled = false;
638
+ const routeLifecycle = lifecycleMetadataMap[route.handlerName] || {};
639
+ const filters = routeLifecycle.filters || [];
640
+ for (const filter of filters) {
641
+ if (typeof filter === 'function' && filter.prototype?.catch) {
642
+ let filterInstance = this.container.resolve(filter);
643
+ if (!filterInstance)
644
+ filterInstance = new filter();
645
+ await filterInstance.catch(err, { req, res });
646
+ handled = true;
647
+ break;
648
+ }
649
+ else if (typeof filter === 'object' &&
650
+ typeof filter.catch === 'function') {
651
+ await filter.catch(err, { req, res });
652
+ handled = true;
653
+ break;
654
+ }
655
+ }
656
+ if (!handled) {
657
+ if (next)
658
+ next(err);
659
+ else
660
+ console.error(`[Exis ${method.toUpperCase()} Error]`, err);
661
+ }
662
+ }
663
+ };
664
+ let finalHandler;
665
+ if (method === 'ws' || method === 'sse') {
666
+ finalHandler = async (streamOrSocket, rawReq) => {
667
+ await executeCore(rawReq, undefined, undefined, streamOrSocket);
668
+ };
669
+ }
670
+ else {
671
+ finalHandler = async (req, res, next) => {
672
+ await executeCore(req, res, next);
673
+ };
674
+ }
675
+ const fullPath = (basePath + prefix + route.path).replace(/\/+/g, '/') || '/';
676
+ const routeSpecificMiddlewares = methodMiddlewares[route.handlerName] || [];
677
+ const allMiddlewares = [
678
+ ...prefixMiddlewares,
679
+ ...classMiddlewares,
680
+ ...routeSpecificMiddlewares,
681
+ ];
682
+ const routeMeta = routeMetadataMap[route.handlerName] || {};
683
+ const finalHost = routeMeta.hosts || host;
684
+ if (route.schema || finalHost) {
685
+ const schema = route.schema || {};
686
+ if (finalHost)
687
+ schema.host = finalHost;
688
+ this.router[method](fullPath, ...allMiddlewares, schema, finalHandler);
689
+ }
690
+ else {
691
+ ;
692
+ this.router[method](fullPath, ...allMiddlewares, finalHandler);
693
+ }
694
+ }
695
+ }
696
+ return this;
697
+ }
698
+ // ─── Dependency Injection ───────────────────────────────────────────────────
699
+ provide(token, provider) {
700
+ this.container.provide(token, provider);
701
+ return this;
702
+ }
703
+ resolve(token, requestCache) {
704
+ return this.container.resolve(token, requestCache);
705
+ }
706
+ // ─── Built-in Feature Toggles ─────────────────────────────────────────────────
707
+ applyBuiltins() {
708
+ if (this._configured)
709
+ return;
710
+ this._configured = true;
711
+ this.ensureLogger();
712
+ const { cors: corsOpt, helmet: helmetOpt, logger: logOpt, compression: compressionOpt, } = this.options;
713
+ // Request ID always on
714
+ this.globalMiddleware.unshift((0, middleware_1.requestId)());
715
+ if (logOpt !== false) {
716
+ this.globalMiddleware.push((0, middleware_1.requestLogger)(this.log));
717
+ }
718
+ if (compressionOpt) {
719
+ this.globalMiddleware.push((0, middleware_1.compression)());
720
+ }
721
+ if (helmetOpt !== false) {
722
+ this.globalMiddleware.push((0, middleware_1.helmet)());
723
+ }
724
+ if (corsOpt !== false) {
725
+ this.globalMiddleware.push((0, middleware_1.cors)(corsOpt === true ? {} : corsOpt));
726
+ }
727
+ if (this.options.plugins) {
728
+ for (const plugin of this.options.plugins) {
729
+ if (!this.hasPlugin(plugin.name)) {
730
+ // We can't await inside sync constructor easily, but plugins are usually sync in register.
731
+ // For async plugins in config, they should be loaded before app starts.
732
+ // App.register is async, so we'll push the promise and warn if it's not awaited
733
+ this.register(plugin).catch((err) => console.error(`[Exis] Failed to register plugin ${plugin.name} from config:`, err));
734
+ }
735
+ }
736
+ }
737
+ // rate limiting removed from core
738
+ // default error handler
739
+ if (this.errorHandlers.length === 0) {
740
+ this.errorHandlers.push((0, errors_1.createErrorHandler)(this.options.env === 'development'));
741
+ }
742
+ }
743
+ getCompiledPipeline() {
744
+ if (this._compiledPipeline)
745
+ return this._compiledPipeline;
746
+ this.applyBuiltins();
747
+ this._compiledPipeline = [
748
+ ...this.globalMiddleware,
749
+ (req, res, next) => this.router.handle(req, res, next),
750
+ middleware_1.notFound,
751
+ ];
752
+ return this._compiledPipeline;
753
+ }
754
+ // ─── Web Standard Fetch (Edge Runtime) & Inject ──────────────────────────────
755
+ fetch(request, env, ctx) {
756
+ // Dynamic require to avoid circular dependencies and top-level execution
757
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
758
+ const { handleFetch } = require('../adapters/fetch');
759
+ return handleFetch(this, request, env, ctx);
760
+ }
761
+ async inject(options) {
762
+ const method = (options.method || 'GET').toUpperCase();
763
+ const url = options.url.startsWith('http')
764
+ ? options.url
765
+ : `http://localhost${options.url}`;
766
+ const headers = new Headers();
767
+ if (options.headers) {
768
+ for (const [k, v] of Object.entries(options.headers)) {
769
+ headers.set(k, v);
770
+ }
771
+ }
772
+ let bodyStr;
773
+ if (options.body) {
774
+ bodyStr =
775
+ typeof options.body === 'string'
776
+ ? options.body
777
+ : JSON.stringify(options.body);
778
+ if (!headers.has('content-type') && typeof options.body !== 'string') {
779
+ headers.set('content-type', 'application/json');
780
+ }
781
+ }
782
+ const req = new Request(url, {
783
+ method,
784
+ headers,
785
+ body: ['GET', 'HEAD', 'OPTIONS'].includes(method) ? undefined : bodyStr,
786
+ });
787
+ const fetchRes = await this.fetch(req);
788
+ const resText = await fetchRes.text();
789
+ let parsedBody = resText;
790
+ try {
791
+ parsedBody = JSON.parse(resText);
792
+ }
793
+ catch {
794
+ // ignore
795
+ }
796
+ const resHeaders = {};
797
+ fetchRes.headers.forEach((v, k) => {
798
+ resHeaders[k] = v;
799
+ });
800
+ return {
801
+ status: fetchRes.status,
802
+ headers: resHeaders,
803
+ body: parsedBody,
804
+ text: resText,
805
+ };
806
+ }
807
+ // ─── Private Internal Pipeline Handler ─────────────────────────────────────────────────────
808
+ _executeWithContext(req, res, execution) {
809
+ const store = {
810
+ state: {},
811
+ afterCallbacks: [],
812
+ req: req,
813
+ res: res,
814
+ app: this,
815
+ diCache: new Map(),
816
+ };
817
+ res.raw.on('finish', () => {
818
+ for (const cb of store.afterCallbacks) {
819
+ try {
820
+ const promise = cb();
821
+ if (promise instanceof Promise) {
822
+ promise.catch((err) => {
823
+ this.log.error({ err }, 'Error in after() background task');
824
+ });
825
+ }
826
+ }
827
+ catch (err) {
828
+ this.log.error({ err }, 'Error in after() background task');
829
+ }
830
+ }
831
+ });
832
+ context_1.executionContext.run(store, execution);
833
+ }
834
+ handle(rawReq, rawRes) {
835
+ const res = new response_1.ExisResponse(rawRes);
836
+ const req = new request_1.ExisRequest(rawReq, res, this.options.trustProxy, this.options.bodyLimit);
837
+ res.req = req;
838
+ res.etagEnabled = this.options.etag === true;
839
+ req.log = this.log;
840
+ req._dataloaderFns = this._dataloaders;
841
+ this._executeWithContext(req, res, () => {
842
+ if (this.hooks.request.length === 0 && this.hooks.response.length === 0) {
843
+ (0, router_1.runHandlers)(this.getCompiledPipeline(), req, res, (err) => {
844
+ if (err) {
845
+ this._runErrorHandlers(err, req, res).catch((e) => {
846
+ this.log.error({ err: e }, 'Error in error handler');
847
+ });
848
+ }
849
+ });
850
+ return;
851
+ }
852
+ this._handleWithHooks(req, res, rawRes);
853
+ });
854
+ }
855
+ /**
856
+ * Handle a request from the uWebSockets.js backend.
857
+ * Uses the same middleware pipeline as Node HTTP but with uWS shims.
858
+ */
859
+ handleUws(shimReq, shimRes) {
860
+ // Wrap uWS shims in ExisRequest/ExisResponse using type coercion
861
+ // The shims implement the same interface that ExisRequest/ExisResponse expect
862
+ const res = new response_1.ExisResponse(shimRes);
863
+ const req = new request_1.ExisRequest(shimReq, res, this.options.trustProxy, this.options.bodyLimit);
864
+ res.req = req;
865
+ res.etagEnabled = this.options.etag === true;
866
+ req.log = this.log;
867
+ req._dataloaderFns = this._dataloaders;
868
+ this._executeWithContext(req, res, () => {
869
+ if (this.hooks.request.length === 0 && this.hooks.response.length === 0) {
870
+ (0, router_1.runHandlers)(this.getCompiledPipeline(), req, res, (err) => {
871
+ if (err) {
872
+ this._runErrorHandlers(err, req, res).catch((e) => {
873
+ this.log.error({ err: e }, 'Error in error handler');
874
+ });
875
+ }
876
+ });
877
+ return;
878
+ }
879
+ this._handleWithHooks(req, res, shimRes);
880
+ });
881
+ }
882
+ async _handleWithHooks(req, res, rawRes) {
883
+ // ─── onRequest Hook ───
884
+ if (this.hooks.request.length > 0) {
885
+ // eslint-disable-next-line @typescript-eslint/prefer-for-of
886
+ for (let i = 0; i < this.hooks.request.length; i++) {
887
+ await this.hooks.request[i](req, res);
888
+ }
889
+ }
890
+ if (res.headersSent)
891
+ return;
892
+ // ─── onResponse Hook ───
893
+ if (this.hooks.response.length > 0) {
894
+ rawRes.on('finish', () => {
895
+ // eslint-disable-next-line @typescript-eslint/prefer-for-of
896
+ for (let i = 0; i < this.hooks.response.length; i++) {
897
+ Promise.resolve(this.hooks.response[i](req, res)).catch((err) => {
898
+ this.log.error({ err }, 'Error in onResponse hook');
899
+ });
900
+ }
901
+ });
902
+ }
903
+ (0, router_1.runHandlers)(this.getCompiledPipeline(), req, res, (err) => {
904
+ if (err) {
905
+ this._runErrorHandlers(err, req, res).catch((e) => {
906
+ this.log.error({ err: e }, 'Error in error handler');
907
+ });
908
+ }
909
+ });
910
+ }
911
+ async _runErrorHandlers(err, req, res) {
912
+ // ─── onError Hook ───
913
+ if (this.hooks.error.length > 0) {
914
+ // eslint-disable-next-line @typescript-eslint/prefer-for-of
915
+ for (let i = 0; i < this.hooks.error.length; i++) {
916
+ await this.hooks.error[i](err, req, res);
917
+ }
918
+ }
919
+ if (res.headersSent)
920
+ return;
921
+ for (const handler of this.errorHandlers) {
922
+ let handled = false;
923
+ await handler(err, req, res, () => {
924
+ handled = true;
925
+ });
926
+ if (!handled || res.headersSent)
927
+ return;
928
+ }
929
+ // fallback if no error handler ran
930
+ const defaultHandler = (0, errors_1.createErrorHandler)(this.options.env === 'development');
931
+ await defaultHandler(err, req, res, () => {
932
+ /* noop */
933
+ });
934
+ }
935
+ // ─── WebSocket Upgrade Handler ───────────────────────────────────────────────
936
+ async _handleUpgrade(rawReq, socket, head) {
937
+ this.applyBuiltins();
938
+ // Match route
939
+ const reqPath = (rawReq.url || '').split('?')[0];
940
+ const matched = this.router.match('WS', reqPath);
941
+ if (!matched) {
942
+ socket.write('HTTP/1.1 404 Not Found\\r\\n\\r\\n');
943
+ socket.destroy();
944
+ return;
945
+ }
946
+ // Create a dummy ServerResponse so standard HTTP middleware can run and reject if needed
947
+ const rawRes = new node_http_1.ServerResponse(rawReq);
948
+ rawRes.assignSocket(socket);
949
+ const res = new response_1.ExisResponse(rawRes);
950
+ const originalMethod = rawReq.method;
951
+ const req = new request_1.ExisRequest(rawReq, res, this.options.trustProxy, this.options.bodyLimit);
952
+ res.req = req;
953
+ req.method = 'WS';
954
+ req.params = matched.params;
955
+ req.log = this.log;
956
+ // Build the middleware pipeline specifically for this WebSocket route
957
+ const pipeline = [
958
+ ...this.globalMiddleware,
959
+ async (req, res, next) => {
960
+ // Exclude the last handler which is the actual WsHandler wrapper
961
+ const routeHandlers = matched.route.handlers.slice(0, -1);
962
+ (0, router_1.runHandlers)(routeHandlers, req, res, (err) => {
963
+ if (err)
964
+ return next(err);
965
+ next();
966
+ });
967
+ },
968
+ ];
969
+ await (0, router_1.runHandlers)(pipeline, req, res, async (err) => {
970
+ if (err) {
971
+ this.log.error({ err }, 'Error during WebSocket upgrade middleware');
972
+ socket.write('HTTP/1.1 500 Internal Server Error\\r\\n\\r\\n');
973
+ socket.destroy();
974
+ return;
975
+ }
976
+ if (res.headersSent) {
977
+ // A middleware intercepted and sent an HTTP response (e.g. 401 Unauthorized)
978
+ socket.destroy();
979
+ return;
980
+ }
981
+ // Restore original HTTP method (usually 'GET') so the `ws` package doesn't reject with 405
982
+ rawReq.method = originalMethod;
983
+ // Middleware passed, perform upgrade
984
+ this.rawWsServer.handleUpgrade(rawReq, socket, head, (ws) => {
985
+ const exisWs = new socket_1.ExisWebSocket(ws, req, this.wsServer);
986
+ this.wsServer.track(exisWs);
987
+ req.ws = exisWs;
988
+ const finalHandler = matched.route.handlers[matched.route.handlers.length - 1];
989
+ Promise.resolve(finalHandler(req, res, () => {
990
+ /* noop */
991
+ })).catch((err) => {
992
+ this.log.error({ err }, 'Error in WebSocket handler');
993
+ ws.close(1011, 'Internal Server Error');
994
+ });
995
+ });
996
+ });
997
+ }
998
+ /**
999
+ * WebSocket Upgrade Handler for uWebSockets.js
1000
+ * Runs the middleware pipeline and if successful, upgrades the connection.
1001
+ */
1002
+ async handleUwsUpgrade(shimReq, uwsRes, context) {
1003
+ this.applyBuiltins();
1004
+ const reqPath = (shimReq.url || '').split('?')[0];
1005
+ const matched = this.router.match('WS', reqPath);
1006
+ if (!matched) {
1007
+ // 404
1008
+ uwsRes.cork(() => {
1009
+ uwsRes.writeStatus('404 Not Found');
1010
+ uwsRes.end();
1011
+ });
1012
+ return;
1013
+ }
1014
+ let isAborted = false;
1015
+ uwsRes.onAborted(() => {
1016
+ isAborted = true;
1017
+ });
1018
+ const shimRes = new uws_adapter_1.UwsServerResponse(uwsRes);
1019
+ const res = new response_1.ExisResponse(shimRes);
1020
+ const req = new request_1.ExisRequest(shimReq, res, this.options.trustProxy, this.options.bodyLimit);
1021
+ res.req = req;
1022
+ req.method = 'WS';
1023
+ req.params = matched.params;
1024
+ req.log = this.log;
1025
+ const pipeline = [
1026
+ ...this.globalMiddleware,
1027
+ async (req, res, next) => {
1028
+ const routeHandlers = matched.route.handlers.slice(0, -1);
1029
+ (0, router_1.runHandlers)(routeHandlers, req, res, (err) => {
1030
+ if (err)
1031
+ return next(err);
1032
+ next();
1033
+ });
1034
+ },
1035
+ ];
1036
+ await (0, router_1.runHandlers)(pipeline, req, res, async (err) => {
1037
+ if (isAborted)
1038
+ return;
1039
+ if (err) {
1040
+ this.log.error({ err }, 'Error during uWS WebSocket upgrade middleware');
1041
+ uwsRes.cork(() => {
1042
+ uwsRes.writeStatus('500 Internal Server Error');
1043
+ uwsRes.end();
1044
+ });
1045
+ return;
1046
+ }
1047
+ if (res.headersSent) {
1048
+ return; // Middleware sent an HTTP response
1049
+ }
1050
+ // We need to fetch headers directly from the shimReq.headers
1051
+ const secWebSocketKey = shimReq.headers['sec-websocket-key'] || '';
1052
+ const secWebSocketProtocol = shimReq.headers['sec-websocket-protocol'] || '';
1053
+ const secWebSocketExtensions = shimReq.headers['sec-websocket-extensions'] || '';
1054
+ // Prepare user data for the upgraded websocket
1055
+ const userData = {
1056
+ req,
1057
+ res,
1058
+ exisWs: new socket_1.ExisWebSocket(null, req, this.wsServer), // the raw will be set to shim in open()
1059
+ finalHandler: matched.route.handlers[matched.route.handlers.length - 1],
1060
+ };
1061
+ this.wsServer.track(userData.exisWs);
1062
+ req.ws = userData.exisWs;
1063
+ uwsRes.cork(() => {
1064
+ uwsRes.upgrade(userData, secWebSocketKey, secWebSocketProtocol, secWebSocketExtensions, context);
1065
+ });
1066
+ // The open handler in createUwsApp will call finalHandler?
1067
+ // Actually we should just execute the finalHandler here since upgrade succeeds immediately if we don't block
1068
+ Promise.resolve(userData.finalHandler(req, res, () => {
1069
+ /* noop */
1070
+ })).catch((e) => {
1071
+ this.log.error({ err: e }, 'Error in uWS WebSocket handler');
1072
+ });
1073
+ });
1074
+ }
1075
+ // ─── Listen ───────────────────────────────────────────────────────────────────
1076
+ listen(portOrOptions, callback) {
1077
+ if (process.env.EXIS_CLI_MODE === '1') {
1078
+ return undefined;
1079
+ }
1080
+ this.applyBuiltins();
1081
+ let port;
1082
+ let host;
1083
+ let onListen;
1084
+ if (typeof portOrOptions === 'number') {
1085
+ port = portOrOptions;
1086
+ host = this.options.host;
1087
+ onListen = callback ? () => callback() : undefined;
1088
+ }
1089
+ else if (typeof portOrOptions === 'object') {
1090
+ if (portOrOptions.ssl) {
1091
+ this.options.ssl = portOrOptions.ssl;
1092
+ // Re-initialize server if ssl is passed during listen
1093
+ this.server = this._initServer();
1094
+ }
1095
+ if (portOrOptions.redirectHttp !== undefined) {
1096
+ this.options.redirectHttp = portOrOptions.redirectHttp;
1097
+ }
1098
+ port = portOrOptions.port ?? this.options.port;
1099
+ host = portOrOptions.host ?? this.options.host;
1100
+ onListen = portOrOptions.onListen;
1101
+ }
1102
+ else {
1103
+ port = this.options.port;
1104
+ host = this.options.host;
1105
+ }
1106
+ // Auto HTTP -> HTTPS redirect server (only for Node HTTP backend)
1107
+ // ─── uWebSockets.js listen path ───
1108
+ if (this._useUws) {
1109
+ this._uwsApp = (0, uws_adapter_1.createUwsApp)(this.handleUws.bind(this), this.handleUwsUpgrade.bind(this), this.options.ssl);
1110
+ this._uwsApp.listen(port, host, async (token) => {
1111
+ if (!token) {
1112
+ this.log.error(`Failed to listen on ${host}:${port} (uWS)`);
1113
+ return;
1114
+ }
1115
+ this._uwsListenToken = token;
1116
+ const address = { port, host };
1117
+ if (this.hotReloader) {
1118
+ this.hotReloader.stop();
1119
+ }
1120
+ if (onListen) {
1121
+ onListen(address);
1122
+ }
1123
+ else {
1124
+ const url = `http://${host === '0.0.0.0' ? 'localhost' : host}:${port}`;
1125
+ this.log.info({ url, env: this.options.env, backend: 'uws' }, `Server running at ${url} (uWebSockets.js)`);
1126
+ }
1127
+ if (this.options.env === 'production') {
1128
+ const handleShutdown = async () => {
1129
+ this.log.info('Received shutdown signal (SIGTERM/SIGINT)');
1130
+ try {
1131
+ await this.close();
1132
+ process.exit(0);
1133
+ }
1134
+ catch (err) {
1135
+ this.log.error({ err }, 'Error during graceful shutdown');
1136
+ process.exit(1);
1137
+ }
1138
+ };
1139
+ process.once('SIGTERM', handleShutdown);
1140
+ process.once('SIGINT', handleShutdown);
1141
+ }
1142
+ // ─── onReady Hook ───
1143
+ for (const hook of this.hooks.ready) {
1144
+ await hook();
1145
+ }
1146
+ if (callback)
1147
+ callback();
1148
+ });
1149
+ // Return a minimal server-like object for compatibility
1150
+ return this.server;
1151
+ }
1152
+ // ─── Node HTTP listen path ───
1153
+ // Auto HTTP -> HTTPS redirect server
1154
+ if (this.options.ssl &&
1155
+ this.options.redirectHttp !== undefined &&
1156
+ this.options.redirectHttp !== false) {
1157
+ const redirectPort = typeof this.options.redirectHttp === 'number'
1158
+ ? this.options.redirectHttp
1159
+ : 80;
1160
+ this.redirectServer = (0, node_http_1.createServer)((req, res) => {
1161
+ const targetHost = req.headers.host?.split(':')[0] || host;
1162
+ const actualPort = port === 0
1163
+ ? this.server.address()?.port ||
1164
+ port
1165
+ : port;
1166
+ const targetPort = actualPort === 443 ? '' : `:${actualPort}`;
1167
+ res.writeHead(301, {
1168
+ Location: `https://${targetHost}${targetPort}${req.url || '/'}`,
1169
+ });
1170
+ res.end();
1171
+ });
1172
+ this.redirectServer.listen(redirectPort, host, () => {
1173
+ this.log.info(`Redirect server listening on port ${redirectPort} -> HTTPS port ${port}`);
1174
+ });
1175
+ }
1176
+ this.server.on('error', (err) => {
1177
+ if (err.code === 'EADDRINUSE') {
1178
+ console.error(`\n\x1b[31m✗ Port ${port} is already in use.\x1b[0m`);
1179
+ console.error(` Try killing the process or use a different port in exis.config.ts\n`);
1180
+ process.exit(1);
1181
+ }
1182
+ else {
1183
+ this.log.error({ err }, 'Failed to start server');
1184
+ process.exit(1);
1185
+ }
1186
+ });
1187
+ this.server.listen(port, host, async () => {
1188
+ const address = { port, host };
1189
+ if (this.options.env === 'production') {
1190
+ const handleShutdown = async () => {
1191
+ this.log.info('Received shutdown signal (SIGTERM/SIGINT)');
1192
+ try {
1193
+ await this.close();
1194
+ process.exit(0);
1195
+ }
1196
+ catch (err) {
1197
+ this.log.error({ err }, 'Error during graceful shutdown');
1198
+ process.exit(1);
1199
+ }
1200
+ };
1201
+ process.once('SIGTERM', handleShutdown);
1202
+ process.once('SIGINT', handleShutdown);
1203
+ }
1204
+ // ─── onReady Hook ───
1205
+ for (const hook of this.hooks.ready) {
1206
+ await hook();
1207
+ }
1208
+ // Start queue worker if initialized
1209
+ if (this._queueWorker) {
1210
+ await this._queueWorker.start();
1211
+ }
1212
+ if (onListen) {
1213
+ onListen(address);
1214
+ }
1215
+ else {
1216
+ if (!process.env.__EXIS_DEV_SERVER && !process.env.__EXIS_CLI) {
1217
+ const url = `http://${host === '0.0.0.0' ? 'localhost' : host}:${port}`;
1218
+ this.log.info({ url, env: this.options.env }, `Server running at ${url}`);
1219
+ }
1220
+ }
1221
+ if (callback)
1222
+ callback();
1223
+ });
1224
+ return this.server;
1225
+ }
1226
+ // ─── Banner ───────────────────────────────────────────────────────────────────
1227
+ async _printStartupBanner() {
1228
+ const port = this.options.port ?? 3000;
1229
+ const host = this.options.host ?? 'localhost';
1230
+ const c = {
1231
+ cyan: '\x1b[36m',
1232
+ green: '\x1b[32m',
1233
+ gray: '\x1b[90m',
1234
+ dim: '\x1b[2m',
1235
+ bold: '\x1b[1m',
1236
+ reset: '\x1b[0m',
1237
+ magenta: '\x1b[35m',
1238
+ bgMagenta: '\x1b[45m',
1239
+ white: '\x1b[37m',
1240
+ primary: '\x1b[38;2;160;70;255m',
1241
+ blue: '\x1b[38;2;41;169;206m',
1242
+ };
1243
+ let fwVersion = '0.0.0';
1244
+ try {
1245
+ const fs = await Promise.resolve().then(() => __importStar(require('node:fs')));
1246
+ const path = await Promise.resolve().then(() => __importStar(require('node:path')));
1247
+ const pkgPath = path.join(__dirname, '../../package.json');
1248
+ fwVersion = JSON.parse(fs.readFileSync(pkgPath, 'utf-8')).version;
1249
+ }
1250
+ catch {
1251
+ /* ignore */
1252
+ }
1253
+ const displayHost = host === '0.0.0.0' || host === '127.0.0.1' ? 'localhost' : host;
1254
+ let localIp = '<YOUR_IP>';
1255
+ try {
1256
+ const os = await Promise.resolve().then(() => __importStar(require('node:os')));
1257
+ const nets = os.networkInterfaces();
1258
+ for (const name of Object.keys(nets)) {
1259
+ for (const net of nets[name] || []) {
1260
+ if (net.family === 'IPv4' && !net.internal) {
1261
+ localIp = net.address;
1262
+ break;
1263
+ }
1264
+ }
1265
+ if (localIp !== '<YOUR_IP>')
1266
+ break;
1267
+ }
1268
+ }
1269
+ catch {
1270
+ /* ignore */
1271
+ }
1272
+ const networkHost = host === '0.0.0.0'
1273
+ ? `http://${localIp}:${port}`
1274
+ : 'use --host 0.0.0.0 to expose';
1275
+ const readyMs = 125;
1276
+ const displayEnv = this.options.env || process.env.NODE_ENV || 'development';
1277
+ console.log(`\n ${c.primary}${c.bold}EXIS v${fwVersion}${c.reset} ready in ${c.bold}${readyMs} ms${c.reset}\n`);
1278
+ console.log(` ${c.white}➜${c.reset} ${c.bold}Local:${c.reset} ${c.blue}http://${displayHost}:${port}/${c.reset}`);
1279
+ console.log(` ${c.white}➜${c.reset} ${c.dim}Network:${c.reset} ${c.blue}${networkHost}/${c.reset}`);
1280
+ console.log(` ${c.white}➜${c.reset} ${c.dim}Environ:${c.reset} ${c.green}${displayEnv}${c.reset}`);
1281
+ console.log(` ${c.white}➜${c.reset} press ${c.bold}h + enter${c.reset} ${c.dim}to show help${c.reset}\n`);
1282
+ }
1283
+ // ─── Graceful Shutdown ────────────────────────────────────────────────────────
1284
+ onShutdown(hook) {
1285
+ this.shutdownHooks.push(hook);
1286
+ return this;
1287
+ }
1288
+ close(timeout = 5000) {
1289
+ return new Promise((resolve, reject) => {
1290
+ this.log.info('Initiating graceful shutdown');
1291
+ let finishCalled = false;
1292
+ const finish = async () => {
1293
+ if (finishCalled)
1294
+ return;
1295
+ finishCalled = true;
1296
+ try {
1297
+ for (const hook of this.shutdownHooks) {
1298
+ await hook();
1299
+ }
1300
+ // Also execute onClose hooks
1301
+ for (const hook of this.hooks.close) {
1302
+ await hook();
1303
+ }
1304
+ if (this.onCloseHook) {
1305
+ await this.onCloseHook(this);
1306
+ }
1307
+ if (this._cronScheduler) {
1308
+ this._cronScheduler.stop();
1309
+ }
1310
+ if (this._queueWorker) {
1311
+ await this._queueWorker.stop();
1312
+ }
1313
+ if (this._queueClient) {
1314
+ await this._queueClient.close();
1315
+ }
1316
+ if (this.hotReloader) {
1317
+ await this.hotReloader.stop();
1318
+ }
1319
+ this.log.info('Graceful shutdown completed');
1320
+ resolve();
1321
+ }
1322
+ catch (err) {
1323
+ this.log.error({ err }, 'Error executing shutdown hooks');
1324
+ reject(err);
1325
+ }
1326
+ };
1327
+ if (!this._useUws && (!this.server || !this.server.listening)) {
1328
+ finish();
1329
+ return;
1330
+ }
1331
+ if (this._useUws && !this._uwsListenToken) {
1332
+ finish();
1333
+ return;
1334
+ }
1335
+ // Close idle keep-alive connections immediately
1336
+ if (!this._useUws &&
1337
+ this.server &&
1338
+ 'closeIdleConnections' in this.server) {
1339
+ ;
1340
+ this.server.closeIdleConnections?.();
1341
+ }
1342
+ // Set timeout to force close active connections
1343
+ const timer = setTimeout(() => {
1344
+ this.log.warn(`Shutdown timeout of ${timeout}ms exceeded, forcefully terminating active connections`);
1345
+ if (!this._useUws &&
1346
+ this.server &&
1347
+ 'closeAllConnections' in this.server) {
1348
+ ;
1349
+ this.server.closeAllConnections?.();
1350
+ }
1351
+ finish(); // Ensure finish is called on timeout
1352
+ }, timeout);
1353
+ const cleanupAndFinish = async () => {
1354
+ clearTimeout(timer);
1355
+ await finish();
1356
+ };
1357
+ this.wsServer.close();
1358
+ for (const client of this.rawWsServer.clients) {
1359
+ client.terminate();
1360
+ }
1361
+ this.rawWsServer.close();
1362
+ if (this.redirectServer) {
1363
+ this.redirectServer.close();
1364
+ }
1365
+ if (this._useUws && this._uwsApp) {
1366
+ this._uwsApp.close(this._uwsListenToken);
1367
+ this._uwsListenToken = null;
1368
+ cleanupAndFinish();
1369
+ }
1370
+ else {
1371
+ this.server.close((err) => {
1372
+ if (err)
1373
+ reject(err);
1374
+ else
1375
+ cleanupAndFinish();
1376
+ });
1377
+ }
1378
+ });
1379
+ }
1380
+ // ─── Expose internals ─────────────────────────────────────────────────────────
1381
+ getRouter() {
1382
+ return this.router;
1383
+ }
1384
+ async loadAllRoutes() {
1385
+ for (const [filePath, entry] of this.lazyRoutes.entries()) {
1386
+ if (!entry.loaded) {
1387
+ entry.loaded = true;
1388
+ this.router.removeRoutesBySource('lazy:' + filePath);
1389
+ const routePath = this.routeMap.get(filePath);
1390
+ if (routePath) {
1391
+ try {
1392
+ await this.mountRouteFile(filePath, routePath);
1393
+ }
1394
+ catch (e) {
1395
+ this.log.error({ err: e, file: filePath }, 'Failed to eager-load route');
1396
+ }
1397
+ }
1398
+ }
1399
+ }
1400
+ }
1401
+ getServer() {
1402
+ return this.server;
1403
+ }
1404
+ getConfig() {
1405
+ return this.options;
1406
+ }
1407
+ async create(cwd) {
1408
+ // eslint-disable-next-line @typescript-eslint/no-this-alias
1409
+ exports.activeAppInstance = this;
1410
+ const root = cwd ?? process.cwd();
1411
+ if (!this._configLoaded) {
1412
+ const { loadConfig } = await Promise.resolve().then(() => __importStar(require('../utils/config')));
1413
+ const fileConfig = await loadConfig(root);
1414
+ // File config takes precedence over defaults, but explicit options take highest precedence
1415
+ this.options = (0, config_1.mergeConfig)(config_1.defaultConfig, fileConfig);
1416
+ this.options = (0, config_1.mergeConfig)(this.options, this.explicitOptions);
1417
+ // Recreate logger now that the user's config has been loaded
1418
+ this._loggerCreated = false;
1419
+ this.ensureLogger();
1420
+ this._configLoaded = true;
1421
+ if ((process.env.__EXIS_DEV_SERVER || process.env.__EXIS_CLI) &&
1422
+ !process.env.__EXIS_REPL &&
1423
+ !process.env.__EXIS_TEST) {
1424
+ await this._printStartupBanner();
1425
+ }
1426
+ }
1427
+ // Initialize Queue after config is loaded
1428
+ if (this.options.queue && !this._queueClient) {
1429
+ this._initQueue(this.options.queue);
1430
+ }
1431
+ // Flush any pending jobs that were registered before config was loaded
1432
+ if (this._pendingQueueJobs.length > 0) {
1433
+ if (!this._queueWorker) {
1434
+ if (this.options.queue?.enableWorkers !== false) {
1435
+ throw new Error('Queue worker is not initialized. Please configure ExisConfig.queue first.');
1436
+ }
1437
+ }
1438
+ else {
1439
+ for (const job of this._pendingQueueJobs) {
1440
+ this._queueWorker.registerJob(job);
1441
+ if (this._cronScheduler && job.cron) {
1442
+ this._cronScheduler.registerJob(job);
1443
+ }
1444
+ }
1445
+ }
1446
+ this._pendingQueueJobs = [];
1447
+ }
1448
+ if (this._routesMounted)
1449
+ return this;
1450
+ // Automatically scan and mount file-based routes and jobs
1451
+ await this.autoMountRoutes(root);
1452
+ await this.autoMountJobs(root);
1453
+ this._routesMounted = true;
1454
+ // Start Hot Route Reloading in dev mode
1455
+ if (this.options.env === 'development' && this._allApiDirs) {
1456
+ this.hotReloader = new hot_reload_1.HotReloader({
1457
+ apiDirs: this._allApiDirs,
1458
+ router: this.router,
1459
+ routeMap: this.routeMap,
1460
+ mountRoute: (filePath, routePath) => this.mountRouteFile(filePath, routePath),
1461
+ });
1462
+ await this.hotReloader.start();
1463
+ }
1464
+ return this;
1465
+ }
1466
+ // ─── Auto-Mount File-Based Routes ─────────────────────────────────────────────
1467
+ async autoMountRoutes(root) {
1468
+ const fs = await Promise.resolve().then(() => __importStar(require('node:fs/promises')));
1469
+ const path = await Promise.resolve().then(() => __importStar(require('node:path')));
1470
+ const isProd = process.env.NODE_ENV === 'production';
1471
+ const isDev = this.options.env === 'development';
1472
+ // Try to load the pre-built manifest first (O(1) boot)
1473
+ const manifestPath = path.join(root, '.exis', 'routes-manifest.js');
1474
+ try {
1475
+ const stat = await fs.stat(manifestPath);
1476
+ if (stat.isFile()) {
1477
+ let url = (0, node_url_1.pathToFileURL)(manifestPath).href;
1478
+ if (!isProd)
1479
+ url += '?t=' + Date.now(); // cache bust for dev
1480
+ const dynamicImport = new Function('specifier', 'return import(specifier)');
1481
+ const mod = await dynamicImport(url);
1482
+ const manifest = mod.manifest;
1483
+ if (Array.isArray(manifest)) {
1484
+ for (const entry of manifest) {
1485
+ const { routePath, module: routeMod, filePath } = entry;
1486
+ const normalizedPath = path.resolve(root, filePath);
1487
+ this.routeMap.set(normalizedPath, routePath);
1488
+ const CONTROLLER_PREFIX = Symbol.for('exisjs:controller_prefix');
1489
+ const isController = (obj) => obj &&
1490
+ obj.prototype &&
1491
+ obj.prototype[CONTROLLER_PREFIX] !== undefined;
1492
+ const routerInstance = routeMod.router || routeMod.default || routeMod;
1493
+ if (isController(routerInstance)) {
1494
+ this.registerControllers([routerInstance], routePath);
1495
+ }
1496
+ else {
1497
+ this.mountRouteWithSource(routePath, routerInstance, normalizedPath);
1498
+ }
1499
+ }
1500
+ // Skip the filesystem scan completely!
1501
+ return;
1502
+ }
1503
+ }
1504
+ }
1505
+ catch {
1506
+ // Fall back to scanning the filesystem
1507
+ }
1508
+ const searchDirs = isProd
1509
+ ? [
1510
+ path.join(root, '.exis', 'server', 'src', 'http'),
1511
+ path.join(root, '.exis', 'server', 'http'),
1512
+ path.join(root, 'dist', 'src', 'http'),
1513
+ path.join(root, 'dist', 'http'),
1514
+ path.join(root, 'src', 'http'),
1515
+ path.join(root, 'http'),
1516
+ ]
1517
+ : [path.join(root, 'src', 'http'), path.join(root, 'http')];
1518
+ const appDirs = [];
1519
+ for (const dir of searchDirs) {
1520
+ try {
1521
+ const stat = await fs.stat(dir);
1522
+ if (stat.isDirectory()) {
1523
+ const baseName = path.basename(path.dirname(dir)) === 'src' ? 'src/http' : 'http';
1524
+ const alreadyHasCompiledOrRawForThisBase = appDirs.some((existing) => {
1525
+ const existingBase = path.basename(path.dirname(existing)) === 'src'
1526
+ ? 'src/http'
1527
+ : 'http';
1528
+ return existingBase === baseName;
1529
+ });
1530
+ if (!alreadyHasCompiledOrRawForThisBase) {
1531
+ appDirs.push(dir);
1532
+ }
1533
+ }
1534
+ }
1535
+ catch {
1536
+ /* ignore */
1537
+ }
1538
+ }
1539
+ if (appDirs.length === 0)
1540
+ return;
1541
+ this.apiDir = appDirs[0] // keep property name for backwards compatibility
1542
+ ;
1543
+ this._allApiDirs = appDirs;
1544
+ const cc = {
1545
+ green: '\x1b[32m',
1546
+ cyan: '\x1b[36m',
1547
+ gray: '\x1b[90m',
1548
+ reset: '\x1b[0m',
1549
+ };
1550
+ for (const appDir of appDirs) {
1551
+ const routes = await this.scanDirectory(appDir);
1552
+ for (const { filePath, routePath } of routes) {
1553
+ if (filePath.endsWith('route.ts') || filePath.endsWith('route.js')) {
1554
+ const normalized = path.resolve(filePath);
1555
+ this.routeMap.set(normalized, routePath);
1556
+ if (isDev) {
1557
+ const lazyKey = normalized;
1558
+ this.lazyRoutes.set(lazyKey, {
1559
+ filePath: normalized,
1560
+ loaded: false,
1561
+ });
1562
+ const lazyHandler = async (req, res, next) => {
1563
+ const entry = this.lazyRoutes.get(lazyKey);
1564
+ if (entry && !entry.loaded) {
1565
+ entry.loaded = true;
1566
+ this.router.removeRoutesBySource('lazy:' + lazyKey);
1567
+ try {
1568
+ await this.mountRouteFile(normalized, routePath);
1569
+ const relative = path
1570
+ .relative(process.cwd(), normalized)
1571
+ .replace(/\\/g, '/');
1572
+ console.log(` ${cc.green}✓${cc.reset} Lazy loaded ${cc.cyan}${relative}${cc.reset}`);
1573
+ }
1574
+ catch (err) {
1575
+ (0, dev_error_overlay_1.formatDevError)(err, normalized);
1576
+ return;
1577
+ }
1578
+ await this.router.handle(req, res, next);
1579
+ }
1580
+ else {
1581
+ next?.();
1582
+ }
1583
+ };
1584
+ const lazyRouter = new router_1.Router();
1585
+ lazyRouter.all(routePath, lazyHandler);
1586
+ lazyRouter.all(routePath + '/*path', lazyHandler);
1587
+ for (const r of lazyRouter.getRoutes()) {
1588
+ r.sourceFile = 'lazy:' + lazyKey;
1589
+ this.router.addRawRoute(r);
1590
+ }
1591
+ }
1592
+ else {
1593
+ try {
1594
+ await this.mountRouteFile(normalized, routePath);
1595
+ }
1596
+ catch (err) {
1597
+ this.log.error({ err, file: filePath }, `Failed to load route file: ${filePath}`);
1598
+ }
1599
+ }
1600
+ }
1601
+ }
1602
+ }
1603
+ }
1604
+ // ─── Route File Mounting (shared by autoMount and HotReloader) ──────────────
1605
+ // ─── Auto-Mount File-Based Jobs ──────────────────────────────────────────────
1606
+ async autoMountJobs(root) {
1607
+ if (!this._queueWorker)
1608
+ return;
1609
+ const fs = await Promise.resolve().then(() => __importStar(require('node:fs/promises')));
1610
+ const path = await Promise.resolve().then(() => __importStar(require('node:path')));
1611
+ const jobsDir = path.join(root, 'src', 'jobs');
1612
+ try {
1613
+ const entries = await fs.readdir(jobsDir, { withFileTypes: true });
1614
+ for (const entry of entries) {
1615
+ if (entry.isFile() &&
1616
+ (entry.name.endsWith('.ts') || entry.name.endsWith('.js'))) {
1617
+ const filePath = path.join(jobsDir, entry.name);
1618
+ const name = entry.name.replace(/\.(ts|js)$/, '');
1619
+ try {
1620
+ // Load the definition directly (no compilation needed here just to get the schema)
1621
+ const mod = await Promise.resolve(`${filePath}`).then(s => __importStar(require(s)));
1622
+ const jobDef = mod.default || mod;
1623
+ if (jobDef) {
1624
+ const def = {
1625
+ name: jobDef.name || name,
1626
+ filePath, // Attach filePath for Thread Pool execution
1627
+ cron: jobDef.cron,
1628
+ schema: jobDef.schema,
1629
+ defaultOptions: jobDef.defaultOptions,
1630
+ };
1631
+ this._queueWorker.registerJob(def);
1632
+ if (this._cronScheduler && jobDef.cron) {
1633
+ this._cronScheduler.registerJob(def);
1634
+ }
1635
+ this.log.info(`Registered background job: ${name}`);
1636
+ }
1637
+ }
1638
+ catch (err) {
1639
+ this.log.error(`Failed to load job file ${filePath}: ${err}`);
1640
+ }
1641
+ }
1642
+ }
1643
+ }
1644
+ catch {
1645
+ // Ignore if jobs directory does not exist
1646
+ }
1647
+ }
1648
+ async mountRouteFile(filePath, routePath) {
1649
+ let mod;
1650
+ const dynamicImport = new Function('specifier', 'return import(specifier)');
1651
+ try {
1652
+ const url = (0, node_url_1.pathToFileURL)(filePath).href + '?t=' + Date.now();
1653
+ mod = await dynamicImport(url);
1654
+ }
1655
+ catch {
1656
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
1657
+ mod = require(filePath);
1658
+ }
1659
+ const path = await Promise.resolve().then(() => __importStar(require('node:path')));
1660
+ const fs = await Promise.resolve().then(() => __importStar(require('node:fs/promises')));
1661
+ const dirname = path.dirname(filePath);
1662
+ const apiDir = this.apiDir || dirname;
1663
+ const normDirname = path.resolve(dirname).replace(/\\/g, '/').toLowerCase();
1664
+ const normApiDir = path.resolve(apiDir).replace(/\\/g, '/').toLowerCase();
1665
+ const segments = normDirname.startsWith(normApiDir)
1666
+ ? normDirname.slice(normApiDir.length).split('/').filter(Boolean)
1667
+ : [];
1668
+ const allMiddlewares = [];
1669
+ const allFilters = [];
1670
+ const allGuards = [];
1671
+ const allInterceptors = [];
1672
+ let allMetadata = {};
1673
+ let allCors = undefined;
1674
+ let allHeaders = {};
1675
+ const dirsToCheck = [apiDir];
1676
+ let tempDir = apiDir;
1677
+ for (const segment of segments) {
1678
+ tempDir = path.join(tempDir, segment);
1679
+ dirsToCheck.push(tempDir);
1680
+ }
1681
+ for (const dir of dirsToCheck) {
1682
+ try {
1683
+ const gwPathJs = path.join(dir, 'gateway.js');
1684
+ const gwPathTs = path.join(dir, 'gateway.ts');
1685
+ let targetGw = '';
1686
+ if (await fs.stat(gwPathTs).catch(() => null))
1687
+ targetGw = gwPathTs;
1688
+ else if (await fs.stat(gwPathJs).catch(() => null))
1689
+ targetGw = gwPathJs;
1690
+ if (targetGw) {
1691
+ const url = (0, node_url_1.pathToFileURL)(targetGw).href + '?t=' + Date.now();
1692
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
1693
+ const gwMod = await dynamicImport(url).catch(() => require(targetGw));
1694
+ const gwConfig = gwMod.default || gwMod;
1695
+ if (gwConfig) {
1696
+ const isExcluded = (reqPath, reqMethod) => {
1697
+ if (!gwConfig.exclude)
1698
+ return false;
1699
+ for (const rule of gwConfig.exclude) {
1700
+ if (typeof rule === 'string') {
1701
+ if (rule.endsWith('/*')) {
1702
+ if (reqPath.startsWith(rule.slice(0, -2)))
1703
+ return true;
1704
+ }
1705
+ else if (reqPath === rule)
1706
+ return true;
1707
+ }
1708
+ else {
1709
+ const pathMatch = rule.path.endsWith('/*')
1710
+ ? reqPath.startsWith(rule.path.slice(0, -2))
1711
+ : reqPath === rule.path;
1712
+ if (pathMatch) {
1713
+ if (!rule.methods ||
1714
+ rule.methods.includes(reqMethod))
1715
+ return true;
1716
+ }
1717
+ }
1718
+ }
1719
+ return false;
1720
+ };
1721
+ if (gwConfig.middleware) {
1722
+ const wrapped = gwConfig.middleware.map((m) => (req, res, next) => isExcluded(req.path, req.method) ? next() : m(req, res, next));
1723
+ allMiddlewares.push(...wrapped);
1724
+ }
1725
+ if (gwConfig.filters) {
1726
+ const wrapped = gwConfig.filters.map((f) => (err, req, res, next) => isExcluded(req.path, req.method)
1727
+ ? next(err)
1728
+ : f.prototype && f.prototype.catch
1729
+ ? new f().catch(err, req, res, next)
1730
+ : f(err, req, res, next));
1731
+ allFilters.push(...wrapped);
1732
+ }
1733
+ if (gwConfig.guards) {
1734
+ const wrapped = gwConfig.guards.map((g) => {
1735
+ return async (req) => {
1736
+ if (isExcluded(req.path, req.method))
1737
+ return true;
1738
+ return typeof g === 'function'
1739
+ ? g.prototype?.canActivate
1740
+ ? new g().canActivate(req)
1741
+ : g(req)
1742
+ : g.canActivate(req);
1743
+ };
1744
+ });
1745
+ allGuards.push(...wrapped);
1746
+ }
1747
+ if (gwConfig.interceptors) {
1748
+ const wrapped = gwConfig.interceptors.map((i) => {
1749
+ return async (req, res) => {
1750
+ if (isExcluded(req.path, req.method))
1751
+ return;
1752
+ return typeof i === 'function'
1753
+ ? i.prototype?.intercept
1754
+ ? new i().intercept(req, res)
1755
+ : i(req, res)
1756
+ : i.intercept(req, res);
1757
+ };
1758
+ });
1759
+ allInterceptors.push(...wrapped);
1760
+ }
1761
+ if (gwConfig.timeout !== undefined) {
1762
+ const baseTimeout = gwConfig.timeout;
1763
+ const timeoutMiddleware = (req, res, next) => {
1764
+ if (isExcluded(req.path, req.method))
1765
+ return next();
1766
+ const ms = typeof baseTimeout === 'function'
1767
+ ? baseTimeout(req)
1768
+ : baseTimeout;
1769
+ if (ms) {
1770
+ req.timeoutTimer = setTimeout(() => {
1771
+ if (!res.headersSent) {
1772
+ res
1773
+ .status(408)
1774
+ .json({
1775
+ success: false,
1776
+ error: {
1777
+ code: 'REQUEST_TIMEOUT',
1778
+ message: 'Request Timeout',
1779
+ },
1780
+ });
1781
+ }
1782
+ }, ms);
1783
+ res.raw.on('finish', () => clearTimeout(req.timeoutTimer));
1784
+ }
1785
+ next();
1786
+ };
1787
+ allMiddlewares.push(timeoutMiddleware);
1788
+ }
1789
+ if (gwConfig.metadata) {
1790
+ allMetadata = { ...allMetadata, ...gwConfig.metadata };
1791
+ }
1792
+ if (gwConfig.cors !== undefined)
1793
+ allCors = gwConfig.cors;
1794
+ if (gwConfig.headers)
1795
+ allHeaders = { ...allHeaders, ...gwConfig.headers };
1796
+ if (gwConfig.plugins) {
1797
+ for (const plugin of gwConfig.plugins) {
1798
+ await this.register(plugin);
1799
+ }
1800
+ }
1801
+ if (gwConfig.imports) {
1802
+ for (const mod of gwConfig.imports) {
1803
+ const name = 'plugin' in mod ? mod.plugin.name : mod.name;
1804
+ if (!this.hasPlugin(name)) {
1805
+ await this.register(mod);
1806
+ }
1807
+ }
1808
+ }
1809
+ if (gwConfig.providers) {
1810
+ for (const [token, providerConfig] of gwConfig.providers) {
1811
+ this.provide(token, providerConfig);
1812
+ }
1813
+ }
1814
+ }
1815
+ }
1816
+ }
1817
+ catch {
1818
+ /* ignore */
1819
+ }
1820
+ }
1821
+ if (!mod.config)
1822
+ mod.config = {};
1823
+ if (mod.config.middleware)
1824
+ allMiddlewares.push(...mod.config.middleware);
1825
+ if (mod.config.cors !== undefined)
1826
+ allCors = mod.config.cors;
1827
+ if (mod.config.headers)
1828
+ allHeaders = { ...allHeaders, ...mod.config.headers };
1829
+ if (mod.config.plugins) {
1830
+ for (const plugin of mod.config.plugins) {
1831
+ await this.register(plugin);
1832
+ }
1833
+ }
1834
+ // Inject Gateway features into module config for compileFunctionalController and OOP
1835
+ mod.config.filters = [
1836
+ ...allFilters,
1837
+ ...(mod.config.filters
1838
+ ? Array.isArray(mod.config.filters)
1839
+ ? mod.config.filters
1840
+ : [mod.config.filters]
1841
+ : []),
1842
+ ];
1843
+ mod.config.guards = [
1844
+ ...allGuards,
1845
+ ...(mod.config.guards
1846
+ ? Array.isArray(mod.config.guards)
1847
+ ? mod.config.guards
1848
+ : [mod.config.guards]
1849
+ : []),
1850
+ ];
1851
+ mod.config.interceptors = [
1852
+ ...allInterceptors,
1853
+ ...(mod.config.interceptors
1854
+ ? Array.isArray(mod.config.interceptors)
1855
+ ? mod.config.interceptors
1856
+ : [mod.config.interceptors]
1857
+ : []),
1858
+ ];
1859
+ mod.config.metadata = { ...allMetadata, ...(mod.config.metadata || {}) };
1860
+ const prefixMiddlewares = [];
1861
+ if (allCors !== undefined) {
1862
+ if (allCors === true)
1863
+ prefixMiddlewares.push((0, middleware_1.cors)({}));
1864
+ else if (allCors !== false)
1865
+ prefixMiddlewares.push((0, middleware_1.cors)(allCors));
1866
+ }
1867
+ if (Object.keys(allHeaders).length > 0) {
1868
+ prefixMiddlewares.push((req, res, next) => {
1869
+ for (const [k, v] of Object.entries(allHeaders))
1870
+ res.setHeader(k, v);
1871
+ next();
1872
+ });
1873
+ }
1874
+ prefixMiddlewares.push(...allMiddlewares);
1875
+ const isRouter = (obj) => obj && 'handle' in obj && 'get' in obj;
1876
+ const CONTROLLER_PREFIX = Symbol.for('exisjs:controller_prefix');
1877
+ const isController = (obj) => obj && obj.prototype && obj.prototype[CONTROLLER_PREFIX] !== undefined;
1878
+ if (mod.default && mod.default.__isController) {
1879
+ // ─── THE NEW PERFECT FUNCTIONAL CONTROLLER ───
1880
+ const compiledRouter = this.compileFunctionalController(mod.default);
1881
+ this.mountRouteWithSource(routePath, compiledRouter, filePath, prefixMiddlewares);
1882
+ }
1883
+ else if (mod.router && isRouter(mod.router)) {
1884
+ this.mountRouteWithSource(routePath, mod.router, filePath, prefixMiddlewares);
1885
+ }
1886
+ else if (mod.default && isController(mod.default)) {
1887
+ this.registerControllers([mod.default], routePath, prefixMiddlewares);
1888
+ }
1889
+ else if (mod.default && isRouter(mod.default)) {
1890
+ this.mountRouteWithSource(routePath, mod.default, filePath, prefixMiddlewares);
1891
+ }
1892
+ else if (isRouter(mod)) {
1893
+ this.mountRouteWithSource(routePath, mod, filePath, prefixMiddlewares);
1894
+ }
1895
+ else {
1896
+ this.log.warn(`\n[WARN] Route file \x1b[36m${filePath}\x1b[0m does not export a recognized router or controller.\n`);
1897
+ }
1898
+ }
1899
+ compileFunctionalController(config) {
1900
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
1901
+ const { Router } = require('../router/router');
1902
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
1903
+ const { cors } = require('../middleware/middleware');
1904
+ const router = new Router();
1905
+ const fileMiddleware = [];
1906
+ if (config.cors) {
1907
+ fileMiddleware.push(config.cors === true ? cors({}) : cors(config.cors));
1908
+ }
1909
+ if (config.middleware) {
1910
+ fileMiddleware.push(...(Array.isArray(config.middleware)
1911
+ ? config.middleware
1912
+ : [config.middleware]));
1913
+ }
1914
+ const { onError, onResponse } = config;
1915
+ for (const [key, routeConfig] of Object.entries(config)) {
1916
+ if ([
1917
+ 'cors',
1918
+ 'middleware',
1919
+ 'onError',
1920
+ 'onResponse',
1921
+ '__isController',
1922
+ ].includes(key))
1923
+ continue;
1924
+ const rc = routeConfig;
1925
+ if (!rc || !rc.method || !rc.path || !rc.handle)
1926
+ continue;
1927
+ const routeMiddlewares = [...fileMiddleware];
1928
+ if (rc.cors) {
1929
+ routeMiddlewares.push(rc.cors === true ? cors({}) : cors(rc.cors));
1930
+ }
1931
+ if (rc.middleware) {
1932
+ routeMiddlewares.push(...(Array.isArray(rc.middleware) ? rc.middleware : [rc.middleware]));
1933
+ }
1934
+ // ─── SUPER HANDLER WRAPPER ───
1935
+ const superHandler = async (req, res, next) => {
1936
+ if (onResponse) {
1937
+ res.raw.on('finish', () => onResponse(req, res));
1938
+ }
1939
+ try {
1940
+ // 1. Run Guards
1941
+ const routeGuards = [...(config.guards || []), ...(rc.guards || [])];
1942
+ for (const guard of routeGuards) {
1943
+ const allowed = await (typeof guard === 'function'
1944
+ ? guard.prototype?.canActivate
1945
+ ? new guard().canActivate(req)
1946
+ : guard(req)
1947
+ : guard.canActivate(req));
1948
+ if (!allowed) {
1949
+ if (res && !res.headersSent) {
1950
+ res
1951
+ .status(403)
1952
+ .json({
1953
+ success: false,
1954
+ error: { code: 'FORBIDDEN', message: 'Forbidden by Guard' },
1955
+ });
1956
+ }
1957
+ return;
1958
+ }
1959
+ }
1960
+ // Build Context
1961
+ const ctx = {
1962
+ body: req.body,
1963
+ query: req.query,
1964
+ params: req.params,
1965
+ headers: req.headers,
1966
+ req,
1967
+ res,
1968
+ app: this,
1969
+ socket: req.ws,
1970
+ ...req, // Spread req to allow access to user, session, etc.
1971
+ };
1972
+ const result = await rc.handle(ctx);
1973
+ // 2. Run Interceptors
1974
+ const routeInterceptors = [
1975
+ ...(config.interceptors || []),
1976
+ ...(rc.interceptors || []),
1977
+ ];
1978
+ for (const interceptor of routeInterceptors) {
1979
+ await (typeof interceptor === 'function'
1980
+ ? interceptor.prototype?.intercept
1981
+ ? new interceptor().intercept(req, res)
1982
+ : interceptor(req, res)
1983
+ : interceptor.intercept(req, res));
1984
+ }
1985
+ if (result !== undefined && !res.headersSent) {
1986
+ if (typeof result === 'object' && result !== null) {
1987
+ res.json(result);
1988
+ }
1989
+ else {
1990
+ res.send(String(result));
1991
+ }
1992
+ }
1993
+ }
1994
+ catch (err) {
1995
+ if (onError) {
1996
+ try {
1997
+ await onError(err, req, res);
1998
+ }
1999
+ catch (hookErr) {
2000
+ next(hookErr);
2001
+ }
2002
+ }
2003
+ else {
2004
+ next(err); // Hands off to ExisJS global error handler (which handles HttpErrors automatically!)
2005
+ }
2006
+ }
2007
+ };
2008
+ // Mount to router
2009
+ const method = rc.method.toLowerCase();
2010
+ const schema = {};
2011
+ const isValidatorOrPipe = (v) => v.parse ||
2012
+ v.transform ||
2013
+ (typeof v === 'function' && v.prototype?.transform);
2014
+ if (rc.body) {
2015
+ schema.body = isValidatorOrPipe(rc.body) ? rc.body : validator_1.v.object(rc.body);
2016
+ }
2017
+ if (rc.query) {
2018
+ schema.query = isValidatorOrPipe(rc.query)
2019
+ ? rc.query
2020
+ : validator_1.v.object(rc.query);
2021
+ }
2022
+ if (rc.params) {
2023
+ schema.params = isValidatorOrPipe(rc.params)
2024
+ ? rc.params
2025
+ : validator_1.v.object(rc.params);
2026
+ }
2027
+ if (rc.host) {
2028
+ schema.host = rc.host;
2029
+ }
2030
+ const combinedFilters = [
2031
+ ...(config.filters
2032
+ ? Array.isArray(config.filters)
2033
+ ? config.filters
2034
+ : [config.filters]
2035
+ : []),
2036
+ ...(rc.filters
2037
+ ? Array.isArray(rc.filters)
2038
+ ? rc.filters
2039
+ : [rc.filters]
2040
+ : []),
2041
+ ];
2042
+ if (combinedFilters.length > 0) {
2043
+ schema.filters = combinedFilters;
2044
+ }
2045
+ const combinedMetadata = {
2046
+ ...(config.metadata || {}),
2047
+ ...(rc.metadata || {}),
2048
+ };
2049
+ if (Object.keys(combinedMetadata).length > 0) {
2050
+ schema.metadata = combinedMetadata;
2051
+ }
2052
+ if (method === 'sse') {
2053
+ const sseHandler = async (stream, rawReq) => {
2054
+ try {
2055
+ const ctx = {
2056
+ body: rawReq.body,
2057
+ query: rawReq.query,
2058
+ params: rawReq.params,
2059
+ headers: rawReq.headers,
2060
+ req: rawReq,
2061
+ res: undefined, // Res is not available in standard SSE after handshake
2062
+ app: this,
2063
+ stream,
2064
+ ...rawReq,
2065
+ };
2066
+ await rc.handle(ctx);
2067
+ }
2068
+ catch (err) {
2069
+ if (onError) {
2070
+ await onError(err, rawReq, {});
2071
+ }
2072
+ else {
2073
+ console.error('[Exis SSE Error]', err);
2074
+ }
2075
+ }
2076
+ };
2077
+ router.sse(rc.path, ...routeMiddlewares, sseHandler);
2078
+ }
2079
+ else {
2080
+ if (Object.keys(schema).length > 0) {
2081
+ router[method](rc.path, ...routeMiddlewares, schema, superHandler);
2082
+ }
2083
+ else {
2084
+ router[method](rc.path, ...routeMiddlewares, superHandler);
2085
+ }
2086
+ }
2087
+ }
2088
+ return router;
2089
+ }
2090
+ mountRouteWithSource(prefix, subRouter, sourceFile, prefixMiddlewares = []) {
2091
+ for (const route of subRouter.getRoutes()) {
2092
+ const fullPath = (prefix + route.path).replace(/\/+/g, '/');
2093
+ const r = { ...route, path: fullPath, sourceFile };
2094
+ if (prefixMiddlewares.length > 0) {
2095
+ r.handlers = [...prefixMiddlewares, ...r.handlers];
2096
+ }
2097
+ this.router.addRawRoute(r);
2098
+ if (this.hooks.route.length > 0) {
2099
+ // eslint-disable-next-line @typescript-eslint/prefer-for-of
2100
+ for (let i = 0; i < this.hooks.route.length; i++) {
2101
+ this.hooks.route[i]({ method: route.method, path: fullPath });
2102
+ }
2103
+ }
2104
+ }
2105
+ return this;
2106
+ }
2107
+ async scanDirectory(dir, baseRoute = '/') {
2108
+ const fs = await Promise.resolve().then(() => __importStar(require('node:fs/promises')));
2109
+ const path = await Promise.resolve().then(() => __importStar(require('node:path')));
2110
+ const entries = await fs.readdir(dir, { withFileTypes: true });
2111
+ const results = [];
2112
+ for (const entry of entries) {
2113
+ const fullPath = path.join(dir, entry.name);
2114
+ if (entry.isDirectory()) {
2115
+ let segment = entry.name;
2116
+ if (segment.startsWith('(') && segment.endsWith(')')) {
2117
+ const subResults = await this.scanDirectory(fullPath, baseRoute);
2118
+ results.push(...subResults);
2119
+ continue;
2120
+ }
2121
+ if (segment.startsWith('[...') && segment.endsWith(']')) {
2122
+ segment = '*' + segment.slice(4, -1);
2123
+ }
2124
+ else {
2125
+ segment = segment.replace(/\[(.*?)\]/g, ':$1');
2126
+ }
2127
+ const nextBase = baseRoute === '/' ? `/${segment}` : `${baseRoute}/${segment}`;
2128
+ const subResults = await this.scanDirectory(fullPath, nextBase);
2129
+ results.push(...subResults);
2130
+ }
2131
+ else {
2132
+ results.push({ filePath: fullPath, routePath: baseRoute });
2133
+ }
2134
+ }
2135
+ return results;
2136
+ }
2137
+ // ─── Queues ─────────────────────────────────────────────────────────────────
2138
+ /**
2139
+ * Registers a background job worker handler.
2140
+ */
2141
+ queue(name, handler, options) {
2142
+ if (!this._queueWorker) {
2143
+ if (this.options.queue?.enableWorkers === false) {
2144
+ // workers disabled explicitly, gracefully skip registering
2145
+ return;
2146
+ }
2147
+ // Worker not initialized yet (e.g. config loading is pending), buffer the job
2148
+ this._pendingQueueJobs.push({
2149
+ name,
2150
+ handler: handler,
2151
+ ...options,
2152
+ });
2153
+ return;
2154
+ }
2155
+ this._queueWorker.registerJob({
2156
+ name,
2157
+ handler: handler,
2158
+ ...options,
2159
+ });
2160
+ }
2161
+ /**
2162
+ * Enqueues a payload for background processing.
2163
+ */
2164
+ async enqueue(name, payload, opts) {
2165
+ if (!this._queueClient) {
2166
+ throw new Error(`Queue is not initialized. Please configure ExisConfig.queue first.`);
2167
+ }
2168
+ return this._queueClient.enqueue(name, payload, opts);
2169
+ }
2170
+ _initQueue(qConfig) {
2171
+ if (!this._queueClient) {
2172
+ this._queueClient = new client_1.ExisQueue(qConfig);
2173
+ }
2174
+ const enableWorkers = qConfig.enableWorkers ?? true;
2175
+ if (enableWorkers && !this._queueWorker) {
2176
+ this._queueWorker = new worker_1.ExisWorker(qConfig, this.log, this._queueClient.getDriver());
2177
+ this._queueWorker.start();
2178
+ const redisClient = this._queueWorker.redis || null;
2179
+ this._cronScheduler = new scheduler_1.CronScheduler(this._queueWorker, redisClient, this.log);
2180
+ this._cronScheduler.start();
2181
+ }
2182
+ }
2183
+ }
2184
+ exports.App = App;
2185
+ // ─── Exports ──────────────────────────────────────────────────────────────────
2186
+ exports.app = new App();
2187
+ exports.activeAppInstance = null;
2188
+ function getActiveApp() {
2189
+ return exports.activeAppInstance || exports.app;
2190
+ }
2191
+ //# sourceMappingURL=app.js.map