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.
- package/dist/cli/commands/build.d.ts.map +1 -1
- package/dist/cli/commands/build.js +12 -0
- package/dist/cli/commands/build.js.map +1 -1
- package/dist/cli/commands/clean.d.ts +5 -0
- package/dist/cli/commands/clean.d.ts.map +1 -0
- package/dist/cli/commands/clean.js +30 -0
- package/dist/cli/commands/clean.js.map +1 -0
- package/dist/cli/commands/dev.d.ts.map +1 -1
- package/dist/cli/commands/dev.js +171 -86
- package/dist/cli/commands/dev.js.map +1 -1
- package/dist/cli/commands/generate.d.ts +6 -1
- package/dist/cli/commands/generate.d.ts.map +1 -1
- package/dist/cli/commands/generate.js +57 -2
- package/dist/cli/commands/generate.js.map +1 -1
- package/dist/cli/commands/start.d.ts.map +1 -1
- package/dist/cli/commands/start.js +54 -16
- package/dist/cli/commands/start.js.map +1 -1
- package/dist/cli/index.js +26 -3
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/manifest.d.ts +1 -0
- package/dist/cli/manifest.d.ts.map +1 -1
- package/dist/cli/manifest.js +264 -33
- package/dist/cli/manifest.js.map +1 -1
- package/dist/cli/resolve-aliases.d.ts +1 -1
- package/dist/cli/resolve-aliases.d.ts.map +1 -1
- package/dist/cli/resolve-aliases.js +30 -13
- package/dist/cli/resolve-aliases.js.map +1 -1
- package/dist/config/config.d.ts +3 -1
- package/dist/config/config.d.ts.map +1 -1
- package/dist/config/config.js +9 -0
- package/dist/config/config.js.map +1 -1
- package/dist/config/types.d.ts +6 -0
- package/dist/config/types.d.ts.map +1 -1
- package/dist/cron/expressions.d.ts +45 -0
- package/dist/cron/expressions.d.ts.map +1 -0
- package/dist/cron/expressions.js +87 -0
- package/dist/cron/expressions.js.map +1 -0
- package/dist/cron/index.d.ts +6 -0
- package/dist/cron/index.d.ts.map +1 -0
- package/dist/cron/index.js +22 -0
- package/dist/cron/index.js.map +1 -0
- package/dist/cron/job.d.ts +50 -0
- package/dist/cron/job.d.ts.map +1 -0
- package/dist/cron/job.js +247 -0
- package/dist/cron/job.js.map +1 -0
- package/dist/cron/manager.d.ts +105 -0
- package/dist/cron/manager.d.ts.map +1 -0
- package/dist/cron/manager.js +245 -0
- package/dist/cron/manager.js.map +1 -0
- package/dist/cron/parser.d.ts +22 -0
- package/dist/cron/parser.d.ts.map +1 -0
- package/dist/cron/parser.js +269 -0
- package/dist/cron/parser.js.map +1 -0
- package/dist/cron/types.d.ts +76 -0
- package/dist/cron/types.d.ts.map +1 -0
- package/dist/cron/types.js +3 -0
- package/dist/cron/types.js.map +1 -0
- package/dist/decorators/constants.d.ts +2 -0
- package/dist/decorators/constants.d.ts.map +1 -1
- package/dist/decorators/constants.js +3 -1
- package/dist/decorators/constants.js.map +1 -1
- package/dist/decorators/core.d.ts +29 -11
- package/dist/decorators/core.d.ts.map +1 -1
- package/dist/decorators/core.js +49 -11
- package/dist/decorators/core.js.map +1 -1
- package/dist/decorators/cron.d.ts +57 -0
- package/dist/decorators/cron.d.ts.map +1 -0
- package/dist/decorators/cron.js +143 -0
- package/dist/decorators/cron.js.map +1 -0
- package/dist/decorators/index.d.ts +2 -0
- package/dist/decorators/index.d.ts.map +1 -1
- package/dist/decorators/index.js +2 -0
- package/dist/decorators/index.js.map +1 -1
- package/dist/decorators/middleware.d.ts +65 -2
- package/dist/decorators/middleware.d.ts.map +1 -1
- package/dist/decorators/middleware.js +148 -62
- package/dist/decorators/middleware.js.map +1 -1
- package/dist/decorators/params.d.ts +102 -2
- package/dist/decorators/params.d.ts.map +1 -1
- package/dist/decorators/params.js +105 -3
- package/dist/decorators/params.js.map +1 -1
- package/dist/decorators/response.d.ts.map +1 -1
- package/dist/decorators/response.js +32 -22
- package/dist/decorators/response.js.map +1 -1
- package/dist/decorators/routing.d.ts +81 -16
- package/dist/decorators/routing.d.ts.map +1 -1
- package/dist/decorators/routing.js +81 -16
- package/dist/decorators/routing.js.map +1 -1
- package/dist/di/container.d.ts.map +1 -1
- package/dist/di/container.js +6 -1
- package/dist/di/container.js.map +1 -1
- package/dist/docs/content/cli/commands.mdx +1 -1
- package/dist/docs/content/controllers.mdx +127 -10
- package/dist/docs/content/cron.mdx +196 -0
- package/dist/docs/content/decorators.mdx +22 -4
- package/dist/docs/content/errors.mdx +58 -9
- package/dist/docs/content/introduction.mdx +7 -7
- package/dist/docs/content/middleware.mdx +269 -6
- package/dist/docs/content/requests.mdx +180 -6
- package/dist/docs/content/responses.mdx +283 -32
- package/dist/docs/content/routing.mdx +46 -0
- package/dist/docs/content/swagger.mdx +228 -0
- package/dist/docs/content/validation.mdx +190 -11
- package/dist/docs/reference/context.mdx +11 -0
- package/dist/docs/reference/cron.mdx +300 -0
- package/dist/docs/reference/decorators.mdx +22 -4
- package/dist/docs/reference/error.mdx +88 -0
- package/dist/docs/reference/logger.mdx +175 -43
- package/dist/docs/reference/middleware.mdx +31 -0
- package/dist/docs/reference/router.mdx +22 -0
- package/dist/docs/reference/swagger.mdx +143 -0
- package/dist/docs/reference/validator.mdx +83 -23
- package/dist/error/errors.d.ts.map +1 -1
- package/dist/error/errors.js +95 -43
- package/dist/error/errors.js.map +1 -1
- package/dist/error/index.d.ts +1 -0
- package/dist/error/index.d.ts.map +1 -1
- package/dist/error/index.js +11 -1
- package/dist/error/index.js.map +1 -1
- package/dist/error/overlay.d.ts +42 -0
- package/dist/error/overlay.d.ts.map +1 -0
- package/dist/error/overlay.js +306 -0
- package/dist/error/overlay.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +22 -1
- package/dist/index.js.map +1 -1
- package/dist/lib/start-server.js +33 -7
- package/dist/lib/start-server.js.map +1 -1
- package/dist/logger/index.d.ts +1 -1
- package/dist/logger/index.d.ts.map +1 -1
- package/dist/logger/index.js +9 -1
- package/dist/logger/index.js.map +1 -1
- package/dist/middleware/guard.d.ts +2 -2
- package/dist/middleware/guard.d.ts.map +1 -1
- package/dist/middleware/guard.js.map +1 -1
- package/dist/middleware/ip-filter.d.ts +1 -0
- package/dist/middleware/ip-filter.d.ts.map +1 -1
- package/dist/middleware/ip-filter.js +2 -0
- package/dist/middleware/ip-filter.js.map +1 -1
- package/dist/middleware/middleware.d.ts +2 -2
- package/dist/middleware/middleware.d.ts.map +1 -1
- package/dist/middleware/middleware.js +20 -2
- package/dist/middleware/middleware.js.map +1 -1
- package/dist/middleware/security.d.ts +43 -1
- package/dist/middleware/security.d.ts.map +1 -1
- package/dist/middleware/security.js +101 -0
- package/dist/middleware/security.js.map +1 -1
- package/dist/response/index.d.ts +229 -6
- package/dist/response/index.d.ts.map +1 -1
- package/dist/response/index.js +272 -2
- package/dist/response/index.js.map +1 -1
- package/dist/router/boundary.d.ts +41 -11
- package/dist/router/boundary.d.ts.map +1 -1
- package/dist/router/boundary.js +24 -2
- package/dist/router/boundary.js.map +1 -1
- package/dist/router/controller-registrar.d.ts.map +1 -1
- package/dist/router/controller-registrar.js +108 -4
- package/dist/router/controller-registrar.js.map +1 -1
- package/dist/router/index.d.ts +2 -2
- package/dist/router/index.d.ts.map +1 -1
- package/dist/router/index.js +2 -1
- package/dist/router/index.js.map +1 -1
- package/dist/router/route-builder.d.ts +262 -65
- package/dist/router/route-builder.d.ts.map +1 -1
- package/dist/router/route-builder.js +181 -23
- package/dist/router/route-builder.js.map +1 -1
- package/dist/router/route-scanner.d.ts +6 -0
- package/dist/router/route-scanner.d.ts.map +1 -1
- package/dist/router/route-scanner.js +71 -792
- package/dist/router/route-scanner.js.map +1 -1
- package/dist/router/router.d.ts.map +1 -1
- package/dist/router/router.js +62 -17
- package/dist/router/router.js.map +1 -1
- package/dist/router/scanner/boundary-loader.d.ts +18 -0
- package/dist/router/scanner/boundary-loader.d.ts.map +1 -0
- package/dist/router/scanner/boundary-loader.js +539 -0
- package/dist/router/scanner/boundary-loader.js.map +1 -0
- package/dist/router/scanner/cron-mounter.d.ts +15 -0
- package/dist/router/scanner/cron-mounter.d.ts.map +1 -0
- package/dist/router/scanner/cron-mounter.js +216 -0
- package/dist/router/scanner/cron-mounter.js.map +1 -0
- package/dist/router/scanner/error-mounter.d.ts +14 -0
- package/dist/router/scanner/error-mounter.d.ts.map +1 -0
- package/dist/router/scanner/error-mounter.js +200 -0
- package/dist/router/scanner/error-mounter.js.map +1 -0
- package/dist/router/scanner/fs-scanner.d.ts +25 -0
- package/dist/router/scanner/fs-scanner.d.ts.map +1 -0
- package/dist/router/scanner/fs-scanner.js +115 -0
- package/dist/router/scanner/fs-scanner.js.map +1 -0
- package/dist/router/scanner/functional-compiler.d.ts +9 -0
- package/dist/router/scanner/functional-compiler.d.ts.map +1 -0
- package/dist/router/scanner/functional-compiler.js +233 -0
- package/dist/router/scanner/functional-compiler.js.map +1 -0
- package/dist/router/scanner/index.d.ts +9 -0
- package/dist/router/scanner/index.d.ts.map +1 -0
- package/dist/router/scanner/index.js +19 -0
- package/dist/router/scanner/index.js.map +1 -0
- package/dist/router/types.d.ts +23 -0
- package/dist/router/types.d.ts.map +1 -1
- package/dist/server/app.d.ts +3 -0
- package/dist/server/app.d.ts.map +1 -1
- package/dist/server/app.js +13 -0
- package/dist/server/app.js.map +1 -1
- package/dist/server/bootstrapper.d.ts.map +1 -1
- package/dist/server/bootstrapper.js +91 -30
- package/dist/server/bootstrapper.js.map +1 -1
- package/dist/server/cluster.d.ts.map +1 -1
- package/dist/server/cluster.js +13 -2
- package/dist/server/cluster.js.map +1 -1
- package/dist/server/context.d.ts +8 -0
- package/dist/server/context.d.ts.map +1 -1
- package/dist/server/context.js +50 -6
- package/dist/server/context.js.map +1 -1
- package/dist/server/helpers/body-parser.d.ts +18 -0
- package/dist/server/helpers/body-parser.d.ts.map +1 -0
- package/dist/server/helpers/body-parser.js +246 -0
- package/dist/server/helpers/body-parser.js.map +1 -0
- package/dist/server/helpers/cookie.d.ts +4 -0
- package/dist/server/helpers/cookie.d.ts.map +1 -0
- package/dist/server/helpers/cookie.js +61 -0
- package/dist/server/helpers/cookie.js.map +1 -0
- package/dist/server/helpers/etag.d.ts +3 -0
- package/dist/server/helpers/etag.d.ts.map +1 -0
- package/dist/server/helpers/etag.js +25 -0
- package/dist/server/helpers/etag.js.map +1 -0
- package/dist/server/helpers/index.d.ts +6 -0
- package/dist/server/helpers/index.d.ts.map +1 -0
- package/dist/server/helpers/index.js +21 -0
- package/dist/server/helpers/index.js.map +1 -0
- package/dist/server/helpers/ip.d.ts +9 -0
- package/dist/server/helpers/ip.d.ts.map +1 -0
- package/dist/server/helpers/ip.js +62 -0
- package/dist/server/helpers/ip.js.map +1 -0
- package/dist/server/helpers/stream.d.ts +4 -0
- package/dist/server/helpers/stream.d.ts.map +1 -0
- package/dist/server/helpers/stream.js +193 -0
- package/dist/server/helpers/stream.js.map +1 -0
- package/dist/server/lifecycle.d.ts +22 -0
- package/dist/server/lifecycle.d.ts.map +1 -0
- package/dist/server/lifecycle.js +106 -0
- package/dist/server/lifecycle.js.map +1 -0
- package/dist/server/request-handler.d.ts +1 -0
- package/dist/server/request-handler.d.ts.map +1 -1
- package/dist/server/request-handler.js +27 -17
- package/dist/server/request-handler.js.map +1 -1
- package/dist/server/request.d.ts +1 -139
- package/dist/server/request.d.ts.map +1 -1
- package/dist/server/request.js +37 -433
- package/dist/server/request.js.map +1 -1
- package/dist/server/response.d.ts +112 -156
- package/dist/server/response.d.ts.map +1 -1
- package/dist/server/response.js +129 -279
- package/dist/server/response.js.map +1 -1
- package/dist/swagger/decorators.d.ts +81 -0
- package/dist/swagger/decorators.d.ts.map +1 -0
- package/dist/swagger/decorators.js +172 -0
- package/dist/swagger/decorators.js.map +1 -0
- package/dist/swagger/generator.d.ts +19 -0
- package/dist/swagger/generator.d.ts.map +1 -0
- package/dist/swagger/generator.js +344 -0
- package/dist/swagger/generator.js.map +1 -0
- package/dist/swagger/index.d.ts +31 -0
- package/dist/swagger/index.d.ts.map +1 -0
- package/dist/swagger/index.js +53 -0
- package/dist/swagger/index.js.map +1 -0
- package/dist/swagger/mounter.d.ts +7 -0
- package/dist/swagger/mounter.d.ts.map +1 -0
- package/dist/swagger/mounter.js +56 -0
- package/dist/swagger/mounter.js.map +1 -0
- package/dist/swagger/types.d.ts +190 -0
- package/dist/swagger/types.d.ts.map +1 -0
- package/dist/swagger/types.js +3 -0
- package/dist/swagger/types.js.map +1 -0
- package/dist/swagger/ui.d.ts +6 -0
- package/dist/swagger/ui.d.ts.map +1 -0
- package/dist/swagger/ui.js +118 -0
- package/dist/swagger/ui.js.map +1 -0
- package/dist/testing/index.d.ts.map +1 -1
- package/dist/testing/index.js +6 -1
- package/dist/testing/index.js.map +1 -1
- package/dist/testing/mocks.d.ts.map +1 -1
- package/dist/testing/mocks.js +15 -3
- package/dist/testing/mocks.js.map +1 -1
- package/dist/utils/logger.d.ts +8 -0
- package/dist/utils/logger.d.ts.map +1 -1
- package/dist/utils/logger.js +184 -1
- package/dist/utils/logger.js.map +1 -1
- package/dist/validator/builder.d.ts +223 -0
- package/dist/validator/builder.d.ts.map +1 -0
- package/dist/validator/builder.js +517 -0
- package/dist/validator/builder.js.map +1 -0
- package/dist/validator/engine.d.ts +19 -0
- package/dist/validator/engine.d.ts.map +1 -0
- package/dist/validator/engine.js +568 -0
- package/dist/validator/engine.js.map +1 -0
- package/dist/validator/error.d.ts +12 -0
- package/dist/validator/error.d.ts.map +1 -0
- package/dist/validator/error.js +63 -0
- package/dist/validator/error.js.map +1 -0
- package/dist/validator/pagination.d.ts +40 -0
- package/dist/validator/pagination.d.ts.map +1 -0
- package/dist/validator/pagination.js +60 -0
- package/dist/validator/pagination.js.map +1 -0
- package/dist/validator/tex-types.d.ts +2 -1
- package/dist/validator/tex-types.d.ts.map +1 -1
- package/dist/validator/tex-types.js +2 -1
- package/dist/validator/tex-types.js.map +1 -1
- package/dist/validator/tex.d.ts +6 -142
- package/dist/validator/tex.d.ts.map +1 -1
- package/dist/validator/tex.js +14 -733
- package/dist/validator/tex.js.map +1 -1
- package/dist/validator/types.d.ts +83 -0
- package/dist/validator/types.d.ts.map +1 -0
- package/dist/validator/types.js +3 -0
- package/dist/validator/types.js.map +1 -0
- package/package.json +8 -4
- package/dist/dev/error-overlay.d.ts +0 -3
- package/dist/dev/error-overlay.d.ts.map +0 -1
- package/dist/dev/error-overlay.js +0 -118
- 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
|
|
39
|
+
const path = __importStar(require("node:path"));
|
|
40
40
|
const node_url_1 = require("node:url");
|
|
41
|
-
const
|
|
42
|
-
const
|
|
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 [
|
|
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
|
-
|
|
121
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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,
|
|
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
|
|
317
|
-
|
|
318
|
-
|
|
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
|
-
|
|
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
|
-
...
|
|
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
|
-
...
|
|
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
|
-
...
|
|
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
|
-
...
|
|
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
|
|
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) =>
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
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;
|