exisjs 0.7.5 → 0.7.7

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 (321) hide show
  1. package/dist/cli/commands/build.d.ts.map +1 -1
  2. package/dist/cli/commands/build.js +12 -0
  3. package/dist/cli/commands/build.js.map +1 -1
  4. package/dist/cli/commands/clean.d.ts +5 -0
  5. package/dist/cli/commands/clean.d.ts.map +1 -0
  6. package/dist/cli/commands/clean.js +30 -0
  7. package/dist/cli/commands/clean.js.map +1 -0
  8. package/dist/cli/commands/dev.d.ts.map +1 -1
  9. package/dist/cli/commands/dev.js +171 -86
  10. package/dist/cli/commands/dev.js.map +1 -1
  11. package/dist/cli/commands/generate.d.ts +6 -1
  12. package/dist/cli/commands/generate.d.ts.map +1 -1
  13. package/dist/cli/commands/generate.js +57 -2
  14. package/dist/cli/commands/generate.js.map +1 -1
  15. package/dist/cli/commands/start.d.ts.map +1 -1
  16. package/dist/cli/commands/start.js +54 -16
  17. package/dist/cli/commands/start.js.map +1 -1
  18. package/dist/cli/index.js +26 -3
  19. package/dist/cli/index.js.map +1 -1
  20. package/dist/cli/manifest.d.ts +1 -0
  21. package/dist/cli/manifest.d.ts.map +1 -1
  22. package/dist/cli/manifest.js +264 -33
  23. package/dist/cli/manifest.js.map +1 -1
  24. package/dist/cli/resolve-aliases.d.ts +1 -1
  25. package/dist/cli/resolve-aliases.d.ts.map +1 -1
  26. package/dist/cli/resolve-aliases.js +30 -13
  27. package/dist/cli/resolve-aliases.js.map +1 -1
  28. package/dist/config/config.d.ts +3 -1
  29. package/dist/config/config.d.ts.map +1 -1
  30. package/dist/config/config.js +9 -0
  31. package/dist/config/config.js.map +1 -1
  32. package/dist/config/types.d.ts +6 -0
  33. package/dist/config/types.d.ts.map +1 -1
  34. package/dist/cron/expressions.d.ts +45 -0
  35. package/dist/cron/expressions.d.ts.map +1 -0
  36. package/dist/cron/expressions.js +87 -0
  37. package/dist/cron/expressions.js.map +1 -0
  38. package/dist/cron/index.d.ts +6 -0
  39. package/dist/cron/index.d.ts.map +1 -0
  40. package/dist/cron/index.js +22 -0
  41. package/dist/cron/index.js.map +1 -0
  42. package/dist/cron/job.d.ts +50 -0
  43. package/dist/cron/job.d.ts.map +1 -0
  44. package/dist/cron/job.js +247 -0
  45. package/dist/cron/job.js.map +1 -0
  46. package/dist/cron/manager.d.ts +105 -0
  47. package/dist/cron/manager.d.ts.map +1 -0
  48. package/dist/cron/manager.js +245 -0
  49. package/dist/cron/manager.js.map +1 -0
  50. package/dist/cron/parser.d.ts +22 -0
  51. package/dist/cron/parser.d.ts.map +1 -0
  52. package/dist/cron/parser.js +269 -0
  53. package/dist/cron/parser.js.map +1 -0
  54. package/dist/cron/types.d.ts +76 -0
  55. package/dist/cron/types.d.ts.map +1 -0
  56. package/dist/cron/types.js +3 -0
  57. package/dist/cron/types.js.map +1 -0
  58. package/dist/decorators/constants.d.ts +2 -0
  59. package/dist/decorators/constants.d.ts.map +1 -1
  60. package/dist/decorators/constants.js +3 -1
  61. package/dist/decorators/constants.js.map +1 -1
  62. package/dist/decorators/core.d.ts +29 -11
  63. package/dist/decorators/core.d.ts.map +1 -1
  64. package/dist/decorators/core.js +49 -11
  65. package/dist/decorators/core.js.map +1 -1
  66. package/dist/decorators/cron.d.ts +57 -0
  67. package/dist/decorators/cron.d.ts.map +1 -0
  68. package/dist/decorators/cron.js +143 -0
  69. package/dist/decorators/cron.js.map +1 -0
  70. package/dist/decorators/index.d.ts +2 -0
  71. package/dist/decorators/index.d.ts.map +1 -1
  72. package/dist/decorators/index.js +2 -0
  73. package/dist/decorators/index.js.map +1 -1
  74. package/dist/decorators/middleware.d.ts +65 -2
  75. package/dist/decorators/middleware.d.ts.map +1 -1
  76. package/dist/decorators/middleware.js +148 -62
  77. package/dist/decorators/middleware.js.map +1 -1
  78. package/dist/decorators/params.d.ts +102 -2
  79. package/dist/decorators/params.d.ts.map +1 -1
  80. package/dist/decorators/params.js +105 -3
  81. package/dist/decorators/params.js.map +1 -1
  82. package/dist/decorators/response.d.ts.map +1 -1
  83. package/dist/decorators/response.js +32 -22
  84. package/dist/decorators/response.js.map +1 -1
  85. package/dist/decorators/routing.d.ts +81 -16
  86. package/dist/decorators/routing.d.ts.map +1 -1
  87. package/dist/decorators/routing.js +81 -16
  88. package/dist/decorators/routing.js.map +1 -1
  89. package/dist/di/container.d.ts.map +1 -1
  90. package/dist/di/container.js +6 -1
  91. package/dist/di/container.js.map +1 -1
  92. package/dist/docs/content/cli/commands.mdx +1 -1
  93. package/dist/docs/content/controllers.mdx +127 -10
  94. package/dist/docs/content/cron.mdx +196 -0
  95. package/dist/docs/content/decorators.mdx +22 -4
  96. package/dist/docs/content/errors.mdx +58 -9
  97. package/dist/docs/content/introduction.mdx +7 -7
  98. package/dist/docs/content/middleware.mdx +269 -6
  99. package/dist/docs/content/requests.mdx +180 -6
  100. package/dist/docs/content/responses.mdx +283 -32
  101. package/dist/docs/content/routing.mdx +46 -0
  102. package/dist/docs/content/swagger.mdx +228 -0
  103. package/dist/docs/content/validation.mdx +190 -11
  104. package/dist/docs/reference/context.mdx +11 -0
  105. package/dist/docs/reference/cron.mdx +300 -0
  106. package/dist/docs/reference/decorators.mdx +22 -4
  107. package/dist/docs/reference/error.mdx +88 -0
  108. package/dist/docs/reference/logger.mdx +175 -43
  109. package/dist/docs/reference/middleware.mdx +31 -0
  110. package/dist/docs/reference/router.mdx +22 -0
  111. package/dist/docs/reference/swagger.mdx +143 -0
  112. package/dist/docs/reference/validator.mdx +83 -23
  113. package/dist/error/errors.d.ts.map +1 -1
  114. package/dist/error/errors.js +95 -43
  115. package/dist/error/errors.js.map +1 -1
  116. package/dist/error/index.d.ts +1 -0
  117. package/dist/error/index.d.ts.map +1 -1
  118. package/dist/error/index.js +11 -1
  119. package/dist/error/index.js.map +1 -1
  120. package/dist/error/overlay.d.ts +42 -0
  121. package/dist/error/overlay.d.ts.map +1 -0
  122. package/dist/error/overlay.js +306 -0
  123. package/dist/error/overlay.js.map +1 -0
  124. package/dist/index.d.ts +1 -0
  125. package/dist/index.d.ts.map +1 -1
  126. package/dist/index.js +22 -1
  127. package/dist/index.js.map +1 -1
  128. package/dist/lib/start-server.js +33 -7
  129. package/dist/lib/start-server.js.map +1 -1
  130. package/dist/logger/index.d.ts +1 -1
  131. package/dist/logger/index.d.ts.map +1 -1
  132. package/dist/logger/index.js +9 -1
  133. package/dist/logger/index.js.map +1 -1
  134. package/dist/middleware/guard.d.ts +2 -2
  135. package/dist/middleware/guard.d.ts.map +1 -1
  136. package/dist/middleware/guard.js.map +1 -1
  137. package/dist/middleware/ip-filter.d.ts +1 -0
  138. package/dist/middleware/ip-filter.d.ts.map +1 -1
  139. package/dist/middleware/ip-filter.js +2 -0
  140. package/dist/middleware/ip-filter.js.map +1 -1
  141. package/dist/middleware/middleware.d.ts +2 -2
  142. package/dist/middleware/middleware.d.ts.map +1 -1
  143. package/dist/middleware/middleware.js +20 -2
  144. package/dist/middleware/middleware.js.map +1 -1
  145. package/dist/middleware/security.d.ts +43 -1
  146. package/dist/middleware/security.d.ts.map +1 -1
  147. package/dist/middleware/security.js +101 -0
  148. package/dist/middleware/security.js.map +1 -1
  149. package/dist/response/index.d.ts +229 -6
  150. package/dist/response/index.d.ts.map +1 -1
  151. package/dist/response/index.js +272 -2
  152. package/dist/response/index.js.map +1 -1
  153. package/dist/router/boundary.d.ts +41 -11
  154. package/dist/router/boundary.d.ts.map +1 -1
  155. package/dist/router/boundary.js +24 -2
  156. package/dist/router/boundary.js.map +1 -1
  157. package/dist/router/controller-registrar.d.ts.map +1 -1
  158. package/dist/router/controller-registrar.js +108 -4
  159. package/dist/router/controller-registrar.js.map +1 -1
  160. package/dist/router/index.d.ts +2 -2
  161. package/dist/router/index.d.ts.map +1 -1
  162. package/dist/router/index.js +2 -1
  163. package/dist/router/index.js.map +1 -1
  164. package/dist/router/route-builder.d.ts +262 -65
  165. package/dist/router/route-builder.d.ts.map +1 -1
  166. package/dist/router/route-builder.js +181 -23
  167. package/dist/router/route-builder.js.map +1 -1
  168. package/dist/router/route-scanner.d.ts +6 -0
  169. package/dist/router/route-scanner.d.ts.map +1 -1
  170. package/dist/router/route-scanner.js +71 -792
  171. package/dist/router/route-scanner.js.map +1 -1
  172. package/dist/router/router.d.ts.map +1 -1
  173. package/dist/router/router.js +62 -17
  174. package/dist/router/router.js.map +1 -1
  175. package/dist/router/scanner/boundary-loader.d.ts +18 -0
  176. package/dist/router/scanner/boundary-loader.d.ts.map +1 -0
  177. package/dist/router/scanner/boundary-loader.js +539 -0
  178. package/dist/router/scanner/boundary-loader.js.map +1 -0
  179. package/dist/router/scanner/cron-mounter.d.ts +15 -0
  180. package/dist/router/scanner/cron-mounter.d.ts.map +1 -0
  181. package/dist/router/scanner/cron-mounter.js +216 -0
  182. package/dist/router/scanner/cron-mounter.js.map +1 -0
  183. package/dist/router/scanner/error-mounter.d.ts +14 -0
  184. package/dist/router/scanner/error-mounter.d.ts.map +1 -0
  185. package/dist/router/scanner/error-mounter.js +200 -0
  186. package/dist/router/scanner/error-mounter.js.map +1 -0
  187. package/dist/router/scanner/fs-scanner.d.ts +25 -0
  188. package/dist/router/scanner/fs-scanner.d.ts.map +1 -0
  189. package/dist/router/scanner/fs-scanner.js +115 -0
  190. package/dist/router/scanner/fs-scanner.js.map +1 -0
  191. package/dist/router/scanner/functional-compiler.d.ts +9 -0
  192. package/dist/router/scanner/functional-compiler.d.ts.map +1 -0
  193. package/dist/router/scanner/functional-compiler.js +233 -0
  194. package/dist/router/scanner/functional-compiler.js.map +1 -0
  195. package/dist/router/scanner/index.d.ts +9 -0
  196. package/dist/router/scanner/index.d.ts.map +1 -0
  197. package/dist/router/scanner/index.js +19 -0
  198. package/dist/router/scanner/index.js.map +1 -0
  199. package/dist/router/types.d.ts +23 -0
  200. package/dist/router/types.d.ts.map +1 -1
  201. package/dist/server/app.d.ts +3 -0
  202. package/dist/server/app.d.ts.map +1 -1
  203. package/dist/server/app.js +13 -0
  204. package/dist/server/app.js.map +1 -1
  205. package/dist/server/bootstrapper.d.ts.map +1 -1
  206. package/dist/server/bootstrapper.js +91 -30
  207. package/dist/server/bootstrapper.js.map +1 -1
  208. package/dist/server/cluster.d.ts.map +1 -1
  209. package/dist/server/cluster.js +13 -2
  210. package/dist/server/cluster.js.map +1 -1
  211. package/dist/server/context.d.ts +8 -0
  212. package/dist/server/context.d.ts.map +1 -1
  213. package/dist/server/context.js +50 -6
  214. package/dist/server/context.js.map +1 -1
  215. package/dist/server/helpers/body-parser.d.ts +18 -0
  216. package/dist/server/helpers/body-parser.d.ts.map +1 -0
  217. package/dist/server/helpers/body-parser.js +246 -0
  218. package/dist/server/helpers/body-parser.js.map +1 -0
  219. package/dist/server/helpers/cookie.d.ts +4 -0
  220. package/dist/server/helpers/cookie.d.ts.map +1 -0
  221. package/dist/server/helpers/cookie.js +61 -0
  222. package/dist/server/helpers/cookie.js.map +1 -0
  223. package/dist/server/helpers/etag.d.ts +3 -0
  224. package/dist/server/helpers/etag.d.ts.map +1 -0
  225. package/dist/server/helpers/etag.js +25 -0
  226. package/dist/server/helpers/etag.js.map +1 -0
  227. package/dist/server/helpers/index.d.ts +6 -0
  228. package/dist/server/helpers/index.d.ts.map +1 -0
  229. package/dist/server/helpers/index.js +21 -0
  230. package/dist/server/helpers/index.js.map +1 -0
  231. package/dist/server/helpers/ip.d.ts +9 -0
  232. package/dist/server/helpers/ip.d.ts.map +1 -0
  233. package/dist/server/helpers/ip.js +62 -0
  234. package/dist/server/helpers/ip.js.map +1 -0
  235. package/dist/server/helpers/stream.d.ts +4 -0
  236. package/dist/server/helpers/stream.d.ts.map +1 -0
  237. package/dist/server/helpers/stream.js +193 -0
  238. package/dist/server/helpers/stream.js.map +1 -0
  239. package/dist/server/lifecycle.d.ts +22 -0
  240. package/dist/server/lifecycle.d.ts.map +1 -0
  241. package/dist/server/lifecycle.js +106 -0
  242. package/dist/server/lifecycle.js.map +1 -0
  243. package/dist/server/request-handler.d.ts +1 -0
  244. package/dist/server/request-handler.d.ts.map +1 -1
  245. package/dist/server/request-handler.js +27 -17
  246. package/dist/server/request-handler.js.map +1 -1
  247. package/dist/server/request.d.ts +1 -139
  248. package/dist/server/request.d.ts.map +1 -1
  249. package/dist/server/request.js +37 -433
  250. package/dist/server/request.js.map +1 -1
  251. package/dist/server/response.d.ts +112 -156
  252. package/dist/server/response.d.ts.map +1 -1
  253. package/dist/server/response.js +129 -279
  254. package/dist/server/response.js.map +1 -1
  255. package/dist/swagger/decorators.d.ts +81 -0
  256. package/dist/swagger/decorators.d.ts.map +1 -0
  257. package/dist/swagger/decorators.js +172 -0
  258. package/dist/swagger/decorators.js.map +1 -0
  259. package/dist/swagger/generator.d.ts +19 -0
  260. package/dist/swagger/generator.d.ts.map +1 -0
  261. package/dist/swagger/generator.js +344 -0
  262. package/dist/swagger/generator.js.map +1 -0
  263. package/dist/swagger/index.d.ts +31 -0
  264. package/dist/swagger/index.d.ts.map +1 -0
  265. package/dist/swagger/index.js +53 -0
  266. package/dist/swagger/index.js.map +1 -0
  267. package/dist/swagger/mounter.d.ts +7 -0
  268. package/dist/swagger/mounter.d.ts.map +1 -0
  269. package/dist/swagger/mounter.js +56 -0
  270. package/dist/swagger/mounter.js.map +1 -0
  271. package/dist/swagger/types.d.ts +190 -0
  272. package/dist/swagger/types.d.ts.map +1 -0
  273. package/dist/swagger/types.js +3 -0
  274. package/dist/swagger/types.js.map +1 -0
  275. package/dist/swagger/ui.d.ts +6 -0
  276. package/dist/swagger/ui.d.ts.map +1 -0
  277. package/dist/swagger/ui.js +118 -0
  278. package/dist/swagger/ui.js.map +1 -0
  279. package/dist/testing/index.d.ts.map +1 -1
  280. package/dist/testing/index.js +6 -1
  281. package/dist/testing/index.js.map +1 -1
  282. package/dist/testing/mocks.d.ts.map +1 -1
  283. package/dist/testing/mocks.js +15 -3
  284. package/dist/testing/mocks.js.map +1 -1
  285. package/dist/utils/logger.d.ts +8 -0
  286. package/dist/utils/logger.d.ts.map +1 -1
  287. package/dist/utils/logger.js +184 -1
  288. package/dist/utils/logger.js.map +1 -1
  289. package/dist/validator/builder.d.ts +223 -0
  290. package/dist/validator/builder.d.ts.map +1 -0
  291. package/dist/validator/builder.js +517 -0
  292. package/dist/validator/builder.js.map +1 -0
  293. package/dist/validator/engine.d.ts +19 -0
  294. package/dist/validator/engine.d.ts.map +1 -0
  295. package/dist/validator/engine.js +568 -0
  296. package/dist/validator/engine.js.map +1 -0
  297. package/dist/validator/error.d.ts +12 -0
  298. package/dist/validator/error.d.ts.map +1 -0
  299. package/dist/validator/error.js +63 -0
  300. package/dist/validator/error.js.map +1 -0
  301. package/dist/validator/pagination.d.ts +40 -0
  302. package/dist/validator/pagination.d.ts.map +1 -0
  303. package/dist/validator/pagination.js +60 -0
  304. package/dist/validator/pagination.js.map +1 -0
  305. package/dist/validator/tex-types.d.ts +2 -1
  306. package/dist/validator/tex-types.d.ts.map +1 -1
  307. package/dist/validator/tex-types.js +2 -1
  308. package/dist/validator/tex-types.js.map +1 -1
  309. package/dist/validator/tex.d.ts +6 -142
  310. package/dist/validator/tex.d.ts.map +1 -1
  311. package/dist/validator/tex.js +14 -733
  312. package/dist/validator/tex.js.map +1 -1
  313. package/dist/validator/types.d.ts +83 -0
  314. package/dist/validator/types.d.ts.map +1 -0
  315. package/dist/validator/types.js +3 -0
  316. package/dist/validator/types.js.map +1 -0
  317. package/package.json +8 -4
  318. package/dist/dev/error-overlay.d.ts +0 -3
  319. package/dist/dev/error-overlay.d.ts.map +0 -1
  320. package/dist/dev/error-overlay.js +0 -118
  321. package/dist/dev/error-overlay.js.map +0 -1
@@ -36,10 +36,10 @@ Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.RouteScanner = void 0;
37
37
  const router_1 = require("../router/router");
38
38
  const middleware_1 = require("../middleware/middleware");
39
- const index_1 = require("../validator/index");
39
+ const path = __importStar(require("node:path"));
40
40
  const node_url_1 = require("node:url");
41
- const context_1 = require("../server/context");
42
- const error_overlay_1 = require("../dev/error-overlay");
41
+ const overlay_1 = require("../error/overlay");
42
+ const scanner_1 = require("./scanner");
43
43
  class RouteScanner {
44
44
  constructor(app) {
45
45
  this.app = app;
@@ -50,17 +50,17 @@ class RouteScanner {
50
50
  this.globalParadigm = null;
51
51
  }
52
52
  async loadAllRoutes() {
53
- for (const [filePath, entry] of this.lazyRoutes.entries()) {
53
+ for (const [, entry] of this.lazyRoutes.entries()) {
54
54
  if (!entry.loaded) {
55
55
  entry.loaded = true;
56
- this.app.getRouter().removeRoutesBySource('lazy:' + filePath);
57
- const routePath = this.routeMap.get(filePath);
56
+ this.app.getRouter().removeRoutesBySource('lazy:' + entry.filePath);
57
+ const routePath = this.routeMap.get(entry.filePath);
58
58
  if (routePath) {
59
59
  try {
60
- await this.mountRouteFile(filePath, routePath);
60
+ await this.mountRouteFile(entry.filePath, routePath);
61
61
  }
62
62
  catch (e) {
63
- this.app.log.error({ err: e, file: filePath }, 'Failed to eager-load route');
63
+ this.app.log.error({ err: e, file: entry.filePath }, 'Failed to eager-load route');
64
64
  }
65
65
  }
66
66
  }
@@ -80,6 +80,8 @@ class RouteScanner {
80
80
  }
81
81
  // Try to load the pre-built manifest first (O(1) boot)
82
82
  let manifest = undefined;
83
+ let manifestErrorHandler = undefined;
84
+ let manifestCronJobs = undefined;
83
85
  // STANDALONE MODE: If the bundler statically injected the manifest, skip filesystem!
84
86
  if (globalThis.__EXIS_STANDALONE_MANIFEST__) {
85
87
  manifest = globalThis.__EXIS_STANDALONE_MANIFEST__;
@@ -101,6 +103,8 @@ class RouteScanner {
101
103
  mod = await dynamicImport(url);
102
104
  }
103
105
  manifest = mod.manifest;
106
+ manifestErrorHandler = mod.errorHandler;
107
+ manifestCronJobs = mod.cronJobs;
104
108
  }
105
109
  }
106
110
  catch (err) {
@@ -112,13 +116,25 @@ class RouteScanner {
112
116
  const searchDirs = isProd
113
117
  ? [
114
118
  path.join(root, '.exis', 'server', 'src', 'http'),
115
- path.join(root, 'dist', 'src', 'http'),
116
119
  path.join(root, 'src', 'http'),
117
120
  ]
118
121
  : [path.join(root, 'src', 'http')];
119
122
  if (Array.isArray(manifest)) {
120
- for (const dir of searchDirs) {
121
- await this.mountErrorHandler(dir);
123
+ if (manifestErrorHandler !== undefined) {
124
+ if (manifestErrorHandler) {
125
+ this.mountErrorHandlerFromModule(manifestErrorHandler, 'manifest:error');
126
+ }
127
+ }
128
+ else {
129
+ for (const dir of searchDirs) {
130
+ await this.mountErrorHandler(dir);
131
+ }
132
+ }
133
+ if (Array.isArray(manifestCronJobs)) {
134
+ this.mountCronJobsFromEntries(manifestCronJobs);
135
+ }
136
+ else {
137
+ await this.mountCronJobs(root);
122
138
  }
123
139
  for (const entry of manifest) {
124
140
  const { routePath, module: routeMod, filePath } = entry;
@@ -178,25 +194,20 @@ class RouteScanner {
178
194
  /* ignore */
179
195
  }
180
196
  }
181
- if (appDirs.length === 0)
197
+ if (appDirs.length === 0) {
198
+ await this.mountCronJobs(root);
182
199
  return;
183
- this.apiDir = appDirs[0] // keep property name for backwards compatibility
184
- ;
200
+ }
201
+ this.apiDir = appDirs[0]; // keep property name for backwards compatibility
185
202
  this._allApiDirs = appDirs;
186
203
  for (const appDir of appDirs) {
187
204
  await this.mountErrorHandler(appDir);
188
205
  const routes = await this.scanDirectory(appDir);
189
206
  for (const { filePath, routePath } of routes) {
190
- const isBoundaryFile = filePath.endsWith('boundary.ts') ||
191
- filePath.endsWith('boundary.js') ||
192
- /\.boundary\.[jt]s$/.test(filePath);
193
- if (isBoundaryFile) {
207
+ if ((0, scanner_1.isBoundaryFile)(filePath)) {
194
208
  this.hasBoundaries = true;
195
209
  }
196
- const isRouteFile = filePath.endsWith('route.ts') ||
197
- filePath.endsWith('route.js') ||
198
- /\.route\.[jt]s$/.test(filePath);
199
- if (isRouteFile) {
210
+ if ((0, scanner_1.isRouteFile)(filePath)) {
200
211
  const normalized = path.resolve(filePath);
201
212
  this.routeMap.set(normalized, routePath);
202
213
  if (isDev) {
@@ -214,7 +225,7 @@ class RouteScanner {
214
225
  await this.mountRouteFile(normalized, routePath);
215
226
  }
216
227
  catch (err) {
217
- (0, error_overlay_1.formatDevError)(err, normalized);
228
+ (0, overlay_1.formatDevError)(err, normalized);
218
229
  return;
219
230
  }
220
231
  await this.app.getRouter().handle(req, res, next);
@@ -242,6 +253,7 @@ class RouteScanner {
242
253
  }
243
254
  }
244
255
  }
256
+ await this.mountCronJobs(root);
245
257
  // ─── Eager Paradigm Validation ──────────────────────────────────────────────
246
258
  // Even in dev mode (lazy routes), pre-scan all route files at startup to
247
259
  // detect mixed paradigms (OOP + functional) and throw a fatal error immediately.
@@ -293,6 +305,14 @@ class RouteScanner {
293
305
  if (!this.hasBoundaries && !isProd) {
294
306
  this.app.log.warn('No boundary.ts found — applying default security headers.');
295
307
  }
308
+ const docsConfig = this.app.options.docs || this.app.options.swagger;
309
+ if (docsConfig &&
310
+ (typeof docsConfig === 'boolean'
311
+ ? docsConfig
312
+ : docsConfig.enabled !== false)) {
313
+ const { mountDocumentation } = await Promise.resolve().then(() => __importStar(require('../swagger/mounter.js')));
314
+ mountDocumentation(this.app, typeof docsConfig === 'object' ? docsConfig : {});
315
+ }
296
316
  }
297
317
  // ─── Route File Mounting (shared by autoMount and HotReloader) ──────────────
298
318
  async mountRouteFile(filePath, routePath) {
@@ -313,461 +333,13 @@ class RouteScanner {
313
333
  // eslint-disable-next-line @typescript-eslint/no-require-imports
314
334
  mod = require(filePath);
315
335
  }
316
- const path = await Promise.resolve().then(() => __importStar(require('node:path')));
317
- const fs = await Promise.resolve().then(() => __importStar(require('node:fs/promises')));
318
- const dirname = path.dirname(filePath);
319
- const apiDir = this.apiDir || dirname;
320
- const normDirname = path.resolve(dirname).replace(/\\/g, '/').toLowerCase();
321
- const normApiDir = path.resolve(apiDir).replace(/\\/g, '/').toLowerCase();
322
- const segments = normDirname.startsWith(normApiDir)
323
- ? normDirname.slice(normApiDir.length).split('/').filter(Boolean)
324
- : [];
325
- const allMiddlewares = [];
326
- const allFilters = [];
327
- const allGuards = [];
328
- const allInterceptors = [];
329
- let allMetadata = {};
330
- let allCors = undefined;
331
- let allHeaders = {};
332
- const activeBoundaries = [];
333
- const dirsToCheck = [apiDir];
334
- let tempDir = apiDir;
335
- for (const segment of segments) {
336
- tempDir = path.join(tempDir, segment);
337
- dirsToCheck.push(tempDir);
338
- }
339
- for (const dir of dirsToCheck) {
340
- try {
341
- // Detect deprecated gateway.ts / gateway.js
342
- const gatewayPathTs = path.join(dir, 'gateway.ts');
343
- const gatewayPathJs = path.join(dir, 'gateway.js');
344
- if ((await fs.stat(gatewayPathTs).catch(() => null)) ||
345
- (await fs.stat(gatewayPathJs).catch(() => null))) {
346
- this.app.log.warn(`Found deprecated 'gateway' file in '${dir}'. Gateways have been renamed to 'boundary.ts' in ExisJS v0.7+. Please rename it to boundary.ts.`);
347
- }
348
- const boundaryPathTs = path.join(dir, 'boundary.ts');
349
- const boundaryPathJs = path.join(dir, 'boundary.js');
350
- let targetBoundary = '';
351
- if (await fs.stat(boundaryPathTs).catch(() => null))
352
- targetBoundary = boundaryPathTs;
353
- else if (await fs.stat(boundaryPathJs).catch(() => null))
354
- targetBoundary = boundaryPathJs;
355
- else {
356
- // Check for named boundaries like user.boundary.ts
357
- const dirFiles = await fs.readdir(dir).catch(() => []);
358
- const namedBoundary = dirFiles.find((f) => /\.boundary\.[jt]s$/.test(f));
359
- if (namedBoundary) {
360
- targetBoundary = path.join(dir, namedBoundary);
361
- }
362
- }
363
- if (targetBoundary) {
364
- activeBoundaries.push(targetBoundary);
365
- this.hasBoundaries = true;
366
- const boundaryUrl = process.env.VITEST || process.env.NODE_ENV === 'test'
367
- ? (0, node_url_1.pathToFileURL)(targetBoundary).href
368
- : (0, node_url_1.pathToFileURL)(targetBoundary).href + '?t=' + Date.now();
369
- let boundaryMod;
370
- try {
371
- if (process.env.VITEST || process.env.NODE_ENV === 'test') {
372
- boundaryMod = await Promise.resolve(`${boundaryUrl}`).then(s => __importStar(require(s)));
373
- }
374
- else {
375
- const dynamicImportBoundary = new Function('specifier', 'return import(specifier)');
376
- boundaryMod = await dynamicImportBoundary(boundaryUrl);
377
- }
378
- }
379
- catch {
380
- // eslint-disable-next-line @typescript-eslint/no-require-imports
381
- boundaryMod = require(targetBoundary);
382
- }
383
- // Extract config: check boundaryMod.config, boundaryMod.default, or boundaryMod itself
384
- let boundaryConfig = null;
385
- const BOUNDARY_CONFIG = Symbol.for('exisjs:boundary_config');
386
- const boundaryDefault = boundaryMod && boundaryMod.default && boundaryMod.default.default
387
- ? boundaryMod.default.default
388
- : boundaryMod && boundaryMod.default
389
- ? boundaryMod.default
390
- : null;
391
- if (boundaryMod &&
392
- boundaryMod.config &&
393
- typeof boundaryMod.config === 'object') {
394
- boundaryConfig = boundaryMod.config;
395
- }
396
- else if (boundaryDefault &&
397
- typeof boundaryDefault === 'function' &&
398
- boundaryDefault.prototype &&
399
- boundaryDefault.prototype[BOUNDARY_CONFIG]) {
400
- // Class-based boundary with @Boundary
401
- boundaryConfig = boundaryDefault.prototype[BOUNDARY_CONFIG];
402
- }
403
- else if (boundaryDefault && typeof boundaryDefault === 'object') {
404
- boundaryConfig = boundaryDefault;
405
- }
406
- else if (boundaryMod && typeof boundaryMod === 'object') {
407
- boundaryConfig = boundaryMod;
408
- }
409
- const isExcluded = (reqPath, reqMethod) => {
410
- if (!boundaryConfig || !boundaryConfig.exclude)
411
- return false;
412
- for (const rule of boundaryConfig.exclude) {
413
- if (typeof rule === 'string') {
414
- if (rule.endsWith('/*')) {
415
- if (reqPath.startsWith(rule.slice(0, -2)))
416
- return true;
417
- }
418
- else if (reqPath === rule)
419
- return true;
420
- }
421
- else {
422
- const pathMatch = rule.path.endsWith('/*')
423
- ? reqPath.startsWith(rule.path.slice(0, -2))
424
- : reqPath === rule.path;
425
- if (pathMatch) {
426
- const methods = rule.methods || (rule.method ? [rule.method] : undefined);
427
- if (!methods || methods.includes(reqMethod))
428
- return true;
429
- }
430
- }
431
- }
432
- return false;
433
- };
434
- // 1. Auto-detect named middleware functions: (req, res, next)
435
- if (boundaryMod && typeof boundaryMod === 'object') {
436
- for (const [exportKey, exportVal] of Object.entries(boundaryMod)) {
437
- if (exportKey !== 'default' &&
438
- exportKey !== 'config' &&
439
- exportKey !== 'beforeHandle' &&
440
- exportKey !== 'afterHandle' &&
441
- typeof exportVal === 'function' &&
442
- exportVal.length >= 3 // (req, res, next)
443
- ) {
444
- const namedMiddleware = (req, res, next) => isExcluded(req.path, req.method)
445
- ? next()
446
- : exportVal(req, res, next);
447
- allMiddlewares.push(namedMiddleware);
448
- }
449
- }
450
- if (typeof boundaryMod.beforeHandle === 'function' &&
451
- !boundaryConfig?.beforeHandle) {
452
- if (!boundaryConfig)
453
- boundaryConfig = {};
454
- boundaryConfig.beforeHandle = boundaryMod.beforeHandle;
455
- }
456
- if (typeof boundaryMod.afterHandle === 'function' &&
457
- !boundaryConfig?.afterHandle) {
458
- if (!boundaryConfig)
459
- boundaryConfig = {};
460
- boundaryConfig.afterHandle = boundaryMod.afterHandle;
461
- }
462
- }
463
- // 2. Auto-detect pipeline wrapper function: export default async function(ctx, next)
464
- // or class method named `handle(ctx, next)`
465
- let wrapperFn = null;
466
- if (boundaryDefault &&
467
- typeof boundaryDefault === 'function' &&
468
- boundaryDefault.length === 2 &&
469
- !boundaryDefault.prototype?.[BOUNDARY_CONFIG]) {
470
- wrapperFn = boundaryDefault;
471
- }
472
- else if (boundaryDefault &&
473
- typeof boundaryDefault === 'function' &&
474
- boundaryDefault.prototype &&
475
- boundaryDefault.prototype[BOUNDARY_CONFIG]) {
476
- const proto = boundaryDefault.prototype;
477
- if (typeof proto.handle === 'function') {
478
- const boundaryInstance = new boundaryDefault();
479
- wrapperFn = proto.handle.bind(boundaryInstance);
480
- }
481
- // Also inspect method middlewares on class boundary
482
- for (const prop of Object.getOwnPropertyNames(proto)) {
483
- if (prop !== 'constructor' &&
484
- prop !== 'handle' &&
485
- prop !== 'beforeHandle' &&
486
- prop !== 'afterHandle') {
487
- const methodVal = proto[prop];
488
- if (typeof methodVal === 'function' && methodVal.length >= 3) {
489
- const boundaryInstance = new boundaryDefault();
490
- const namedMiddleware = (req, res, next) => isExcluded(req.path, req.method)
491
- ? next()
492
- : methodVal.call(boundaryInstance, req, res, next);
493
- allMiddlewares.push(namedMiddleware);
494
- }
495
- }
496
- }
497
- if (typeof proto.beforeHandle === 'function' &&
498
- !boundaryConfig?.beforeHandle) {
499
- const boundaryInstance = new boundaryDefault();
500
- if (!boundaryConfig)
501
- boundaryConfig = {};
502
- boundaryConfig.beforeHandle =
503
- proto.beforeHandle.bind(boundaryInstance);
504
- }
505
- if (typeof proto.afterHandle === 'function' &&
506
- !boundaryConfig?.afterHandle) {
507
- const boundaryInstance = new boundaryDefault();
508
- if (!boundaryConfig)
509
- boundaryConfig = {};
510
- boundaryConfig.afterHandle =
511
- proto.afterHandle.bind(boundaryInstance);
512
- }
513
- }
514
- if (wrapperFn) {
515
- const capturedWrapper = wrapperFn;
516
- const wrapperMiddleware = (req, res, next) => {
517
- if (isExcluded(req.path, req.method))
518
- return next();
519
- let nextCalled = false;
520
- let nextResolve;
521
- let _nextReject;
522
- const nextPromise = new Promise((resolve, reject) => {
523
- nextResolve = resolve;
524
- _nextReject = reject;
525
- });
526
- // Intercept response methods to catch handler return values
527
- const originalJson = res.json.bind(res);
528
- const originalSend = res.send.bind(res);
529
- let handledByRoute = false;
530
- res.json = function (body) {
531
- if (!handledByRoute) {
532
- handledByRoute = true;
533
- nextResolve(body);
534
- }
535
- else {
536
- return originalJson(body);
537
- }
538
- return this;
539
- };
540
- res.send = function (body) {
541
- if (!handledByRoute) {
542
- handledByRoute = true;
543
- nextResolve(body);
544
- }
545
- else {
546
- return originalSend(body);
547
- }
548
- return this;
549
- };
550
- const ctx = {
551
- req,
552
- res,
553
- app: this.app,
554
- resolve: (token) => this.app.resolve(token, req._diCache),
555
- };
556
- capturedWrapper(ctx, async () => {
557
- if (!nextCalled) {
558
- nextCalled = true;
559
- next();
560
- }
561
- return nextPromise;
562
- })
563
- .then((wrapperResult) => {
564
- if (wrapperResult !== undefined && !res.headersSent) {
565
- if (typeof wrapperResult === 'object' &&
566
- wrapperResult !== null) {
567
- originalJson(wrapperResult);
568
- }
569
- else {
570
- originalSend(String(wrapperResult));
571
- }
572
- }
573
- })
574
- .catch((wrapperErr) => {
575
- next(wrapperErr);
576
- });
577
- };
578
- allMiddlewares.push(wrapperMiddleware);
579
- }
580
- if (boundaryConfig) {
581
- const bMiddleware = boundaryConfig.middleware || boundaryConfig.middlewares;
582
- if (bMiddleware) {
583
- const mList = Array.isArray(bMiddleware)
584
- ? bMiddleware
585
- : [bMiddleware];
586
- const wrapped = mList.map((m) => (req, res, next) => isExcluded(req.path, req.method) ? next() : m(req, res, next));
587
- allMiddlewares.push(...wrapped);
588
- }
589
- if (boundaryConfig.filters) {
590
- const wrapped = boundaryConfig.filters.map((f) => (err, req, res, next) => isExcluded(req.path, req.method)
591
- ? next(err)
592
- : f.prototype && f.prototype.catch
593
- ? new f().catch(err, req, res, next)
594
- : f(err, req, res, next));
595
- allFilters.push(...wrapped);
596
- }
597
- if (boundaryConfig.guards) {
598
- const wrapped = boundaryConfig.guards.map((g) => {
599
- return async (req) => {
600
- if (isExcluded(req.path, req.method))
601
- return true;
602
- return typeof g === 'function'
603
- ? g.prototype?.canActivate
604
- ? new g().canActivate(req)
605
- : g(req)
606
- : g.canActivate(req);
607
- };
608
- });
609
- allGuards.push(...wrapped);
610
- }
611
- if (boundaryConfig.interceptors) {
612
- const wrapped = boundaryConfig.interceptors.map((i) => {
613
- return async (req, res) => {
614
- if (isExcluded(req.path, req.method))
615
- return;
616
- return typeof i === 'function'
617
- ? i.prototype?.intercept
618
- ? new i().intercept(req, res)
619
- : i(req, res)
620
- : i.intercept(req, res);
621
- };
622
- });
623
- allInterceptors.push(...wrapped);
624
- }
625
- if (boundaryConfig.beforeHandle || boundaryConfig.afterHandle) {
626
- const beforeHooks = (Array.isArray(boundaryConfig.beforeHandle)
627
- ? boundaryConfig.beforeHandle
628
- : boundaryConfig.beforeHandle
629
- ? [boundaryConfig.beforeHandle]
630
- : []);
631
- const afterHooks = (Array.isArray(boundaryConfig.afterHandle)
632
- ? boundaryConfig.afterHandle
633
- : boundaryConfig.afterHandle
634
- ? [boundaryConfig.afterHandle]
635
- : []);
636
- const hookMiddleware = async (req, res, next) => {
637
- if (isExcluded(req.path, req.method))
638
- return next();
639
- for (const hook of beforeHooks) {
640
- try {
641
- const result = await hook(req, res);
642
- if (result === false) {
643
- if (!res.headersSent) {
644
- res.status(403).json({
645
- success: false,
646
- error: {
647
- code: 'FORBIDDEN',
648
- message: 'Forbidden by boundary guard',
649
- },
650
- });
651
- }
652
- return;
653
- }
654
- if (res.headersSent)
655
- return;
656
- }
657
- catch (err) {
658
- return next(err);
659
- }
660
- }
661
- if (afterHooks.length === 0) {
662
- return next();
663
- }
664
- const originalJson = res.json.bind(res);
665
- const originalSend = res.send.bind(res);
666
- let handled = false;
667
- res.json = function (body) {
668
- if (handled)
669
- return originalJson(body);
670
- handled = true;
671
- const runAfterHooks = async () => {
672
- let currentData = body;
673
- for (const afterHook of afterHooks) {
674
- const transformed = await afterHook(req, res, currentData);
675
- if (transformed !== undefined) {
676
- currentData = transformed;
677
- }
678
- if (res.headersSent)
679
- return;
680
- }
681
- originalJson(currentData);
682
- };
683
- runAfterHooks().catch((err) => {
684
- next(err);
685
- });
686
- return this;
687
- };
688
- res.send = function (body) {
689
- if (handled)
690
- return originalSend(body);
691
- handled = true;
692
- const runAfterHooks = async () => {
693
- let currentData = body;
694
- for (const afterHook of afterHooks) {
695
- const transformed = await afterHook(req, res, currentData);
696
- if (transformed !== undefined) {
697
- currentData = transformed;
698
- }
699
- if (res.headersSent)
700
- return;
701
- }
702
- originalSend(currentData);
703
- };
704
- runAfterHooks().catch((err) => {
705
- next(err);
706
- });
707
- return this;
708
- };
709
- next();
710
- };
711
- allMiddlewares.push(hookMiddleware);
712
- }
713
- if (boundaryConfig.timeout !== undefined) {
714
- const baseTimeout = boundaryConfig.timeout;
715
- const timeoutMiddleware = (req, res, next) => {
716
- if (isExcluded(req.path, req.method))
717
- return next();
718
- const ms = typeof baseTimeout === 'function'
719
- ? baseTimeout(req)
720
- : baseTimeout;
721
- if (ms) {
722
- req.timeoutTimer = setTimeout(() => {
723
- if (!res.headersSent) {
724
- res.status(408).json({
725
- success: false,
726
- error: {
727
- code: 'REQUEST_TIMEOUT',
728
- message: 'Request Timeout',
729
- },
730
- });
731
- }
732
- }, ms);
733
- res.raw.on('finish', () => clearTimeout(req.timeoutTimer));
734
- }
735
- next();
736
- };
737
- allMiddlewares.push(timeoutMiddleware);
738
- }
739
- if (boundaryConfig.metadata) {
740
- allMetadata = { ...allMetadata, ...boundaryConfig.metadata };
741
- }
742
- if (boundaryConfig.cors !== undefined)
743
- allCors = boundaryConfig.cors;
744
- if (boundaryConfig.headers)
745
- allHeaders = { ...allHeaders, ...boundaryConfig.headers };
746
- if (boundaryConfig.plugins) {
747
- for (const plugin of boundaryConfig.plugins) {
748
- await this.app.pluginManager.register(plugin);
749
- }
750
- }
751
- if (boundaryConfig.imports) {
752
- for (const mod of boundaryConfig.imports) {
753
- const name = 'plugin' in mod ? mod.plugin.name : mod.name;
754
- if (!this.app.pluginManager.hasPlugin(name)) {
755
- await this.app.pluginManager.register(mod);
756
- }
757
- }
758
- }
759
- if (boundaryConfig.providers) {
760
- for (const [token, providerConfig] of boundaryConfig.providers) {
761
- this.app.container.provide(token, providerConfig);
762
- }
763
- }
764
- }
765
- }
766
- }
767
- catch {
768
- /* ignore */
769
- }
336
+ const { activeBoundaries, boundaryMiddlewares, boundaryFilters, boundaryGuards, boundaryInterceptors, boundaryMetadata, boundaryCors, boundaryHeaders, hasBoundaries, } = await (0, scanner_1.loadActiveBoundaries)(filePath, this.apiDir || path.dirname(filePath), this.app);
337
+ if (hasBoundaries) {
338
+ this.hasBoundaries = true;
770
339
  }
340
+ const allMiddlewares = [...boundaryMiddlewares];
341
+ let allCors = boundaryCors;
342
+ let allHeaders = { ...boundaryHeaders };
771
343
  const currentMod = mod && mod.default && mod.default.default
772
344
  ? mod.default.default
773
345
  : mod && mod.default
@@ -784,10 +356,12 @@ class RouteScanner {
784
356
  else if (allCors === undefined && this.app.options.cors !== undefined)
785
357
  allCors = this.app.options.cors;
786
358
  const rMiddleware = routeConfig.middleware || routeConfig.middlewares;
787
- if (rMiddleware)
359
+ if (rMiddleware) {
788
360
  allMiddlewares.push(...(Array.isArray(rMiddleware) ? rMiddleware : [rMiddleware]));
789
- if (routeConfig.headers)
361
+ }
362
+ if (routeConfig.headers) {
790
363
  allHeaders = { ...allHeaders, ...routeConfig.headers };
364
+ }
791
365
  if (routeConfig.plugins) {
792
366
  for (const plugin of routeConfig.plugins) {
793
367
  await this.app.pluginManager.register(plugin);
@@ -795,7 +369,7 @@ class RouteScanner {
795
369
  }
796
370
  // Pass boundary features to controllers via metadata if necessary
797
371
  const combinedFilters = [
798
- ...allFilters,
372
+ ...boundaryFilters,
799
373
  ...(routeConfig.filters
800
374
  ? Array.isArray(routeConfig.filters)
801
375
  ? routeConfig.filters
@@ -803,7 +377,7 @@ class RouteScanner {
803
377
  : []),
804
378
  ];
805
379
  const combinedGuards = [
806
- ...allGuards,
380
+ ...boundaryGuards,
807
381
  ...(routeConfig.guards
808
382
  ? Array.isArray(routeConfig.guards)
809
383
  ? routeConfig.guards
@@ -811,7 +385,7 @@ class RouteScanner {
811
385
  : []),
812
386
  ];
813
387
  const combinedInterceptors = [
814
- ...allInterceptors,
388
+ ...boundaryInterceptors,
815
389
  ...(routeConfig.interceptors
816
390
  ? Array.isArray(routeConfig.interceptors)
817
391
  ? routeConfig.interceptors
@@ -833,7 +407,7 @@ class RouteScanner {
833
407
  if (!mod.default.metadata)
834
408
  mod.default.metadata = {};
835
409
  Object.assign(mod.default.metadata, {
836
- ...allMetadata,
410
+ ...boundaryMetadata,
837
411
  ...(routeConfig.metadata || {}),
838
412
  });
839
413
  }
@@ -843,13 +417,10 @@ class RouteScanner {
843
417
  }
844
418
  const prefixMiddlewares = [];
845
419
  if (this.app.options.debugRouting) {
846
- const pathLib = await Promise.resolve().then(() => __importStar(require('node:path')));
847
- const relFile = pathLib
848
- .relative(process.cwd(), filePath)
849
- .replace(/\\/g, '/');
420
+ const relFile = path.relative(process.cwd(), filePath).replace(/\\/g, '/');
850
421
  const boundaryStr = activeBoundaries.length > 0
851
422
  ? activeBoundaries
852
- .map((g) => pathLib.relative(process.cwd(), g).replace(/\\/g, '/'))
423
+ .map((g) => path.relative(process.cwd(), g).replace(/\\/g, '/'))
853
424
  .join(' -> ') + ' -> '
854
425
  : '';
855
426
  prefixMiddlewares.push((req, res, next) => {
@@ -912,224 +483,7 @@ class RouteScanner {
912
483
  }
913
484
  }
914
485
  compileFunctionalController(config) {
915
- const router = new router_1.Router();
916
- const fileMiddleware = [];
917
- if (config.cors) {
918
- fileMiddleware.push(config.cors === true ? (0, middleware_1.cors)({}) : (0, middleware_1.cors)(config.cors));
919
- }
920
- const fileMiddlewareConfig = config.middleware || config.middlewares;
921
- if (fileMiddlewareConfig) {
922
- fileMiddleware.push(...(Array.isArray(fileMiddlewareConfig)
923
- ? fileMiddlewareConfig
924
- : [fileMiddlewareConfig]));
925
- }
926
- const { onError, onResponse } = config;
927
- for (const [key, routeConfig] of Object.entries(config)) {
928
- if ([
929
- 'cors',
930
- 'middleware',
931
- 'middlewares',
932
- 'onError',
933
- 'onResponse',
934
- '__isController',
935
- ].includes(key))
936
- continue;
937
- const rc = routeConfig;
938
- if (!rc || !rc.method || !rc.path || !rc.handle)
939
- continue;
940
- const routeMiddlewares = [...fileMiddleware];
941
- if (rc.cors) {
942
- routeMiddlewares.push(rc.cors === true ? (0, middleware_1.cors)({}) : (0, middleware_1.cors)(rc.cors));
943
- }
944
- const rcMiddleware = rc.middleware || rc.middlewares;
945
- if (rcMiddleware) {
946
- routeMiddlewares.push(...(Array.isArray(rcMiddleware) ? rcMiddleware : [rcMiddleware]));
947
- }
948
- // ─── SUPER HANDLER WRAPPER ───
949
- const superHandler = async (req, res, next) => {
950
- if (onResponse) {
951
- res.raw.on('finish', () => onResponse(req, res));
952
- }
953
- try {
954
- // Dynamic Route Timeout Override
955
- const routeTimeoutVal = rc.timeoutMs !== undefined ? rc.timeoutMs : rc.timeout;
956
- if (routeTimeoutVal !== undefined &&
957
- typeof req.setTimeout === 'function') {
958
- const ms = typeof routeTimeoutVal === 'number'
959
- ? routeTimeoutVal
960
- : routeTimeoutVal?.ms;
961
- if (typeof ms === 'number') {
962
- if (ms <= 0)
963
- req.clearTimeout?.();
964
- else
965
- req.setTimeout(ms);
966
- }
967
- }
968
- // 0. Enforce Route Permissions (Role Authorization)
969
- if (rc.permissions && rc.permissions.length > 0) {
970
- if (!req.user) {
971
- res.status(401).json({
972
- success: false,
973
- error: {
974
- code: 'UNAUTHORIZED',
975
- message: 'Unauthorized: User not found on request',
976
- },
977
- });
978
- return;
979
- }
980
- const userPerms = req.user.permissions || req.user.roles || req.user.role || [];
981
- const permsArray = Array.isArray(userPerms)
982
- ? userPerms
983
- : [userPerms];
984
- const hasPerm = rc.permissions.every((p) => permsArray.includes(p));
985
- if (!hasPerm) {
986
- res.status(403).json({
987
- success: false,
988
- error: {
989
- code: 'FORBIDDEN',
990
- message: 'Forbidden: Insufficient permissions',
991
- },
992
- });
993
- return;
994
- }
995
- }
996
- // 1. Run Guards
997
- const routeGuards = [...(config.guards || []), ...(rc.guards || [])];
998
- for (const guard of routeGuards) {
999
- const allowed = await (typeof guard === 'function'
1000
- ? guard.prototype?.canActivate
1001
- ? new guard().canActivate(req)
1002
- : guard(req)
1003
- : guard.canActivate(req));
1004
- if (!allowed) {
1005
- if (res && !res.headersSent) {
1006
- res.status(403).json({
1007
- success: false,
1008
- error: { code: 'FORBIDDEN', message: 'Forbidden by Guard' },
1009
- });
1010
- }
1011
- return;
1012
- }
1013
- }
1014
- // Build Context
1015
- const ctx = {
1016
- body: req.body,
1017
- query: req.query,
1018
- params: req.params,
1019
- headers: req.headers,
1020
- file: req.file,
1021
- files: req.files,
1022
- fields: req.body,
1023
- req,
1024
- res,
1025
- app: this.app,
1026
- resolve: (token) => this.app.resolve(token, req._diCache),
1027
- socket: req.ws,
1028
- state: context_1.executionContext.getStore()?.state || {},
1029
- };
1030
- if (req.user !== undefined)
1031
- ctx.user = req.user;
1032
- if (req.session !== undefined)
1033
- ctx.session = req.session;
1034
- const result = await rc.handle(ctx);
1035
- // 2. Run Interceptors
1036
- const routeInterceptors = [
1037
- ...(config.interceptors || []),
1038
- ...(rc.interceptors || []),
1039
- ];
1040
- for (const interceptor of routeInterceptors) {
1041
- await (typeof interceptor === 'function'
1042
- ? interceptor.prototype?.intercept
1043
- ? new interceptor().intercept(req, res)
1044
- : interceptor(req, res)
1045
- : interceptor.intercept(req, res));
1046
- }
1047
- if (result !== undefined && !res.headersSent) {
1048
- if (typeof result === 'object' && result !== null) {
1049
- res.json(result);
1050
- }
1051
- else {
1052
- res.send(String(result));
1053
- }
1054
- }
1055
- }
1056
- catch (err) {
1057
- if (onError) {
1058
- try {
1059
- await onError(err, req, res);
1060
- }
1061
- catch (hookErr) {
1062
- next(hookErr);
1063
- }
1064
- }
1065
- else {
1066
- next(err); // Hands off to ExisJS global error handler (which handles HttpErrors automatically!)
1067
- }
1068
- }
1069
- };
1070
- // Mount to router
1071
- const method = rc.method.toLowerCase();
1072
- const schema = {};
1073
- const isValidatorOrPipe = (v) => v.parse ||
1074
- v.transform ||
1075
- (typeof v === 'function' && v.prototype?.transform);
1076
- if (rc.body) {
1077
- schema.body = isValidatorOrPipe(rc.body) ? rc.body : index_1.tex.object(rc.body);
1078
- }
1079
- if (rc.query) {
1080
- schema.query = isValidatorOrPipe(rc.query)
1081
- ? rc.query
1082
- : index_1.tex.object(rc.query);
1083
- }
1084
- if (rc.params) {
1085
- schema.params = isValidatorOrPipe(rc.params)
1086
- ? rc.params
1087
- : index_1.tex.object(rc.params);
1088
- }
1089
- if (rc.host) {
1090
- schema.host = rc.host;
1091
- }
1092
- if (rc.timeoutMs !== undefined) {
1093
- schema.timeoutMs = rc.timeoutMs;
1094
- }
1095
- if (rc.timeout !== undefined) {
1096
- schema.timeout = rc.timeout;
1097
- }
1098
- if (rc.upload !== undefined) {
1099
- schema.upload = rc.upload;
1100
- }
1101
- const combinedFilters = [
1102
- ...(config.filters
1103
- ? Array.isArray(config.filters)
1104
- ? config.filters
1105
- : [config.filters]
1106
- : []),
1107
- ...(rc.filters
1108
- ? Array.isArray(rc.filters)
1109
- ? rc.filters
1110
- : [rc.filters]
1111
- : []),
1112
- ];
1113
- if (combinedFilters.length > 0) {
1114
- schema.filters = combinedFilters;
1115
- }
1116
- const combinedMetadata = {
1117
- ...(config.metadata || {}),
1118
- ...(rc.metadata || {}),
1119
- };
1120
- if (Object.keys(combinedMetadata).length > 0) {
1121
- schema.metadata = combinedMetadata;
1122
- }
1123
- if (Object.keys(schema).length > 0) {
1124
- ;
1125
- router[method](rc.path, ...routeMiddlewares, schema, superHandler);
1126
- }
1127
- else {
1128
- ;
1129
- router[method](rc.path, ...routeMiddlewares, superHandler);
1130
- }
1131
- }
1132
- return router;
486
+ return (0, scanner_1.compileFunctionalController)(config, this.app);
1133
487
  }
1134
488
  mountRouteWithSource(prefix, subRouter, sourceFile, prefixMiddlewares = []) {
1135
489
  for (const route of subRouter.getRoutes()) {
@@ -1151,95 +505,20 @@ class RouteScanner {
1151
505
  }
1152
506
  return this;
1153
507
  }
508
+ mountErrorHandlerFromModule(mod, errorFile = 'error.ts') {
509
+ (0, scanner_1.mountErrorHandlerFromModule)(mod, this.app, errorFile);
510
+ }
1154
511
  async mountErrorHandler(dir) {
1155
- const fs = await Promise.resolve().then(() => __importStar(require('node:fs/promises')));
1156
- const path = await Promise.resolve().then(() => __importStar(require('node:path')));
1157
- const candidateFiles = [
1158
- path.join(dir, 'error.ts'),
1159
- path.join(dir, 'error.js'),
1160
- path.join(dir, 'error.mjs'),
1161
- path.join(dir, 'error.cjs'),
1162
- ];
1163
- let errorFile = null;
1164
- for (const file of candidateFiles) {
1165
- try {
1166
- const stat = await fs.stat(file);
1167
- if (stat.isFile()) {
1168
- errorFile = file;
1169
- break;
1170
- }
1171
- }
1172
- catch {
1173
- // ignore
1174
- }
1175
- }
1176
- if (!errorFile)
1177
- return;
1178
- try {
1179
- const url = process.env.VITEST || process.env.NODE_ENV === 'test'
1180
- ? (0, node_url_1.pathToFileURL)(errorFile).href
1181
- : (0, node_url_1.pathToFileURL)(errorFile).href + '?t=' + Date.now();
1182
- let mod;
1183
- if (process.env.VITEST || process.env.NODE_ENV === 'test') {
1184
- mod = await Promise.resolve(`${url}`).then(s => __importStar(require(s)));
1185
- }
1186
- else {
1187
- const dynamicImport = new Function('specifier', 'return import(specifier)');
1188
- mod = await dynamicImport(url);
1189
- }
1190
- const unwrapped = mod && mod.default && mod.default.default
1191
- ? mod.default.default
1192
- : mod && mod.default
1193
- ? mod.default
1194
- : mod;
1195
- const handler = mod.onError ||
1196
- (unwrapped && unwrapped.onError) ||
1197
- (typeof unwrapped === 'function' ? unwrapped : null) ||
1198
- mod.errorHandler ||
1199
- mod.handleError;
1200
- if (typeof handler === 'function') {
1201
- if (handler.length === 4) {
1202
- this.app.use(handler);
1203
- }
1204
- else {
1205
- this.app.onError(handler);
1206
- }
1207
- this.app.log.info({ file: errorFile }, 'Mounted global error handler from error.ts');
1208
- }
1209
- }
1210
- catch (err) {
1211
- this.app.log.error({ err, file: errorFile }, `Failed to load error handler file: ${errorFile}`);
1212
- }
512
+ await (0, scanner_1.mountErrorHandler)(dir, this.app);
1213
513
  }
1214
514
  async scanDirectory(dir, baseRoute = '/') {
1215
- const fs = await Promise.resolve().then(() => __importStar(require('node:fs/promises')));
1216
- const path = await Promise.resolve().then(() => __importStar(require('node:path')));
1217
- const entries = await fs.readdir(dir, { withFileTypes: true });
1218
- const results = [];
1219
- for (const entry of entries) {
1220
- const fullPath = path.join(dir, entry.name);
1221
- if (entry.isDirectory()) {
1222
- let segment = entry.name;
1223
- if (segment.startsWith('(') && segment.endsWith(')')) {
1224
- const subResults = await this.scanDirectory(fullPath, baseRoute);
1225
- results.push(...subResults);
1226
- continue;
1227
- }
1228
- if (segment.startsWith('[...') && segment.endsWith(']')) {
1229
- segment = '*' + segment.slice(4, -1);
1230
- }
1231
- else {
1232
- segment = segment.replace(/\[(.*?)\]/g, ':$1');
1233
- }
1234
- const nextBase = baseRoute === '/' ? `/${segment}` : `${baseRoute}/${segment}`;
1235
- const subResults = await this.scanDirectory(fullPath, nextBase);
1236
- results.push(...subResults);
1237
- }
1238
- else {
1239
- results.push({ filePath: fullPath, routePath: baseRoute });
1240
- }
1241
- }
1242
- return results;
515
+ return (0, scanner_1.scanDirectory)(dir, baseRoute);
516
+ }
517
+ async mountCronJobs(root) {
518
+ await (0, scanner_1.mountCronJobs)(root, this.app, this._allApiDirs);
519
+ }
520
+ mountCronJobsFromEntries(entries) {
521
+ (0, scanner_1.mountCronJobsFromEntries)(entries, this.app);
1243
522
  }
1244
523
  }
1245
524
  exports.RouteScanner = RouteScanner;