@webpieces/http-server 0.2.15 → 0.2.17

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.
@@ -1 +0,0 @@
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(\n (a, b) => b.definition.priority - a.definition.priority,\n );\n }\n}\n"]}
@@ -1,22 +0,0 @@
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
- }
@@ -1,20 +0,0 @@
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
@@ -1 +0,0 @@
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"]}
@@ -1,62 +0,0 @@
1
- import { Filter, WpResponse, Service } from '@webpieces/http-filters';
2
- import { MethodMeta } from '../MethodMeta';
3
- /**
4
- * DI tokens for JsonFilter.
5
- */
6
- export declare const FILTER_TYPES: {
7
- JsonFilterConfig: symbol;
8
- };
9
- /**
10
- * Configuration for JsonFilter.
11
- * Register this in your DI container to customize JsonFilter behavior.
12
- */
13
- export declare class JsonFilterConfig {
14
- }
15
- /**
16
- * JsonFilter - Handles JSON serialization/deserialization and writes response to HTTP body.
17
- *
18
- * Similar to Java WebPieces JacksonCatchAllFilter.
19
- *
20
- * Flow:
21
- * 1. Log request
22
- * 2. Deserialize request body to DTO and set on meta.requestDto
23
- * 3. Call next filter/controller
24
- * 4. Get response (WpResponse)
25
- * 5. Write response to Express response
26
- * 6. On ANY exception, send 500
27
- */
28
- export declare class JsonFilter extends Filter<MethodMeta, WpResponse<unknown>> {
29
- private config;
30
- constructor(config: JsonFilterConfig);
31
- filter(meta: MethodMeta, nextFilter: Service<MethodMeta, WpResponse<unknown>>): Promise<WpResponse<unknown>>;
32
- /**
33
- * Deserialize request body to DTO and set on meta.requestDto.
34
- */
35
- private deserializeRequest;
36
- /**
37
- * Write WpResponse to HTTP response body as JSON.
38
- */
39
- private writeResponse;
40
- /**
41
- * Log the incoming request.
42
- */
43
- private logRequest;
44
- /**
45
- * Log the outgoing response.
46
- */
47
- private logResponse;
48
- }
49
- /**
50
- * Exception thrown when validation fails.
51
- */
52
- export declare class ValidationException extends Error {
53
- violations: string[];
54
- constructor(violations: string[]);
55
- }
56
- /**
57
- * HTTP exception with status code.
58
- */
59
- export declare class HttpException extends Error {
60
- statusCode: number;
61
- constructor(message: string, statusCode: number);
62
- }
@@ -1,146 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.HttpException = exports.ValidationException = exports.JsonFilter = exports.JsonFilterConfig = exports.FILTER_TYPES = 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 http_filters_1 = require("@webpieces/http-filters");
8
- const core_util_1 = require("@webpieces/core-util");
9
- /**
10
- * DI tokens for JsonFilter.
11
- */
12
- exports.FILTER_TYPES = {
13
- JsonFilterConfig: Symbol.for('JsonFilterConfig'),
14
- };
15
- /**
16
- * Configuration for JsonFilter.
17
- * Register this in your DI container to customize JsonFilter behavior.
18
- */
19
- let JsonFilterConfig = class JsonFilterConfig {
20
- };
21
- exports.JsonFilterConfig = JsonFilterConfig;
22
- exports.JsonFilterConfig = JsonFilterConfig = tslib_1.__decorate([
23
- (0, inversify_1.injectable)()
24
- ], JsonFilterConfig);
25
- /**
26
- * JsonFilter - Handles JSON serialization/deserialization and writes response to HTTP body.
27
- *
28
- * Similar to Java WebPieces JacksonCatchAllFilter.
29
- *
30
- * Flow:
31
- * 1. Log request
32
- * 2. Deserialize request body to DTO and set on meta.requestDto
33
- * 3. Call next filter/controller
34
- * 4. Get response (WpResponse)
35
- * 5. Write response to Express response
36
- * 6. On ANY exception, send 500
37
- */
38
- let JsonFilter = class JsonFilter extends http_filters_1.Filter {
39
- constructor(config) {
40
- super();
41
- this.config = config;
42
- }
43
- async filter(meta, nextFilter) {
44
- // Get Express Request/Response from routeRequest
45
- const expressRequest = meta.routeRequest.request;
46
- const expressResponse = meta.routeRequest.response;
47
- try {
48
- // 1. Log request
49
- this.logRequest(meta, expressRequest);
50
- // 2. Deserialize request body to DTO
51
- this.deserializeRequest(meta, expressRequest);
52
- // 3. Call next filter/controller
53
- const responseWrapper = await nextFilter.invoke(meta);
54
- // 4. Log response
55
- this.logResponse(responseWrapper);
56
- // 5. Write response to Express response
57
- this.writeResponse(expressResponse, responseWrapper);
58
- return responseWrapper;
59
- }
60
- catch (err) {
61
- const error = (0, core_util_1.toError)(err);
62
- // 6. On ANY exception, send 500
63
- console.error('[JsonFilter] Error:', error);
64
- const errorResponse = new http_filters_1.WpResponse({ error: 'Internal server error' }, 500);
65
- this.writeResponse(expressResponse, errorResponse);
66
- return errorResponse;
67
- }
68
- }
69
- /**
70
- * Deserialize request body to DTO and set on meta.requestDto.
71
- */
72
- deserializeRequest(meta, expressRequest) {
73
- if (expressRequest.body) {
74
- // Set the deserialized body as requestDto
75
- meta.requestDto = expressRequest.body;
76
- }
77
- }
78
- /**
79
- * Write WpResponse to HTTP response body as JSON.
80
- */
81
- writeResponse(expressResponse, responseWrapper) {
82
- // Set status code
83
- expressResponse.status(responseWrapper.statusCode);
84
- // Set headers from wrapper
85
- responseWrapper.headers.forEach((value, name) => {
86
- expressResponse.setHeader(name, value);
87
- });
88
- // Set content type to JSON
89
- expressResponse.setHeader('Content-Type', 'application/json');
90
- // Serialize and write response body
91
- if (responseWrapper.response !== undefined) {
92
- expressResponse.json(responseWrapper.response);
93
- }
94
- else {
95
- expressResponse.end();
96
- }
97
- }
98
- /**
99
- * Log the incoming request.
100
- */
101
- logRequest(meta, expressRequest) {
102
- console.log(`[JsonFilter] ${meta.httpMethod} ${meta.path}`);
103
- if (expressRequest.body) {
104
- console.log('[JsonFilter] Request body:', JSON.stringify(expressRequest.body, null, 2));
105
- }
106
- }
107
- /**
108
- * Log the outgoing response.
109
- */
110
- logResponse(responseWrapper) {
111
- console.log(`[JsonFilter] Response: ${responseWrapper.statusCode}`);
112
- if (responseWrapper.response) {
113
- console.log('[JsonFilter] Response body:', JSON.stringify(responseWrapper.response, null, 2));
114
- }
115
- }
116
- };
117
- exports.JsonFilter = JsonFilter;
118
- exports.JsonFilter = JsonFilter = tslib_1.__decorate([
119
- (0, http_routing_1.provideSingleton)(),
120
- (0, inversify_1.injectable)(),
121
- tslib_1.__param(0, (0, inversify_1.inject)(exports.FILTER_TYPES.JsonFilterConfig)),
122
- tslib_1.__metadata("design:paramtypes", [JsonFilterConfig])
123
- ], JsonFilter);
124
- /**
125
- * Exception thrown when validation fails.
126
- */
127
- class ValidationException extends Error {
128
- constructor(violations) {
129
- super('Validation failed');
130
- this.violations = violations;
131
- this.name = 'ValidationException';
132
- }
133
- }
134
- exports.ValidationException = ValidationException;
135
- /**
136
- * HTTP exception with status code.
137
- */
138
- class HttpException extends Error {
139
- constructor(message, statusCode) {
140
- super(message);
141
- this.statusCode = statusCode;
142
- this.name = 'HttpException';
143
- }
144
- }
145
- exports.HttpException = HttpException;
146
- //# sourceMappingURL=JsonFilter.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"JsonFilter.js","sourceRoot":"","sources":["../../../../../../packages/http/http-server/src/filters/JsonFilter.ts"],"names":[],"mappings":"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","sourcesContent":["import { injectable, inject } from 'inversify';\nimport { provideSingleton } from '@webpieces/http-routing';\nimport { Filter, WpResponse, Service } from '@webpieces/http-filters';\nimport { toError } from '@webpieces/core-util';\nimport { Request, Response } from 'express';\nimport { MethodMeta } from '../MethodMeta';\n\n/**\n * DI tokens for JsonFilter.\n */\nexport const FILTER_TYPES = {\n JsonFilterConfig: Symbol.for('JsonFilterConfig'),\n};\n\n/**\n * Configuration for JsonFilter.\n * Register this in your DI container to customize JsonFilter behavior.\n */\n@injectable()\nexport class JsonFilterConfig {\n // Configuration options can be added here\n}\n\n/**\n * JsonFilter - Handles JSON serialization/deserialization and writes response to HTTP body.\n *\n * Similar to Java WebPieces JacksonCatchAllFilter.\n *\n * Flow:\n * 1. Log request\n * 2. Deserialize request body to DTO and set on meta.requestDto\n * 3. Call next filter/controller\n * 4. Get response (WpResponse)\n * 5. Write response to Express response\n * 6. On ANY exception, send 500\n */\n@provideSingleton()\n@injectable()\nexport class JsonFilter extends Filter<MethodMeta, WpResponse<unknown>> {\n constructor(@inject(FILTER_TYPES.JsonFilterConfig) private config: JsonFilterConfig) {\n super();\n }\n\n async filter(\n meta: MethodMeta,\n nextFilter: Service<MethodMeta, WpResponse<unknown>>,\n ): Promise<WpResponse<unknown>> {\n // Get Express Request/Response from routeRequest\n const expressRequest = meta.routeRequest.request as Request;\n const expressResponse = meta.routeRequest.response as Response;\n\n try {\n // 1. Log request\n this.logRequest(meta, expressRequest);\n\n // 2. Deserialize request body to DTO\n this.deserializeRequest(meta, expressRequest);\n\n // 3. Call next filter/controller\n const responseWrapper = await nextFilter.invoke(meta);\n\n // 4. Log response\n this.logResponse(responseWrapper);\n\n // 5. Write response to Express response\n this.writeResponse(expressResponse, responseWrapper);\n\n return responseWrapper;\n } catch (err: unknown) {\n const error = toError(err);\n // 6. On ANY exception, send 500\n console.error('[JsonFilter] Error:', error);\n const errorResponse = new WpResponse({ error: 'Internal server error' }, 500);\n this.writeResponse(expressResponse, errorResponse);\n return errorResponse;\n }\n }\n\n /**\n * Deserialize request body to DTO and set on meta.requestDto.\n */\n private deserializeRequest(meta: MethodMeta, expressRequest: Request): void {\n if (expressRequest.body) {\n // Set the deserialized body as requestDto\n meta.requestDto = expressRequest.body;\n }\n }\n\n /**\n * Write WpResponse to HTTP response body as JSON.\n */\n private writeResponse(expressResponse: Response, responseWrapper: WpResponse<unknown>): void {\n // Set status code\n expressResponse.status(responseWrapper.statusCode);\n\n // Set headers from wrapper\n responseWrapper.headers.forEach((value, name) => {\n expressResponse.setHeader(name, value);\n });\n\n // Set content type to JSON\n expressResponse.setHeader('Content-Type', 'application/json');\n\n // Serialize and write response body\n if (responseWrapper.response !== undefined) {\n expressResponse.json(responseWrapper.response);\n } else {\n expressResponse.end();\n }\n }\n\n /**\n * Log the incoming request.\n */\n private logRequest(meta: MethodMeta, expressRequest: Request): void {\n console.log(`[JsonFilter] ${meta.httpMethod} ${meta.path}`);\n if (expressRequest.body) {\n console.log('[JsonFilter] Request body:', JSON.stringify(expressRequest.body, null, 2));\n }\n }\n\n /**\n * Log the outgoing response.\n */\n private logResponse(responseWrapper: WpResponse<unknown>): void {\n console.log(`[JsonFilter] Response: ${responseWrapper.statusCode}`);\n if (responseWrapper.response) {\n console.log(\n '[JsonFilter] Response body:',\n JSON.stringify(responseWrapper.response, null, 2),\n );\n }\n }\n}\n\n/**\n * Exception thrown when validation fails.\n */\nexport class ValidationException extends Error {\n constructor(public violations: string[]) {\n super('Validation failed');\n this.name = 'ValidationException';\n }\n}\n\n/**\n * HTTP exception with status code.\n */\nexport class HttpException extends Error {\n constructor(\n message: string,\n public statusCode: number,\n ) {\n super(message);\n this.name = 'HttpException';\n }\n}\n"]}