exisjs 0.7.6 → 0.7.8

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 (211) 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 +15 -1
  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/index.js +16 -2
  16. package/dist/cli/index.js.map +1 -1
  17. package/dist/cli/manifest.d.ts +1 -0
  18. package/dist/cli/manifest.d.ts.map +1 -1
  19. package/dist/cli/manifest.js +161 -23
  20. package/dist/cli/manifest.js.map +1 -1
  21. package/dist/config/config.d.ts.map +1 -1
  22. package/dist/config/config.js +9 -0
  23. package/dist/config/config.js.map +1 -1
  24. package/dist/config/types.d.ts +4 -0
  25. package/dist/config/types.d.ts.map +1 -1
  26. package/dist/cron/expressions.d.ts +45 -0
  27. package/dist/cron/expressions.d.ts.map +1 -0
  28. package/dist/cron/expressions.js +87 -0
  29. package/dist/cron/expressions.js.map +1 -0
  30. package/dist/cron/index.d.ts +6 -0
  31. package/dist/cron/index.d.ts.map +1 -0
  32. package/dist/cron/index.js +22 -0
  33. package/dist/cron/index.js.map +1 -0
  34. package/dist/cron/job.d.ts +50 -0
  35. package/dist/cron/job.d.ts.map +1 -0
  36. package/dist/cron/job.js +247 -0
  37. package/dist/cron/job.js.map +1 -0
  38. package/dist/cron/manager.d.ts +105 -0
  39. package/dist/cron/manager.d.ts.map +1 -0
  40. package/dist/cron/manager.js +245 -0
  41. package/dist/cron/manager.js.map +1 -0
  42. package/dist/cron/parser.d.ts +22 -0
  43. package/dist/cron/parser.d.ts.map +1 -0
  44. package/dist/cron/parser.js +269 -0
  45. package/dist/cron/parser.js.map +1 -0
  46. package/dist/cron/types.d.ts +76 -0
  47. package/dist/cron/types.d.ts.map +1 -0
  48. package/dist/cron/types.js +3 -0
  49. package/dist/cron/types.js.map +1 -0
  50. package/dist/decorators/constants.d.ts +2 -0
  51. package/dist/decorators/constants.d.ts.map +1 -1
  52. package/dist/decorators/constants.js +3 -1
  53. package/dist/decorators/constants.js.map +1 -1
  54. package/dist/decorators/core.d.ts +29 -11
  55. package/dist/decorators/core.d.ts.map +1 -1
  56. package/dist/decorators/core.js +49 -11
  57. package/dist/decorators/core.js.map +1 -1
  58. package/dist/decorators/cron.d.ts +57 -0
  59. package/dist/decorators/cron.d.ts.map +1 -0
  60. package/dist/decorators/cron.js +143 -0
  61. package/dist/decorators/cron.js.map +1 -0
  62. package/dist/decorators/index.d.ts +2 -0
  63. package/dist/decorators/index.d.ts.map +1 -1
  64. package/dist/decorators/index.js +2 -0
  65. package/dist/decorators/index.js.map +1 -1
  66. package/dist/decorators/params.d.ts +97 -2
  67. package/dist/decorators/params.d.ts.map +1 -1
  68. package/dist/decorators/params.js +94 -2
  69. package/dist/decorators/params.js.map +1 -1
  70. package/dist/decorators/routing.d.ts +81 -16
  71. package/dist/decorators/routing.d.ts.map +1 -1
  72. package/dist/decorators/routing.js +81 -16
  73. package/dist/decorators/routing.js.map +1 -1
  74. package/dist/docs/content/cron.mdx +196 -0
  75. package/dist/docs/content/errors.mdx +58 -9
  76. package/dist/docs/content/structure.mdx +1 -1
  77. package/dist/docs/content/swagger.mdx +289 -0
  78. package/dist/docs/reference/cron.mdx +300 -0
  79. package/dist/docs/reference/decorators.mdx +8 -0
  80. package/dist/docs/reference/error.mdx +44 -0
  81. package/dist/docs/reference/swagger.mdx +226 -0
  82. package/dist/docs/reference/validator.mdx +1 -1
  83. package/dist/router/boundary.d.ts +24 -2
  84. package/dist/router/boundary.d.ts.map +1 -1
  85. package/dist/router/boundary.js +24 -2
  86. package/dist/router/boundary.js.map +1 -1
  87. package/dist/router/controller-registrar.d.ts.map +1 -1
  88. package/dist/router/controller-registrar.js +52 -3
  89. package/dist/router/controller-registrar.js.map +1 -1
  90. package/dist/router/route-builder.d.ts +196 -33
  91. package/dist/router/route-builder.d.ts.map +1 -1
  92. package/dist/router/route-builder.js +171 -11
  93. package/dist/router/route-builder.js.map +1 -1
  94. package/dist/router/route-scanner.d.ts +6 -0
  95. package/dist/router/route-scanner.d.ts.map +1 -1
  96. package/dist/router/route-scanner.js +69 -812
  97. package/dist/router/route-scanner.js.map +1 -1
  98. package/dist/router/router.d.ts.map +1 -1
  99. package/dist/router/router.js +27 -0
  100. package/dist/router/router.js.map +1 -1
  101. package/dist/router/scanner/boundary-loader.d.ts +18 -0
  102. package/dist/router/scanner/boundary-loader.d.ts.map +1 -0
  103. package/dist/router/scanner/boundary-loader.js +539 -0
  104. package/dist/router/scanner/boundary-loader.js.map +1 -0
  105. package/dist/router/scanner/cron-mounter.d.ts +15 -0
  106. package/dist/router/scanner/cron-mounter.d.ts.map +1 -0
  107. package/dist/router/scanner/cron-mounter.js +216 -0
  108. package/dist/router/scanner/cron-mounter.js.map +1 -0
  109. package/dist/router/scanner/error-mounter.d.ts +14 -0
  110. package/dist/router/scanner/error-mounter.d.ts.map +1 -0
  111. package/dist/router/scanner/error-mounter.js +200 -0
  112. package/dist/router/scanner/error-mounter.js.map +1 -0
  113. package/dist/router/scanner/fs-scanner.d.ts +25 -0
  114. package/dist/router/scanner/fs-scanner.d.ts.map +1 -0
  115. package/dist/router/scanner/fs-scanner.js +115 -0
  116. package/dist/router/scanner/fs-scanner.js.map +1 -0
  117. package/dist/router/scanner/functional-compiler.d.ts +9 -0
  118. package/dist/router/scanner/functional-compiler.d.ts.map +1 -0
  119. package/dist/router/scanner/functional-compiler.js +233 -0
  120. package/dist/router/scanner/functional-compiler.js.map +1 -0
  121. package/dist/router/scanner/index.d.ts +9 -0
  122. package/dist/router/scanner/index.d.ts.map +1 -0
  123. package/dist/router/scanner/index.js +19 -0
  124. package/dist/router/scanner/index.js.map +1 -0
  125. package/dist/router/types.d.ts +15 -0
  126. package/dist/router/types.d.ts.map +1 -1
  127. package/dist/server/app.d.ts +2 -0
  128. package/dist/server/app.d.ts.map +1 -1
  129. package/dist/server/app.js +3 -0
  130. package/dist/server/app.js.map +1 -1
  131. package/dist/server/helpers/body-parser.d.ts +18 -0
  132. package/dist/server/helpers/body-parser.d.ts.map +1 -0
  133. package/dist/server/helpers/body-parser.js +246 -0
  134. package/dist/server/helpers/body-parser.js.map +1 -0
  135. package/dist/server/helpers/cookie.d.ts +4 -0
  136. package/dist/server/helpers/cookie.d.ts.map +1 -0
  137. package/dist/server/helpers/cookie.js +61 -0
  138. package/dist/server/helpers/cookie.js.map +1 -0
  139. package/dist/server/helpers/etag.d.ts +3 -0
  140. package/dist/server/helpers/etag.d.ts.map +1 -0
  141. package/dist/server/helpers/etag.js +25 -0
  142. package/dist/server/helpers/etag.js.map +1 -0
  143. package/dist/server/helpers/index.d.ts +6 -0
  144. package/dist/server/helpers/index.d.ts.map +1 -0
  145. package/dist/server/helpers/index.js +21 -0
  146. package/dist/server/helpers/index.js.map +1 -0
  147. package/dist/server/helpers/ip.d.ts +9 -0
  148. package/dist/server/helpers/ip.d.ts.map +1 -0
  149. package/dist/server/helpers/ip.js +62 -0
  150. package/dist/server/helpers/ip.js.map +1 -0
  151. package/dist/server/helpers/stream.d.ts +4 -0
  152. package/dist/server/helpers/stream.d.ts.map +1 -0
  153. package/dist/server/helpers/stream.js +193 -0
  154. package/dist/server/helpers/stream.js.map +1 -0
  155. package/dist/server/request.d.ts +1 -139
  156. package/dist/server/request.d.ts.map +1 -1
  157. package/dist/server/request.js +19 -433
  158. package/dist/server/request.js.map +1 -1
  159. package/dist/server/response.d.ts +105 -158
  160. package/dist/server/response.d.ts.map +1 -1
  161. package/dist/server/response.js +121 -423
  162. package/dist/server/response.js.map +1 -1
  163. package/dist/swagger/decorators.d.ts +81 -0
  164. package/dist/swagger/decorators.d.ts.map +1 -0
  165. package/dist/swagger/decorators.js +172 -0
  166. package/dist/swagger/decorators.js.map +1 -0
  167. package/dist/swagger/generator.d.ts +19 -0
  168. package/dist/swagger/generator.d.ts.map +1 -0
  169. package/dist/swagger/generator.js +344 -0
  170. package/dist/swagger/generator.js.map +1 -0
  171. package/dist/swagger/index.d.ts +31 -0
  172. package/dist/swagger/index.d.ts.map +1 -0
  173. package/dist/swagger/index.js +53 -0
  174. package/dist/swagger/index.js.map +1 -0
  175. package/dist/swagger/mounter.d.ts +7 -0
  176. package/dist/swagger/mounter.d.ts.map +1 -0
  177. package/dist/swagger/mounter.js +56 -0
  178. package/dist/swagger/mounter.js.map +1 -0
  179. package/dist/swagger/types.d.ts +190 -0
  180. package/dist/swagger/types.d.ts.map +1 -0
  181. package/dist/swagger/types.js +3 -0
  182. package/dist/swagger/types.js.map +1 -0
  183. package/dist/swagger/ui.d.ts +6 -0
  184. package/dist/swagger/ui.d.ts.map +1 -0
  185. package/dist/swagger/ui.js +118 -0
  186. package/dist/swagger/ui.js.map +1 -0
  187. package/dist/validator/builder.d.ts +223 -0
  188. package/dist/validator/builder.d.ts.map +1 -0
  189. package/dist/validator/builder.js +517 -0
  190. package/dist/validator/builder.js.map +1 -0
  191. package/dist/validator/engine.d.ts +19 -0
  192. package/dist/validator/engine.d.ts.map +1 -0
  193. package/dist/validator/engine.js +568 -0
  194. package/dist/validator/engine.js.map +1 -0
  195. package/dist/validator/error.d.ts +12 -0
  196. package/dist/validator/error.d.ts.map +1 -0
  197. package/dist/validator/error.js +63 -0
  198. package/dist/validator/error.js.map +1 -0
  199. package/dist/validator/pagination.d.ts +40 -0
  200. package/dist/validator/pagination.d.ts.map +1 -0
  201. package/dist/validator/pagination.js +60 -0
  202. package/dist/validator/pagination.js.map +1 -0
  203. package/dist/validator/tex.d.ts +6 -158
  204. package/dist/validator/tex.d.ts.map +1 -1
  205. package/dist/validator/tex.js +14 -971
  206. package/dist/validator/tex.js.map +1 -1
  207. package/dist/validator/types.d.ts +83 -0
  208. package/dist/validator/types.d.ts.map +1 -0
  209. package/dist/validator/types.js +3 -0
  210. package/dist/validator/types.js.map +1 -0
  211. package/package.json +9 -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
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) {
@@ -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,483 +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
- if (boundaryConfig &&
410
- boundaryConfig.providers &&
411
- Array.isArray(boundaryConfig.providers)) {
412
- for (const p of boundaryConfig.providers) {
413
- if (Array.isArray(p)) {
414
- this.app.container.provide(p[0], p[1]);
415
- }
416
- else {
417
- this.app.container.provide(p, p);
418
- }
419
- }
420
- }
421
- const isExcluded = (reqPath, reqMethod) => {
422
- if (!boundaryConfig || !boundaryConfig.exclude)
423
- return false;
424
- for (const rule of boundaryConfig.exclude) {
425
- if (typeof rule === 'string') {
426
- if (rule.endsWith('/*')) {
427
- if (reqPath.startsWith(rule.slice(0, -2)))
428
- return true;
429
- }
430
- else if (reqPath === rule)
431
- return true;
432
- }
433
- else {
434
- const pathMatch = rule.path.endsWith('/*')
435
- ? reqPath.startsWith(rule.path.slice(0, -2))
436
- : reqPath === rule.path;
437
- if (pathMatch) {
438
- const methods = rule.methods || (rule.method ? [rule.method] : undefined);
439
- if (!methods || methods.includes(reqMethod))
440
- return true;
441
- }
442
- }
443
- }
444
- return false;
445
- };
446
- // 1. Auto-detect named middleware functions: (req, res, next)
447
- if (boundaryMod && typeof boundaryMod === 'object') {
448
- for (const [exportKey, exportVal] of Object.entries(boundaryMod)) {
449
- if (exportKey !== 'default' &&
450
- exportKey !== 'config' &&
451
- exportKey !== 'beforeHandle' &&
452
- exportKey !== 'afterHandle' &&
453
- typeof exportVal === 'function' &&
454
- exportVal.length >= 3 // (req, res, next)
455
- ) {
456
- const namedMiddleware = (req, res, next) => isExcluded(req.path, req.method)
457
- ? next()
458
- : exportVal(req, res, next);
459
- allMiddlewares.push(namedMiddleware);
460
- }
461
- }
462
- if (typeof boundaryMod.beforeHandle === 'function' &&
463
- !boundaryConfig?.beforeHandle) {
464
- if (!boundaryConfig)
465
- boundaryConfig = {};
466
- boundaryConfig.beforeHandle = boundaryMod.beforeHandle;
467
- }
468
- if (typeof boundaryMod.afterHandle === 'function' &&
469
- !boundaryConfig?.afterHandle) {
470
- if (!boundaryConfig)
471
- boundaryConfig = {};
472
- boundaryConfig.afterHandle = boundaryMod.afterHandle;
473
- }
474
- }
475
- // 2. Auto-detect pipeline wrapper function: export default async function(ctx, next)
476
- // or class method named `handle(ctx, next)`
477
- let wrapperFn = null;
478
- if (boundaryDefault &&
479
- typeof boundaryDefault === 'function' &&
480
- boundaryDefault.length === 2 &&
481
- !boundaryDefault.prototype?.[BOUNDARY_CONFIG]) {
482
- wrapperFn = boundaryDefault;
483
- }
484
- else if (boundaryDefault &&
485
- typeof boundaryDefault === 'function' &&
486
- boundaryDefault.prototype &&
487
- boundaryDefault.prototype[BOUNDARY_CONFIG]) {
488
- const proto = boundaryDefault.prototype;
489
- if (typeof proto.handle === 'function') {
490
- const boundaryInstance = new boundaryDefault();
491
- wrapperFn = proto.handle.bind(boundaryInstance);
492
- }
493
- // Also inspect method middlewares on class boundary
494
- for (const prop of Object.getOwnPropertyNames(proto)) {
495
- if (prop !== 'constructor' &&
496
- prop !== 'handle' &&
497
- prop !== 'beforeHandle' &&
498
- prop !== 'afterHandle') {
499
- const methodVal = proto[prop];
500
- if (typeof methodVal === 'function' && methodVal.length >= 3) {
501
- const boundaryInstance = new boundaryDefault();
502
- const namedMiddleware = (req, res, next) => isExcluded(req.path, req.method)
503
- ? next()
504
- : methodVal.call(boundaryInstance, req, res, next);
505
- allMiddlewares.push(namedMiddleware);
506
- }
507
- }
508
- }
509
- if (typeof proto.beforeHandle === 'function' &&
510
- !boundaryConfig?.beforeHandle) {
511
- const boundaryInstance = new boundaryDefault();
512
- if (!boundaryConfig)
513
- boundaryConfig = {};
514
- boundaryConfig.beforeHandle =
515
- proto.beforeHandle.bind(boundaryInstance);
516
- }
517
- if (typeof proto.afterHandle === 'function' &&
518
- !boundaryConfig?.afterHandle) {
519
- const boundaryInstance = new boundaryDefault();
520
- if (!boundaryConfig)
521
- boundaryConfig = {};
522
- boundaryConfig.afterHandle =
523
- proto.afterHandle.bind(boundaryInstance);
524
- }
525
- }
526
- if (wrapperFn) {
527
- const capturedWrapper = wrapperFn;
528
- const wrapperMiddleware = (req, res, next) => {
529
- if (isExcluded(req.path, req.method))
530
- return next();
531
- let nextCalled = false;
532
- let nextResolve;
533
- let _nextReject;
534
- const nextPromise = new Promise((resolve, reject) => {
535
- nextResolve = resolve;
536
- _nextReject = reject;
537
- });
538
- // Intercept response methods to catch handler return values
539
- const originalJson = res.json.bind(res);
540
- const originalSend = res.send.bind(res);
541
- let handledByRoute = false;
542
- res.json = function (body) {
543
- if (!handledByRoute) {
544
- handledByRoute = true;
545
- nextResolve(body);
546
- }
547
- else {
548
- return originalJson(body);
549
- }
550
- return this;
551
- };
552
- res.send = function (body) {
553
- if (!handledByRoute) {
554
- handledByRoute = true;
555
- nextResolve(body);
556
- }
557
- else {
558
- return originalSend(body);
559
- }
560
- return this;
561
- };
562
- const ctx = {
563
- req,
564
- res,
565
- app: this.app,
566
- resolve: (token) => this.app.resolve(token, req._diCache),
567
- };
568
- capturedWrapper(ctx, async () => {
569
- if (!nextCalled) {
570
- nextCalled = true;
571
- next();
572
- }
573
- return nextPromise;
574
- })
575
- .then((wrapperResult) => {
576
- if (wrapperResult !== undefined && !res.headersSent) {
577
- if (typeof wrapperResult === 'object' &&
578
- wrapperResult !== null) {
579
- originalJson(wrapperResult);
580
- }
581
- else {
582
- originalSend(String(wrapperResult));
583
- }
584
- }
585
- })
586
- .catch((wrapperErr) => {
587
- next(wrapperErr);
588
- });
589
- };
590
- allMiddlewares.push(wrapperMiddleware);
591
- }
592
- if (boundaryConfig) {
593
- if (boundaryConfig.blockProbes ||
594
- boundaryConfig.blockSuspiciousProbes) {
595
- const probeOpts = typeof boundaryConfig.blockProbes === 'object'
596
- ? boundaryConfig.blockProbes
597
- : typeof boundaryConfig.blockSuspiciousProbes === 'object'
598
- ? boundaryConfig.blockSuspiciousProbes
599
- : {};
600
- const { blockSuspiciousProbes } = await Promise.resolve().then(() => __importStar(require('../middleware/security')));
601
- allMiddlewares.push(blockSuspiciousProbes(probeOpts));
602
- }
603
- const bMiddleware = boundaryConfig.middleware || boundaryConfig.middlewares;
604
- if (bMiddleware) {
605
- const mList = Array.isArray(bMiddleware)
606
- ? bMiddleware
607
- : [bMiddleware];
608
- const wrapped = mList.map((m) => (req, res, next) => isExcluded(req.path, req.method) ? next() : m(req, res, next));
609
- allMiddlewares.push(...wrapped);
610
- }
611
- if (boundaryConfig.filters) {
612
- const wrapped = boundaryConfig.filters.map((f) => (err, req, res, next) => isExcluded(req.path, req.method)
613
- ? next(err)
614
- : f.prototype && f.prototype.catch
615
- ? new f().catch(err, req, res, next)
616
- : f(err, req, res, next));
617
- allFilters.push(...wrapped);
618
- }
619
- if (boundaryConfig.guards) {
620
- const wrapped = boundaryConfig.guards.map((g) => {
621
- return async (req) => {
622
- if (isExcluded(req.path, req.method))
623
- return true;
624
- return typeof g === 'function'
625
- ? g.prototype?.canActivate
626
- ? new g().canActivate(req)
627
- : g(req)
628
- : g.canActivate(req);
629
- };
630
- });
631
- allGuards.push(...wrapped);
632
- }
633
- if (boundaryConfig.interceptors) {
634
- const wrapped = boundaryConfig.interceptors.map((i) => {
635
- return async (req, res) => {
636
- if (isExcluded(req.path, req.method))
637
- return;
638
- return typeof i === 'function'
639
- ? i.prototype?.intercept
640
- ? new i().intercept(req, res)
641
- : i(req, res)
642
- : i.intercept(req, res);
643
- };
644
- });
645
- allInterceptors.push(...wrapped);
646
- }
647
- if (boundaryConfig.beforeHandle || boundaryConfig.afterHandle) {
648
- const beforeHooks = (Array.isArray(boundaryConfig.beforeHandle)
649
- ? boundaryConfig.beforeHandle
650
- : boundaryConfig.beforeHandle
651
- ? [boundaryConfig.beforeHandle]
652
- : []);
653
- const afterHooks = (Array.isArray(boundaryConfig.afterHandle)
654
- ? boundaryConfig.afterHandle
655
- : boundaryConfig.afterHandle
656
- ? [boundaryConfig.afterHandle]
657
- : []);
658
- const hookMiddleware = async (req, res, next) => {
659
- if (isExcluded(req.path, req.method))
660
- return next();
661
- for (const hook of beforeHooks) {
662
- try {
663
- const result = await hook(req, res);
664
- if (result === false) {
665
- if (!res.headersSent) {
666
- res.status(403).json({
667
- success: false,
668
- error: {
669
- code: 'FORBIDDEN',
670
- message: 'Forbidden by boundary guard',
671
- },
672
- });
673
- }
674
- return;
675
- }
676
- if (res.headersSent)
677
- return;
678
- }
679
- catch (err) {
680
- return next(err);
681
- }
682
- }
683
- if (afterHooks.length === 0) {
684
- return next();
685
- }
686
- const originalJson = res.json.bind(res);
687
- const originalSend = res.send.bind(res);
688
- let handled = false;
689
- res.json = function (body) {
690
- if (handled)
691
- return originalJson(body);
692
- handled = true;
693
- const runAfterHooks = async () => {
694
- let currentData = body;
695
- for (const afterHook of afterHooks) {
696
- const transformed = await afterHook(req, res, currentData);
697
- if (transformed !== undefined) {
698
- currentData = transformed;
699
- }
700
- if (res.headersSent)
701
- return;
702
- }
703
- originalJson(currentData);
704
- };
705
- runAfterHooks().catch((err) => {
706
- next(err);
707
- });
708
- return this;
709
- };
710
- res.send = function (body) {
711
- if (handled)
712
- return originalSend(body);
713
- handled = true;
714
- const runAfterHooks = async () => {
715
- let currentData = body;
716
- for (const afterHook of afterHooks) {
717
- const transformed = await afterHook(req, res, currentData);
718
- if (transformed !== undefined) {
719
- currentData = transformed;
720
- }
721
- if (res.headersSent)
722
- return;
723
- }
724
- originalSend(currentData);
725
- };
726
- runAfterHooks().catch((err) => {
727
- next(err);
728
- });
729
- return this;
730
- };
731
- next();
732
- };
733
- allMiddlewares.push(hookMiddleware);
734
- }
735
- if (boundaryConfig.timeout !== undefined) {
736
- const baseTimeout = boundaryConfig.timeout;
737
- const timeoutMiddleware = (req, res, next) => {
738
- if (isExcluded(req.path, req.method))
739
- return next();
740
- const ms = typeof baseTimeout === 'function'
741
- ? baseTimeout(req)
742
- : baseTimeout;
743
- if (ms) {
744
- req.timeoutTimer = setTimeout(() => {
745
- if (!res.headersSent) {
746
- res.status(408).json({
747
- success: false,
748
- error: {
749
- code: 'REQUEST_TIMEOUT',
750
- message: 'Request Timeout',
751
- },
752
- });
753
- }
754
- }, ms);
755
- res.raw.on('finish', () => clearTimeout(req.timeoutTimer));
756
- }
757
- next();
758
- };
759
- allMiddlewares.push(timeoutMiddleware);
760
- }
761
- if (boundaryConfig.metadata) {
762
- allMetadata = { ...allMetadata, ...boundaryConfig.metadata };
763
- }
764
- if (boundaryConfig.cors !== undefined)
765
- allCors = boundaryConfig.cors;
766
- if (boundaryConfig.headers)
767
- allHeaders = { ...allHeaders, ...boundaryConfig.headers };
768
- if (boundaryConfig.plugins) {
769
- for (const plugin of boundaryConfig.plugins) {
770
- await this.app.pluginManager.register(plugin);
771
- }
772
- }
773
- if (boundaryConfig.imports) {
774
- for (const mod of boundaryConfig.imports) {
775
- const name = 'plugin' in mod ? mod.plugin.name : mod.name;
776
- if (!this.app.pluginManager.hasPlugin(name)) {
777
- await this.app.pluginManager.register(mod);
778
- }
779
- }
780
- }
781
- if (boundaryConfig.providers) {
782
- for (const [token, providerConfig] of boundaryConfig.providers) {
783
- this.app.container.provide(token, providerConfig);
784
- }
785
- }
786
- }
787
- }
788
- }
789
- catch {
790
- /* ignore */
791
- }
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;
792
339
  }
340
+ const allMiddlewares = [...boundaryMiddlewares];
341
+ let allCors = boundaryCors;
342
+ let allHeaders = { ...boundaryHeaders };
793
343
  const currentMod = mod && mod.default && mod.default.default
794
344
  ? mod.default.default
795
345
  : mod && mod.default
@@ -806,10 +356,12 @@ class RouteScanner {
806
356
  else if (allCors === undefined && this.app.options.cors !== undefined)
807
357
  allCors = this.app.options.cors;
808
358
  const rMiddleware = routeConfig.middleware || routeConfig.middlewares;
809
- if (rMiddleware)
359
+ if (rMiddleware) {
810
360
  allMiddlewares.push(...(Array.isArray(rMiddleware) ? rMiddleware : [rMiddleware]));
811
- if (routeConfig.headers)
361
+ }
362
+ if (routeConfig.headers) {
812
363
  allHeaders = { ...allHeaders, ...routeConfig.headers };
364
+ }
813
365
  if (routeConfig.plugins) {
814
366
  for (const plugin of routeConfig.plugins) {
815
367
  await this.app.pluginManager.register(plugin);
@@ -817,7 +369,7 @@ class RouteScanner {
817
369
  }
818
370
  // Pass boundary features to controllers via metadata if necessary
819
371
  const combinedFilters = [
820
- ...allFilters,
372
+ ...boundaryFilters,
821
373
  ...(routeConfig.filters
822
374
  ? Array.isArray(routeConfig.filters)
823
375
  ? routeConfig.filters
@@ -825,7 +377,7 @@ class RouteScanner {
825
377
  : []),
826
378
  ];
827
379
  const combinedGuards = [
828
- ...allGuards,
380
+ ...boundaryGuards,
829
381
  ...(routeConfig.guards
830
382
  ? Array.isArray(routeConfig.guards)
831
383
  ? routeConfig.guards
@@ -833,7 +385,7 @@ class RouteScanner {
833
385
  : []),
834
386
  ];
835
387
  const combinedInterceptors = [
836
- ...allInterceptors,
388
+ ...boundaryInterceptors,
837
389
  ...(routeConfig.interceptors
838
390
  ? Array.isArray(routeConfig.interceptors)
839
391
  ? routeConfig.interceptors
@@ -855,7 +407,7 @@ class RouteScanner {
855
407
  if (!mod.default.metadata)
856
408
  mod.default.metadata = {};
857
409
  Object.assign(mod.default.metadata, {
858
- ...allMetadata,
410
+ ...boundaryMetadata,
859
411
  ...(routeConfig.metadata || {}),
860
412
  });
861
413
  }
@@ -865,13 +417,10 @@ class RouteScanner {
865
417
  }
866
418
  const prefixMiddlewares = [];
867
419
  if (this.app.options.debugRouting) {
868
- const pathLib = await Promise.resolve().then(() => __importStar(require('node:path')));
869
- const relFile = pathLib
870
- .relative(process.cwd(), filePath)
871
- .replace(/\\/g, '/');
420
+ const relFile = path.relative(process.cwd(), filePath).replace(/\\/g, '/');
872
421
  const boundaryStr = activeBoundaries.length > 0
873
422
  ? activeBoundaries
874
- .map((g) => pathLib.relative(process.cwd(), g).replace(/\\/g, '/'))
423
+ .map((g) => path.relative(process.cwd(), g).replace(/\\/g, '/'))
875
424
  .join(' -> ') + ' -> '
876
425
  : '';
877
426
  prefixMiddlewares.push((req, res, next) => {
@@ -934,224 +483,7 @@ class RouteScanner {
934
483
  }
935
484
  }
936
485
  compileFunctionalController(config) {
937
- const router = new router_1.Router();
938
- const fileMiddleware = [];
939
- if (config.cors) {
940
- fileMiddleware.push(config.cors === true ? (0, middleware_1.cors)({}) : (0, middleware_1.cors)(config.cors));
941
- }
942
- const fileMiddlewareConfig = config.middleware || config.middlewares;
943
- if (fileMiddlewareConfig) {
944
- fileMiddleware.push(...(Array.isArray(fileMiddlewareConfig)
945
- ? fileMiddlewareConfig
946
- : [fileMiddlewareConfig]));
947
- }
948
- const { onError, onResponse } = config;
949
- for (const [key, routeConfig] of Object.entries(config)) {
950
- if ([
951
- 'cors',
952
- 'middleware',
953
- 'middlewares',
954
- 'onError',
955
- 'onResponse',
956
- '__isController',
957
- ].includes(key))
958
- continue;
959
- const rc = routeConfig;
960
- if (!rc || !rc.method || !rc.path || !rc.handle)
961
- continue;
962
- const routeMiddlewares = [...fileMiddleware];
963
- if (rc.cors) {
964
- routeMiddlewares.push(rc.cors === true ? (0, middleware_1.cors)({}) : (0, middleware_1.cors)(rc.cors));
965
- }
966
- const rcMiddleware = rc.middleware || rc.middlewares;
967
- if (rcMiddleware) {
968
- routeMiddlewares.push(...(Array.isArray(rcMiddleware) ? rcMiddleware : [rcMiddleware]));
969
- }
970
- // ─── SUPER HANDLER WRAPPER ───
971
- const superHandler = async (req, res, next) => {
972
- if (onResponse) {
973
- res.raw.on('finish', () => onResponse(req, res));
974
- }
975
- try {
976
- // Dynamic Route Timeout Override
977
- const routeTimeoutVal = rc.timeoutMs !== undefined ? rc.timeoutMs : rc.timeout;
978
- if (routeTimeoutVal !== undefined &&
979
- typeof req.setTimeout === 'function') {
980
- const ms = typeof routeTimeoutVal === 'number'
981
- ? routeTimeoutVal
982
- : routeTimeoutVal?.ms;
983
- if (typeof ms === 'number') {
984
- if (ms <= 0)
985
- req.clearTimeout?.();
986
- else
987
- req.setTimeout(ms);
988
- }
989
- }
990
- // 0. Enforce Route Permissions (Role Authorization)
991
- if (rc.permissions && rc.permissions.length > 0) {
992
- if (!req.user) {
993
- res.status(401).json({
994
- success: false,
995
- error: {
996
- code: 'UNAUTHORIZED',
997
- message: 'Unauthorized: User not found on request',
998
- },
999
- });
1000
- return;
1001
- }
1002
- const userPerms = req.user.permissions || req.user.roles || req.user.role || [];
1003
- const permsArray = Array.isArray(userPerms)
1004
- ? userPerms
1005
- : [userPerms];
1006
- const hasPerm = rc.permissions.every((p) => permsArray.includes(p));
1007
- if (!hasPerm) {
1008
- res.status(403).json({
1009
- success: false,
1010
- error: {
1011
- code: 'FORBIDDEN',
1012
- message: 'Forbidden: Insufficient permissions',
1013
- },
1014
- });
1015
- return;
1016
- }
1017
- }
1018
- // 1. Run Guards
1019
- const routeGuards = [...(config.guards || []), ...(rc.guards || [])];
1020
- for (const guard of routeGuards) {
1021
- const allowed = await (typeof guard === 'function'
1022
- ? guard.prototype?.canActivate
1023
- ? new guard().canActivate(req)
1024
- : guard(req)
1025
- : guard.canActivate(req));
1026
- if (!allowed) {
1027
- if (res && !res.headersSent) {
1028
- res.status(403).json({
1029
- success: false,
1030
- error: { code: 'FORBIDDEN', message: 'Forbidden by Guard' },
1031
- });
1032
- }
1033
- return;
1034
- }
1035
- }
1036
- // Build Context
1037
- const ctx = {
1038
- body: req.body,
1039
- query: req.query,
1040
- params: req.params,
1041
- headers: req.headers,
1042
- file: req.file,
1043
- files: req.files,
1044
- fields: req.body,
1045
- req,
1046
- res,
1047
- app: this.app,
1048
- resolve: (token) => this.app.resolve(token, req._diCache),
1049
- socket: req.ws,
1050
- state: context_1.executionContext.getStore()?.state || {},
1051
- };
1052
- if (req.user !== undefined)
1053
- ctx.user = req.user;
1054
- if (req.session !== undefined)
1055
- ctx.session = req.session;
1056
- const result = await rc.handle(ctx);
1057
- // 2. Run Interceptors
1058
- const routeInterceptors = [
1059
- ...(config.interceptors || []),
1060
- ...(rc.interceptors || []),
1061
- ];
1062
- for (const interceptor of routeInterceptors) {
1063
- await (typeof interceptor === 'function'
1064
- ? interceptor.prototype?.intercept
1065
- ? new interceptor().intercept(req, res)
1066
- : interceptor(req, res)
1067
- : interceptor.intercept(req, res));
1068
- }
1069
- if (result !== undefined && !res.headersSent) {
1070
- if (typeof result === 'object' && result !== null) {
1071
- res.json(result);
1072
- }
1073
- else {
1074
- res.send(String(result));
1075
- }
1076
- }
1077
- }
1078
- catch (err) {
1079
- if (onError) {
1080
- try {
1081
- await onError(err, req, res);
1082
- }
1083
- catch (hookErr) {
1084
- next(hookErr);
1085
- }
1086
- }
1087
- else {
1088
- next(err); // Hands off to ExisJS global error handler (which handles HttpErrors automatically!)
1089
- }
1090
- }
1091
- };
1092
- // Mount to router
1093
- const method = rc.method.toLowerCase();
1094
- const schema = {};
1095
- const isValidatorOrPipe = (v) => v.parse ||
1096
- v.transform ||
1097
- (typeof v === 'function' && v.prototype?.transform);
1098
- if (rc.body) {
1099
- schema.body = isValidatorOrPipe(rc.body) ? rc.body : index_1.tex.object(rc.body);
1100
- }
1101
- if (rc.query) {
1102
- schema.query = isValidatorOrPipe(rc.query)
1103
- ? rc.query
1104
- : index_1.tex.object(rc.query);
1105
- }
1106
- if (rc.params) {
1107
- schema.params = isValidatorOrPipe(rc.params)
1108
- ? rc.params
1109
- : index_1.tex.object(rc.params);
1110
- }
1111
- if (rc.host) {
1112
- schema.host = rc.host;
1113
- }
1114
- if (rc.timeoutMs !== undefined) {
1115
- schema.timeoutMs = rc.timeoutMs;
1116
- }
1117
- if (rc.timeout !== undefined) {
1118
- schema.timeout = rc.timeout;
1119
- }
1120
- if (rc.upload !== undefined) {
1121
- schema.upload = rc.upload;
1122
- }
1123
- const combinedFilters = [
1124
- ...(config.filters
1125
- ? Array.isArray(config.filters)
1126
- ? config.filters
1127
- : [config.filters]
1128
- : []),
1129
- ...(rc.filters
1130
- ? Array.isArray(rc.filters)
1131
- ? rc.filters
1132
- : [rc.filters]
1133
- : []),
1134
- ];
1135
- if (combinedFilters.length > 0) {
1136
- schema.filters = combinedFilters;
1137
- }
1138
- const combinedMetadata = {
1139
- ...(config.metadata || {}),
1140
- ...(rc.metadata || {}),
1141
- };
1142
- if (Object.keys(combinedMetadata).length > 0) {
1143
- schema.metadata = combinedMetadata;
1144
- }
1145
- if (Object.keys(schema).length > 0) {
1146
- ;
1147
- router[method](rc.path, ...routeMiddlewares, schema, superHandler);
1148
- }
1149
- else {
1150
- ;
1151
- router[method](rc.path, ...routeMiddlewares, superHandler);
1152
- }
1153
- }
1154
- return router;
486
+ return (0, scanner_1.compileFunctionalController)(config, this.app);
1155
487
  }
1156
488
  mountRouteWithSource(prefix, subRouter, sourceFile, prefixMiddlewares = []) {
1157
489
  for (const route of subRouter.getRoutes()) {
@@ -1173,95 +505,20 @@ class RouteScanner {
1173
505
  }
1174
506
  return this;
1175
507
  }
508
+ mountErrorHandlerFromModule(mod, errorFile = 'error.ts') {
509
+ (0, scanner_1.mountErrorHandlerFromModule)(mod, this.app, errorFile);
510
+ }
1176
511
  async mountErrorHandler(dir) {
1177
- const fs = await Promise.resolve().then(() => __importStar(require('node:fs/promises')));
1178
- const path = await Promise.resolve().then(() => __importStar(require('node:path')));
1179
- const candidateFiles = [
1180
- path.join(dir, 'error.ts'),
1181
- path.join(dir, 'error.js'),
1182
- path.join(dir, 'error.mjs'),
1183
- path.join(dir, 'error.cjs'),
1184
- ];
1185
- let errorFile = null;
1186
- for (const file of candidateFiles) {
1187
- try {
1188
- const stat = await fs.stat(file);
1189
- if (stat.isFile()) {
1190
- errorFile = file;
1191
- break;
1192
- }
1193
- }
1194
- catch {
1195
- // ignore
1196
- }
1197
- }
1198
- if (!errorFile)
1199
- return;
1200
- try {
1201
- const url = process.env.VITEST || process.env.NODE_ENV === 'test'
1202
- ? (0, node_url_1.pathToFileURL)(errorFile).href
1203
- : (0, node_url_1.pathToFileURL)(errorFile).href + '?t=' + Date.now();
1204
- let mod;
1205
- if (process.env.VITEST || process.env.NODE_ENV === 'test') {
1206
- mod = await Promise.resolve(`${url}`).then(s => __importStar(require(s)));
1207
- }
1208
- else {
1209
- const dynamicImport = new Function('specifier', 'return import(specifier)');
1210
- mod = await dynamicImport(url);
1211
- }
1212
- const unwrapped = mod && mod.default && mod.default.default
1213
- ? mod.default.default
1214
- : mod && mod.default
1215
- ? mod.default
1216
- : mod;
1217
- const handler = mod.onError ||
1218
- (unwrapped && unwrapped.onError) ||
1219
- (typeof unwrapped === 'function' ? unwrapped : null) ||
1220
- mod.errorHandler ||
1221
- mod.handleError;
1222
- if (typeof handler === 'function') {
1223
- if (handler.length === 4) {
1224
- this.app.use(handler);
1225
- }
1226
- else {
1227
- this.app.onError(handler);
1228
- }
1229
- this.app.log.info({ file: errorFile }, 'Mounted global error handler from error.ts');
1230
- }
1231
- }
1232
- catch (err) {
1233
- this.app.log.error({ err, file: errorFile }, `Failed to load error handler file: ${errorFile}`);
1234
- }
512
+ await (0, scanner_1.mountErrorHandler)(dir, this.app);
1235
513
  }
1236
514
  async scanDirectory(dir, baseRoute = '/') {
1237
- const fs = await Promise.resolve().then(() => __importStar(require('node:fs/promises')));
1238
- const path = await Promise.resolve().then(() => __importStar(require('node:path')));
1239
- const entries = await fs.readdir(dir, { withFileTypes: true });
1240
- const results = [];
1241
- for (const entry of entries) {
1242
- const fullPath = path.join(dir, entry.name);
1243
- if (entry.isDirectory()) {
1244
- let segment = entry.name;
1245
- if (segment.startsWith('(') && segment.endsWith(')')) {
1246
- const subResults = await this.scanDirectory(fullPath, baseRoute);
1247
- results.push(...subResults);
1248
- continue;
1249
- }
1250
- if (segment.startsWith('[...') && segment.endsWith(']')) {
1251
- segment = '*' + segment.slice(4, -1);
1252
- }
1253
- else {
1254
- segment = segment.replace(/\[(.*?)\]/g, ':$1');
1255
- }
1256
- const nextBase = baseRoute === '/' ? `/${segment}` : `${baseRoute}/${segment}`;
1257
- const subResults = await this.scanDirectory(fullPath, nextBase);
1258
- results.push(...subResults);
1259
- }
1260
- else {
1261
- results.push({ filePath: fullPath, routePath: baseRoute });
1262
- }
1263
- }
1264
- 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);
1265
522
  }
1266
523
  }
1267
524
  exports.RouteScanner = RouteScanner;