@expressots/adapter-express 3.0.0-beta.4.2 → 4.0.0-preview.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +39 -96
- package/lib/CHANGELOG.md +43 -0
- package/lib/README.md +39 -96
- package/lib/cjs/adapter-express/application-express.base.js +3 -1
- package/lib/cjs/adapter-express/application-express.js +1049 -85
- package/lib/cjs/adapter-express/express-utils/conditional-middleware.js +102 -0
- package/lib/cjs/adapter-express/express-utils/constants.js +17 -0
- package/lib/cjs/adapter-express/express-utils/content-negotiation-decorators.js +129 -0
- package/lib/cjs/adapter-express/express-utils/decorators.js +186 -49
- package/lib/cjs/adapter-express/express-utils/exception-filter-decorators.js +11 -0
- package/lib/cjs/adapter-express/express-utils/guard-context-factory.js +84 -0
- package/lib/cjs/adapter-express/express-utils/guard-middleware.js +115 -0
- package/lib/cjs/adapter-express/express-utils/guard-utils.js +18 -0
- package/lib/cjs/adapter-express/express-utils/http-context-store.js +15 -0
- package/lib/cjs/adapter-express/express-utils/http-status-middleware.js +37 -2
- package/lib/cjs/adapter-express/express-utils/index.js +67 -1
- package/lib/cjs/adapter-express/express-utils/interceptor-middleware.js +132 -0
- package/lib/cjs/adapter-express/express-utils/inversify-express-server.js +810 -63
- package/lib/cjs/adapter-express/express-utils/lazy-module-middleware.js +241 -0
- package/lib/cjs/adapter-express/express-utils/middleware-composition.js +95 -0
- package/lib/cjs/adapter-express/express-utils/permission-preloader.middleware.js +48 -0
- package/lib/cjs/adapter-express/express-utils/route-constraints.js +95 -0
- package/lib/cjs/adapter-express/express-utils/scope-extractor.interface.js +2 -0
- package/lib/cjs/adapter-express/express-utils/scope-extractor.js +66 -0
- package/lib/cjs/adapter-express/express-utils/setup-authorization.js +71 -0
- package/lib/cjs/adapter-express/express-utils/setup-event-system.js +113 -0
- package/lib/cjs/adapter-express/express-utils/setup-interceptors.js +103 -0
- package/lib/cjs/adapter-express/express-utils/setup-lazy-loading.js +228 -0
- package/lib/cjs/adapter-express/express-utils/utils.js +30 -12
- package/lib/cjs/adapter-express/express-utils/validation-decorators.js +205 -0
- package/lib/cjs/adapter-express/express-utils/validation-service.js +252 -0
- package/lib/cjs/adapter-express/index.js +7 -5
- package/lib/cjs/adapter-express/micro-api/application-express-micro-route.js +31 -1
- package/lib/cjs/adapter-express/micro-api/application-express-micro.js +11 -37
- package/lib/cjs/adapter-express/micro-api/gateway/circuit-breaker.js +174 -0
- package/lib/cjs/adapter-express/micro-api/gateway/index.js +11 -0
- package/lib/cjs/adapter-express/micro-api/gateway/service-proxy.js +214 -0
- package/lib/cjs/adapter-express/micro-api/index.js +27 -3
- package/lib/cjs/adapter-express/micro-api/micro.js +217 -0
- package/lib/cjs/adapter-express/micro-api/queue/index.js +8 -0
- package/lib/cjs/adapter-express/micro-api/queue/queue.interface.js +2 -0
- package/lib/cjs/adapter-express/micro-api/queue/rabbitmq-consumer.js +255 -0
- package/lib/cjs/adapter-express/micro-api/serverless/aws-lambda.adapter.js +183 -0
- package/lib/cjs/adapter-express/micro-api/serverless/cloudflare.adapter.js +158 -0
- package/lib/cjs/adapter-express/micro-api/serverless/index.js +12 -0
- package/lib/cjs/adapter-express/micro-api/serverless/vercel.adapter.js +102 -0
- package/lib/cjs/adapter-express/micro-api/service-mesh/index.js +10 -0
- package/lib/cjs/adapter-express/micro-api/service-mesh/service-client.js +194 -0
- package/lib/cjs/adapter-express/micro-api/service-mesh/service-discovery.js +261 -0
- package/lib/cjs/adapter-express/middleware/index.js +21 -0
- package/lib/cjs/adapter-express/middleware/request-logging.middleware.js +244 -0
- package/lib/cjs/adapter-express/render/engine.js +15 -15
- package/lib/cjs/adapter-express/render/index.js +5 -0
- package/lib/cjs/adapter-express/studio/index.js +9 -0
- package/lib/cjs/adapter-express/studio/studio-integration.js +214 -0
- package/lib/cjs/index.js +1 -1
- package/lib/cjs/types/adapter-express/application-express.base.d.ts +20 -7
- package/lib/cjs/types/adapter-express/application-express.d.ts +273 -32
- package/lib/cjs/types/adapter-express/express-utils/base-middleware.d.ts +2 -2
- package/lib/cjs/types/adapter-express/express-utils/conditional-middleware.d.ts +97 -0
- package/lib/cjs/types/adapter-express/express-utils/constants.d.ts +13 -0
- package/lib/cjs/types/adapter-express/express-utils/content-negotiation-decorators.d.ts +94 -0
- package/lib/cjs/types/adapter-express/express-utils/decorators.d.ts +54 -6
- package/lib/cjs/types/adapter-express/express-utils/exception-filter-decorators.d.ts +6 -0
- package/lib/cjs/types/adapter-express/express-utils/guard-context-factory.d.ts +17 -0
- package/lib/cjs/types/adapter-express/express-utils/guard-middleware.d.ts +22 -0
- package/lib/cjs/types/adapter-express/express-utils/guard-utils.d.ts +11 -0
- package/lib/cjs/types/adapter-express/express-utils/http-context-store.d.ts +20 -0
- package/lib/cjs/types/adapter-express/express-utils/httpResponseMessage.d.ts +1 -1
- package/lib/cjs/types/adapter-express/express-utils/index.d.ts +30 -2
- package/lib/cjs/types/adapter-express/express-utils/interceptor-middleware.d.ts +40 -0
- package/lib/cjs/types/adapter-express/express-utils/interfaces.d.ts +42 -5
- package/lib/cjs/types/adapter-express/express-utils/inversify-express-server.d.ts +114 -2
- package/lib/cjs/types/adapter-express/express-utils/lazy-module-middleware.d.ts +122 -0
- package/lib/cjs/types/adapter-express/express-utils/middleware-composition.d.ts +85 -0
- package/lib/cjs/types/adapter-express/express-utils/permission-preloader.middleware.d.ts +10 -0
- package/lib/cjs/types/adapter-express/express-utils/route-constraints.d.ts +89 -0
- package/lib/cjs/types/adapter-express/express-utils/scope-extractor.d.ts +21 -0
- package/lib/cjs/types/adapter-express/express-utils/scope-extractor.interface.d.ts +12 -0
- package/lib/cjs/types/adapter-express/express-utils/setup-authorization.d.ts +34 -0
- package/lib/cjs/types/adapter-express/express-utils/setup-event-system.d.ts +118 -0
- package/lib/cjs/types/adapter-express/express-utils/setup-interceptors.d.ts +115 -0
- package/lib/cjs/types/adapter-express/express-utils/setup-lazy-loading.d.ts +123 -0
- package/lib/cjs/types/adapter-express/express-utils/utils.d.ts +17 -2
- package/lib/cjs/types/adapter-express/express-utils/validation-decorators.d.ts +145 -0
- package/lib/cjs/types/adapter-express/express-utils/validation-service.d.ts +88 -0
- package/lib/cjs/types/adapter-express/index.d.ts +6 -4
- package/lib/cjs/types/adapter-express/micro-api/application-express-micro-route.d.ts +25 -14
- package/lib/cjs/types/adapter-express/micro-api/application-express-micro.d.ts +3 -10
- package/lib/cjs/types/adapter-express/micro-api/gateway/circuit-breaker.d.ts +111 -0
- package/lib/cjs/types/adapter-express/micro-api/gateway/index.d.ts +5 -0
- package/lib/cjs/types/adapter-express/micro-api/gateway/service-proxy.d.ts +83 -0
- package/lib/cjs/types/adapter-express/micro-api/index.d.ts +7 -1
- package/lib/cjs/types/adapter-express/micro-api/micro.d.ts +66 -0
- package/lib/cjs/types/adapter-express/micro-api/queue/index.d.ts +5 -0
- package/lib/cjs/types/adapter-express/micro-api/queue/queue.interface.d.ts +60 -0
- package/lib/cjs/types/adapter-express/micro-api/queue/rabbitmq-consumer.d.ts +86 -0
- package/lib/cjs/types/adapter-express/micro-api/serverless/aws-lambda.adapter.d.ts +77 -0
- package/lib/cjs/types/adapter-express/micro-api/serverless/cloudflare.adapter.d.ts +64 -0
- package/lib/cjs/types/adapter-express/micro-api/serverless/index.d.ts +6 -0
- package/lib/cjs/types/adapter-express/micro-api/serverless/vercel.adapter.d.ts +56 -0
- package/lib/cjs/types/adapter-express/micro-api/service-mesh/index.d.ts +5 -0
- package/lib/cjs/types/adapter-express/micro-api/service-mesh/service-client.d.ts +122 -0
- package/lib/cjs/types/adapter-express/micro-api/service-mesh/service-discovery.d.ts +150 -0
- package/lib/cjs/types/adapter-express/middleware/index.d.ts +5 -0
- package/lib/cjs/types/adapter-express/middleware/request-logging.middleware.d.ts +65 -0
- package/lib/cjs/types/adapter-express/render/index.d.ts +1 -0
- package/lib/cjs/types/adapter-express/studio/index.d.ts +1 -0
- package/lib/cjs/types/adapter-express/studio/studio-integration.d.ts +92 -0
- package/lib/cjs/types/index.d.ts +1 -1
- package/lib/esm/adapter-express/application-express.base.js +24 -0
- package/lib/esm/adapter-express/application-express.js +1300 -0
- package/lib/esm/adapter-express/application-express.types.js +1 -0
- package/lib/esm/adapter-express/express-utils/base-middleware.js +19 -0
- package/lib/esm/adapter-express/express-utils/conditional-middleware.js +96 -0
- package/lib/esm/adapter-express/express-utils/constants.js +63 -0
- package/lib/esm/adapter-express/express-utils/content/httpContent.js +6 -0
- package/lib/esm/adapter-express/express-utils/content-negotiation-decorators.js +120 -0
- package/lib/esm/adapter-express/express-utils/decorators.js +575 -0
- package/lib/esm/adapter-express/express-utils/exception-filter-decorators.js +6 -0
- package/lib/esm/adapter-express/express-utils/guard-context-factory.js +83 -0
- package/lib/esm/adapter-express/express-utils/guard-middleware.js +115 -0
- package/lib/esm/adapter-express/express-utils/guard-utils.js +14 -0
- package/lib/esm/adapter-express/express-utils/http-context-store.js +10 -0
- package/lib/esm/adapter-express/express-utils/http-status-middleware.js +116 -0
- package/lib/esm/adapter-express/express-utils/httpResponseMessage.js +29 -0
- package/lib/esm/adapter-express/express-utils/index.js +24 -0
- package/lib/esm/adapter-express/express-utils/interceptor-middleware.js +130 -0
- package/lib/esm/adapter-express/express-utils/interfaces.js +1 -0
- package/lib/esm/adapter-express/express-utils/inversify-express-server.js +1031 -0
- package/lib/esm/adapter-express/express-utils/lazy-module-middleware.js +236 -0
- package/lib/esm/adapter-express/express-utils/middleware-composition.js +89 -0
- package/lib/esm/adapter-express/express-utils/permission-preloader.middleware.js +45 -0
- package/lib/esm/adapter-express/express-utils/resolver-multer.js +30 -0
- package/lib/esm/adapter-express/express-utils/route-constraints.js +91 -0
- package/lib/esm/adapter-express/express-utils/scope-extractor.interface.js +1 -0
- package/lib/esm/adapter-express/express-utils/scope-extractor.js +63 -0
- package/lib/esm/adapter-express/express-utils/setup-authorization.js +68 -0
- package/lib/esm/adapter-express/express-utils/setup-event-system.js +110 -0
- package/lib/esm/adapter-express/express-utils/setup-interceptors.js +100 -0
- package/lib/esm/adapter-express/express-utils/setup-lazy-loading.js +225 -0
- package/lib/esm/adapter-express/express-utils/utils.js +68 -0
- package/lib/esm/adapter-express/express-utils/validation-decorators.js +199 -0
- package/lib/esm/adapter-express/express-utils/validation-service.js +251 -0
- package/lib/esm/adapter-express/index.js +7 -0
- package/lib/esm/adapter-express/micro-api/application-express-micro-container.js +48 -0
- package/lib/esm/adapter-express/micro-api/application-express-micro-route.js +128 -0
- package/lib/esm/adapter-express/micro-api/application-express-micro.js +161 -0
- package/lib/esm/adapter-express/micro-api/gateway/circuit-breaker.js +174 -0
- package/lib/esm/adapter-express/micro-api/gateway/index.js +5 -0
- package/lib/esm/adapter-express/micro-api/gateway/service-proxy.js +210 -0
- package/lib/esm/adapter-express/micro-api/index.js +10 -0
- package/lib/esm/adapter-express/micro-api/micro.js +211 -0
- package/lib/esm/adapter-express/micro-api/queue/index.js +4 -0
- package/lib/esm/adapter-express/micro-api/queue/queue.interface.js +1 -0
- package/lib/esm/adapter-express/micro-api/queue/rabbitmq-consumer.js +229 -0
- package/lib/esm/adapter-express/micro-api/serverless/aws-lambda.adapter.js +180 -0
- package/lib/esm/adapter-express/micro-api/serverless/cloudflare.adapter.js +155 -0
- package/lib/esm/adapter-express/micro-api/serverless/index.js +6 -0
- package/lib/esm/adapter-express/micro-api/serverless/vercel.adapter.js +99 -0
- package/lib/esm/adapter-express/micro-api/service-mesh/index.js +5 -0
- package/lib/esm/adapter-express/micro-api/service-mesh/service-client.js +191 -0
- package/lib/esm/adapter-express/micro-api/service-mesh/service-discovery.js +259 -0
- package/lib/esm/adapter-express/middleware/index.js +5 -0
- package/lib/esm/adapter-express/middleware/request-logging.middleware.js +239 -0
- package/lib/esm/adapter-express/render/constants.js +37 -0
- package/lib/esm/adapter-express/render/engine.js +51 -0
- package/lib/esm/adapter-express/render/index.js +1 -0
- package/lib/esm/adapter-express/render/resolve-render.js +30 -0
- package/lib/esm/adapter-express/studio/index.js +1 -0
- package/lib/esm/adapter-express/studio/studio-integration.js +184 -0
- package/lib/esm/index.mjs +1 -0
- package/lib/esm/package.json +3 -0
- package/lib/esm/types/adapter-express/application-express.base.d.ts +77 -0
- package/lib/esm/types/adapter-express/application-express.d.ts +411 -0
- package/lib/esm/types/adapter-express/application-express.types.d.ts +23 -0
- package/lib/esm/types/adapter-express/express-utils/base-middleware.d.ts +8 -0
- package/lib/esm/types/adapter-express/express-utils/conditional-middleware.d.ts +97 -0
- package/lib/esm/types/adapter-express/express-utils/constants.d.ts +57 -0
- package/lib/esm/types/adapter-express/express-utils/content/httpContent.d.ts +6 -0
- package/lib/esm/types/adapter-express/express-utils/content-negotiation-decorators.d.ts +94 -0
- package/lib/esm/types/adapter-express/express-utils/decorators.d.ts +257 -0
- package/lib/esm/types/adapter-express/express-utils/exception-filter-decorators.d.ts +6 -0
- package/lib/esm/types/adapter-express/express-utils/guard-context-factory.d.ts +17 -0
- package/lib/esm/types/adapter-express/express-utils/guard-middleware.d.ts +22 -0
- package/lib/esm/types/adapter-express/express-utils/guard-utils.d.ts +11 -0
- package/lib/esm/types/adapter-express/express-utils/http-context-store.d.ts +20 -0
- package/lib/esm/types/adapter-express/express-utils/http-status-middleware.d.ts +26 -0
- package/lib/esm/types/adapter-express/express-utils/httpResponseMessage.d.ts +14 -0
- package/lib/esm/types/adapter-express/express-utils/index.d.ts +30 -0
- package/lib/esm/types/adapter-express/express-utils/interceptor-middleware.d.ts +40 -0
- package/lib/esm/types/adapter-express/express-utils/interfaces.d.ts +115 -0
- package/lib/esm/types/adapter-express/express-utils/inversify-express-server.d.ts +172 -0
- package/lib/esm/types/adapter-express/express-utils/lazy-module-middleware.d.ts +122 -0
- package/lib/esm/types/adapter-express/express-utils/middleware-composition.d.ts +85 -0
- package/lib/esm/types/adapter-express/express-utils/permission-preloader.middleware.d.ts +10 -0
- package/lib/esm/types/adapter-express/express-utils/resolver-multer.d.ts +7 -0
- package/lib/esm/types/adapter-express/express-utils/route-constraints.d.ts +89 -0
- package/lib/esm/types/adapter-express/express-utils/scope-extractor.d.ts +21 -0
- package/lib/esm/types/adapter-express/express-utils/scope-extractor.interface.d.ts +12 -0
- package/lib/esm/types/adapter-express/express-utils/setup-authorization.d.ts +34 -0
- package/lib/esm/types/adapter-express/express-utils/setup-event-system.d.ts +118 -0
- package/lib/esm/types/adapter-express/express-utils/setup-interceptors.d.ts +115 -0
- package/lib/esm/types/adapter-express/express-utils/setup-lazy-loading.d.ts +123 -0
- package/lib/esm/types/adapter-express/express-utils/utils.d.ts +24 -0
- package/lib/esm/types/adapter-express/express-utils/validation-decorators.d.ts +145 -0
- package/lib/esm/types/adapter-express/express-utils/validation-service.d.ts +88 -0
- package/lib/esm/types/adapter-express/index.d.ts +7 -0
- package/lib/esm/types/adapter-express/micro-api/application-express-micro-container.d.ts +47 -0
- package/lib/esm/types/adapter-express/micro-api/application-express-micro-route.d.ts +104 -0
- package/lib/esm/types/adapter-express/micro-api/application-express-micro.d.ts +72 -0
- package/lib/esm/types/adapter-express/micro-api/gateway/circuit-breaker.d.ts +111 -0
- package/lib/esm/types/adapter-express/micro-api/gateway/index.d.ts +5 -0
- package/lib/esm/types/adapter-express/micro-api/gateway/service-proxy.d.ts +83 -0
- package/lib/esm/types/adapter-express/micro-api/index.d.ts +7 -0
- package/lib/esm/types/adapter-express/micro-api/micro.d.ts +66 -0
- package/lib/esm/types/adapter-express/micro-api/queue/index.d.ts +5 -0
- package/lib/esm/types/adapter-express/micro-api/queue/queue.interface.d.ts +60 -0
- package/lib/esm/types/adapter-express/micro-api/queue/rabbitmq-consumer.d.ts +86 -0
- package/lib/esm/types/adapter-express/micro-api/serverless/aws-lambda.adapter.d.ts +77 -0
- package/lib/esm/types/adapter-express/micro-api/serverless/cloudflare.adapter.d.ts +64 -0
- package/lib/esm/types/adapter-express/micro-api/serverless/index.d.ts +6 -0
- package/lib/esm/types/adapter-express/micro-api/serverless/vercel.adapter.d.ts +56 -0
- package/lib/esm/types/adapter-express/micro-api/service-mesh/index.d.ts +5 -0
- package/lib/esm/types/adapter-express/micro-api/service-mesh/service-client.d.ts +122 -0
- package/lib/esm/types/adapter-express/micro-api/service-mesh/service-discovery.d.ts +150 -0
- package/lib/esm/types/adapter-express/middleware/index.d.ts +5 -0
- package/lib/esm/types/adapter-express/middleware/request-logging.middleware.d.ts +65 -0
- package/lib/esm/types/adapter-express/render/constants.d.ts +26 -0
- package/lib/esm/types/adapter-express/render/engine.d.ts +20 -0
- package/lib/esm/types/adapter-express/render/index.d.ts +5 -0
- package/lib/esm/types/adapter-express/render/resolve-render.d.ts +7 -0
- package/lib/esm/types/adapter-express/studio/index.d.ts +1 -0
- package/lib/esm/types/adapter-express/studio/studio-integration.d.ts +92 -0
- package/lib/esm/types/index.d.ts +1 -0
- package/lib/package.json +156 -146
- package/package.json +156 -146
- package/lib/cjs/di/di.interfaces.js +0 -10
- package/lib/cjs/types/di/di.interfaces.d.ts +0 -289
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lazy Module Auto-Load Middleware
|
|
3
|
+
*
|
|
4
|
+
* Automatically loads lazy modules when their routes are accessed.
|
|
5
|
+
* This provides seamless lazy loading without 404 errors.
|
|
6
|
+
*
|
|
7
|
+
* @module adapter-express
|
|
8
|
+
*/
|
|
9
|
+
import { Router } from "express";
|
|
10
|
+
// ============================================================================
|
|
11
|
+
// Lazy Module Middleware
|
|
12
|
+
// ============================================================================
|
|
13
|
+
/**
|
|
14
|
+
* Create middleware that auto-loads lazy modules when their routes are accessed.
|
|
15
|
+
*
|
|
16
|
+
* @layer public
|
|
17
|
+
* @audience application-developers
|
|
18
|
+
* @concept lazy-loading
|
|
19
|
+
*
|
|
20
|
+
* UNIQUE: Seamless lazy loading - no 404s! Modules load automatically
|
|
21
|
+
* when their routes are accessed for the first time.
|
|
22
|
+
*
|
|
23
|
+
* @example
|
|
24
|
+
* ```typescript
|
|
25
|
+
* const middleware = createLazyModuleMiddleware({
|
|
26
|
+
* routes: [
|
|
27
|
+
* { prefix: "/admin", moduleName: "AdminModule", loaded: false },
|
|
28
|
+
* { prefix: "/reports", moduleName: "ReportsModule", loaded: false }
|
|
29
|
+
* ],
|
|
30
|
+
* loader: lazyModuleLoader,
|
|
31
|
+
* globalPrefix: "/api"
|
|
32
|
+
* });
|
|
33
|
+
*
|
|
34
|
+
* app.use(middleware);
|
|
35
|
+
* ```
|
|
36
|
+
*
|
|
37
|
+
* @param config - Middleware configuration
|
|
38
|
+
* @returns Express middleware function
|
|
39
|
+
*
|
|
40
|
+
* @public API
|
|
41
|
+
*/
|
|
42
|
+
export function createLazyModuleMiddleware(config) {
|
|
43
|
+
const { routes, loader, globalPrefix = "" } = config;
|
|
44
|
+
// Create a map for O(1) prefix lookup
|
|
45
|
+
const routeMap = new Map();
|
|
46
|
+
for (const route of routes) {
|
|
47
|
+
const fullPrefix = globalPrefix + route.prefix;
|
|
48
|
+
routeMap.set(fullPrefix.toLowerCase(), route);
|
|
49
|
+
}
|
|
50
|
+
// Track modules currently being loaded (to prevent duplicate loads)
|
|
51
|
+
const loadingModules = new Set();
|
|
52
|
+
return async (req, res, next) => {
|
|
53
|
+
const path = req.path.toLowerCase();
|
|
54
|
+
// Find matching route prefix
|
|
55
|
+
let matchedRoute;
|
|
56
|
+
for (const [prefix, route] of routeMap.entries()) {
|
|
57
|
+
if (path.startsWith(prefix)) {
|
|
58
|
+
matchedRoute = route;
|
|
59
|
+
break;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
// No matching lazy module route
|
|
63
|
+
if (!matchedRoute) {
|
|
64
|
+
return next();
|
|
65
|
+
}
|
|
66
|
+
// Already loaded
|
|
67
|
+
if (matchedRoute.loaded || loader.isLoaded(matchedRoute.moduleName)) {
|
|
68
|
+
matchedRoute.loaded = true;
|
|
69
|
+
return next();
|
|
70
|
+
}
|
|
71
|
+
// Currently loading - wait for it
|
|
72
|
+
if (loadingModules.has(matchedRoute.moduleName)) {
|
|
73
|
+
// Poll until loaded (with timeout)
|
|
74
|
+
const maxWait = 30000; // 30 seconds
|
|
75
|
+
const pollInterval = 50;
|
|
76
|
+
let waited = 0;
|
|
77
|
+
while (loadingModules.has(matchedRoute.moduleName) && waited < maxWait) {
|
|
78
|
+
await new Promise((resolve) => setTimeout(resolve, pollInterval));
|
|
79
|
+
waited += pollInterval;
|
|
80
|
+
}
|
|
81
|
+
if (loader.isLoaded(matchedRoute.moduleName)) {
|
|
82
|
+
matchedRoute.loaded = true;
|
|
83
|
+
return next();
|
|
84
|
+
}
|
|
85
|
+
else {
|
|
86
|
+
res.status(503).json({
|
|
87
|
+
error: "Service Unavailable",
|
|
88
|
+
message: `Module '${matchedRoute.moduleName}' is still loading. Please try again.`,
|
|
89
|
+
});
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
// Start loading the module
|
|
94
|
+
loadingModules.add(matchedRoute.moduleName);
|
|
95
|
+
const startTime = Date.now();
|
|
96
|
+
config.onLoadStart?.(matchedRoute.moduleName, req.path);
|
|
97
|
+
try {
|
|
98
|
+
await loader.load(matchedRoute.moduleName);
|
|
99
|
+
matchedRoute.loaded = true;
|
|
100
|
+
const loadTime = Date.now() - startTime;
|
|
101
|
+
config.onLoadComplete?.(matchedRoute.moduleName, loadTime);
|
|
102
|
+
console.log(`[Lazy Loading] Module '${matchedRoute.moduleName}' loaded on-demand in ${loadTime}ms ` +
|
|
103
|
+
`(triggered by ${req.method} ${req.path})`);
|
|
104
|
+
loadingModules.delete(matchedRoute.moduleName);
|
|
105
|
+
// Dynamic route registration: If callbacks are provided, register routes
|
|
106
|
+
// and continue the request without redirecting
|
|
107
|
+
if (config.onRegisterRoutes && config.expressApp) {
|
|
108
|
+
try {
|
|
109
|
+
const moduleRouter = Router();
|
|
110
|
+
config.onRegisterRoutes(matchedRoute.moduleName, moduleRouter);
|
|
111
|
+
// Mount the new routes (Express will pick them up for future requests)
|
|
112
|
+
// For THIS request, we continue to let it fall through
|
|
113
|
+
console.log(`[Lazy Loading] Dynamically registered routes for '${matchedRoute.moduleName}'`);
|
|
114
|
+
}
|
|
115
|
+
catch (registrationError) {
|
|
116
|
+
console.warn(`[Lazy Loading] Dynamic route registration failed for '${matchedRoute.moduleName}':`, registrationError);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
// Continue to next middleware - the routes should now be available
|
|
120
|
+
// This works because Express route matching happens per-request
|
|
121
|
+
// and the container now has the lazy module's controllers bound.
|
|
122
|
+
//
|
|
123
|
+
// If dynamic registration isn't set up, fall back to 307 redirect
|
|
124
|
+
if (config.onRegisterRoutes) {
|
|
125
|
+
// Re-emit the request through Express routing
|
|
126
|
+
// The route should now match a real handler
|
|
127
|
+
return next("route");
|
|
128
|
+
}
|
|
129
|
+
else {
|
|
130
|
+
// Fallback: 307 redirect for single round-trip (temporary redirect preserves method)
|
|
131
|
+
res.redirect(307, req.originalUrl);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
catch (error) {
|
|
135
|
+
loadingModules.delete(matchedRoute.moduleName);
|
|
136
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
137
|
+
config.onLoadError?.(matchedRoute.moduleName, err);
|
|
138
|
+
console.error(`[Lazy Loading] Failed to load module '${matchedRoute.moduleName}':`, err.message);
|
|
139
|
+
res.status(500).json({
|
|
140
|
+
error: "Module Load Failed",
|
|
141
|
+
message: `Failed to load module for route '${req.path}'. ${err.message}`,
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
// ============================================================================
|
|
147
|
+
// Helper Functions
|
|
148
|
+
// ============================================================================
|
|
149
|
+
/**
|
|
150
|
+
* Extract route prefixes from a lazy module.
|
|
151
|
+
*
|
|
152
|
+
* Uses auto-detected routes from CreateLazyModule (which analyzes @controller decorators),
|
|
153
|
+
* falls back to prefetchOn config, and finally infers from module name.
|
|
154
|
+
*
|
|
155
|
+
* @param lazyModule - The lazy module to analyze
|
|
156
|
+
* @returns Detected route prefixes
|
|
157
|
+
*
|
|
158
|
+
* @public API
|
|
159
|
+
*/
|
|
160
|
+
export function extractRoutePrefixes(lazyModule) {
|
|
161
|
+
const prefixes = [];
|
|
162
|
+
// 1. Use auto-detected routePrefixes from CreateLazyModule (best source!)
|
|
163
|
+
if (lazyModule.config.routePrefixes && lazyModule.config.routePrefixes.length > 0) {
|
|
164
|
+
return lazyModule.config.routePrefixes;
|
|
165
|
+
}
|
|
166
|
+
// 2. Try prefetchOn config as fallback
|
|
167
|
+
const prefetchOn = lazyModule.config.prefetchOn || [];
|
|
168
|
+
for (const prefetch of prefetchOn) {
|
|
169
|
+
const parts = prefetch.route.split("/").filter(Boolean);
|
|
170
|
+
if (parts.length > 0) {
|
|
171
|
+
prefixes.push("/" + parts[0]);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
if (prefixes.length > 0) {
|
|
175
|
+
return [...new Set(prefixes)];
|
|
176
|
+
}
|
|
177
|
+
// 3. Last resort: infer from module name
|
|
178
|
+
// "AdminModule" -> "/admin", "UserSettingsModule" -> "/usersettings"
|
|
179
|
+
const inferredPrefix = "/" +
|
|
180
|
+
lazyModule.name
|
|
181
|
+
.replace(/Module$/i, "")
|
|
182
|
+
.replace(/([A-Z])/g, (match, p1, offset) => offset > 0 ? `-${p1.toLowerCase()}` : p1.toLowerCase());
|
|
183
|
+
return [inferredPrefix];
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Create route mappings from lazy modules.
|
|
187
|
+
*
|
|
188
|
+
* Route detection priority:
|
|
189
|
+
* 1. Manual routePrefixMap (if provided)
|
|
190
|
+
* 2. Auto-detected from @controller() decorators (via CreateLazyModule)
|
|
191
|
+
* 3. prefetchOn config
|
|
192
|
+
* 4. Inferred from module name
|
|
193
|
+
*
|
|
194
|
+
* @param lazyModules - Array of lazy modules
|
|
195
|
+
* @param routePrefixMap - Optional manual prefix mappings { moduleName: prefix }
|
|
196
|
+
* @returns Array of route mappings
|
|
197
|
+
*
|
|
198
|
+
* @example
|
|
199
|
+
* ```typescript
|
|
200
|
+
* // Zero-config: routes auto-detected from controllers!
|
|
201
|
+
* const mappings = createRouteMappings([AdminModule, ReportsModule]);
|
|
202
|
+
* // Automatically maps @controller("/admin") -> /admin
|
|
203
|
+
*
|
|
204
|
+
* // Or with manual overrides
|
|
205
|
+
* const mappings = createRouteMappings(
|
|
206
|
+
* [AdminModule, ReportsModule],
|
|
207
|
+
* { "AdminModule": "/admin-panel" } // Override auto-detection
|
|
208
|
+
* );
|
|
209
|
+
* ```
|
|
210
|
+
*
|
|
211
|
+
* @public API
|
|
212
|
+
*/
|
|
213
|
+
export function createRouteMappings(lazyModules, routePrefixMap) {
|
|
214
|
+
const mappings = [];
|
|
215
|
+
for (const module of lazyModules) {
|
|
216
|
+
// Use manual mapping if provided (highest priority)
|
|
217
|
+
if (routePrefixMap && routePrefixMap[module.name]) {
|
|
218
|
+
mappings.push({
|
|
219
|
+
prefix: routePrefixMap[module.name],
|
|
220
|
+
moduleName: module.name,
|
|
221
|
+
loaded: module.isLoaded,
|
|
222
|
+
});
|
|
223
|
+
continue;
|
|
224
|
+
}
|
|
225
|
+
// Use auto-detected prefixes (includes @controller detection)
|
|
226
|
+
const prefixes = extractRoutePrefixes(module);
|
|
227
|
+
for (const prefix of prefixes) {
|
|
228
|
+
mappings.push({
|
|
229
|
+
prefix,
|
|
230
|
+
moduleName: module.name,
|
|
231
|
+
loaded: module.isLoaded,
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
return mappings;
|
|
236
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Symbol to identify composed middleware configuration objects.
|
|
3
|
+
* Used internally for type checking.
|
|
4
|
+
*/
|
|
5
|
+
export const COMPOSED_MIDDLEWARE_SYMBOL = Symbol("ComposedMiddleware");
|
|
6
|
+
/**
|
|
7
|
+
* Type guard to check if an object is a ComposedMiddlewareConfig.
|
|
8
|
+
*/
|
|
9
|
+
export function isComposedMiddleware(item) {
|
|
10
|
+
return (typeof item === "object" &&
|
|
11
|
+
item !== null &&
|
|
12
|
+
COMPOSED_MIDDLEWARE_SYMBOL in item &&
|
|
13
|
+
"middleware" in item &&
|
|
14
|
+
"type" in item &&
|
|
15
|
+
Array.isArray(item.middleware) &&
|
|
16
|
+
(item.type === "combine" ||
|
|
17
|
+
item.type === "sequence"));
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Combines multiple middleware into a single middleware that executes all of them sequentially.
|
|
21
|
+
* All middleware will execute in order. If any middleware calls `next(error)`, the error
|
|
22
|
+
* is propagated to Express's error handling system.
|
|
23
|
+
*
|
|
24
|
+
* @param middleware - Array of middleware to combine
|
|
25
|
+
* @returns ComposedMiddlewareConfig object
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* ```typescript
|
|
29
|
+
* // Combine multiple middleware into a reusable group
|
|
30
|
+
* @Get("/api", combine(AuthMiddleware, LoggingMiddleware, RateLimitMiddleware))
|
|
31
|
+
* async apiHandler() {}
|
|
32
|
+
* ```
|
|
33
|
+
*
|
|
34
|
+
* @example
|
|
35
|
+
* ```typescript
|
|
36
|
+
* // Combine with conditional middleware (Phase 3)
|
|
37
|
+
* @Get("/admin",
|
|
38
|
+
* when(req => req.method === "POST", combine(BodyParser, ValidationMiddleware)),
|
|
39
|
+
* combine(AuthMiddleware, LoggingMiddleware)
|
|
40
|
+
* )
|
|
41
|
+
* async adminHandler() {}
|
|
42
|
+
* ```
|
|
43
|
+
*/
|
|
44
|
+
export function combine(...middleware) {
|
|
45
|
+
if (middleware.length === 0) {
|
|
46
|
+
throw new Error("combine() requires at least one middleware");
|
|
47
|
+
}
|
|
48
|
+
return {
|
|
49
|
+
middleware,
|
|
50
|
+
type: "combine",
|
|
51
|
+
[COMPOSED_MIDDLEWARE_SYMBOL]: true,
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Sequences multiple middleware that execute one after another.
|
|
56
|
+
* Similar to `combine()`, but semantically indicates sequential execution
|
|
57
|
+
* where each middleware depends on the previous one completing successfully.
|
|
58
|
+
* If any middleware calls `next(error)`, execution stops and the error is propagated.
|
|
59
|
+
*
|
|
60
|
+
* @param middleware - Array of middleware to sequence
|
|
61
|
+
* @returns ComposedMiddlewareConfig object
|
|
62
|
+
*
|
|
63
|
+
* @example
|
|
64
|
+
* ```typescript
|
|
65
|
+
* // Sequence middleware where each depends on the previous
|
|
66
|
+
* @Get("/api", sequence(ValidateMiddleware, TransformMiddleware, ProcessMiddleware))
|
|
67
|
+
* async apiHandler() {}
|
|
68
|
+
* ```
|
|
69
|
+
*
|
|
70
|
+
* @example
|
|
71
|
+
* ```typescript
|
|
72
|
+
* // Sequence with conditional middleware
|
|
73
|
+
* @Get("/data",
|
|
74
|
+
* when(req => req.method === "POST", sequence(BodyParser, ValidateMiddleware)),
|
|
75
|
+
* sequence(AuthMiddleware, ProcessMiddleware)
|
|
76
|
+
* )
|
|
77
|
+
* async dataHandler() {}
|
|
78
|
+
* ```
|
|
79
|
+
*/
|
|
80
|
+
export function sequence(...middleware) {
|
|
81
|
+
if (middleware.length === 0) {
|
|
82
|
+
throw new Error("sequence() requires at least one middleware");
|
|
83
|
+
}
|
|
84
|
+
return {
|
|
85
|
+
middleware,
|
|
86
|
+
type: "sequence",
|
|
87
|
+
[COMPOSED_MIDDLEWARE_SYMBOL]: true,
|
|
88
|
+
};
|
|
89
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
8
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
9
|
+
};
|
|
10
|
+
import { inject } from "@expressots/core";
|
|
11
|
+
import { BaseMiddleware } from "./base-middleware.js";
|
|
12
|
+
/**
|
|
13
|
+
* Middleware that preloads permissions for authenticated users
|
|
14
|
+
* Caches permissions in request-scoped SecurityContext
|
|
15
|
+
*/
|
|
16
|
+
export class PermissionPreloaderMiddleware extends BaseMiddleware {
|
|
17
|
+
securityContext;
|
|
18
|
+
async handler(req, res, next) {
|
|
19
|
+
try {
|
|
20
|
+
// Get HttpContext
|
|
21
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
22
|
+
const httpContext = req.__expressotsHttpContext;
|
|
23
|
+
if (!httpContext) {
|
|
24
|
+
return next();
|
|
25
|
+
}
|
|
26
|
+
const principal = httpContext.user;
|
|
27
|
+
// Preload permissions if user is authenticated
|
|
28
|
+
if (principal && (await principal.isAuthenticated())) {
|
|
29
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
30
|
+
const userId = principal.details?.id;
|
|
31
|
+
if (userId && this.securityContext) {
|
|
32
|
+
await this.securityContext.preload(userId);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
next();
|
|
36
|
+
}
|
|
37
|
+
catch (error) {
|
|
38
|
+
next(error);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
__decorate([
|
|
43
|
+
inject("ISecurityContext"),
|
|
44
|
+
__metadata("design:type", Object)
|
|
45
|
+
], PermissionPreloaderMiddleware.prototype, "securityContext", void 0);
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
+
import { Logger } from "@expressots/core";
|
|
3
|
+
/**
|
|
4
|
+
* Resolve package from the current working directory.
|
|
5
|
+
* @param packageName
|
|
6
|
+
* @param options
|
|
7
|
+
* @returns
|
|
8
|
+
*/
|
|
9
|
+
export function packageResolver(packageName) {
|
|
10
|
+
const logger = new Logger();
|
|
11
|
+
try {
|
|
12
|
+
const hasPackage = require.resolve(packageName, {
|
|
13
|
+
paths: [process.cwd()],
|
|
14
|
+
});
|
|
15
|
+
if (hasPackage) {
|
|
16
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
17
|
+
const packageResolved = require(hasPackage);
|
|
18
|
+
if (typeof packageResolved === "function") {
|
|
19
|
+
return packageResolved;
|
|
20
|
+
}
|
|
21
|
+
if (packageResolved.default && typeof packageResolved.default === "function") {
|
|
22
|
+
return packageResolved.default;
|
|
23
|
+
}
|
|
24
|
+
return packageResolved;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
catch (error) {
|
|
28
|
+
logger.warn(`Package [${packageName}] not installed. Please install it using your package manager.`, "package-resolver");
|
|
29
|
+
}
|
|
30
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Route parameter patterns for common use cases.
|
|
3
|
+
* These are Express regex patterns that can be used in route paths.
|
|
4
|
+
*
|
|
5
|
+
* @example
|
|
6
|
+
* ```typescript
|
|
7
|
+
* import { Patterns, pattern } from "@expressots/adapter-express";
|
|
8
|
+
*
|
|
9
|
+
* @Get(`/users/${pattern("id", Patterns.NUMERIC_ID)}`)
|
|
10
|
+
* getUserById(@param("id") id: number) {
|
|
11
|
+
* // Only matches numeric IDs like /users/123
|
|
12
|
+
* }
|
|
13
|
+
*
|
|
14
|
+
* @Get(`/documents/${pattern("uuid", Patterns.UUID)}`)
|
|
15
|
+
* getDocument(@param("uuid") uuid: string) {
|
|
16
|
+
* // Only matches valid UUIDs
|
|
17
|
+
* }
|
|
18
|
+
* ```
|
|
19
|
+
*
|
|
20
|
+
* @public API
|
|
21
|
+
*/
|
|
22
|
+
export const Patterns = {
|
|
23
|
+
/**
|
|
24
|
+
* Matches one or more digits (numeric ID)
|
|
25
|
+
* Example: /users/123 ✅, /users/abc ❌
|
|
26
|
+
*/
|
|
27
|
+
NUMERIC_ID: "(\\d+)",
|
|
28
|
+
/**
|
|
29
|
+
* Matches a valid UUID v4 format
|
|
30
|
+
* Example: /documents/550e8400-e29b-41d4-a716-446655440000 ✅
|
|
31
|
+
*/
|
|
32
|
+
UUID: "([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})",
|
|
33
|
+
/**
|
|
34
|
+
* Matches lowercase letters, numbers, and hyphens (URL-friendly slug)
|
|
35
|
+
* Example: /posts/my-awesome-post ✅, /posts/My_Post ❌
|
|
36
|
+
*/
|
|
37
|
+
SLUG: "([a-z0-9-]+)",
|
|
38
|
+
/**
|
|
39
|
+
* Matches alphanumeric characters (letters and numbers only)
|
|
40
|
+
* Example: /codes/ABC123 ✅, /codes/ABC-123 ❌
|
|
41
|
+
*/
|
|
42
|
+
ALPHANUMERIC: "([a-zA-Z0-9]+)",
|
|
43
|
+
/**
|
|
44
|
+
* Matches lowercase letters only
|
|
45
|
+
* Example: /tags/javascript ✅, /tags/JavaScript ❌
|
|
46
|
+
*/
|
|
47
|
+
LOWERCASE: "([a-z]+)",
|
|
48
|
+
/**
|
|
49
|
+
* Matches uppercase letters only
|
|
50
|
+
* Example: /codes/USD ✅, /codes/usd ❌
|
|
51
|
+
*/
|
|
52
|
+
UPPERCASE: "([A-Z]+)",
|
|
53
|
+
/**
|
|
54
|
+
* Matches email format (basic validation)
|
|
55
|
+
* Example: /users/user@example.com ✅
|
|
56
|
+
*/
|
|
57
|
+
EMAIL: "([a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,})",
|
|
58
|
+
/**
|
|
59
|
+
* Matches hexadecimal string (e.g., color codes, hash)
|
|
60
|
+
* Example: /colors/ff5733 ✅, /colors/xyz ❌
|
|
61
|
+
*/
|
|
62
|
+
HEXADECIMAL: "([0-9a-fA-F]+)",
|
|
63
|
+
/**
|
|
64
|
+
* Matches MongoDB ObjectId format (24 hex characters)
|
|
65
|
+
* Example: /documents/507f1f77bcf86cd799439011 ✅
|
|
66
|
+
*/
|
|
67
|
+
MONGO_ID: "([0-9a-fA-F]{24})",
|
|
68
|
+
};
|
|
69
|
+
/**
|
|
70
|
+
* Helper function to build route parameter patterns.
|
|
71
|
+
* This is optional - you can also use Patterns directly in template strings.
|
|
72
|
+
*
|
|
73
|
+
* @param paramName - The parameter name (e.g., "id", "uuid")
|
|
74
|
+
* @param pattern - The pattern from Patterns
|
|
75
|
+
* @returns The formatted route parameter with pattern
|
|
76
|
+
*
|
|
77
|
+
* @example
|
|
78
|
+
* ```typescript
|
|
79
|
+
* import { pattern, Patterns } from "@expressots/adapter-express";
|
|
80
|
+
*
|
|
81
|
+
* @Get(`/users/${pattern("id", Patterns.NUMERIC_ID)}`)
|
|
82
|
+
* getUserById(@param("id") id: number) {
|
|
83
|
+
* // Route: /users/:id(\\d+)
|
|
84
|
+
* }
|
|
85
|
+
* ```
|
|
86
|
+
*
|
|
87
|
+
* @public API
|
|
88
|
+
*/
|
|
89
|
+
export function pattern(paramName, patternValue) {
|
|
90
|
+
return `:${paramName}(${patternValue})`;
|
|
91
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
import { injectable } from "@expressots/core";
|
|
8
|
+
/**
|
|
9
|
+
* Extracts scope information from requests
|
|
10
|
+
* Supports tenant extraction from subdomain, header, or param
|
|
11
|
+
* Note: This is bound manually in setupAuthorizationForExpress() to allow user overrides
|
|
12
|
+
*/
|
|
13
|
+
let ScopeExtractor = class ScopeExtractor {
|
|
14
|
+
async extract(req) {
|
|
15
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
16
|
+
const sessionId = req.session?.id;
|
|
17
|
+
return {
|
|
18
|
+
tenant: this.extractTenant(req),
|
|
19
|
+
request: this.generateRequestId(req),
|
|
20
|
+
session: sessionId,
|
|
21
|
+
transaction: req.headers["x-transaction-id"],
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Extract tenant ID from multiple sources
|
|
26
|
+
* @private
|
|
27
|
+
*/
|
|
28
|
+
extractTenant(req) {
|
|
29
|
+
// Try subdomain first
|
|
30
|
+
if (req.subdomains && req.subdomains.length > 0) {
|
|
31
|
+
return req.subdomains[0];
|
|
32
|
+
}
|
|
33
|
+
// Try header
|
|
34
|
+
const headerTenant = req.headers["x-tenant-id"];
|
|
35
|
+
if (headerTenant) {
|
|
36
|
+
return headerTenant;
|
|
37
|
+
}
|
|
38
|
+
// Try route parameter
|
|
39
|
+
const paramTenant = req.params.tenantId;
|
|
40
|
+
if (paramTenant) {
|
|
41
|
+
return paramTenant;
|
|
42
|
+
}
|
|
43
|
+
return undefined;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Generate or retrieve request ID
|
|
47
|
+
* @private
|
|
48
|
+
*/
|
|
49
|
+
generateRequestId(req) {
|
|
50
|
+
// Try to get existing request ID (if set by middleware)
|
|
51
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
52
|
+
const existingId = req.id;
|
|
53
|
+
if (existingId) {
|
|
54
|
+
return existingId;
|
|
55
|
+
}
|
|
56
|
+
// Generate new ID
|
|
57
|
+
return `req-${Date.now()}-${Math.random().toString(36).substring(7)}`;
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
ScopeExtractor = __decorate([
|
|
61
|
+
injectable()
|
|
62
|
+
], ScopeExtractor);
|
|
63
|
+
export { ScopeExtractor };
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { setupAuthorization } from "@expressots/core";
|
|
2
|
+
import { ScopeExtractor } from "./scope-extractor.js";
|
|
3
|
+
import { GuardContextFactory } from "./guard-context-factory.js";
|
|
4
|
+
import { GuardMiddleware } from "./guard-middleware.js";
|
|
5
|
+
import { PermissionPreloaderMiddleware } from "./permission-preloader.middleware.js";
|
|
6
|
+
import { TYPE } from "./constants.js";
|
|
7
|
+
/**
|
|
8
|
+
* Express-specific authorization setup
|
|
9
|
+
* Automatically registers all adapter-specific services and middleware
|
|
10
|
+
*
|
|
11
|
+
* @param container - DI container
|
|
12
|
+
* @param config - Authorization configuration
|
|
13
|
+
* @param middleware - Optional middleware manager to add PermissionPreloaderMiddleware
|
|
14
|
+
* @param authProvider - Optional AuthProvider class (if not already bound)
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```typescript
|
|
18
|
+
* export class App extends AppExpress {
|
|
19
|
+
* async configureServices(): Promise<void> {
|
|
20
|
+
* setupAuthorizationForExpress(
|
|
21
|
+
* this.config.Container,
|
|
22
|
+
* {
|
|
23
|
+
* enablePreloading: true,
|
|
24
|
+
* enableCaching: true,
|
|
25
|
+
* permissionHierarchy: {
|
|
26
|
+
* admin: ["moderator", "user"],
|
|
27
|
+
* moderator: ["user"],
|
|
28
|
+
* },
|
|
29
|
+
* },
|
|
30
|
+
* this.Middleware,
|
|
31
|
+
* );
|
|
32
|
+
* }
|
|
33
|
+
* }
|
|
34
|
+
* ```
|
|
35
|
+
*/
|
|
36
|
+
export function setupAuthorizationForExpress(container, config = {}, middleware, authProvider) {
|
|
37
|
+
// Setup core authorization system
|
|
38
|
+
setupAuthorization(container, config);
|
|
39
|
+
// Register adapter-specific services
|
|
40
|
+
if (!container.isBound("IScopeExtractor")) {
|
|
41
|
+
container.bind("IScopeExtractor").to(ScopeExtractor).inSingletonScope();
|
|
42
|
+
}
|
|
43
|
+
if (!container.isBound(GuardContextFactory)) {
|
|
44
|
+
container.bind(GuardContextFactory).toSelf().inSingletonScope();
|
|
45
|
+
}
|
|
46
|
+
if (!container.isBound(GuardMiddleware)) {
|
|
47
|
+
container.bind(GuardMiddleware).toSelf().inSingletonScope();
|
|
48
|
+
}
|
|
49
|
+
// Bind AuthProvider if provided and not already bound
|
|
50
|
+
if (authProvider && !container.isBound(TYPE.AuthProvider)) {
|
|
51
|
+
container.bind(TYPE.AuthProvider).to(authProvider).inSingletonScope();
|
|
52
|
+
}
|
|
53
|
+
// Add permission preloader middleware if enabled and middleware manager provided
|
|
54
|
+
// PermissionPreloaderMiddleware extends BaseMiddleware which has a handler() method
|
|
55
|
+
// The middleware system accepts BaseMiddleware instances as IExpressoMiddleware
|
|
56
|
+
if (config.enablePreloading !== false && middleware) {
|
|
57
|
+
// Bind PermissionPreloaderMiddleware if not already bound
|
|
58
|
+
if (!container.isBound(PermissionPreloaderMiddleware)) {
|
|
59
|
+
container.bind(PermissionPreloaderMiddleware).toSelf().inSingletonScope();
|
|
60
|
+
}
|
|
61
|
+
// Get instance from container (DI will inject dependencies)
|
|
62
|
+
// Cast to IExpressoMiddleware - BaseMiddleware instances are handled specially
|
|
63
|
+
// by the middleware resolution system (see inversify-express-server.ts)
|
|
64
|
+
const middlewareInstance = container.get(PermissionPreloaderMiddleware);
|
|
65
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
66
|
+
middleware.add(middlewareInstance);
|
|
67
|
+
}
|
|
68
|
+
}
|