@webpieces/http-server 0.2.16 → 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.
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@webpieces/http-server",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.17",
|
|
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-util": "0.2.
|
|
26
|
-
"@webpieces/http-routing": "0.2.
|
|
27
|
-
"@webpieces/http-filters": "0.2.
|
|
25
|
+
"@webpieces/core-util": "0.2.17",
|
|
26
|
+
"@webpieces/http-routing": "0.2.17",
|
|
27
|
+
"@webpieces/http-filters": "0.2.17"
|
|
28
28
|
}
|
|
29
29
|
}
|
|
@@ -5,13 +5,29 @@ import { Service, WpResponse } from '@webpieces/http-filters';
|
|
|
5
5
|
export declare class ExpressWrapper {
|
|
6
6
|
private service;
|
|
7
7
|
private routeMeta;
|
|
8
|
-
private jsonSerializer;
|
|
9
8
|
constructor(service: Service<MethodMeta, WpResponse<unknown>>, routeMeta: RouteMetadata);
|
|
10
9
|
execute(req: Request, res: Response, next: NextFunction): Promise<void>;
|
|
11
10
|
/**
|
|
12
|
-
*
|
|
11
|
+
* Read raw request body as text.
|
|
12
|
+
* Used to manually parse JSON (instead of express.json() middleware).
|
|
13
|
+
*/
|
|
14
|
+
private readRequestBody;
|
|
15
|
+
/**
|
|
16
|
+
* Handle errors - translate to JSON ProtocolError (SYMMETRIC with ClientErrorTranslator).
|
|
13
17
|
* PUBLIC so wrapExpress can call it for symmetric error handling.
|
|
14
18
|
* Maps HttpError subclasses to appropriate HTTP status codes and ProtocolError response.
|
|
19
|
+
*
|
|
20
|
+
* Maps all HttpError types (must match ClientErrorTranslator.translateError()):
|
|
21
|
+
* - HttpUserError → 266 (with errorCode)
|
|
22
|
+
* - HttpBadRequestError → 400 (with field, guiAlertMessage)
|
|
23
|
+
* - HttpUnauthorizedError → 401
|
|
24
|
+
* - HttpForbiddenError → 403
|
|
25
|
+
* - HttpNotFoundError → 404
|
|
26
|
+
* - HttpTimeoutError → 408
|
|
27
|
+
* - HttpInternalServerError → 500
|
|
28
|
+
* - HttpBadGatewayError → 502
|
|
29
|
+
* - HttpGatewayTimeoutError → 504
|
|
30
|
+
* - HttpVendorError → 598 (with waitSeconds)
|
|
15
31
|
*/
|
|
16
32
|
handleError(res: Response, error: unknown): void;
|
|
17
33
|
}
|
|
@@ -6,43 +6,70 @@ const inversify_1 = require("inversify");
|
|
|
6
6
|
const http_routing_1 = require("@webpieces/http-routing");
|
|
7
7
|
const http_api_1 = require("@webpieces/http-api");
|
|
8
8
|
const core_util_1 = require("@webpieces/core-util");
|
|
9
|
-
const typescript_json_serializer_1 = require("typescript-json-serializer");
|
|
10
9
|
class ExpressWrapper {
|
|
11
10
|
constructor(service, routeMeta) {
|
|
12
11
|
this.service = service;
|
|
13
12
|
this.routeMeta = routeMeta;
|
|
14
|
-
this.jsonSerializer = new typescript_json_serializer_1.JsonSerializer();
|
|
15
13
|
}
|
|
16
14
|
async execute(req, res, next) {
|
|
17
15
|
try {
|
|
18
|
-
// 1.
|
|
19
|
-
|
|
20
|
-
if (
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
16
|
+
// 1. Parse JSON request body manually (SYMMETRIC with client's JSON.stringify)
|
|
17
|
+
let requestDto = {};
|
|
18
|
+
if (['POST', 'PUT', 'PATCH'].includes(req.method)) {
|
|
19
|
+
// Read raw body as text
|
|
20
|
+
const bodyText = await this.readRequestBody(req);
|
|
21
|
+
// Parse JSON
|
|
22
|
+
requestDto = bodyText ? JSON.parse(bodyText) : {};
|
|
23
|
+
}
|
|
24
|
+
// 2. Create MethodMeta with request DTO
|
|
25
25
|
const methodMeta = new http_routing_1.MethodMeta(this.routeMeta, requestDto);
|
|
26
|
-
//
|
|
26
|
+
// 3. Invoke the service (filter chain + controller)
|
|
27
27
|
const wpResponse = await this.service.invoke(methodMeta);
|
|
28
28
|
if (!wpResponse.response)
|
|
29
29
|
throw new Error(`Route chain(filters & all) is not returning a response. ${this.routeMeta.controllerClassName}.${this.routeMeta.methodName}`);
|
|
30
|
-
//
|
|
31
|
-
const
|
|
32
|
-
|
|
33
|
-
res.status(200);
|
|
34
|
-
res.setHeader('Content-Type', 'application/json');
|
|
35
|
-
res.json(responseDtoStr);
|
|
30
|
+
// 4. Serialize response DTO to JSON (SYMMETRIC with client's response.json())
|
|
31
|
+
const responseJson = JSON.stringify(wpResponse.response);
|
|
32
|
+
res.status(200).setHeader('Content-Type', 'application/json').send(responseJson);
|
|
36
33
|
}
|
|
37
34
|
catch (err) {
|
|
38
|
-
//
|
|
35
|
+
// 5. Handle errors
|
|
39
36
|
this.handleError(res, err);
|
|
40
37
|
}
|
|
41
38
|
}
|
|
42
39
|
/**
|
|
43
|
-
*
|
|
40
|
+
* Read raw request body as text.
|
|
41
|
+
* Used to manually parse JSON (instead of express.json() middleware).
|
|
42
|
+
*/
|
|
43
|
+
async readRequestBody(req) {
|
|
44
|
+
return new Promise((resolve, reject) => {
|
|
45
|
+
let body = '';
|
|
46
|
+
req.on('data', (chunk) => {
|
|
47
|
+
body += chunk.toString();
|
|
48
|
+
});
|
|
49
|
+
req.on('end', () => {
|
|
50
|
+
resolve(body);
|
|
51
|
+
});
|
|
52
|
+
req.on('error', (err) => {
|
|
53
|
+
reject(err);
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Handle errors - translate to JSON ProtocolError (SYMMETRIC with ClientErrorTranslator).
|
|
44
59
|
* PUBLIC so wrapExpress can call it for symmetric error handling.
|
|
45
60
|
* Maps HttpError subclasses to appropriate HTTP status codes and ProtocolError response.
|
|
61
|
+
*
|
|
62
|
+
* Maps all HttpError types (must match ClientErrorTranslator.translateError()):
|
|
63
|
+
* - HttpUserError → 266 (with errorCode)
|
|
64
|
+
* - HttpBadRequestError → 400 (with field, guiAlertMessage)
|
|
65
|
+
* - HttpUnauthorizedError → 401
|
|
66
|
+
* - HttpForbiddenError → 403
|
|
67
|
+
* - HttpNotFoundError → 404
|
|
68
|
+
* - HttpTimeoutError → 408
|
|
69
|
+
* - HttpInternalServerError → 500
|
|
70
|
+
* - HttpBadGatewayError → 502
|
|
71
|
+
* - HttpGatewayTimeoutError → 504
|
|
72
|
+
* - HttpVendorError → 598 (with waitSeconds)
|
|
46
73
|
*/
|
|
47
74
|
handleError(res, error) {
|
|
48
75
|
if (res.headersSent) {
|
|
@@ -50,27 +77,59 @@ class ExpressWrapper {
|
|
|
50
77
|
}
|
|
51
78
|
const protocolError = new http_api_1.ProtocolError();
|
|
52
79
|
if (error instanceof http_api_1.HttpError) {
|
|
80
|
+
// Set common fields for all HttpError types
|
|
53
81
|
protocolError.message = error.message;
|
|
54
82
|
protocolError.subType = error.subType;
|
|
55
83
|
protocolError.name = error.name;
|
|
56
|
-
|
|
84
|
+
// Set type-specific fields (MUST match ClientErrorTranslator)
|
|
85
|
+
if (error instanceof http_api_1.HttpUserError) {
|
|
86
|
+
console.log('[ExpressWrapper] User Error:', error.message);
|
|
87
|
+
protocolError.errorCode = error.errorCode;
|
|
88
|
+
}
|
|
89
|
+
else if (error instanceof http_api_1.HttpBadRequestError) {
|
|
90
|
+
console.log('[ExpressWrapper] Bad Request:', error.message);
|
|
57
91
|
protocolError.field = error.field;
|
|
58
92
|
protocolError.guiAlertMessage = error.guiMessage;
|
|
59
93
|
}
|
|
60
|
-
if (error instanceof http_api_1.
|
|
94
|
+
else if (error instanceof http_api_1.HttpNotFoundError) {
|
|
95
|
+
console.log('[ExpressWrapper] Not Found:', error.message);
|
|
96
|
+
}
|
|
97
|
+
else if (error instanceof http_api_1.HttpTimeoutError) {
|
|
98
|
+
console.error('[ExpressWrapper] Timeout Error:', error.message);
|
|
99
|
+
}
|
|
100
|
+
else if (error instanceof http_api_1.HttpVendorError) {
|
|
101
|
+
console.error('[ExpressWrapper] Vendor Error:', error.message);
|
|
61
102
|
protocolError.waitSeconds = error.waitSeconds;
|
|
62
103
|
}
|
|
63
|
-
if (error instanceof http_api_1.
|
|
64
|
-
|
|
104
|
+
else if (error instanceof http_api_1.HttpUnauthorizedError) {
|
|
105
|
+
console.log('[ExpressWrapper] Unauthorized:', error.message);
|
|
106
|
+
}
|
|
107
|
+
else if (error instanceof http_api_1.HttpForbiddenError) {
|
|
108
|
+
console.log('[ExpressWrapper] Forbidden:', error.message);
|
|
109
|
+
}
|
|
110
|
+
else if (error instanceof http_api_1.HttpInternalServerError) {
|
|
111
|
+
console.error('[ExpressWrapper] Internal Server Error:', error.message);
|
|
112
|
+
}
|
|
113
|
+
else if (error instanceof http_api_1.HttpBadGatewayError) {
|
|
114
|
+
console.error('[ExpressWrapper] Bad Gateway:', error.message);
|
|
115
|
+
}
|
|
116
|
+
else if (error instanceof http_api_1.HttpGatewayTimeoutError) {
|
|
117
|
+
console.error('[ExpressWrapper] Gateway Timeout:', error.message);
|
|
118
|
+
}
|
|
119
|
+
else {
|
|
120
|
+
console.log('[ExpressWrapper] Generic HttpError:', error.message);
|
|
65
121
|
}
|
|
66
|
-
|
|
122
|
+
// Serialize ProtocolError to JSON (SYMMETRIC with client)
|
|
123
|
+
const responseJson = JSON.stringify(protocolError);
|
|
124
|
+
res.status(error.code).setHeader('Content-Type', 'application/json').send(responseJson);
|
|
67
125
|
}
|
|
68
126
|
else {
|
|
69
127
|
// Unknown error - 500
|
|
70
128
|
const err = (0, core_util_1.toError)(error);
|
|
71
129
|
protocolError.message = 'Internal Server Error';
|
|
72
|
-
console.error('[
|
|
73
|
-
|
|
130
|
+
console.error('[ExpressWrapper] Unexpected error:', err);
|
|
131
|
+
const responseJson = JSON.stringify(protocolError);
|
|
132
|
+
res.status(500).setHeader('Content-Type', 'application/json').send(responseJson);
|
|
74
133
|
}
|
|
75
134
|
}
|
|
76
135
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"WebpiecesMiddleware.js","sourceRoot":"","sources":["../../../../../packages/http/http-server/src/WebpiecesMiddleware.ts"],"names":[],"mappings":"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","sourcesContent":["import { Request, Response, NextFunction } from 'express';\nimport { injectable } from 'inversify';\nimport { provideSingleton, MethodMeta, ExpressRouteHandler } from '@webpieces/http-routing';\nimport {\n ProtocolError,\n HttpError,\n HttpBadRequestError,\n HttpVendorError,\n HttpUserError,\n RouteMetadata,\n} from '@webpieces/http-api';\nimport { Service, WpResponse } from '@webpieces/http-filters';\nimport { toError } from '@webpieces/core-util';\nimport { JsonSerializer } from 'typescript-json-serializer';\n\nexport class ExpressWrapper {\n private jsonSerializer = new JsonSerializer();\n\n constructor(\n private service: Service<MethodMeta, WpResponse<unknown>>,\n private routeMeta: RouteMetadata\n ) {\n }\n\n public async execute(req: Request, res: Response, next: NextFunction) {\n try {\n // 1. Get request DTO class from routeMeta\n const requestDtoClass = this.routeMeta.parameterTypes?.[0];\n if(!requestDtoClass)\n throw new Error('No request DTO class found for route');\n\n // 2. Deserialize req.body → DTO instance\n const requestDto = this.jsonSerializer.deserializeObject(req.body, requestDtoClass);\n // 3. Create MethodMeta with deserialized DTO\n const methodMeta = new MethodMeta(this.routeMeta, requestDto);\n // 4. Invoke the service (filter chain + controller)\n const wpResponse = await this.service.invoke(methodMeta);\n if(!wpResponse.response)\n throw new Error(`Route chain(filters & all) is not returning a response. ${this.routeMeta.controllerClassName}.${this.routeMeta.methodName}`);\n\n // 6. Serialize response → plain object\n const responseDtoStr = this.jsonSerializer.serializeObject(wpResponse.response);\n\n // 7. WRITE response as JSON (SYMMETRIC with reading request!)\n res.status(200);\n res.setHeader('Content-Type', 'application/json');\n res.json(responseDtoStr);\n } catch (err: unknown) {\n // 8. Handle errors (SYMMETRIC - wrapExpress owns error handling!)\n this.handleError(res, err);\n }\n }\n\n /**\n * Handle errors - translate to JSON ProtocolError.\n * PUBLIC so wrapExpress can call it for symmetric error handling.\n * Maps HttpError subclasses to appropriate HTTP status codes and ProtocolError response.\n */\n public handleError(res: Response, error: unknown): void {\n if (res.headersSent) {\n return;\n }\n\n const protocolError = new ProtocolError();\n\n if (error instanceof HttpError) {\n protocolError.message = error.message;\n protocolError.subType = error.subType;\n protocolError.name = error.name;\n\n if (error instanceof HttpBadRequestError) {\n protocolError.field = error.field;\n protocolError.guiAlertMessage = error.guiMessage;\n }\n if (error instanceof HttpVendorError) {\n protocolError.waitSeconds = error.waitSeconds;\n }\n if (error instanceof HttpUserError) {\n protocolError.errorCode = error.errorCode;\n }\n\n res.status(error.code).json(protocolError);\n } else {\n // Unknown error - 500\n const err = toError(error);\n protocolError.message = 'Internal Server Error';\n console.error('[JsonTranslator] Unexpected error:', err);\n res.status(500).json(protocolError);\n }\n }\n}\n/**\n * WebpiecesMiddleware - Express middleware for WebPieces server.\n *\n * This class contains all Express middleware used by WebpiecesServer:\n * 1. globalErrorHandler - Outermost error handler, returns HTML 500 page\n * 2. logNextLayer - Request/response logging\n * 3. jsonTranslator - JSON Content-Type validation and error translation\n *\n * The middleware is injected into WebpiecesServerImpl and registered with Express\n * in the start() method.\n *\n * IMPORTANT: jsonTranslator does NOT dispatch routes - route dispatch happens via\n * Express's registered route handlers (created by RouteBuilder.createHandler()).\n * jsonTranslator only validates Content-Type and translates errors to JSON.\n *\n * DI Pattern: This class is registered via @provideSingleton() with no dependencies.\n */\n@provideSingleton()\n@injectable()\nexport class WebpiecesMiddleware {\n\n /**\n * Global error handler middleware - catches ALL unhandled errors.\n * Returns HTML 500 error page for any errors that escape the filter chain.\n *\n * This is the outermost safety net - JsonTranslator catches JSON API errors,\n * this catches everything else.\n */\n async globalErrorHandler(\n req: Request,\n res: Response,\n next: NextFunction,\n ): Promise<void> {\n console.log('🔴 [Layer 1: GlobalErrorHandler] Request START:', req.method, req.path);\n\n try {\n // await next() catches BOTH:\n // 1. Synchronous throws from next() itself\n // 2. Rejected promises from downstream async middleware\n await next();\n console.log(\n '🔴 [Layer 1: GlobalErrorHandler] Request END (success):',\n req.method,\n req.path,\n );\n } catch (err: unknown) {\n const error = toError(err);\n console.error('🔴 [Layer 1: GlobalErrorHandler] Caught unhandled error:', error);\n if (!res.headersSent) {\n // Return HTML error page (not JSON - JsonTranslator handles JSON errors)\n res.status(500).send(`\n <!DOCTYPE html>\n <html>\n <head><title>Server Error</title></head>\n <body>\n <h1>You hit a server error</h1>\n <p>An unexpected error occurred while processing your request.</p>\n <pre>${error.message}</pre>\n </body>\n </html>\n `);\n }\n console.log(\n '🔴 [Layer 1: GlobalErrorHandler] Request END (error):',\n req.method,\n req.path,\n );\n }\n }\n\n /**\n * Logging middleware - logs request/response flow.\n * Demonstrates middleware execution order.\n * IMPORTANT: Must be async and await next() to properly chain with async middleware.\n */\n async logNextLayer(req: Request, res: Response, next: NextFunction): Promise<void> {\n console.log('🟡 [Layer 2: LogNextLayer] Before next() -', req.method, req.path);\n await next();\n console.log('🟡 [Layer 2: LogNextLayer] After next() -', req.method, req.path);\n }\n\n /**\n * Create an ExpressWrapper for a route.\n * The wrapper handles the full request/response cycle (symmetric design).\n *\n * @param service - The service wrapping the filter chain and controller\n * @param routeMeta - Route metadata for MethodMeta and DTO type\n * @returns ExpressWrapper instance\n */\n createExpressWrapper(\n service: Service<MethodMeta, WpResponse<unknown>>,\n routeMeta: RouteMetadata,\n ): ExpressWrapper {\n return new ExpressWrapper(service, routeMeta);\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"WebpiecesMiddleware.js","sourceRoot":"","sources":["../../../../../packages/http/http-server/src/WebpiecesMiddleware.ts"],"names":[],"mappings":"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","sourcesContent":["import { Request, Response, NextFunction } from 'express';\nimport { injectable } from 'inversify';\nimport { provideSingleton, MethodMeta, ExpressRouteHandler } from '@webpieces/http-routing';\nimport {\n ProtocolError,\n HttpError,\n HttpBadRequestError,\n HttpVendorError,\n HttpUserError,\n HttpNotFoundError,\n HttpTimeoutError,\n HttpUnauthorizedError,\n HttpForbiddenError,\n HttpInternalServerError,\n HttpBadGatewayError,\n HttpGatewayTimeoutError,\n RouteMetadata,\n} from '@webpieces/http-api';\nimport { Service, WpResponse } from '@webpieces/http-filters';\nimport { toError } from '@webpieces/core-util';\n\nexport class ExpressWrapper {\n constructor(\n private service: Service<MethodMeta, WpResponse<unknown>>,\n private routeMeta: RouteMetadata\n ) {\n }\n\n public async execute(req: Request, res: Response, next: NextFunction) {\n try {\n // 1. Parse JSON request body manually (SYMMETRIC with client's JSON.stringify)\n let requestDto: unknown = {};\n if (['POST', 'PUT', 'PATCH'].includes(req.method)) {\n // Read raw body as text\n const bodyText = await this.readRequestBody(req);\n // Parse JSON\n requestDto = bodyText ? JSON.parse(bodyText) : {};\n }\n\n // 2. Create MethodMeta with request DTO\n const methodMeta = new MethodMeta(this.routeMeta, requestDto);\n\n // 3. Invoke the service (filter chain + controller)\n const wpResponse = await this.service.invoke(methodMeta);\n if(!wpResponse.response)\n throw new Error(`Route chain(filters & all) is not returning a response. ${this.routeMeta.controllerClassName}.${this.routeMeta.methodName}`);\n\n // 4. Serialize response DTO to JSON (SYMMETRIC with client's response.json())\n const responseJson = JSON.stringify(wpResponse.response);\n res.status(200).setHeader('Content-Type', 'application/json').send(responseJson);\n } catch (err: unknown) {\n // 5. Handle errors\n this.handleError(res, err);\n }\n }\n\n /**\n * Read raw request body as text.\n * Used to manually parse JSON (instead of express.json() middleware).\n */\n private async readRequestBody(req: Request): Promise<string> {\n return new Promise((resolve, reject) => {\n let body = '';\n req.on('data', (chunk) => {\n body += chunk.toString();\n });\n req.on('end', () => {\n resolve(body);\n });\n req.on('error', (err) => {\n reject(err);\n });\n });\n }\n\n /**\n * Handle errors - translate to JSON ProtocolError (SYMMETRIC with ClientErrorTranslator).\n * PUBLIC so wrapExpress can call it for symmetric error handling.\n * Maps HttpError subclasses to appropriate HTTP status codes and ProtocolError response.\n *\n * Maps all HttpError types (must match ClientErrorTranslator.translateError()):\n * - HttpUserError → 266 (with errorCode)\n * - HttpBadRequestError → 400 (with field, guiAlertMessage)\n * - HttpUnauthorizedError → 401\n * - HttpForbiddenError → 403\n * - HttpNotFoundError → 404\n * - HttpTimeoutError → 408\n * - HttpInternalServerError → 500\n * - HttpBadGatewayError → 502\n * - HttpGatewayTimeoutError → 504\n * - HttpVendorError → 598 (with waitSeconds)\n */\n public handleError(res: Response, error: unknown): void {\n if (res.headersSent) {\n return;\n }\n\n const protocolError = new ProtocolError();\n\n if (error instanceof HttpError) {\n // Set common fields for all HttpError types\n protocolError.message = error.message;\n protocolError.subType = error.subType;\n protocolError.name = error.name;\n\n // Set type-specific fields (MUST match ClientErrorTranslator)\n if (error instanceof HttpUserError) {\n console.log('[ExpressWrapper] User Error:', error.message);\n protocolError.errorCode = error.errorCode;\n } else if (error instanceof HttpBadRequestError) {\n console.log('[ExpressWrapper] Bad Request:', error.message);\n protocolError.field = error.field;\n protocolError.guiAlertMessage = error.guiMessage;\n } else if (error instanceof HttpNotFoundError) {\n console.log('[ExpressWrapper] Not Found:', error.message);\n } else if (error instanceof HttpTimeoutError) {\n console.error('[ExpressWrapper] Timeout Error:', error.message);\n } else if (error instanceof HttpVendorError) {\n console.error('[ExpressWrapper] Vendor Error:', error.message);\n protocolError.waitSeconds = error.waitSeconds;\n } else if (error instanceof HttpUnauthorizedError) {\n console.log('[ExpressWrapper] Unauthorized:', error.message);\n } else if (error instanceof HttpForbiddenError) {\n console.log('[ExpressWrapper] Forbidden:', error.message);\n } else if (error instanceof HttpInternalServerError) {\n console.error('[ExpressWrapper] Internal Server Error:', error.message);\n } else if (error instanceof HttpBadGatewayError) {\n console.error('[ExpressWrapper] Bad Gateway:', error.message);\n } else if (error instanceof HttpGatewayTimeoutError) {\n console.error('[ExpressWrapper] Gateway Timeout:', error.message);\n } else {\n console.log('[ExpressWrapper] Generic HttpError:', error.message);\n }\n\n // Serialize ProtocolError to JSON (SYMMETRIC with client)\n const responseJson = JSON.stringify(protocolError);\n res.status(error.code).setHeader('Content-Type', 'application/json').send(responseJson);\n } else {\n // Unknown error - 500\n const err = toError(error);\n protocolError.message = 'Internal Server Error';\n console.error('[ExpressWrapper] Unexpected error:', err);\n const responseJson = JSON.stringify(protocolError);\n res.status(500).setHeader('Content-Type', 'application/json').send(responseJson);\n }\n }\n}\n/**\n * WebpiecesMiddleware - Express middleware for WebPieces server.\n *\n * This class contains all Express middleware used by WebpiecesServer:\n * 1. globalErrorHandler - Outermost error handler, returns HTML 500 page\n * 2. logNextLayer - Request/response logging\n * 3. jsonTranslator - JSON Content-Type validation and error translation\n *\n * The middleware is injected into WebpiecesServerImpl and registered with Express\n * in the start() method.\n *\n * IMPORTANT: jsonTranslator does NOT dispatch routes - route dispatch happens via\n * Express's registered route handlers (created by RouteBuilder.createHandler()).\n * jsonTranslator only validates Content-Type and translates errors to JSON.\n *\n * DI Pattern: This class is registered via @provideSingleton() with no dependencies.\n */\n@provideSingleton()\n@injectable()\nexport class WebpiecesMiddleware {\n\n /**\n * Global error handler middleware - catches ALL unhandled errors.\n * Returns HTML 500 error page for any errors that escape the filter chain.\n *\n * This is the outermost safety net - JsonTranslator catches JSON API errors,\n * this catches everything else.\n */\n async globalErrorHandler(\n req: Request,\n res: Response,\n next: NextFunction,\n ): Promise<void> {\n console.log('🔴 [Layer 1: GlobalErrorHandler] Request START:', req.method, req.path);\n\n try {\n // await next() catches BOTH:\n // 1. Synchronous throws from next() itself\n // 2. Rejected promises from downstream async middleware\n await next();\n console.log(\n '🔴 [Layer 1: GlobalErrorHandler] Request END (success):',\n req.method,\n req.path,\n );\n } catch (err: unknown) {\n const error = toError(err);\n console.error('🔴 [Layer 1: GlobalErrorHandler] Caught unhandled error:', error);\n if (!res.headersSent) {\n // Return HTML error page (not JSON - JsonTranslator handles JSON errors)\n res.status(500).send(`\n <!DOCTYPE html>\n <html>\n <head><title>Server Error</title></head>\n <body>\n <h1>You hit a server error</h1>\n <p>An unexpected error occurred while processing your request.</p>\n <pre>${error.message}</pre>\n </body>\n </html>\n `);\n }\n console.log(\n '🔴 [Layer 1: GlobalErrorHandler] Request END (error):',\n req.method,\n req.path,\n );\n }\n }\n\n /**\n * Logging middleware - logs request/response flow.\n * Demonstrates middleware execution order.\n * IMPORTANT: Must be async and await next() to properly chain with async middleware.\n */\n async logNextLayer(req: Request, res: Response, next: NextFunction): Promise<void> {\n console.log('🟡 [Layer 2: LogNextLayer] Before next() -', req.method, req.path);\n await next();\n console.log('🟡 [Layer 2: LogNextLayer] After next() -', req.method, req.path);\n }\n\n /**\n * Create an ExpressWrapper for a route.\n * The wrapper handles the full request/response cycle (symmetric design).\n *\n * @param service - The service wrapping the filter chain and controller\n * @param routeMeta - Route metadata for MethodMeta and DTO type\n * @returns ExpressWrapper instance\n */\n createExpressWrapper(\n service: Service<MethodMeta, WpResponse<unknown>>,\n routeMeta: RouteMetadata,\n ): ExpressWrapper {\n return new ExpressWrapper(service, routeMeta);\n }\n}\n"]}
|