@webpieces/http-server 0.2.11 → 0.2.13

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@webpieces/http-server",
3
- "version": "0.2.11",
3
+ "version": "0.2.13",
4
4
  "description": "WebPieces server with filter chain and dependency injection",
5
5
  "type": "commonjs",
6
6
  "main": "./src/index.js",
@@ -22,8 +22,8 @@
22
22
  "access": "public"
23
23
  },
24
24
  "dependencies": {
25
- "@webpieces/core-meta": "0.2.11",
26
- "@webpieces/http-routing": "0.2.11",
27
- "@webpieces/http-filters": "0.2.11"
25
+ "@webpieces/core-meta": "0.2.13",
26
+ "@webpieces/http-routing": "0.2.13",
27
+ "@webpieces/http-filters": "0.2.13"
28
28
  }
29
29
  }
@@ -1,5 +1,10 @@
1
- import { Filter } from '@webpieces/http-filters';
1
+ import { Filter, WpResponse } from '@webpieces/http-filters';
2
+ import { MethodMeta } from './MethodMeta';
2
3
  import { FilterDefinition } from '@webpieces/core-meta';
4
+ /**
5
+ * Type alias for HTTP filters that work with MethodMeta and ResponseWrapper.
6
+ */
7
+ export type HttpFilter = Filter<MethodMeta, WpResponse<unknown>>;
3
8
  /**
4
9
  * FilterMatcher - Matches filters to routes based on filepath patterns.
5
10
  * Similar to Java SharedMatchUtil.findMatchingFilters().
@@ -21,10 +26,7 @@ export declare class FilterMatcher {
21
26
  * @param allFilters - All registered filters with their definitions
22
27
  * @returns Array of matching filters, sorted by priority (highest first)
23
28
  */
24
- static findMatchingFilters(controllerFilepath: string | undefined, allFilters: Array<{
25
- filter: Filter;
26
- definition: FilterDefinition;
27
- }>): Filter[];
29
+ static findMatchingFilters(controllerFilepath: string | undefined, allFilters: Array<FilterDefinition>): HttpFilter[];
28
30
  /**
29
31
  * Normalize a controller filepath for consistent matching.
30
32
  * - Converts backslashes to forward slashes (Windows compatibility)
@@ -25,17 +25,18 @@ class FilterMatcher {
25
25
  */
26
26
  static findMatchingFilters(controllerFilepath, allFilters) {
27
27
  const matchingFilters = [];
28
- for (const { filter, definition } of allFilters) {
28
+ for (const definition of allFilters) {
29
29
  const pattern = definition.filepathPattern;
30
+ const filter = definition.filter;
30
31
  // Special case: '*' matches all controllers (global filter)
31
32
  if (pattern === '*') {
32
- matchingFilters.push({ filter, priority: filter.priority });
33
+ matchingFilters.push({ filter, priority: definition.priority });
33
34
  continue;
34
35
  }
35
36
  // If no filepath available, only match wildcard patterns
36
37
  if (!controllerFilepath) {
37
38
  if (pattern === '**/*') {
38
- matchingFilters.push({ filter, priority: filter.priority });
39
+ matchingFilters.push({ filter, priority: definition.priority });
39
40
  }
40
41
  continue;
41
42
  }
@@ -43,7 +44,7 @@ class FilterMatcher {
43
44
  const normalizedPath = FilterMatcher.normalizeFilepath(controllerFilepath);
44
45
  // Match using minimatch
45
46
  if ((0, minimatch_1.minimatch)(normalizedPath, pattern)) {
46
- matchingFilters.push({ filter, priority: filter.priority });
47
+ matchingFilters.push({ filter, priority: definition.priority });
47
48
  }
48
49
  }
49
50
  // Sort by priority (highest first)
@@ -1 +1 @@
1
- {"version":3,"file":"FilterMatcher.js","sourceRoot":"","sources":["../../../../../packages/http/http-server/src/FilterMatcher.ts"],"names":[],"mappings":";;;AAEA,yCAAsC;AAEtC;;;;;;;;;;;;GAYG;AACH,MAAa,aAAa;IACxB;;;;;;OAMG;IACH,MAAM,CAAC,mBAAmB,CACxB,kBAAsC,EACtC,UAAmE;QAEnE,MAAM,eAAe,GAAgD,EAAE,CAAC;QAExE,KAAK,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,IAAI,UAAU,EAAE,CAAC;YAChD,MAAM,OAAO,GAAG,UAAU,CAAC,eAAe,CAAC;YAE3C,4DAA4D;YAC5D,IAAI,OAAO,KAAK,GAAG,EAAE,CAAC;gBACpB,eAAe,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,CAAC,QAAQ,EAAE,CAAC,CAAC;gBAC5D,SAAS;YACX,CAAC;YAED,yDAAyD;YACzD,IAAI,CAAC,kBAAkB,EAAE,CAAC;gBACxB,IAAI,OAAO,KAAK,MAAM,EAAE,CAAC;oBACvB,eAAe,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,CAAC,QAAQ,EAAE,CAAC,CAAC;gBAC9D,CAAC;gBACD,SAAS;YACX,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;gBACvC,eAAe,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,CAAC,QAAQ,EAAE,CAAC,CAAC;YAC9D,CAAC;QACH,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;IACpD,CAAC;IAED;;;;;;;OAOG;IACH,MAAM,CAAC,iBAAiB,CAAC,QAAgB;QACvC,OAAO,QAAQ;aACZ,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,yCAAyC;aAC7D,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,CAAC,sBAAsB;IACjD,CAAC;CACF;AA3DD,sCA2DC","sourcesContent":["import { Filter } from '@webpieces/http-filters';\nimport { FilterDefinition } from '@webpieces/core-meta';\nimport { minimatch } from 'minimatch';\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<{ filter: Filter; definition: FilterDefinition }>\n ): Filter[] {\n const matchingFilters: Array<{ filter: Filter; priority: number }> = [];\n\n for (const { filter, definition } of allFilters) {\n const pattern = definition.filepathPattern;\n\n // Special case: '*' matches all controllers (global filter)\n if (pattern === '*') {\n matchingFilters.push({ filter, priority: filter.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: filter.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: filter.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"]}
1
+ {"version":3,"file":"FilterMatcher.js","sourceRoot":"","sources":["../../../../../packages/http/http-server/src/FilterMatcher.ts"],"names":[],"mappings":";;;AAGA,yCAAsC;AAOtC;;;;;;;;;;;;GAYG;AACH,MAAa,aAAa;IACxB;;;;;;OAMG;IACH,MAAM,CAAC,mBAAmB,CACxB,kBAAsC,EACtC,UAAmC;QAEnC,MAAM,eAAe,GAAoD,EAAE,CAAC;QAE5E,KAAK,MAAM,UAAU,IAAI,UAAU,EAAE,CAAC;YACpC,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;gBACpB,eAAe,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,UAAU,CAAC,QAAQ,EAAE,CAAC,CAAC;gBAChE,SAAS;YACX,CAAC;YAED,yDAAyD;YACzD,IAAI,CAAC,kBAAkB,EAAE,CAAC;gBACxB,IAAI,OAAO,KAAK,MAAM,EAAE,CAAC;oBACvB,eAAe,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,UAAU,CAAC,QAAQ,EAAE,CAAC,CAAC;gBAClE,CAAC;gBACD,SAAS;YACX,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;gBACvC,eAAe,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,UAAU,CAAC,QAAQ,EAAE,CAAC,CAAC;YAClE,CAAC;QACH,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;IACpD,CAAC;IAED;;;;;;;OAOG;IACH,MAAM,CAAC,iBAAiB,CAAC,QAAgB;QACvC,OAAO,QAAQ;aACZ,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,yCAAyC;aAC7D,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,CAAC,sBAAsB;IACjD,CAAC;CACF;AA5DD,sCA4DC","sourcesContent":["import { Filter, WpResponse } from '@webpieces/http-filters';\nimport { MethodMeta } from './MethodMeta';\nimport { FilterDefinition } from '@webpieces/core-meta';\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,42 @@
1
+ import { RouteMetadata2, RouteRequest } from '@webpieces/core-meta';
2
+ /**
3
+ * Metadata about the method being invoked.
4
+ * Passed to filters and contains request information.
5
+ *
6
+ * MethodMeta is created by WebpiecesCoreServer when handling a request:
7
+ * - routeMeta: Static route information (httpMethod, path, methodName)
8
+ * - routeRequest: Express Request/Response objects
9
+ * - requestDto: Set by JsonFilter after deserializing the request body
10
+ */
11
+ export declare class MethodMeta {
12
+ /**
13
+ * Route metadata (httpMethod, path, methodName, parameterTypes)
14
+ */
15
+ routeMeta: RouteMetadata2;
16
+ /**
17
+ * Express Request and Response objects
18
+ */
19
+ routeRequest: RouteRequest;
20
+ /**
21
+ * The deserialized request DTO.
22
+ * Set by JsonFilter after deserializing the request body.
23
+ */
24
+ requestDto?: unknown;
25
+ /**
26
+ * Additional metadata for storing request-scoped data.
27
+ */
28
+ metadata: Map<string, unknown>;
29
+ constructor(routeMeta: RouteMetadata2, routeRequest: RouteRequest, requestDto?: unknown, metadata?: Map<string, unknown>);
30
+ /**
31
+ * Get the HTTP method (convenience accessor).
32
+ */
33
+ get httpMethod(): string;
34
+ /**
35
+ * Get the request path (convenience accessor).
36
+ */
37
+ get path(): string;
38
+ /**
39
+ * Get the method name (convenience accessor).
40
+ */
41
+ get methodName(): string;
42
+ }
@@ -0,0 +1,40 @@
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 created by WebpiecesCoreServer when handling a request:
9
+ * - routeMeta: Static route information (httpMethod, path, methodName)
10
+ * - routeRequest: Express Request/Response objects
11
+ * - requestDto: Set by JsonFilter after deserializing the request body
12
+ */
13
+ class MethodMeta {
14
+ constructor(routeMeta, routeRequest, requestDto, metadata) {
15
+ this.routeMeta = routeMeta;
16
+ this.routeRequest = routeRequest;
17
+ this.requestDto = requestDto;
18
+ this.metadata = metadata ?? new Map();
19
+ }
20
+ /**
21
+ * Get the HTTP method (convenience accessor).
22
+ */
23
+ get httpMethod() {
24
+ return this.routeMeta.httpMethod;
25
+ }
26
+ /**
27
+ * Get the request path (convenience accessor).
28
+ */
29
+ get path() {
30
+ return this.routeMeta.path;
31
+ }
32
+ /**
33
+ * Get the method name (convenience accessor).
34
+ */
35
+ get methodName() {
36
+ return this.routeMeta.methodName;
37
+ }
38
+ }
39
+ exports.MethodMeta = MethodMeta;
40
+ //# sourceMappingURL=MethodMeta.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MethodMeta.js","sourceRoot":"","sources":["../../../../../packages/http/http-server/src/MethodMeta.ts"],"names":[],"mappings":";;;AAEA;;;;;;;;GAQG;AACH,MAAa,UAAU;IAsBrB,YACE,SAAyB,EACzB,YAA0B,EAC1B,UAAoB,EACpB,QAA+B;QAE/B,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC;QAC3B,IAAI,CAAC,YAAY,GAAG,YAAY,CAAC;QACjC,IAAI,CAAC,UAAU,GAAG,UAAU,CAAC;QAC7B,IAAI,CAAC,QAAQ,GAAG,QAAQ,IAAI,IAAI,GAAG,EAAE,CAAC;IACxC,CAAC;IAED;;OAEG;IACH,IAAI,UAAU;QACZ,OAAO,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC;IACnC,CAAC;IAED;;OAEG;IACH,IAAI,IAAI;QACN,OAAO,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC;IAC7B,CAAC;IAED;;OAEG;IACH,IAAI,UAAU;QACZ,OAAO,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC;IACnC,CAAC;CACF;AAtDD,gCAsDC","sourcesContent":["import { RouteMetadata2, RouteRequest } from '@webpieces/core-meta';\n\n/**\n * Metadata about the method being invoked.\n * Passed to filters and contains request information.\n *\n * MethodMeta is created by WebpiecesCoreServer when handling a request:\n * - routeMeta: Static route information (httpMethod, path, methodName)\n * - routeRequest: Express Request/Response objects\n * - requestDto: Set by JsonFilter after deserializing the request body\n */\nexport class MethodMeta {\n /**\n * Route metadata (httpMethod, path, methodName, parameterTypes)\n */\n routeMeta: RouteMetadata2;\n\n /**\n * Express Request and Response objects\n */\n routeRequest: RouteRequest;\n\n /**\n * The deserialized request DTO.\n * Set by JsonFilter after deserializing the request body.\n */\n requestDto?: unknown;\n\n /**\n * Additional metadata for storing request-scoped data.\n */\n metadata: Map<string, unknown>;\n\n constructor(\n routeMeta: RouteMetadata2,\n routeRequest: RouteRequest,\n requestDto?: unknown,\n metadata?: Map<string, unknown>\n ) {\n this.routeMeta = routeMeta;\n this.routeRequest = routeRequest;\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"]}
@@ -1,21 +1,32 @@
1
1
  import { Container } from 'inversify';
2
- import { RouteBuilder, RouteDefinition, FilterDefinition, RouteHandler } from '@webpieces/core-meta';
3
- import { Filter } from '@webpieces/http-filters';
4
- import { RouteMetadata } from '@webpieces/http-api';
2
+ import { RouteBuilder, RouteDefinition, FilterDefinition } from '@webpieces/core-meta';
3
+ import { Filter, WpResponse } from '@webpieces/http-filters';
4
+ import { RouteHandler } from './RouteHandler';
5
+ import { MethodMeta } from './MethodMeta';
5
6
  /**
6
- * Registered route entry in the route registry.
7
- *
8
- * We use unknown instead of any for better type safety:
9
- * - unknown forces type checking at usage points
10
- * - any allows unsafe operations without checks
7
+ * Type alias for HTTP filters that work with MethodMeta and ResponseWrapper.
8
+ */
9
+ export type HttpFilter = Filter<MethodMeta, WpResponse<unknown>>;
10
+ /**
11
+ * FilterWithMeta - Pairs a resolved filter instance with its definition.
12
+ * Stores both the DI-resolved filter and the metadata needed for matching.
13
+ */
14
+ export declare class FilterWithMeta {
15
+ filter: HttpFilter;
16
+ definition: FilterDefinition;
17
+ constructor(filter: HttpFilter, definition: FilterDefinition);
18
+ }
19
+ /**
20
+ * RouteHandlerWithMeta - Pairs a route handler with its definition.
21
+ * Stores both the handler (which wraps the DI-resolved controller) and the route metadata.
11
22
  *
12
- * Each route has its own TResult type, but we can't store different
13
- * generic types in the same Map, so we use unknown as a type-safe escape hatch.
23
+ * We use unknown for the generic type since we store different TResult types in the same Map.
24
+ * Type safety is maintained through the generic on RouteDefinition at registration time.
14
25
  */
15
- export declare class RegisteredRoute<TResult = unknown> extends RouteDefinition<TResult> {
16
- routeMetadata?: RouteMetadata;
17
- controllerClass?: any;
18
- constructor(method: string, path: string, handler: RouteHandler<TResult>, controllerFilepath?: string, routeMetadata?: RouteMetadata, controllerClass?: any);
26
+ export declare class RouteHandlerWithMeta {
27
+ handler: RouteHandler<unknown>;
28
+ definition: RouteDefinition<unknown>;
29
+ constructor(handler: RouteHandler<unknown>, definition: RouteDefinition<unknown>);
19
30
  }
20
31
  /**
21
32
  * RouteBuilderImpl - Concrete implementation of RouteBuilder interface.
@@ -28,39 +39,52 @@ export declare class RegisteredRoute<TResult = unknown> extends RouteDefinition<
28
39
  * - Improve traceability and debugging
29
40
  * - Make the code easier to understand
30
41
  * - Enable better IDE navigation (Cmd+Click on addRoute works!)
42
+ *
43
+ * DI Pattern: This class is registered in webpiecesContainer via @provideSingleton()
44
+ * but needs appContainer to resolve filters/controllers. The container is set via
45
+ * setContainer() after appContainer is created (late binding pattern).
31
46
  */
32
47
  export declare class RouteBuilderImpl implements RouteBuilder {
33
48
  private routes;
34
49
  private filterRegistry;
35
- private container;
50
+ private container?;
36
51
  /**
37
- * Create a new RouteBuilder.
52
+ * Set the DI container used for resolving filters and controllers.
53
+ * Called by WebpiecesCoreServer after appContainer is created.
38
54
  *
39
- * @param routes - Map to store registered routes (keyed by "METHOD:path")
40
- * @param filterRegistry - Array to store registered filters with their definitions
41
- * @param container - DI container for resolving filter instances
55
+ * @param container - The application DI container (appContainer)
42
56
  */
43
- constructor(routes: Map<string, RegisteredRoute<unknown>>, filterRegistry: Array<{
44
- filter: Filter;
45
- definition: FilterDefinition;
46
- }>, container: Container);
57
+ setContainer(container: Container): void;
47
58
  /**
48
59
  * Register a route with the router.
49
60
  *
61
+ * Resolves the controller from DI container ONCE and creates a handler that uses
62
+ * the resolved controller instance. This is more efficient than resolving on every request.
63
+ *
50
64
  * The route is stored with a key of "METHOD:path" (e.g., "POST:/search/item").
51
- * The TResult generic ensures type safety for the route's return type.
52
65
  *
53
- * @param route - Route definition with method, path, and handler
66
+ * @param route - Route definition with controller class and method name
54
67
  */
55
68
  addRoute<TResult = unknown>(route: RouteDefinition<TResult>): void;
56
69
  /**
57
70
  * Register a filter with the filter chain.
58
71
  *
59
- * Filters are resolved from the DI container and stored with their definitions.
72
+ * Resolves the filter from DI container and pairs it with the filter definition.
60
73
  * The definition includes pattern information used for route-specific filtering.
61
- * Filters will be matched and executed in priority order (higher priority first).
62
74
  *
63
75
  * @param filterDef - Filter definition with priority, filter class, and optional filepath pattern
64
76
  */
65
77
  addFilter(filterDef: FilterDefinition): void;
78
+ /**
79
+ * Get all registered routes.
80
+ *
81
+ * @returns Map of routes with handlers and definitions, keyed by "METHOD:path"
82
+ */
83
+ getRoutes(): Map<string, RouteHandlerWithMeta>;
84
+ /**
85
+ * Get all filters sorted by priority (highest priority first).
86
+ *
87
+ * @returns Array of FilterWithMeta sorted by priority
88
+ */
89
+ getSortedFilters(): Array<FilterWithMeta>;
66
90
  }
@@ -1,25 +1,35 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.RouteBuilderImpl = exports.RegisteredRoute = void 0;
4
- const core_meta_1 = require("@webpieces/core-meta");
3
+ exports.RouteBuilderImpl = exports.RouteHandlerWithMeta = exports.FilterWithMeta = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const inversify_1 = require("inversify");
6
+ const http_routing_1 = require("@webpieces/http-routing");
7
+ const RouteHandler_1 = require("./RouteHandler");
5
8
  /**
6
- * Registered route entry in the route registry.
7
- *
8
- * We use unknown instead of any for better type safety:
9
- * - unknown forces type checking at usage points
10
- * - any allows unsafe operations without checks
9
+ * FilterWithMeta - Pairs a resolved filter instance with its definition.
10
+ * Stores both the DI-resolved filter and the metadata needed for matching.
11
+ */
12
+ class FilterWithMeta {
13
+ constructor(filter, definition) {
14
+ this.filter = filter;
15
+ this.definition = definition;
16
+ }
17
+ }
18
+ exports.FilterWithMeta = FilterWithMeta;
19
+ /**
20
+ * RouteHandlerWithMeta - Pairs a route handler with its definition.
21
+ * Stores both the handler (which wraps the DI-resolved controller) and the route metadata.
11
22
  *
12
- * Each route has its own TResult type, but we can't store different
13
- * generic types in the same Map, so we use unknown as a type-safe escape hatch.
23
+ * We use unknown for the generic type since we store different TResult types in the same Map.
24
+ * Type safety is maintained through the generic on RouteDefinition at registration time.
14
25
  */
15
- class RegisteredRoute extends core_meta_1.RouteDefinition {
16
- constructor(method, path, handler, controllerFilepath, routeMetadata, controllerClass) {
17
- super(method, path, handler, controllerFilepath);
18
- this.routeMetadata = routeMetadata;
19
- this.controllerClass = controllerClass;
26
+ class RouteHandlerWithMeta {
27
+ constructor(handler, definition) {
28
+ this.handler = handler;
29
+ this.definition = definition;
20
30
  }
21
31
  }
22
- exports.RegisteredRoute = RegisteredRoute;
32
+ exports.RouteHandlerWithMeta = RouteHandlerWithMeta;
23
33
  /**
24
34
  * RouteBuilderImpl - Concrete implementation of RouteBuilder interface.
25
35
  *
@@ -31,51 +41,101 @@ exports.RegisteredRoute = RegisteredRoute;
31
41
  * - Improve traceability and debugging
32
42
  * - Make the code easier to understand
33
43
  * - Enable better IDE navigation (Cmd+Click on addRoute works!)
44
+ *
45
+ * DI Pattern: This class is registered in webpiecesContainer via @provideSingleton()
46
+ * but needs appContainer to resolve filters/controllers. The container is set via
47
+ * setContainer() after appContainer is created (late binding pattern).
34
48
  */
35
- class RouteBuilderImpl {
49
+ let RouteBuilderImpl = class RouteBuilderImpl {
50
+ constructor() {
51
+ this.routes = new Map();
52
+ this.filterRegistry = [];
53
+ }
36
54
  /**
37
- * Create a new RouteBuilder.
55
+ * Set the DI container used for resolving filters and controllers.
56
+ * Called by WebpiecesCoreServer after appContainer is created.
38
57
  *
39
- * @param routes - Map to store registered routes (keyed by "METHOD:path")
40
- * @param filterRegistry - Array to store registered filters with their definitions
41
- * @param container - DI container for resolving filter instances
58
+ * @param container - The application DI container (appContainer)
42
59
  */
43
- constructor(routes, filterRegistry, container) {
44
- this.routes = routes;
45
- this.filterRegistry = filterRegistry;
60
+ setContainer(container) {
46
61
  this.container = container;
47
62
  }
48
63
  /**
49
64
  * Register a route with the router.
50
65
  *
66
+ * Resolves the controller from DI container ONCE and creates a handler that uses
67
+ * the resolved controller instance. This is more efficient than resolving on every request.
68
+ *
51
69
  * The route is stored with a key of "METHOD:path" (e.g., "POST:/search/item").
52
- * The TResult generic ensures type safety for the route's return type.
53
70
  *
54
- * @param route - Route definition with method, path, and handler
71
+ * @param route - Route definition with controller class and method name
55
72
  */
56
73
  addRoute(route) {
57
- const key = `${route.method}:${route.path}`;
58
- // Store as RegisteredRoute<unknown> in the map
59
- // Type safety is maintained through the generic on RouteDefinition
60
- this.routes.set(key, route);
74
+ if (!this.container) {
75
+ throw new Error('Container not set. Call setContainer() before registering routes.');
76
+ }
77
+ const routeMeta = route.routeMeta;
78
+ const key = `${routeMeta.httpMethod}:${routeMeta.path}`;
79
+ // Resolve controller instance from DI container ONCE (not on every request!)
80
+ const controller = this.container.get(route.controllerClass);
81
+ // Get the controller method
82
+ const method = controller[routeMeta.methodName];
83
+ if (typeof method !== 'function') {
84
+ const controllerName = route.controllerClass.name || 'Unknown';
85
+ throw new Error(`Method ${routeMeta.methodName} not found on controller ${controllerName}`);
86
+ }
87
+ // Create handler that uses the resolved controller instance
88
+ const handler = new class extends RouteHandler_1.RouteHandler {
89
+ async execute(meta) {
90
+ // Invoke the method with requestDto from meta
91
+ // The controller is already resolved - no DI lookup on every request!
92
+ // Pass requestDto as the single argument to the controller method
93
+ const result = await method.call(controller, meta.requestDto);
94
+ return result;
95
+ }
96
+ };
97
+ // Store handler with route definition
98
+ const routeWithMeta = new RouteHandlerWithMeta(handler, route);
99
+ this.routes.set(key, routeWithMeta);
61
100
  }
62
101
  /**
63
102
  * Register a filter with the filter chain.
64
103
  *
65
- * Filters are resolved from the DI container and stored with their definitions.
104
+ * Resolves the filter from DI container and pairs it with the filter definition.
66
105
  * The definition includes pattern information used for route-specific filtering.
67
- * Filters will be matched and executed in priority order (higher priority first).
68
106
  *
69
107
  * @param filterDef - Filter definition with priority, filter class, and optional filepath pattern
70
108
  */
71
109
  addFilter(filterDef) {
110
+ if (!this.container) {
111
+ throw new Error('Container not set. Call setContainer() before registering filters.');
112
+ }
72
113
  // Resolve filter instance from DI container
73
114
  const filter = this.container.get(filterDef.filterClass);
74
- // Set priority on the filter instance
75
- filter.priority = filterDef.priority;
76
- // Store both filter instance and definition for pattern matching
77
- this.filterRegistry.push({ filter, definition: filterDef });
115
+ // Store filter with its definition
116
+ const filterWithMeta = new FilterWithMeta(filter, filterDef);
117
+ this.filterRegistry.push(filterWithMeta);
78
118
  }
79
- }
119
+ /**
120
+ * Get all registered routes.
121
+ *
122
+ * @returns Map of routes with handlers and definitions, keyed by "METHOD:path"
123
+ */
124
+ getRoutes() {
125
+ return this.routes;
126
+ }
127
+ /**
128
+ * Get all filters sorted by priority (highest priority first).
129
+ *
130
+ * @returns Array of FilterWithMeta sorted by priority
131
+ */
132
+ getSortedFilters() {
133
+ return [...this.filterRegistry].sort((a, b) => b.definition.priority - a.definition.priority);
134
+ }
135
+ };
80
136
  exports.RouteBuilderImpl = RouteBuilderImpl;
137
+ exports.RouteBuilderImpl = RouteBuilderImpl = tslib_1.__decorate([
138
+ (0, http_routing_1.provideSingleton)(),
139
+ (0, inversify_1.injectable)()
140
+ ], RouteBuilderImpl);
81
141
  //# sourceMappingURL=RouteBuilderImpl.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"RouteBuilderImpl.js","sourceRoot":"","sources":["../../../../../packages/http/http-server/src/RouteBuilderImpl.ts"],"names":[],"mappings":";;;AACA,oDAAqG;AAIrG;;;;;;;;;GASG;AACH,MAAa,eAAmC,SAAQ,2BAAwB;IAI9E,YACE,MAAc,EACd,IAAY,EACZ,OAA8B,EAC9B,kBAA2B,EAC3B,aAA6B,EAC7B,eAAqB;QAErB,KAAK,CAAC,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,kBAAkB,CAAC,CAAC;QACjD,IAAI,CAAC,aAAa,GAAG,aAAa,CAAC;QACnC,IAAI,CAAC,eAAe,GAAG,eAAe,CAAC;IACzC,CAAC;CACF;AAhBD,0CAgBC;AAED;;;;;;;;;;;GAWG;AACH,MAAa,gBAAgB;IAK3B;;;;;;OAMG;IACH,YACE,MAA6C,EAC7C,cAAuE,EACvE,SAAoB;QAEpB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,cAAc,GAAG,cAAc,CAAC;QACrC,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC;IAC7B,CAAC;IAED;;;;;;;OAOG;IACH,QAAQ,CAAoB,KAA+B;QACzD,MAAM,GAAG,GAAG,GAAG,KAAK,CAAC,MAAM,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC;QAE5C,+CAA+C;QAC/C,mEAAmE;QACnE,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,KAAiC,CAAC,CAAC;IAC1D,CAAC;IAED;;;;;;;;OAQG;IACH,SAAS,CAAC,SAA2B;QACnC,4CAA4C;QAC5C,MAAM,MAAM,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAS,SAAS,CAAC,WAAW,CAAC,CAAC;QAEjE,sCAAsC;QACtC,MAAM,CAAC,QAAQ,GAAG,SAAS,CAAC,QAAQ,CAAC;QAErC,iEAAiE;QACjE,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,UAAU,EAAE,SAAS,EAAE,CAAC,CAAC;IAC9D,CAAC;CACF;AAzDD,4CAyDC","sourcesContent":["import { Container } from 'inversify';\nimport { RouteBuilder, RouteDefinition, FilterDefinition, RouteHandler } from '@webpieces/core-meta';\nimport { Filter } from '@webpieces/http-filters';\nimport { RouteMetadata } from '@webpieces/http-api';\n\n/**\n * Registered route entry in the route registry.\n *\n * We use unknown instead of any for better type safety:\n * - unknown forces type checking at usage points\n * - any allows unsafe operations without checks\n *\n * Each route has its own TResult type, but we can't store different\n * generic types in the same Map, so we use unknown as a type-safe escape hatch.\n */\nexport class RegisteredRoute<TResult = unknown> extends RouteDefinition<TResult> {\n routeMetadata?: RouteMetadata;\n controllerClass?: any;\n\n constructor(\n method: string,\n path: string,\n handler: RouteHandler<TResult>,\n controllerFilepath?: string,\n routeMetadata?: RouteMetadata,\n controllerClass?: any\n ) {\n super(method, path, handler, controllerFilepath);\n this.routeMetadata = routeMetadata;\n this.controllerClass = controllerClass;\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 */\nexport class RouteBuilderImpl implements RouteBuilder {\n private routes: Map<string, RegisteredRoute<unknown>>;\n private filterRegistry: Array<{ filter: Filter; definition: FilterDefinition }>;\n private container: Container;\n\n /**\n * Create a new RouteBuilder.\n *\n * @param routes - Map to store registered routes (keyed by \"METHOD:path\")\n * @param filterRegistry - Array to store registered filters with their definitions\n * @param container - DI container for resolving filter instances\n */\n constructor(\n routes: Map<string, RegisteredRoute<unknown>>,\n filterRegistry: Array<{ filter: Filter; definition: FilterDefinition }>,\n container: Container\n ) {\n this.routes = routes;\n this.filterRegistry = filterRegistry;\n this.container = container;\n }\n\n /**\n * Register a route with the router.\n *\n * The route is stored with a key of \"METHOD:path\" (e.g., \"POST:/search/item\").\n * The TResult generic ensures type safety for the route's return type.\n *\n * @param route - Route definition with method, path, and handler\n */\n addRoute<TResult = unknown>(route: RouteDefinition<TResult>): void {\n const key = `${route.method}:${route.path}`;\n\n // Store as RegisteredRoute<unknown> in the map\n // Type safety is maintained through the generic on RouteDefinition\n this.routes.set(key, route as RegisteredRoute<unknown>);\n }\n\n /**\n * Register a filter with the filter chain.\n *\n * Filters are resolved from the DI container and stored with their definitions.\n * The definition includes pattern information used for route-specific filtering.\n * Filters will be matched and executed in priority order (higher priority first).\n *\n * @param filterDef - Filter definition with priority, filter class, and optional filepath pattern\n */\n addFilter(filterDef: FilterDefinition): void {\n // Resolve filter instance from DI container\n const filter = this.container.get<Filter>(filterDef.filterClass);\n\n // Set priority on the filter instance\n filter.priority = filterDef.priority;\n\n // Store both filter instance and definition for pattern matching\n this.filterRegistry.push({ filter, definition: filterDef });\n }\n}\n"]}
1
+ {"version":3,"file":"RouteBuilderImpl.js","sourceRoot":"","sources":["../../../../../packages/http/http-server/src/RouteBuilderImpl.ts"],"names":[],"mappings":"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","sourcesContent":["import { Container, injectable } from 'inversify';\nimport { RouteBuilder, RouteDefinition, FilterDefinition } from '@webpieces/core-meta';\nimport { Filter, WpResponse } from '@webpieces/http-filters';\nimport { provideSingleton } from '@webpieces/http-routing';\nimport { RouteHandler } from './RouteHandler';\nimport { MethodMeta } from './MethodMeta';\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 * 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 * 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 handler: RouteHandler<unknown>,\n public definition: RouteDefinition<unknown>\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: Map<string, RouteHandlerWithMeta> = new Map();\n private filterRegistry: Array<FilterWithMeta> = [];\n private container?: Container;\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 * Resolves the controller from DI container ONCE and creates a handler that uses\n * the resolved controller instance. This is more efficient than resolving on every request.\n *\n * The route is stored with a key of \"METHOD:path\" (e.g., \"POST:/search/item\").\n *\n * @param route - Route definition with controller class and method name\n */\n addRoute<TResult = unknown>(route: RouteDefinition<TResult>): void {\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 const key = `${routeMeta.httpMethod}:${routeMeta.path}`;\n\n // Resolve controller instance from DI container ONCE (not on every request!)\n const controller = this.container.get(route.controllerClass);\n\n // Get the controller method\n const method = (controller as Record<string, unknown>)[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 // Create handler that uses the resolved controller instance\n const handler = new class extends RouteHandler<TResult> {\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 // Pass requestDto as the single argument to the controller method\n const result: TResult = await method.call(controller, meta.requestDto);\n return result;\n }\n };\n\n // Store handler with route definition\n const routeWithMeta = new RouteHandlerWithMeta(\n handler as RouteHandler<unknown>,\n route as RouteDefinition<unknown>\n );\n\n this.routes.set(key, routeWithMeta);\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(): Map<string, 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((a, b) => b.definition.priority - a.definition.priority);\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-server/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"]}