@webpieces/http-routing 0.2.15 → 0.2.16
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/package.json +5 -3
- package/src/FilterMatcher.d.ts +39 -0
- package/src/FilterMatcher.js +69 -0
- package/src/FilterMatcher.js.map +1 -0
- package/src/MethodMeta.d.ts +41 -0
- package/src/MethodMeta.js +42 -0
- package/src/MethodMeta.js.map +1 -0
- package/src/RESTApiRoutes.d.ts +1 -1
- package/src/RESTApiRoutes.js +4 -2
- package/src/RESTApiRoutes.js.map +1 -1
- package/src/RouteBuilderImpl.d.ts +159 -0
- package/src/RouteBuilderImpl.js +248 -0
- package/src/RouteBuilderImpl.js.map +1 -0
- package/src/RouteHandler.d.ts +22 -0
- package/src/RouteHandler.js +20 -0
- package/src/RouteHandler.js.map +1 -0
- package/src/WebAppMeta.d.ts +71 -0
- package/src/WebAppMeta.js +37 -0
- package/src/WebAppMeta.js.map +1 -0
- package/src/index.d.ts +5 -0
- package/src/index.js +18 -1
- package/src/index.js.map +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@webpieces/http-routing",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.16",
|
|
4
4
|
"description": "Decorator-based routing with auto-wiring for WebPieces",
|
|
5
5
|
"type": "commonjs",
|
|
6
6
|
"main": "./src/index.js",
|
|
@@ -21,7 +21,9 @@
|
|
|
21
21
|
"access": "public"
|
|
22
22
|
},
|
|
23
23
|
"dependencies": {
|
|
24
|
-
"@webpieces/
|
|
25
|
-
"@webpieces/http-
|
|
24
|
+
"@webpieces/http-api": "0.2.16",
|
|
25
|
+
"@webpieces/http-filters": "0.2.16",
|
|
26
|
+
"inversify": "^7.10.4",
|
|
27
|
+
"minimatch": "^10.0.1"
|
|
26
28
|
}
|
|
27
29
|
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { Filter, WpResponse } from '@webpieces/http-filters';
|
|
2
|
+
import { MethodMeta } from './MethodMeta';
|
|
3
|
+
import { FilterDefinition } from './WebAppMeta';
|
|
4
|
+
/**
|
|
5
|
+
* Type alias for HTTP filters that work with MethodMeta and ResponseWrapper.
|
|
6
|
+
*/
|
|
7
|
+
export type HttpFilter = Filter<MethodMeta, WpResponse<unknown>>;
|
|
8
|
+
/**
|
|
9
|
+
* FilterMatcher - Matches filters to routes based on filepath patterns.
|
|
10
|
+
* Similar to Java SharedMatchUtil.findMatchingFilters().
|
|
11
|
+
*
|
|
12
|
+
* Responsibilities:
|
|
13
|
+
* 1. Filter based on filepath glob pattern matching
|
|
14
|
+
* 2. Sort matching filters by priority (higher first)
|
|
15
|
+
*
|
|
16
|
+
* Differences from Java:
|
|
17
|
+
* - Uses glob patterns instead of regex
|
|
18
|
+
* - Only matches filepaths (no URL path or HTTPS filtering yet)
|
|
19
|
+
* - Simpler API focused on one responsibility
|
|
20
|
+
*/
|
|
21
|
+
export declare class FilterMatcher {
|
|
22
|
+
/**
|
|
23
|
+
* Find filters that match the given controller filepath.
|
|
24
|
+
*
|
|
25
|
+
* @param controllerFilepath - The filepath of the controller source file
|
|
26
|
+
* @param allFilters - All registered filters with their definitions
|
|
27
|
+
* @returns Array of matching filters, sorted by priority (highest first)
|
|
28
|
+
*/
|
|
29
|
+
static findMatchingFilters(controllerFilepath: string | undefined, allFilters: Array<FilterDefinition>): HttpFilter[];
|
|
30
|
+
/**
|
|
31
|
+
* Normalize a controller filepath for consistent matching.
|
|
32
|
+
* - Converts backslashes to forward slashes (Windows compatibility)
|
|
33
|
+
* - Removes leading './'
|
|
34
|
+
*
|
|
35
|
+
* @param filepath - Raw filepath
|
|
36
|
+
* @returns Normalized filepath
|
|
37
|
+
*/
|
|
38
|
+
static normalizeFilepath(filepath: string): string;
|
|
39
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.FilterMatcher = void 0;
|
|
4
|
+
const minimatch_1 = require("minimatch");
|
|
5
|
+
/**
|
|
6
|
+
* FilterMatcher - Matches filters to routes based on filepath patterns.
|
|
7
|
+
* Similar to Java SharedMatchUtil.findMatchingFilters().
|
|
8
|
+
*
|
|
9
|
+
* Responsibilities:
|
|
10
|
+
* 1. Filter based on filepath glob pattern matching
|
|
11
|
+
* 2. Sort matching filters by priority (higher first)
|
|
12
|
+
*
|
|
13
|
+
* Differences from Java:
|
|
14
|
+
* - Uses glob patterns instead of regex
|
|
15
|
+
* - Only matches filepaths (no URL path or HTTPS filtering yet)
|
|
16
|
+
* - Simpler API focused on one responsibility
|
|
17
|
+
*/
|
|
18
|
+
class FilterMatcher {
|
|
19
|
+
/**
|
|
20
|
+
* Find filters that match the given controller filepath.
|
|
21
|
+
*
|
|
22
|
+
* @param controllerFilepath - The filepath of the controller source file
|
|
23
|
+
* @param allFilters - All registered filters with their definitions
|
|
24
|
+
* @returns Array of matching filters, sorted by priority (highest first)
|
|
25
|
+
*/
|
|
26
|
+
static findMatchingFilters(controllerFilepath, allFilters) {
|
|
27
|
+
const matchingFilters = [];
|
|
28
|
+
for (const definition of allFilters) {
|
|
29
|
+
const pattern = definition.filepathPattern;
|
|
30
|
+
const filter = definition.filter;
|
|
31
|
+
// Special case: '*' matches all controllers (global filter)
|
|
32
|
+
if (pattern === '*') {
|
|
33
|
+
matchingFilters.push({ filter, priority: definition.priority });
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
// If no filepath available, only match wildcard patterns
|
|
37
|
+
if (!controllerFilepath) {
|
|
38
|
+
if (pattern === '**/*') {
|
|
39
|
+
matchingFilters.push({ filter, priority: definition.priority });
|
|
40
|
+
}
|
|
41
|
+
continue;
|
|
42
|
+
}
|
|
43
|
+
// Normalize filepath for consistent matching
|
|
44
|
+
const normalizedPath = FilterMatcher.normalizeFilepath(controllerFilepath);
|
|
45
|
+
// Match using minimatch
|
|
46
|
+
if ((0, minimatch_1.minimatch)(normalizedPath, pattern)) {
|
|
47
|
+
matchingFilters.push({ filter, priority: definition.priority });
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
// Sort by priority (highest first)
|
|
51
|
+
matchingFilters.sort((a, b) => b.priority - a.priority);
|
|
52
|
+
return matchingFilters.map((item) => item.filter);
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Normalize a controller filepath for consistent matching.
|
|
56
|
+
* - Converts backslashes to forward slashes (Windows compatibility)
|
|
57
|
+
* - Removes leading './'
|
|
58
|
+
*
|
|
59
|
+
* @param filepath - Raw filepath
|
|
60
|
+
* @returns Normalized filepath
|
|
61
|
+
*/
|
|
62
|
+
static normalizeFilepath(filepath) {
|
|
63
|
+
return filepath
|
|
64
|
+
.replace(/\\/g, '/') // Windows backslashes to forward slashes
|
|
65
|
+
.replace(/^\.\//, ''); // Remove leading './'
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
exports.FilterMatcher = FilterMatcher;
|
|
69
|
+
//# sourceMappingURL=FilterMatcher.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FilterMatcher.js","sourceRoot":"","sources":["../../../../../packages/http/http-routing/src/FilterMatcher.ts"],"names":[],"mappings":";;;AAGA,yCAAsC;AAOtC;;;;;;;;;;;;GAYG;AACH,MAAa,aAAa;IACtB;;;;;;OAMG;IACH,MAAM,CAAC,mBAAmB,CACtB,kBAAsC,EACtC,UAAmC;QAEnC,MAAM,eAAe,GAAoD,EAAE,CAAC;QAE5E,KAAK,MAAM,UAAU,IAAI,UAAU,EAAE,CAAC;YAClC,MAAM,OAAO,GAAG,UAAU,CAAC,eAAe,CAAC;YAC3C,MAAM,MAAM,GAAG,UAAU,CAAC,MAAoB,CAAC;YAE/C,4DAA4D;YAC5D,IAAI,OAAO,KAAK,GAAG,EAAE,CAAC;gBAClB,eAAe,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,UAAU,CAAC,QAAQ,EAAE,CAAC,CAAC;gBAChE,SAAS;YACb,CAAC;YAED,yDAAyD;YACzD,IAAI,CAAC,kBAAkB,EAAE,CAAC;gBACtB,IAAI,OAAO,KAAK,MAAM,EAAE,CAAC;oBACrB,eAAe,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,UAAU,CAAC,QAAQ,EAAE,CAAC,CAAC;gBACpE,CAAC;gBACD,SAAS;YACb,CAAC;YAED,6CAA6C;YAC7C,MAAM,cAAc,GAAG,aAAa,CAAC,iBAAiB,CAAC,kBAAkB,CAAC,CAAC;YAE3E,wBAAwB;YACxB,IAAI,IAAA,qBAAS,EAAC,cAAc,EAAE,OAAO,CAAC,EAAE,CAAC;gBACrC,eAAe,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,UAAU,CAAC,QAAQ,EAAE,CAAC,CAAC;YACpE,CAAC;QACL,CAAC;QAED,mCAAmC;QACnC,eAAe,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,GAAG,CAAC,CAAC,QAAQ,CAAC,CAAC;QAExD,OAAO,eAAe,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACtD,CAAC;IAED;;;;;;;OAOG;IACH,MAAM,CAAC,iBAAiB,CAAC,QAAgB;QACrC,OAAO,QAAQ;aACV,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,yCAAyC;aAC7D,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,CAAC,sBAAsB;IACrD,CAAC;CACJ;AA5DD,sCA4DC","sourcesContent":["import { Filter, WpResponse } from '@webpieces/http-filters';\nimport { MethodMeta } from './MethodMeta';\nimport { FilterDefinition } from './WebAppMeta';\nimport { minimatch } from 'minimatch';\n\n/**\n * Type alias for HTTP filters that work with MethodMeta and ResponseWrapper.\n */\nexport type HttpFilter = Filter<MethodMeta, WpResponse<unknown>>;\n\n/**\n * FilterMatcher - Matches filters to routes based on filepath patterns.\n * Similar to Java SharedMatchUtil.findMatchingFilters().\n *\n * Responsibilities:\n * 1. Filter based on filepath glob pattern matching\n * 2. Sort matching filters by priority (higher first)\n *\n * Differences from Java:\n * - Uses glob patterns instead of regex\n * - Only matches filepaths (no URL path or HTTPS filtering yet)\n * - Simpler API focused on one responsibility\n */\nexport class FilterMatcher {\n /**\n * Find filters that match the given controller filepath.\n *\n * @param controllerFilepath - The filepath of the controller source file\n * @param allFilters - All registered filters with their definitions\n * @returns Array of matching filters, sorted by priority (highest first)\n */\n static findMatchingFilters(\n controllerFilepath: string | undefined,\n allFilters: Array<FilterDefinition>,\n ): HttpFilter[] {\n const matchingFilters: Array<{ filter: HttpFilter; priority: number }> = [];\n\n for (const definition of allFilters) {\n const pattern = definition.filepathPattern;\n const filter = definition.filter as HttpFilter;\n\n // Special case: '*' matches all controllers (global filter)\n if (pattern === '*') {\n matchingFilters.push({ filter, priority: definition.priority });\n continue;\n }\n\n // If no filepath available, only match wildcard patterns\n if (!controllerFilepath) {\n if (pattern === '**/*') {\n matchingFilters.push({ filter, priority: definition.priority });\n }\n continue;\n }\n\n // Normalize filepath for consistent matching\n const normalizedPath = FilterMatcher.normalizeFilepath(controllerFilepath);\n\n // Match using minimatch\n if (minimatch(normalizedPath, pattern)) {\n matchingFilters.push({ filter, priority: definition.priority });\n }\n }\n\n // Sort by priority (highest first)\n matchingFilters.sort((a, b) => b.priority - a.priority);\n\n return matchingFilters.map((item) => item.filter);\n }\n\n /**\n * Normalize a controller filepath for consistent matching.\n * - Converts backslashes to forward slashes (Windows compatibility)\n * - Removes leading './'\n *\n * @param filepath - Raw filepath\n * @returns Normalized filepath\n */\n static normalizeFilepath(filepath: string): string {\n return filepath\n .replace(/\\\\/g, '/') // Windows backslashes to forward slashes\n .replace(/^\\.\\//, ''); // Remove leading './'\n }\n}\n"]}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { RouteMetadata } from '@webpieces/http-api';
|
|
2
|
+
/**
|
|
3
|
+
* Metadata about the method being invoked.
|
|
4
|
+
* Passed to filters and contains request information.
|
|
5
|
+
*
|
|
6
|
+
* MethodMeta is DTO-only - it does NOT contain Express req/res.
|
|
7
|
+
* Express objects are handled by the Express layer (wrapExpress, jsonTranslator).
|
|
8
|
+
*
|
|
9
|
+
* Fields:
|
|
10
|
+
* - routeMeta: Static route information (httpMethod, path, methodName)
|
|
11
|
+
* - requestDto: The deserialized request body
|
|
12
|
+
* - metadata: Request-scoped data for filters to communicate
|
|
13
|
+
*/
|
|
14
|
+
export declare class MethodMeta {
|
|
15
|
+
/**
|
|
16
|
+
* Route metadata (httpMethod, path, methodName, parameterTypes)
|
|
17
|
+
*/
|
|
18
|
+
routeMeta: RouteMetadata;
|
|
19
|
+
/**
|
|
20
|
+
* The deserialized request DTO.
|
|
21
|
+
*/
|
|
22
|
+
requestDto?: unknown;
|
|
23
|
+
/**
|
|
24
|
+
* Additional metadata for storing request-scoped data.
|
|
25
|
+
* Used by filters to pass data to other filters/controllers.
|
|
26
|
+
*/
|
|
27
|
+
metadata: Map<string, unknown>;
|
|
28
|
+
constructor(routeMeta: RouteMetadata, requestDto?: unknown, metadata?: Map<string, unknown>);
|
|
29
|
+
/**
|
|
30
|
+
* Get the HTTP method (convenience accessor).
|
|
31
|
+
*/
|
|
32
|
+
get httpMethod(): string;
|
|
33
|
+
/**
|
|
34
|
+
* Get the request path (convenience accessor).
|
|
35
|
+
*/
|
|
36
|
+
get path(): string;
|
|
37
|
+
/**
|
|
38
|
+
* Get the method name (convenience accessor).
|
|
39
|
+
*/
|
|
40
|
+
get methodName(): string;
|
|
41
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.MethodMeta = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Metadata about the method being invoked.
|
|
6
|
+
* Passed to filters and contains request information.
|
|
7
|
+
*
|
|
8
|
+
* MethodMeta is DTO-only - it does NOT contain Express req/res.
|
|
9
|
+
* Express objects are handled by the Express layer (wrapExpress, jsonTranslator).
|
|
10
|
+
*
|
|
11
|
+
* Fields:
|
|
12
|
+
* - routeMeta: Static route information (httpMethod, path, methodName)
|
|
13
|
+
* - requestDto: The deserialized request body
|
|
14
|
+
* - metadata: Request-scoped data for filters to communicate
|
|
15
|
+
*/
|
|
16
|
+
class MethodMeta {
|
|
17
|
+
constructor(routeMeta, requestDto, metadata) {
|
|
18
|
+
this.routeMeta = routeMeta;
|
|
19
|
+
this.requestDto = requestDto;
|
|
20
|
+
this.metadata = metadata ?? new Map();
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Get the HTTP method (convenience accessor).
|
|
24
|
+
*/
|
|
25
|
+
get httpMethod() {
|
|
26
|
+
return this.routeMeta.httpMethod;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Get the request path (convenience accessor).
|
|
30
|
+
*/
|
|
31
|
+
get path() {
|
|
32
|
+
return this.routeMeta.path;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Get the method name (convenience accessor).
|
|
36
|
+
*/
|
|
37
|
+
get methodName() {
|
|
38
|
+
return this.routeMeta.methodName;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
exports.MethodMeta = MethodMeta;
|
|
42
|
+
//# sourceMappingURL=MethodMeta.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MethodMeta.js","sourceRoot":"","sources":["../../../../../packages/http/http-routing/src/MethodMeta.ts"],"names":[],"mappings":";;;AAEA;;;;;;;;;;;GAWG;AACH,MAAa,UAAU;IAiBnB,YACI,SAAwB,EACxB,UAAoB,EACpB,QAA+B;QAE/B,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC;QAC3B,IAAI,CAAC,UAAU,GAAG,UAAU,CAAC;QAC7B,IAAI,CAAC,QAAQ,GAAG,QAAQ,IAAI,IAAI,GAAG,EAAE,CAAC;IAC1C,CAAC;IAED;;OAEG;IACH,IAAI,UAAU;QACV,OAAO,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC;IACrC,CAAC;IAED;;OAEG;IACH,IAAI,IAAI;QACJ,OAAO,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC;IAC/B,CAAC;IAED;;OAEG;IACH,IAAI,UAAU;QACV,OAAO,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC;IACrC,CAAC;CACJ;AA/CD,gCA+CC","sourcesContent":["import { RouteMetadata } from '@webpieces/http-api';\n\n/**\n * Metadata about the method being invoked.\n * Passed to filters and contains request information.\n *\n * MethodMeta is DTO-only - it does NOT contain Express req/res.\n * Express objects are handled by the Express layer (wrapExpress, jsonTranslator).\n *\n * Fields:\n * - routeMeta: Static route information (httpMethod, path, methodName)\n * - requestDto: The deserialized request body\n * - metadata: Request-scoped data for filters to communicate\n */\nexport class MethodMeta {\n /**\n * Route metadata (httpMethod, path, methodName, parameterTypes)\n */\n routeMeta: RouteMetadata;\n\n /**\n * The deserialized request DTO.\n */\n requestDto?: unknown;\n\n /**\n * Additional metadata for storing request-scoped data.\n * Used by filters to pass data to other filters/controllers.\n */\n metadata: Map<string, unknown>;\n\n constructor(\n routeMeta: RouteMetadata,\n requestDto?: unknown,\n metadata?: Map<string, unknown>,\n ) {\n this.routeMeta = routeMeta;\n this.requestDto = requestDto;\n this.metadata = metadata ?? new Map();\n }\n\n /**\n * Get the HTTP method (convenience accessor).\n */\n get httpMethod(): string {\n return this.routeMeta.httpMethod;\n }\n\n /**\n * Get the request path (convenience accessor).\n */\n get path(): string {\n return this.routeMeta.path;\n }\n\n /**\n * Get the method name (convenience accessor).\n */\n get methodName(): string {\n return this.routeMeta.methodName;\n }\n}\n"]}
|
package/src/RESTApiRoutes.d.ts
CHANGED
package/src/RESTApiRoutes.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.RESTApiRoutes = void 0;
|
|
4
|
-
const
|
|
4
|
+
const WebAppMeta_1 = require("./WebAppMeta");
|
|
5
5
|
const http_api_1 = require("@webpieces/http-api");
|
|
6
6
|
const decorators_1 = require("./decorators");
|
|
7
7
|
/**
|
|
@@ -82,11 +82,13 @@ class RESTApiRoutes {
|
|
|
82
82
|
const apiName = this.apiMetaClass.name || 'Unknown';
|
|
83
83
|
throw new Error(`Method ${route.methodName} in ${apiName} must have both @HttpMethod and @Path decorators`);
|
|
84
84
|
}
|
|
85
|
+
// Set controller class name for logging
|
|
86
|
+
route.controllerClassName = this.controllerClass.name;
|
|
85
87
|
// Extract controller filepath for filter matching
|
|
86
88
|
const controllerFilepath = this.getControllerFilepath();
|
|
87
89
|
// Pass controller class and method name to RouteBuilder
|
|
88
90
|
// RouteBuilder will resolve the controller from DI and create the handler
|
|
89
|
-
routeBuilder.addRoute(new
|
|
91
|
+
routeBuilder.addRoute(new WebAppMeta_1.RouteDefinition(route, this.controllerClass, controllerFilepath));
|
|
90
92
|
}
|
|
91
93
|
/**
|
|
92
94
|
* Get the filepath of the controller source file.
|
package/src/RESTApiRoutes.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RESTApiRoutes.js","sourceRoot":"","sources":["../../../../../packages/http/http-routing/src/RESTApiRoutes.ts"],"names":[],"mappings":";;;AAAA,
|
|
1
|
+
{"version":3,"file":"RESTApiRoutes.js","sourceRoot":"","sources":["../../../../../packages/http/http-routing/src/RESTApiRoutes.ts"],"names":[],"mappings":"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","sourcesContent":["import { Routes, RouteBuilder, RouteDefinition } from './WebAppMeta';\nimport { getRoutes, isApiInterface, RouteMetadata } from '@webpieces/http-api';\nimport { ROUTING_METADATA_KEYS } from './decorators';\n\n/**\n * Type representing a class constructor (abstract or concrete).\n */\nexport type ClassType<T = any> = Function & { prototype: T };\n\n/**\n * RESTApiRoutes - Automatically wire API interfaces to controllers.\n * Similar to Java WebPieces RESTApiRoutes.\n *\n * This class uses reflection (reflect-metadata) to read decorators from\n * an API interface class and automatically register routes that dispatch\n * to the corresponding controller methods.\n *\n * Usage:\n * ```typescript\n * // In your ServerMeta:\n * getRoutes(): Routes[] {\n * return [\n * new RESTApiRoutes(SaveApiPrototype, SaveController),\n * // ... more routes\n * ];\n * }\n * ```\n *\n * The API interface and controller must follow this pattern:\n * - API interface class has @ApiInterface() decorator\n * - Methods have @Post()/@Get()/etc and @Path() decorators\n * - Controller class implements the same interface\n * - Controller class has @Controller() decorator\n *\n * Type Parameters:\n * - TApi: The API prototype class type (abstract class with decorators)\n * - TController: The controller class type (must extend TApi)\n */\nexport class RESTApiRoutes<TApi = any, TController extends TApi = any> implements Routes {\n private apiMetaClass: ClassType<TApi>;\n private controllerClass: ClassType<TController>;\n\n /**\n * Create a new RESTApiRoutes.\n *\n * @param apiMetaClass - The API interface class with decorators (e.g., SaveApiPrototype)\n * @param controllerClass - The controller class that implements the API (e.g., SaveController)\n */\n constructor(apiMetaClass: ClassType<TApi>, controllerClass: ClassType<TController>) {\n this.apiMetaClass = apiMetaClass;\n this.controllerClass = controllerClass;\n\n // Validate that apiMetaClass is marked as @ApiInterface\n if (!isApiInterface(apiMetaClass)) {\n const className = (apiMetaClass as any).name || 'Unknown';\n throw new Error(`Class ${className} must be decorated with @ApiInterface()`);\n }\n\n // Validate that controllerClass implements the methods from apiMetaClass\n this.validateControllerImplementsApi();\n }\n\n /**\n * Validate that the controller implements all methods from the API interface.\n */\n private validateControllerImplementsApi(): void {\n const routes = getRoutes(this.apiMetaClass);\n\n for (const route of routes) {\n const controllerPrototype = this.controllerClass.prototype;\n\n if (typeof controllerPrototype[route.methodName] !== 'function') {\n const controllerName = (this.controllerClass as any).name || 'Unknown';\n const apiName = (this.apiMetaClass as any).name || 'Unknown';\n throw new Error(\n `Controller ${controllerName} must implement method ${route.methodName} from API ${apiName}`,\n );\n }\n }\n }\n\n /**\n * Configure routes by reading metadata from the API interface.\n */\n configure(routeBuilder: RouteBuilder): void {\n const routes = getRoutes(this.apiMetaClass);\n\n for (const route of routes) {\n this.registerRoute(routeBuilder, route);\n }\n }\n\n /**\n * Register a single route with the route builder.\n */\n private registerRoute(routeBuilder: RouteBuilder, route: RouteMetadata): void {\n if (!route.httpMethod || !route.path) {\n const apiName = (this.apiMetaClass as any).name || 'Unknown';\n throw new Error(\n `Method ${route.methodName} in ${apiName} must have both @HttpMethod and @Path decorators`,\n );\n }\n\n // Set controller class name for logging\n route.controllerClassName = (this.controllerClass as any).name;\n\n // Extract controller filepath for filter matching\n const controllerFilepath = this.getControllerFilepath();\n\n // Pass controller class and method name to RouteBuilder\n // RouteBuilder will resolve the controller from DI and create the handler\n routeBuilder.addRoute(new RouteDefinition(route, this.controllerClass, controllerFilepath));\n }\n\n /**\n * Get the filepath of the controller source file.\n * Uses a heuristic based on the controller class name.\n *\n * Since TypeScript doesn't provide source file paths at runtime,\n * we use the class name to create a pattern that filters can match against.\n *\n * @returns Filepath pattern or undefined\n */\n private getControllerFilepath(): string | undefined {\n // Check for explicit @SourceFile decorator metadata\n const filepath = Reflect.getMetadata(\n ROUTING_METADATA_KEYS.SOURCE_FILEPATH,\n this.controllerClass,\n );\n if (filepath) {\n return filepath;\n }\n\n // Fallback to class name pattern\n const className = (this.controllerClass as any).name;\n return className ? `**/${className}.ts` : undefined;\n }\n\n /**\n * Get the API interface class.\n */\n getApiClass(): any {\n return this.apiMetaClass;\n }\n\n /**\n * Get the controller class.\n */\n getControllerClass(): any {\n return this.controllerClass;\n }\n}\n"]}
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import { Container } from 'inversify';
|
|
2
|
+
import { Request, Response, NextFunction } from 'express';
|
|
3
|
+
import { RouteBuilder, RouteDefinition, FilterDefinition } from './WebAppMeta';
|
|
4
|
+
import { RouteHandler } from './RouteHandler';
|
|
5
|
+
import { MethodMeta } from './MethodMeta';
|
|
6
|
+
import { WpResponse, Service } from '@webpieces/http-filters';
|
|
7
|
+
import { HttpFilter } from './FilterMatcher';
|
|
8
|
+
/**
|
|
9
|
+
* Express route handler function type.
|
|
10
|
+
* Used by wrapExpress to create handlers that Express can call.
|
|
11
|
+
*/
|
|
12
|
+
export type ExpressRouteHandler = (req: Request, res: Response, next: NextFunction) => Promise<void>;
|
|
13
|
+
/**
|
|
14
|
+
* FilterWithMeta - Pairs a resolved filter instance with its definition.
|
|
15
|
+
* Stores both the DI-resolved filter and the metadata needed for matching.
|
|
16
|
+
*/
|
|
17
|
+
export declare class FilterWithMeta {
|
|
18
|
+
filter: HttpFilter;
|
|
19
|
+
definition: FilterDefinition;
|
|
20
|
+
constructor(filter: HttpFilter, definition: FilterDefinition);
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* RouteHandlerImpl - Concrete implementation of RouteHandler.
|
|
24
|
+
* Wraps a resolved controller and method to invoke on each request.
|
|
25
|
+
*/
|
|
26
|
+
export declare class RouteHandlerImpl<TResult> implements RouteHandler<TResult> {
|
|
27
|
+
private controller;
|
|
28
|
+
private method;
|
|
29
|
+
constructor(controller: Record<string, unknown>, method: (this: unknown, requestDto?: unknown) => Promise<TResult>);
|
|
30
|
+
execute(meta: MethodMeta): Promise<TResult>;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* RouteHandlerWithMeta - Pairs a route handler with its definition.
|
|
34
|
+
* Stores both the handler (which wraps the DI-resolved controller) and the route metadata.
|
|
35
|
+
*
|
|
36
|
+
* We use unknown for the generic type since we store different TResult types in the same Map.
|
|
37
|
+
* Type safety is maintained through the generic on RouteDefinition at registration time.
|
|
38
|
+
*/
|
|
39
|
+
export declare class RouteHandlerWithMeta {
|
|
40
|
+
invokeControllerHandler: RouteHandler<unknown>;
|
|
41
|
+
definition: RouteDefinition;
|
|
42
|
+
constructor(invokeControllerHandler: RouteHandler<unknown>, definition: RouteDefinition);
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* RouteBuilderImpl - Concrete implementation of RouteBuilder interface.
|
|
46
|
+
*
|
|
47
|
+
* Similar to Java WebPieces RouteBuilder, this class is responsible for:
|
|
48
|
+
* 1. Registering routes with their handlers
|
|
49
|
+
* 2. Registering filters with priority
|
|
50
|
+
*
|
|
51
|
+
* This class is explicit (not anonymous) to:
|
|
52
|
+
* - Improve traceability and debugging
|
|
53
|
+
* - Make the code easier to understand
|
|
54
|
+
* - Enable better IDE navigation (Cmd+Click on addRoute works!)
|
|
55
|
+
*
|
|
56
|
+
* DI Pattern: This class is registered in webpiecesContainer via @provideSingleton()
|
|
57
|
+
* but needs appContainer to resolve filters/controllers. The container is set via
|
|
58
|
+
* setContainer() after appContainer is created (late binding pattern).
|
|
59
|
+
*/
|
|
60
|
+
export declare class RouteBuilderImpl implements RouteBuilder {
|
|
61
|
+
private routes;
|
|
62
|
+
private filterRegistry;
|
|
63
|
+
private container?;
|
|
64
|
+
/**
|
|
65
|
+
* Map for O(1) route lookup by method:path.
|
|
66
|
+
* Used by both addRoute() and createRouteInvoker() for fast route access.
|
|
67
|
+
*/
|
|
68
|
+
private routeMap;
|
|
69
|
+
/**
|
|
70
|
+
* Create route key for consistent lookup.
|
|
71
|
+
* Key format: "${METHOD}:${path}" (e.g., "POST:/search/item")
|
|
72
|
+
*/
|
|
73
|
+
private createRouteKey;
|
|
74
|
+
/**
|
|
75
|
+
* Set the DI container used for resolving filters and controllers.
|
|
76
|
+
* Called by WebpiecesCoreServer after appContainer is created.
|
|
77
|
+
*
|
|
78
|
+
* @param container - The application DI container (appContainer)
|
|
79
|
+
*/
|
|
80
|
+
setContainer(container: Container): void;
|
|
81
|
+
/**
|
|
82
|
+
* Register a route with the router.
|
|
83
|
+
*
|
|
84
|
+
* Uses createRouteHandlerWithMeta() to create the handler, then stores it
|
|
85
|
+
* in both the routes array and the routeMap for O(1) lookup.
|
|
86
|
+
*
|
|
87
|
+
* @param route - Route definition with controller class and method name
|
|
88
|
+
*/
|
|
89
|
+
addRoute(route: RouteDefinition): void;
|
|
90
|
+
/**
|
|
91
|
+
* Create RouteHandlerWithMeta from a RouteDefinition.
|
|
92
|
+
*
|
|
93
|
+
* Resolves controller from DI container ONCE and creates a handler that
|
|
94
|
+
* invokes the controller method with the request DTO.
|
|
95
|
+
*
|
|
96
|
+
* This method is used by:
|
|
97
|
+
* - addRoute() for production route registration
|
|
98
|
+
* - createRouteInvoker() for test clients (via createApiClient)
|
|
99
|
+
*
|
|
100
|
+
* @param route - Route definition with controller class and method name
|
|
101
|
+
* @returns RouteHandlerWithMeta containing the handler and route definition
|
|
102
|
+
*/
|
|
103
|
+
private createRouteHandlerWithMeta;
|
|
104
|
+
/**
|
|
105
|
+
* Register a filter with the filter chain.
|
|
106
|
+
*
|
|
107
|
+
* Resolves the filter from DI container and pairs it with the filter definition.
|
|
108
|
+
* The definition includes pattern information used for route-specific filtering.
|
|
109
|
+
*
|
|
110
|
+
* @param filterDef - Filter definition with priority, filter class, and optional filepath pattern
|
|
111
|
+
*/
|
|
112
|
+
addFilter(filterDef: FilterDefinition): void;
|
|
113
|
+
/**
|
|
114
|
+
* Get all registered routes.
|
|
115
|
+
*
|
|
116
|
+
* @returns Map of routes with handlers and definitions, keyed by "METHOD:path"
|
|
117
|
+
*/
|
|
118
|
+
getRoutes(): RouteHandlerWithMeta[];
|
|
119
|
+
/**
|
|
120
|
+
* Get all filters sorted by priority (highest priority first).
|
|
121
|
+
*
|
|
122
|
+
* @returns Array of FilterWithMeta sorted by priority
|
|
123
|
+
*/
|
|
124
|
+
getSortedFilters(): Array<FilterWithMeta>;
|
|
125
|
+
/**
|
|
126
|
+
* Cached filter definitions for lazy route setup.
|
|
127
|
+
*/
|
|
128
|
+
private cachedFilterDefinitions?;
|
|
129
|
+
/**
|
|
130
|
+
* Get filter definitions, computing once and caching.
|
|
131
|
+
*/
|
|
132
|
+
private getFilterDefinitions;
|
|
133
|
+
/**
|
|
134
|
+
* Setup a single route by creating its filter chain.
|
|
135
|
+
* This is called lazily by createHandler() and getRouteService().
|
|
136
|
+
*
|
|
137
|
+
* Creates a Service that wraps the filter chain and controller invocation.
|
|
138
|
+
* The service is DTO-only and has no Express dependency.
|
|
139
|
+
*
|
|
140
|
+
* @param key - Route key in format "METHOD:path"
|
|
141
|
+
* @param routeWithMeta - Route handler with metadata
|
|
142
|
+
* @returns The service for this route
|
|
143
|
+
*/
|
|
144
|
+
createRouteHandler(routeWithMeta: RouteHandlerWithMeta): Service<MethodMeta, WpResponse<unknown>>;
|
|
145
|
+
/**
|
|
146
|
+
* Create an invoker function for a route (for testing via createApiClient).
|
|
147
|
+
* Uses routeMap for O(1) lookup, sets up the filter chain ONCE,
|
|
148
|
+
* and returns a Service that can be called multiple times without
|
|
149
|
+
* recreating the filter chain.
|
|
150
|
+
*
|
|
151
|
+
* This method is called by WebpiecesServer.createApiClient() during proxy setup.
|
|
152
|
+
* The returned Service is stored as the proxy method and invoked on each call.
|
|
153
|
+
*
|
|
154
|
+
* @param method - HTTP method (GET, POST, etc.)
|
|
155
|
+
* @param path - URL path
|
|
156
|
+
* @returns A Service that invokes the route
|
|
157
|
+
*/
|
|
158
|
+
createRouteInvoker(method: string, path: string): Service<MethodMeta, WpResponse<unknown>>;
|
|
159
|
+
}
|
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.RouteBuilderImpl = exports.RouteHandlerWithMeta = exports.RouteHandlerImpl = exports.FilterWithMeta = void 0;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
const inversify_1 = require("inversify");
|
|
6
|
+
const decorators_1 = require("./decorators");
|
|
7
|
+
const http_filters_1 = require("@webpieces/http-filters");
|
|
8
|
+
const FilterMatcher_1 = require("./FilterMatcher");
|
|
9
|
+
/**
|
|
10
|
+
* FilterWithMeta - Pairs a resolved filter instance with its definition.
|
|
11
|
+
* Stores both the DI-resolved filter and the metadata needed for matching.
|
|
12
|
+
*/
|
|
13
|
+
class FilterWithMeta {
|
|
14
|
+
constructor(filter, definition) {
|
|
15
|
+
this.filter = filter;
|
|
16
|
+
this.definition = definition;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
exports.FilterWithMeta = FilterWithMeta;
|
|
20
|
+
/**
|
|
21
|
+
* RouteHandlerImpl - Concrete implementation of RouteHandler.
|
|
22
|
+
* Wraps a resolved controller and method to invoke on each request.
|
|
23
|
+
*/
|
|
24
|
+
class RouteHandlerImpl {
|
|
25
|
+
constructor(controller, method) {
|
|
26
|
+
this.controller = controller;
|
|
27
|
+
this.method = method;
|
|
28
|
+
}
|
|
29
|
+
async execute(meta) {
|
|
30
|
+
// Invoke the method with requestDto from meta
|
|
31
|
+
// The controller is already resolved - no DI lookup on every request!
|
|
32
|
+
const result = await this.method.call(this.controller, meta.requestDto);
|
|
33
|
+
return result;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
exports.RouteHandlerImpl = RouteHandlerImpl;
|
|
37
|
+
/**
|
|
38
|
+
* RouteHandlerWithMeta - Pairs a route handler with its definition.
|
|
39
|
+
* Stores both the handler (which wraps the DI-resolved controller) and the route metadata.
|
|
40
|
+
*
|
|
41
|
+
* We use unknown for the generic type since we store different TResult types in the same Map.
|
|
42
|
+
* Type safety is maintained through the generic on RouteDefinition at registration time.
|
|
43
|
+
*/
|
|
44
|
+
class RouteHandlerWithMeta {
|
|
45
|
+
constructor(invokeControllerHandler, definition) {
|
|
46
|
+
this.invokeControllerHandler = invokeControllerHandler;
|
|
47
|
+
this.definition = definition;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
exports.RouteHandlerWithMeta = RouteHandlerWithMeta;
|
|
51
|
+
/**
|
|
52
|
+
* RouteBuilderImpl - Concrete implementation of RouteBuilder interface.
|
|
53
|
+
*
|
|
54
|
+
* Similar to Java WebPieces RouteBuilder, this class is responsible for:
|
|
55
|
+
* 1. Registering routes with their handlers
|
|
56
|
+
* 2. Registering filters with priority
|
|
57
|
+
*
|
|
58
|
+
* This class is explicit (not anonymous) to:
|
|
59
|
+
* - Improve traceability and debugging
|
|
60
|
+
* - Make the code easier to understand
|
|
61
|
+
* - Enable better IDE navigation (Cmd+Click on addRoute works!)
|
|
62
|
+
*
|
|
63
|
+
* DI Pattern: This class is registered in webpiecesContainer via @provideSingleton()
|
|
64
|
+
* but needs appContainer to resolve filters/controllers. The container is set via
|
|
65
|
+
* setContainer() after appContainer is created (late binding pattern).
|
|
66
|
+
*/
|
|
67
|
+
let RouteBuilderImpl = class RouteBuilderImpl {
|
|
68
|
+
constructor() {
|
|
69
|
+
this.routes = [];
|
|
70
|
+
this.filterRegistry = [];
|
|
71
|
+
/**
|
|
72
|
+
* Map for O(1) route lookup by method:path.
|
|
73
|
+
* Used by both addRoute() and createRouteInvoker() for fast route access.
|
|
74
|
+
*/
|
|
75
|
+
this.routeMap = new Map();
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Create route key for consistent lookup.
|
|
79
|
+
* Key format: "${METHOD}:${path}" (e.g., "POST:/search/item")
|
|
80
|
+
*/
|
|
81
|
+
createRouteKey(method, path) {
|
|
82
|
+
return `${method.toUpperCase()}:${path}`;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Set the DI container used for resolving filters and controllers.
|
|
86
|
+
* Called by WebpiecesCoreServer after appContainer is created.
|
|
87
|
+
*
|
|
88
|
+
* @param container - The application DI container (appContainer)
|
|
89
|
+
*/
|
|
90
|
+
setContainer(container) {
|
|
91
|
+
this.container = container;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Register a route with the router.
|
|
95
|
+
*
|
|
96
|
+
* Uses createRouteHandlerWithMeta() to create the handler, then stores it
|
|
97
|
+
* in both the routes array and the routeMap for O(1) lookup.
|
|
98
|
+
*
|
|
99
|
+
* @param route - Route definition with controller class and method name
|
|
100
|
+
*/
|
|
101
|
+
addRoute(route) {
|
|
102
|
+
const routeWithMeta = this.createRouteHandlerWithMeta(route);
|
|
103
|
+
this.routes.push(routeWithMeta);
|
|
104
|
+
// Also add to map for O(1) lookup by method:path
|
|
105
|
+
const key = this.createRouteKey(route.routeMeta.httpMethod, route.routeMeta.path);
|
|
106
|
+
this.routeMap.set(key, routeWithMeta);
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Create RouteHandlerWithMeta from a RouteDefinition.
|
|
110
|
+
*
|
|
111
|
+
* Resolves controller from DI container ONCE and creates a handler that
|
|
112
|
+
* invokes the controller method with the request DTO.
|
|
113
|
+
*
|
|
114
|
+
* This method is used by:
|
|
115
|
+
* - addRoute() for production route registration
|
|
116
|
+
* - createRouteInvoker() for test clients (via createApiClient)
|
|
117
|
+
*
|
|
118
|
+
* @param route - Route definition with controller class and method name
|
|
119
|
+
* @returns RouteHandlerWithMeta containing the handler and route definition
|
|
120
|
+
*/
|
|
121
|
+
createRouteHandlerWithMeta(route) {
|
|
122
|
+
if (!this.container) {
|
|
123
|
+
throw new Error('Container not set. Call setContainer() before registering routes.');
|
|
124
|
+
}
|
|
125
|
+
const routeMeta = route.routeMeta;
|
|
126
|
+
// Resolve controller instance from DI container ONCE (not on every request!)
|
|
127
|
+
const controller = this.container.get(route.controllerClass);
|
|
128
|
+
// Get the controller method
|
|
129
|
+
const method = controller[routeMeta.methodName];
|
|
130
|
+
if (typeof method !== 'function') {
|
|
131
|
+
const controllerName = route.controllerClass.name || 'Unknown';
|
|
132
|
+
throw new Error(`Method ${routeMeta.methodName} not found on controller ${controllerName}`);
|
|
133
|
+
}
|
|
134
|
+
const handler = new RouteHandlerImpl(controller, method);
|
|
135
|
+
// Return handler with route definition
|
|
136
|
+
return new RouteHandlerWithMeta(handler, route);
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Register a filter with the filter chain.
|
|
140
|
+
*
|
|
141
|
+
* Resolves the filter from DI container and pairs it with the filter definition.
|
|
142
|
+
* The definition includes pattern information used for route-specific filtering.
|
|
143
|
+
*
|
|
144
|
+
* @param filterDef - Filter definition with priority, filter class, and optional filepath pattern
|
|
145
|
+
*/
|
|
146
|
+
addFilter(filterDef) {
|
|
147
|
+
if (!this.container) {
|
|
148
|
+
throw new Error('Container not set. Call setContainer() before registering filters.');
|
|
149
|
+
}
|
|
150
|
+
// Resolve filter instance from DI container
|
|
151
|
+
const filter = this.container.get(filterDef.filterClass);
|
|
152
|
+
// Store filter with its definition
|
|
153
|
+
const filterWithMeta = new FilterWithMeta(filter, filterDef);
|
|
154
|
+
this.filterRegistry.push(filterWithMeta);
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Get all registered routes.
|
|
158
|
+
*
|
|
159
|
+
* @returns Map of routes with handlers and definitions, keyed by "METHOD:path"
|
|
160
|
+
*/
|
|
161
|
+
getRoutes() {
|
|
162
|
+
return this.routes;
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Get all filters sorted by priority (highest priority first).
|
|
166
|
+
*
|
|
167
|
+
* @returns Array of FilterWithMeta sorted by priority
|
|
168
|
+
*/
|
|
169
|
+
getSortedFilters() {
|
|
170
|
+
return [...this.filterRegistry].sort((a, b) => b.definition.priority - a.definition.priority);
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Get filter definitions, computing once and caching.
|
|
174
|
+
*/
|
|
175
|
+
getFilterDefinitions() {
|
|
176
|
+
if (!this.cachedFilterDefinitions) {
|
|
177
|
+
const sortedFilters = this.getSortedFilters();
|
|
178
|
+
this.cachedFilterDefinitions = sortedFilters.map((fwm) => {
|
|
179
|
+
const def = fwm.definition;
|
|
180
|
+
def.filter = fwm.filter;
|
|
181
|
+
return def;
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
return this.cachedFilterDefinitions;
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* Setup a single route by creating its filter chain.
|
|
188
|
+
* This is called lazily by createHandler() and getRouteService().
|
|
189
|
+
*
|
|
190
|
+
* Creates a Service that wraps the filter chain and controller invocation.
|
|
191
|
+
* The service is DTO-only and has no Express dependency.
|
|
192
|
+
*
|
|
193
|
+
* @param key - Route key in format "METHOD:path"
|
|
194
|
+
* @param routeWithMeta - Route handler with metadata
|
|
195
|
+
* @returns The service for this route
|
|
196
|
+
*/
|
|
197
|
+
createRouteHandler(routeWithMeta) {
|
|
198
|
+
const route = routeWithMeta.definition;
|
|
199
|
+
const routeMeta = route.routeMeta;
|
|
200
|
+
console.log(`[RouteBuilder] Setting up route: ${routeMeta.httpMethod} ${routeMeta.path}`);
|
|
201
|
+
// Get cached filter definitions
|
|
202
|
+
const filterDefinitions = this.getFilterDefinitions();
|
|
203
|
+
// Find matching filters for this route
|
|
204
|
+
const matchingFilters = FilterMatcher_1.FilterMatcher.findMatchingFilters(route.controllerFilepath, filterDefinitions);
|
|
205
|
+
// Create service that wraps the controller execution
|
|
206
|
+
const controllerService = {
|
|
207
|
+
invoke: async (meta) => {
|
|
208
|
+
const result = await routeWithMeta.invokeControllerHandler.execute(meta);
|
|
209
|
+
return new http_filters_1.WpResponse(result);
|
|
210
|
+
},
|
|
211
|
+
};
|
|
212
|
+
// Chain filters with the controller service (reverse order for correct execution)
|
|
213
|
+
let filterChain = matchingFilters[matchingFilters.length - 1];
|
|
214
|
+
for (let i = matchingFilters.length - 2; i >= 0; i--) {
|
|
215
|
+
filterChain = filterChain.chain(matchingFilters[i]);
|
|
216
|
+
}
|
|
217
|
+
return filterChain.chainService(controllerService);
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* Create an invoker function for a route (for testing via createApiClient).
|
|
221
|
+
* Uses routeMap for O(1) lookup, sets up the filter chain ONCE,
|
|
222
|
+
* and returns a Service that can be called multiple times without
|
|
223
|
+
* recreating the filter chain.
|
|
224
|
+
*
|
|
225
|
+
* This method is called by WebpiecesServer.createApiClient() during proxy setup.
|
|
226
|
+
* The returned Service is stored as the proxy method and invoked on each call.
|
|
227
|
+
*
|
|
228
|
+
* @param method - HTTP method (GET, POST, etc.)
|
|
229
|
+
* @param path - URL path
|
|
230
|
+
* @returns A Service that invokes the route
|
|
231
|
+
*/
|
|
232
|
+
createRouteInvoker(method, path) {
|
|
233
|
+
// Use routeMap for O(1) lookup (not linear search!)
|
|
234
|
+
const key = this.createRouteKey(method, path);
|
|
235
|
+
const routeWithMeta = this.routeMap.get(key);
|
|
236
|
+
if (!routeWithMeta) {
|
|
237
|
+
throw new Error(`Route not found: ${method} ${path}`);
|
|
238
|
+
}
|
|
239
|
+
// Setup filter chain ONCE (not on every invocation!)
|
|
240
|
+
return this.createRouteHandler(routeWithMeta);
|
|
241
|
+
}
|
|
242
|
+
};
|
|
243
|
+
exports.RouteBuilderImpl = RouteBuilderImpl;
|
|
244
|
+
exports.RouteBuilderImpl = RouteBuilderImpl = tslib_1.__decorate([
|
|
245
|
+
(0, decorators_1.provideSingleton)(),
|
|
246
|
+
(0, inversify_1.injectable)()
|
|
247
|
+
], RouteBuilderImpl);
|
|
248
|
+
//# sourceMappingURL=RouteBuilderImpl.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RouteBuilderImpl.js","sourceRoot":"","sources":["../../../../../packages/http/http-routing/src/RouteBuilderImpl.ts"],"names":[],"mappings":"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","sourcesContent":["import { Container, injectable } from 'inversify';\nimport { Request, Response, NextFunction } from 'express';\nimport { RouteBuilder, RouteDefinition, FilterDefinition } from './WebAppMeta';\nimport { provideSingleton } from './decorators';\nimport { RouteHandler } from './RouteHandler';\nimport { MethodMeta } from './MethodMeta';\nimport { WpResponse, Service } from '@webpieces/http-filters';\nimport { FilterMatcher, HttpFilter } from './FilterMatcher';\n\n/**\n * Express route handler function type.\n * Used by wrapExpress to create handlers that Express can call.\n */\nexport type ExpressRouteHandler = (\n req: Request,\n res: Response,\n next: NextFunction,\n) => Promise<void>;\n\n/**\n * FilterWithMeta - Pairs a resolved filter instance with its definition.\n * Stores both the DI-resolved filter and the metadata needed for matching.\n */\nexport class FilterWithMeta {\n constructor(\n public filter: HttpFilter,\n public definition: FilterDefinition,\n ) {}\n}\n\n/**\n * RouteHandlerImpl - Concrete implementation of RouteHandler.\n * Wraps a resolved controller and method to invoke on each request.\n */\nexport class RouteHandlerImpl<TResult> implements RouteHandler<TResult> {\n constructor(\n private controller: Record<string, unknown>,\n private method: (this: unknown, requestDto?: unknown) => Promise<TResult>,\n ) {}\n\n async execute(meta: MethodMeta): Promise<TResult> {\n // Invoke the method with requestDto from meta\n // The controller is already resolved - no DI lookup on every request!\n const result: TResult = await this.method.call(this.controller, meta.requestDto);\n return result;\n }\n}\n/**\n * RouteHandlerWithMeta - Pairs a route handler with its definition.\n * Stores both the handler (which wraps the DI-resolved controller) and the route metadata.\n *\n * We use unknown for the generic type since we store different TResult types in the same Map.\n * Type safety is maintained through the generic on RouteDefinition at registration time.\n */\nexport class RouteHandlerWithMeta {\n constructor(\n public invokeControllerHandler: RouteHandler<unknown>,\n public definition: RouteDefinition,\n ) {}\n}\n\n/**\n * RouteBuilderImpl - Concrete implementation of RouteBuilder interface.\n *\n * Similar to Java WebPieces RouteBuilder, this class is responsible for:\n * 1. Registering routes with their handlers\n * 2. Registering filters with priority\n *\n * This class is explicit (not anonymous) to:\n * - Improve traceability and debugging\n * - Make the code easier to understand\n * - Enable better IDE navigation (Cmd+Click on addRoute works!)\n *\n * DI Pattern: This class is registered in webpiecesContainer via @provideSingleton()\n * but needs appContainer to resolve filters/controllers. The container is set via\n * setContainer() after appContainer is created (late binding pattern).\n */\n@provideSingleton()\n@injectable()\nexport class RouteBuilderImpl implements RouteBuilder {\n private routes: RouteHandlerWithMeta[] = [];\n private filterRegistry: Array<FilterWithMeta> = [];\n private container?: Container;\n\n /**\n * Map for O(1) route lookup by method:path.\n * Used by both addRoute() and createRouteInvoker() for fast route access.\n */\n private routeMap: Map<string, RouteHandlerWithMeta> = new Map();\n\n /**\n * Create route key for consistent lookup.\n * Key format: \"${METHOD}:${path}\" (e.g., \"POST:/search/item\")\n */\n private createRouteKey(method: string, path: string): string {\n return `${method.toUpperCase()}:${path}`;\n }\n\n /**\n * Set the DI container used for resolving filters and controllers.\n * Called by WebpiecesCoreServer after appContainer is created.\n *\n * @param container - The application DI container (appContainer)\n */\n setContainer(container: Container): void {\n this.container = container;\n }\n\n /**\n * Register a route with the router.\n *\n * Uses createRouteHandlerWithMeta() to create the handler, then stores it\n * in both the routes array and the routeMap for O(1) lookup.\n *\n * @param route - Route definition with controller class and method name\n */\n addRoute(route: RouteDefinition): void {\n const routeWithMeta = this.createRouteHandlerWithMeta(route);\n this.routes.push(routeWithMeta);\n\n // Also add to map for O(1) lookup by method:path\n const key = this.createRouteKey(\n route.routeMeta.httpMethod,\n route.routeMeta.path\n );\n this.routeMap.set(key, routeWithMeta);\n }\n\n /**\n * Create RouteHandlerWithMeta from a RouteDefinition.\n *\n * Resolves controller from DI container ONCE and creates a handler that\n * invokes the controller method with the request DTO.\n *\n * This method is used by:\n * - addRoute() for production route registration\n * - createRouteInvoker() for test clients (via createApiClient)\n *\n * @param route - Route definition with controller class and method name\n * @returns RouteHandlerWithMeta containing the handler and route definition\n */\n private createRouteHandlerWithMeta<TResult = unknown>(\n route: RouteDefinition,\n ): RouteHandlerWithMeta {\n if (!this.container) {\n throw new Error('Container not set. Call setContainer() before registering routes.');\n }\n\n const routeMeta = route.routeMeta;\n\n // Resolve controller instance from DI container ONCE (not on every request!)\n const controller = this.container.get(route.controllerClass) as Record<string, unknown>;\n\n // Get the controller method\n const method = controller[routeMeta.methodName];\n if (typeof method !== 'function') {\n const controllerName = (route.controllerClass as { name?: string }).name || 'Unknown';\n throw new Error(\n `Method ${routeMeta.methodName} not found on controller ${controllerName}`,\n );\n }\n\n const handler = new RouteHandlerImpl<TResult>(\n controller,\n method as (this: unknown, requestDto?: unknown) => Promise<TResult>\n );\n\n // Return handler with route definition\n return new RouteHandlerWithMeta(\n handler as RouteHandler<unknown>,\n route,\n );\n }\n\n /**\n * Register a filter with the filter chain.\n *\n * Resolves the filter from DI container and pairs it with the filter definition.\n * The definition includes pattern information used for route-specific filtering.\n *\n * @param filterDef - Filter definition with priority, filter class, and optional filepath pattern\n */\n addFilter(filterDef: FilterDefinition): void {\n if (!this.container) {\n throw new Error('Container not set. Call setContainer() before registering filters.');\n }\n\n // Resolve filter instance from DI container\n const filter = this.container.get<HttpFilter>(filterDef.filterClass);\n\n // Store filter with its definition\n const filterWithMeta = new FilterWithMeta(filter, filterDef);\n this.filterRegistry.push(filterWithMeta);\n }\n\n /**\n * Get all registered routes.\n *\n * @returns Map of routes with handlers and definitions, keyed by \"METHOD:path\"\n */\n getRoutes(): RouteHandlerWithMeta[] {\n return this.routes;\n }\n\n /**\n * Get all filters sorted by priority (highest priority first).\n *\n * @returns Array of FilterWithMeta sorted by priority\n */\n getSortedFilters(): Array<FilterWithMeta> {\n return [...this.filterRegistry].sort(\n (a, b) => b.definition.priority - a.definition.priority,\n );\n }\n\n /**\n * Cached filter definitions for lazy route setup.\n */\n private cachedFilterDefinitions?: FilterDefinition[];\n\n /**\n * Get filter definitions, computing once and caching.\n */\n private getFilterDefinitions(): FilterDefinition[] {\n if (!this.cachedFilterDefinitions) {\n const sortedFilters = this.getSortedFilters();\n this.cachedFilterDefinitions = sortedFilters.map((fwm) => {\n const def = fwm.definition;\n def.filter = fwm.filter;\n return def;\n });\n }\n return this.cachedFilterDefinitions;\n }\n\n /**\n * Setup a single route by creating its filter chain.\n * This is called lazily by createHandler() and getRouteService().\n *\n * Creates a Service that wraps the filter chain and controller invocation.\n * The service is DTO-only and has no Express dependency.\n *\n * @param key - Route key in format \"METHOD:path\"\n * @param routeWithMeta - Route handler with metadata\n * @returns The service for this route\n */\n public createRouteHandler(\n routeWithMeta: RouteHandlerWithMeta,\n ): Service<MethodMeta, WpResponse<unknown>> {\n const route = routeWithMeta.definition;\n const routeMeta = route.routeMeta;\n\n console.log(`[RouteBuilder] Setting up route: ${routeMeta.httpMethod} ${routeMeta.path}`);\n\n // Get cached filter definitions\n const filterDefinitions = this.getFilterDefinitions();\n\n // Find matching filters for this route\n const matchingFilters = FilterMatcher.findMatchingFilters(\n route.controllerFilepath,\n filterDefinitions,\n );\n\n // Create service that wraps the controller execution\n const controllerService: Service<MethodMeta, WpResponse<unknown>> = {\n invoke: async (meta: MethodMeta): Promise<WpResponse<unknown>> => {\n const result = await routeWithMeta.invokeControllerHandler.execute(meta);\n return new WpResponse(result);\n },\n };\n\n // Chain filters with the controller service (reverse order for correct execution)\n let filterChain = matchingFilters[matchingFilters.length - 1];\n for (let i = matchingFilters.length - 2; i >= 0; i--) {\n filterChain = filterChain.chain(matchingFilters[i]);\n }\n\n return filterChain.chainService(controllerService);\n }\n\n /**\n * Create an invoker function for a route (for testing via createApiClient).\n * Uses routeMap for O(1) lookup, sets up the filter chain ONCE,\n * and returns a Service that can be called multiple times without\n * recreating the filter chain.\n *\n * This method is called by WebpiecesServer.createApiClient() during proxy setup.\n * The returned Service is stored as the proxy method and invoked on each call.\n *\n * @param method - HTTP method (GET, POST, etc.)\n * @param path - URL path\n * @returns A Service that invokes the route\n */\n createRouteInvoker(method: string, path: string): Service<MethodMeta, WpResponse<unknown>> {\n // Use routeMap for O(1) lookup (not linear search!)\n const key = this.createRouteKey(method, path);\n const routeWithMeta = this.routeMap.get(key);\n\n if (!routeWithMeta) {\n throw new Error(`Route not found: ${method} ${path}`);\n }\n\n // Setup filter chain ONCE (not on every invocation!)\n return this.createRouteHandler(routeWithMeta);\n }\n}\n"]}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { MethodMeta } from './MethodMeta';
|
|
2
|
+
/**
|
|
3
|
+
* Handler class for routes.
|
|
4
|
+
* Takes a MethodMeta and returns the controller method result.
|
|
5
|
+
*
|
|
6
|
+
* Generic type parameter TResult represents the return type of the controller method.
|
|
7
|
+
* Example: RouteHandler<SaveResponse> for a method that returns Promise<SaveResponse>
|
|
8
|
+
*
|
|
9
|
+
* Using unknown as default instead of any forces type safety - consumers must
|
|
10
|
+
* handle the result appropriately rather than assuming any type.
|
|
11
|
+
*
|
|
12
|
+
* This is a class instead of a function type to make it easier to trace
|
|
13
|
+
* who is calling what in the debugger/IDE.
|
|
14
|
+
*/
|
|
15
|
+
export declare abstract class RouteHandler<TResult = unknown> {
|
|
16
|
+
/**
|
|
17
|
+
* Execute the route handler.
|
|
18
|
+
* @param meta - The method metadata containing request info and params
|
|
19
|
+
* @returns Promise of the controller method result
|
|
20
|
+
*/
|
|
21
|
+
abstract execute(meta: MethodMeta): Promise<TResult>;
|
|
22
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.RouteHandler = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Handler class for routes.
|
|
6
|
+
* Takes a MethodMeta and returns the controller method result.
|
|
7
|
+
*
|
|
8
|
+
* Generic type parameter TResult represents the return type of the controller method.
|
|
9
|
+
* Example: RouteHandler<SaveResponse> for a method that returns Promise<SaveResponse>
|
|
10
|
+
*
|
|
11
|
+
* Using unknown as default instead of any forces type safety - consumers must
|
|
12
|
+
* handle the result appropriately rather than assuming any type.
|
|
13
|
+
*
|
|
14
|
+
* This is a class instead of a function type to make it easier to trace
|
|
15
|
+
* who is calling what in the debugger/IDE.
|
|
16
|
+
*/
|
|
17
|
+
class RouteHandler {
|
|
18
|
+
}
|
|
19
|
+
exports.RouteHandler = RouteHandler;
|
|
20
|
+
//# sourceMappingURL=RouteHandler.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RouteHandler.js","sourceRoot":"","sources":["../../../../../packages/http/http-routing/src/RouteHandler.ts"],"names":[],"mappings":";;;AAEA;;;;;;;;;;;;GAYG;AACH,MAAsB,YAAY;CAOjC;AAPD,oCAOC","sourcesContent":["import { MethodMeta } from './MethodMeta';\n\n/**\n * Handler class for routes.\n * Takes a MethodMeta and returns the controller method result.\n *\n * Generic type parameter TResult represents the return type of the controller method.\n * Example: RouteHandler<SaveResponse> for a method that returns Promise<SaveResponse>\n *\n * Using unknown as default instead of any forces type safety - consumers must\n * handle the result appropriately rather than assuming any type.\n *\n * This is a class instead of a function type to make it easier to trace\n * who is calling what in the debugger/IDE.\n */\nexport abstract class RouteHandler<TResult = unknown> {\n /**\n * Execute the route handler.\n * @param meta - The method metadata containing request info and params\n * @returns Promise of the controller method result\n */\n abstract execute(meta: MethodMeta): Promise<TResult>;\n}\n"]}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { ContainerModule } from 'inversify';
|
|
2
|
+
import { RouteMetadata } from "@webpieces/http-api";
|
|
3
|
+
/**
|
|
4
|
+
* Represents a route configuration that can be registered with the router.
|
|
5
|
+
* Similar to Java WebPieces Routes interface.
|
|
6
|
+
*/
|
|
7
|
+
export interface Routes {
|
|
8
|
+
/**
|
|
9
|
+
* Configure routes using the provided RouteBuilder.
|
|
10
|
+
*/
|
|
11
|
+
configure(routeBuilder: RouteBuilder): void;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Builder for registering routes.
|
|
15
|
+
* Will be implemented in http-server package.
|
|
16
|
+
*/
|
|
17
|
+
export interface RouteBuilder {
|
|
18
|
+
addRoute(route: RouteDefinition): void;
|
|
19
|
+
addFilter(filter: FilterDefinition): void;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Definition of a single route.
|
|
23
|
+
*
|
|
24
|
+
* Generic type parameter TResult represents the return type of the route handler.
|
|
25
|
+
* This provides type safety for the entire request/response cycle.
|
|
26
|
+
*/
|
|
27
|
+
export declare class RouteDefinition {
|
|
28
|
+
routeMeta: RouteMetadata;
|
|
29
|
+
controllerClass: any;
|
|
30
|
+
controllerFilepath?: string | undefined;
|
|
31
|
+
constructor(routeMeta: RouteMetadata, controllerClass: any, controllerFilepath?: string | undefined);
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Definition of a filter with priority.
|
|
35
|
+
*
|
|
36
|
+
* Use filepathPattern to scope filters to specific controllers:
|
|
37
|
+
* - 'src/controllers/admin/**' + '/*.ts' - All admin controllers
|
|
38
|
+
* - '**' + '/admin/**' - Any file in admin directories
|
|
39
|
+
* - '**' + '/UserController.ts' - Specific controller file
|
|
40
|
+
*
|
|
41
|
+
* If filepathPattern is not specified, the filter matches all controllers.
|
|
42
|
+
*/
|
|
43
|
+
export declare class FilterDefinition {
|
|
44
|
+
priority: number;
|
|
45
|
+
filterClass: any;
|
|
46
|
+
filter?: any;
|
|
47
|
+
/**
|
|
48
|
+
* Glob pattern to match controller file paths.
|
|
49
|
+
* If not specified, defaults to matching all controllers.
|
|
50
|
+
*/
|
|
51
|
+
filepathPattern: string;
|
|
52
|
+
constructor(priority: number, filterClass: any, filepathPattern: string);
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Main application metadata interface.
|
|
56
|
+
* Similar to Java WebPieces WebAppMeta.
|
|
57
|
+
*
|
|
58
|
+
* This is the entry point that WebpiecesServer calls to configure your application.
|
|
59
|
+
*/
|
|
60
|
+
export interface WebAppMeta {
|
|
61
|
+
/**
|
|
62
|
+
* Returns the list of Inversify container modules for dependency injection.
|
|
63
|
+
* Similar to getGuiceModules() in Java.
|
|
64
|
+
*/
|
|
65
|
+
getDIModules(): ContainerModule[];
|
|
66
|
+
/**
|
|
67
|
+
* Returns the list of route configurations.
|
|
68
|
+
* Similar to getRouteModules() in Java.
|
|
69
|
+
*/
|
|
70
|
+
getRoutes(): Routes[];
|
|
71
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.FilterDefinition = exports.RouteDefinition = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Definition of a single route.
|
|
6
|
+
*
|
|
7
|
+
* Generic type parameter TResult represents the return type of the route handler.
|
|
8
|
+
* This provides type safety for the entire request/response cycle.
|
|
9
|
+
*/
|
|
10
|
+
class RouteDefinition {
|
|
11
|
+
constructor(routeMeta, controllerClass, controllerFilepath) {
|
|
12
|
+
this.routeMeta = routeMeta;
|
|
13
|
+
this.controllerClass = controllerClass;
|
|
14
|
+
this.controllerFilepath = controllerFilepath;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
exports.RouteDefinition = RouteDefinition;
|
|
18
|
+
/**
|
|
19
|
+
* Definition of a filter with priority.
|
|
20
|
+
*
|
|
21
|
+
* Use filepathPattern to scope filters to specific controllers:
|
|
22
|
+
* - 'src/controllers/admin/**' + '/*.ts' - All admin controllers
|
|
23
|
+
* - '**' + '/admin/**' - Any file in admin directories
|
|
24
|
+
* - '**' + '/UserController.ts' - Specific controller file
|
|
25
|
+
*
|
|
26
|
+
* If filepathPattern is not specified, the filter matches all controllers.
|
|
27
|
+
*/
|
|
28
|
+
class FilterDefinition {
|
|
29
|
+
constructor(priority, filterClass, filepathPattern) {
|
|
30
|
+
this.priority = priority;
|
|
31
|
+
this.filterClass = filterClass;
|
|
32
|
+
this.filepathPattern = filepathPattern;
|
|
33
|
+
this.filter = undefined; // Set later by RouteBuilder
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
exports.FilterDefinition = FilterDefinition;
|
|
37
|
+
//# sourceMappingURL=WebAppMeta.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"WebAppMeta.js","sourceRoot":"","sources":["../../../../../packages/http/http-routing/src/WebAppMeta.ts"],"names":[],"mappings":";;;AAuBA;;;;;GAKG;AACH,MAAa,eAAe;IACxB,YACW,SAAwB,EACxB,eAAoB,EACpB,kBAA2B;QAF3B,cAAS,GAAT,SAAS,CAAe;QACxB,oBAAe,GAAf,eAAe,CAAK;QACpB,uBAAkB,GAAlB,kBAAkB,CAAS;IACnC,CAAC;CACP;AAND,0CAMC;AAED;;;;;;;;;GASG;AACH,MAAa,gBAAgB;IAWzB,YAAY,QAAgB,EAAE,WAAgB,EAAE,eAAuB;QACnE,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;QACzB,IAAI,CAAC,WAAW,GAAG,WAAW,CAAC;QAC/B,IAAI,CAAC,eAAe,GAAG,eAAe,CAAC;QACvC,IAAI,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC,4BAA4B;IACzD,CAAC;CACJ;AAjBD,4CAiBC","sourcesContent":["import { ContainerModule } from 'inversify';\nimport {RouteMetadata} from \"@webpieces/http-api\";\n\n/**\n * Represents a route configuration that can be registered with the router.\n * Similar to Java WebPieces Routes interface.\n */\nexport interface Routes {\n /**\n * Configure routes using the provided RouteBuilder.\n */\n configure(routeBuilder: RouteBuilder): void;\n}\n\n/**\n * Builder for registering routes.\n * Will be implemented in http-server package.\n */\nexport interface RouteBuilder {\n addRoute(route: RouteDefinition): void;\n addFilter(filter: FilterDefinition): void;\n}\n\n/**\n * Definition of a single route.\n *\n * Generic type parameter TResult represents the return type of the route handler.\n * This provides type safety for the entire request/response cycle.\n */\nexport class RouteDefinition {\n constructor(\n public routeMeta: RouteMetadata,\n public controllerClass: any,\n public controllerFilepath?: string,\n ) {}\n}\n\n/**\n * Definition of a filter with priority.\n *\n * Use filepathPattern to scope filters to specific controllers:\n * - 'src/controllers/admin/**' + '/*.ts' - All admin controllers\n * - '**' + '/admin/**' - Any file in admin directories\n * - '**' + '/UserController.ts' - Specific controller file\n *\n * If filepathPattern is not specified, the filter matches all controllers.\n */\nexport class FilterDefinition {\n priority: number;\n filterClass: any;\n filter?: any; // Filter instance (set by RouteBuilder when resolving from DI)\n\n /**\n * Glob pattern to match controller file paths.\n * If not specified, defaults to matching all controllers.\n */\n filepathPattern: string;\n\n constructor(priority: number, filterClass: any, filepathPattern: string) {\n this.priority = priority;\n this.filterClass = filterClass;\n this.filepathPattern = filepathPattern;\n this.filter = undefined; // Set later by RouteBuilder\n }\n}\n\n\n/**\n * Main application metadata interface.\n * Similar to Java WebPieces WebAppMeta.\n *\n * This is the entry point that WebpiecesServer calls to configure your application.\n */\nexport interface WebAppMeta {\n /**\n * Returns the list of Inversify container modules for dependency injection.\n * Similar to getGuiceModules() in Java.\n */\n getDIModules(): ContainerModule[];\n\n /**\n * Returns the list of route configurations.\n * Similar to getRouteModules() in Java.\n */\n getRoutes(): Routes[];\n}\n"]}
|
package/src/index.d.ts
CHANGED
|
@@ -1,3 +1,8 @@
|
|
|
1
1
|
export { ApiInterface, Get, Post, Put, Delete, Patch, Path, getRoutes, isApiInterface, RouteMetadata, METADATA_KEYS, ValidateImplementation, } from '@webpieces/http-api';
|
|
2
2
|
export { Controller, isController, provideSingleton, provideTransient, ROUTING_METADATA_KEYS, } from './decorators';
|
|
3
3
|
export { RESTApiRoutes, ClassType } from './RESTApiRoutes';
|
|
4
|
+
export { WebAppMeta, Routes, RouteBuilder, RouteDefinition, FilterDefinition, } from './WebAppMeta';
|
|
5
|
+
export { MethodMeta } from './MethodMeta';
|
|
6
|
+
export { RouteHandler } from './RouteHandler';
|
|
7
|
+
export { RouteBuilderImpl, RouteHandlerWithMeta, FilterWithMeta, ExpressRouteHandler, } from './RouteBuilderImpl';
|
|
8
|
+
export { FilterMatcher, HttpFilter } from './FilterMatcher';
|
package/src/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.RESTApiRoutes = exports.ROUTING_METADATA_KEYS = exports.provideTransient = exports.provideSingleton = exports.isController = exports.Controller = exports.METADATA_KEYS = exports.RouteMetadata = exports.isApiInterface = exports.getRoutes = exports.Path = exports.Patch = exports.Delete = exports.Put = exports.Post = exports.Get = exports.ApiInterface = void 0;
|
|
3
|
+
exports.FilterMatcher = exports.FilterWithMeta = exports.RouteHandlerWithMeta = exports.RouteBuilderImpl = exports.RouteHandler = exports.MethodMeta = exports.FilterDefinition = exports.RouteDefinition = exports.RESTApiRoutes = exports.ROUTING_METADATA_KEYS = exports.provideTransient = exports.provideSingleton = exports.isController = exports.Controller = exports.METADATA_KEYS = exports.RouteMetadata = exports.isApiInterface = exports.getRoutes = exports.Path = exports.Patch = exports.Delete = exports.Put = exports.Post = exports.Get = exports.ApiInterface = void 0;
|
|
4
4
|
// Re-export API decorators from http-api for convenience
|
|
5
5
|
var http_api_1 = require("@webpieces/http-api");
|
|
6
6
|
Object.defineProperty(exports, "ApiInterface", { enumerable: true, get: function () { return http_api_1.ApiInterface; } });
|
|
@@ -23,4 +23,21 @@ Object.defineProperty(exports, "provideTransient", { enumerable: true, get: func
|
|
|
23
23
|
Object.defineProperty(exports, "ROUTING_METADATA_KEYS", { enumerable: true, get: function () { return decorators_1.ROUTING_METADATA_KEYS; } });
|
|
24
24
|
var RESTApiRoutes_1 = require("./RESTApiRoutes");
|
|
25
25
|
Object.defineProperty(exports, "RESTApiRoutes", { enumerable: true, get: function () { return RESTApiRoutes_1.RESTApiRoutes; } });
|
|
26
|
+
// Core routing types (moved from core-meta)
|
|
27
|
+
var WebAppMeta_1 = require("./WebAppMeta");
|
|
28
|
+
Object.defineProperty(exports, "RouteDefinition", { enumerable: true, get: function () { return WebAppMeta_1.RouteDefinition; } });
|
|
29
|
+
Object.defineProperty(exports, "FilterDefinition", { enumerable: true, get: function () { return WebAppMeta_1.FilterDefinition; } });
|
|
30
|
+
// Method metadata and route handler
|
|
31
|
+
var MethodMeta_1 = require("./MethodMeta");
|
|
32
|
+
Object.defineProperty(exports, "MethodMeta", { enumerable: true, get: function () { return MethodMeta_1.MethodMeta; } });
|
|
33
|
+
var RouteHandler_1 = require("./RouteHandler");
|
|
34
|
+
Object.defineProperty(exports, "RouteHandler", { enumerable: true, get: function () { return RouteHandler_1.RouteHandler; } });
|
|
35
|
+
// Route builder implementation
|
|
36
|
+
var RouteBuilderImpl_1 = require("./RouteBuilderImpl");
|
|
37
|
+
Object.defineProperty(exports, "RouteBuilderImpl", { enumerable: true, get: function () { return RouteBuilderImpl_1.RouteBuilderImpl; } });
|
|
38
|
+
Object.defineProperty(exports, "RouteHandlerWithMeta", { enumerable: true, get: function () { return RouteBuilderImpl_1.RouteHandlerWithMeta; } });
|
|
39
|
+
Object.defineProperty(exports, "FilterWithMeta", { enumerable: true, get: function () { return RouteBuilderImpl_1.FilterWithMeta; } });
|
|
40
|
+
// Filter matching
|
|
41
|
+
var FilterMatcher_1 = require("./FilterMatcher");
|
|
42
|
+
Object.defineProperty(exports, "FilterMatcher", { enumerable: true, get: function () { return FilterMatcher_1.FilterMatcher; } });
|
|
26
43
|
//# sourceMappingURL=index.js.map
|
package/src/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../packages/http/http-routing/src/index.ts"],"names":[],"mappings":";;;AAAA,yDAAyD;AACzD,gDAa6B;AAZzB,wGAAA,YAAY,OAAA;AACZ,+FAAA,GAAG,OAAA;AACH,gGAAA,IAAI,OAAA;AACJ,+FAAA,GAAG,OAAA;AACH,kGAAA,MAAM,OAAA;AACN,iGAAA,KAAK,OAAA;AACL,gGAAA,IAAI,OAAA;AACJ,qGAAA,SAAS,OAAA;AACT,0GAAA,cAAc,OAAA;AACd,yGAAA,aAAa,OAAA;AACb,yGAAA,aAAa,OAAA;AAIjB,+CAA+C;AAC/C,2CAMsB;AALlB,wGAAA,UAAU,OAAA;AACV,0GAAA,YAAY,OAAA;AACZ,8GAAA,gBAAgB,OAAA;AAChB,8GAAA,gBAAgB,OAAA;AAChB,mHAAA,qBAAqB,OAAA;AAGzB,iDAA2D;AAAlD,8GAAA,aAAa,OAAA","sourcesContent":["// Re-export API decorators from http-api for convenience\nexport {\n ApiInterface,\n Get,\n Post,\n Put,\n Delete,\n Patch,\n Path,\n getRoutes,\n isApiInterface,\n RouteMetadata,\n METADATA_KEYS,\n ValidateImplementation,\n} from '@webpieces/http-api';\n\n// Server-side routing decorators and utilities\nexport {\n Controller,\n isController,\n provideSingleton,\n provideTransient,\n ROUTING_METADATA_KEYS,\n} from './decorators';\n\nexport { RESTApiRoutes, ClassType } from './RESTApiRoutes';\n"]}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../packages/http/http-routing/src/index.ts"],"names":[],"mappings":";;;AAAA,yDAAyD;AACzD,gDAa6B;AAZzB,wGAAA,YAAY,OAAA;AACZ,+FAAA,GAAG,OAAA;AACH,gGAAA,IAAI,OAAA;AACJ,+FAAA,GAAG,OAAA;AACH,kGAAA,MAAM,OAAA;AACN,iGAAA,KAAK,OAAA;AACL,gGAAA,IAAI,OAAA;AACJ,qGAAA,SAAS,OAAA;AACT,0GAAA,cAAc,OAAA;AACd,yGAAA,aAAa,OAAA;AACb,yGAAA,aAAa,OAAA;AAIjB,+CAA+C;AAC/C,2CAMsB;AALlB,wGAAA,UAAU,OAAA;AACV,0GAAA,YAAY,OAAA;AACZ,8GAAA,gBAAgB,OAAA;AAChB,8GAAA,gBAAgB,OAAA;AAChB,mHAAA,qBAAqB,OAAA;AAGzB,iDAA2D;AAAlD,8GAAA,aAAa,OAAA;AAEtB,4CAA4C;AAC5C,2CAMsB;AAFlB,6GAAA,eAAe,OAAA;AACf,8GAAA,gBAAgB,OAAA;AAGpB,oCAAoC;AACpC,2CAA0C;AAAjC,wGAAA,UAAU,OAAA;AACnB,+CAA8C;AAArC,4GAAA,YAAY,OAAA;AAErB,+BAA+B;AAC/B,uDAK4B;AAJxB,oHAAA,gBAAgB,OAAA;AAChB,wHAAA,oBAAoB,OAAA;AACpB,kHAAA,cAAc,OAAA;AAIlB,kBAAkB;AAClB,iDAA4D;AAAnD,8GAAA,aAAa,OAAA","sourcesContent":["// Re-export API decorators from http-api for convenience\nexport {\n ApiInterface,\n Get,\n Post,\n Put,\n Delete,\n Patch,\n Path,\n getRoutes,\n isApiInterface,\n RouteMetadata,\n METADATA_KEYS,\n ValidateImplementation,\n} from '@webpieces/http-api';\n\n// Server-side routing decorators and utilities\nexport {\n Controller,\n isController,\n provideSingleton,\n provideTransient,\n ROUTING_METADATA_KEYS,\n} from './decorators';\n\nexport { RESTApiRoutes, ClassType } from './RESTApiRoutes';\n\n// Core routing types (moved from core-meta)\nexport {\n WebAppMeta,\n Routes,\n RouteBuilder,\n RouteDefinition,\n FilterDefinition,\n} from './WebAppMeta';\n\n// Method metadata and route handler\nexport { MethodMeta } from './MethodMeta';\nexport { RouteHandler } from './RouteHandler';\n\n// Route builder implementation\nexport {\n RouteBuilderImpl,\n RouteHandlerWithMeta,\n FilterWithMeta,\n ExpressRouteHandler,\n} from './RouteBuilderImpl';\n\n// Filter matching\nexport { FilterMatcher, HttpFilter } from './FilterMatcher';\n"]}
|