@hazeljs/core 0.2.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +522 -0
- package/dist/__tests__/container.test.d.ts +2 -0
- package/dist/__tests__/container.test.d.ts.map +1 -0
- package/dist/__tests__/container.test.js +454 -0
- package/dist/__tests__/decorators.test.d.ts +2 -0
- package/dist/__tests__/decorators.test.d.ts.map +1 -0
- package/dist/__tests__/decorators.test.js +693 -0
- package/dist/__tests__/errors/http.error.test.d.ts +2 -0
- package/dist/__tests__/errors/http.error.test.d.ts.map +1 -0
- package/dist/__tests__/errors/http.error.test.js +117 -0
- package/dist/__tests__/filters/exception-filter.test.d.ts +2 -0
- package/dist/__tests__/filters/exception-filter.test.d.ts.map +1 -0
- package/dist/__tests__/filters/exception-filter.test.js +135 -0
- package/dist/__tests__/filters/http-exception.filter.test.d.ts +2 -0
- package/dist/__tests__/filters/http-exception.filter.test.d.ts.map +1 -0
- package/dist/__tests__/filters/http-exception.filter.test.js +119 -0
- package/dist/__tests__/hazel-app.test.d.ts +2 -0
- package/dist/__tests__/hazel-app.test.d.ts.map +1 -0
- package/dist/__tests__/hazel-app.test.js +682 -0
- package/dist/__tests__/hazel-module.test.d.ts +2 -0
- package/dist/__tests__/hazel-module.test.d.ts.map +1 -0
- package/dist/__tests__/hazel-module.test.js +408 -0
- package/dist/__tests__/hazel-response.test.d.ts +2 -0
- package/dist/__tests__/hazel-response.test.d.ts.map +1 -0
- package/dist/__tests__/hazel-response.test.js +138 -0
- package/dist/__tests__/health.test.d.ts +2 -0
- package/dist/__tests__/health.test.d.ts.map +1 -0
- package/dist/__tests__/health.test.js +147 -0
- package/dist/__tests__/index.test.d.ts +2 -0
- package/dist/__tests__/index.test.d.ts.map +1 -0
- package/dist/__tests__/index.test.js +239 -0
- package/dist/__tests__/interceptors/interceptor.test.d.ts +2 -0
- package/dist/__tests__/interceptors/interceptor.test.d.ts.map +1 -0
- package/dist/__tests__/interceptors/interceptor.test.js +166 -0
- package/dist/__tests__/logger.test.d.ts +2 -0
- package/dist/__tests__/logger.test.d.ts.map +1 -0
- package/dist/__tests__/logger.test.js +141 -0
- package/dist/__tests__/middleware/cors.test.d.ts +2 -0
- package/dist/__tests__/middleware/cors.test.d.ts.map +1 -0
- package/dist/__tests__/middleware/cors.test.js +129 -0
- package/dist/__tests__/middleware/csrf.test.d.ts +2 -0
- package/dist/__tests__/middleware/csrf.test.d.ts.map +1 -0
- package/dist/__tests__/middleware/csrf.test.js +247 -0
- package/dist/__tests__/middleware/global-middleware.test.d.ts +2 -0
- package/dist/__tests__/middleware/global-middleware.test.d.ts.map +1 -0
- package/dist/__tests__/middleware/global-middleware.test.js +259 -0
- package/dist/__tests__/middleware/rate-limit.test.d.ts +2 -0
- package/dist/__tests__/middleware/rate-limit.test.d.ts.map +1 -0
- package/dist/__tests__/middleware/rate-limit.test.js +264 -0
- package/dist/__tests__/middleware/security-headers.test.d.ts +2 -0
- package/dist/__tests__/middleware/security-headers.test.d.ts.map +1 -0
- package/dist/__tests__/middleware/security-headers.test.js +229 -0
- package/dist/__tests__/middleware/timeout.test.d.ts +2 -0
- package/dist/__tests__/middleware/timeout.test.d.ts.map +1 -0
- package/dist/__tests__/middleware/timeout.test.js +132 -0
- package/dist/__tests__/middleware.test.d.ts +2 -0
- package/dist/__tests__/middleware.test.d.ts.map +1 -0
- package/dist/__tests__/middleware.test.js +180 -0
- package/dist/__tests__/pipes/pipe.test.d.ts +2 -0
- package/dist/__tests__/pipes/pipe.test.d.ts.map +1 -0
- package/dist/__tests__/pipes/pipe.test.js +245 -0
- package/dist/__tests__/pipes/validation.pipe.test.d.ts +2 -0
- package/dist/__tests__/pipes/validation.pipe.test.d.ts.map +1 -0
- package/dist/__tests__/pipes/validation.pipe.test.js +297 -0
- package/dist/__tests__/request-parser.test.d.ts +2 -0
- package/dist/__tests__/request-parser.test.d.ts.map +1 -0
- package/dist/__tests__/request-parser.test.js +182 -0
- package/dist/__tests__/router.test.d.ts +2 -0
- package/dist/__tests__/router.test.d.ts.map +1 -0
- package/dist/__tests__/router.test.js +680 -0
- package/dist/__tests__/routing/route-matcher.test.d.ts +2 -0
- package/dist/__tests__/routing/route-matcher.test.d.ts.map +1 -0
- package/dist/__tests__/routing/route-matcher.test.js +219 -0
- package/dist/__tests__/routing/version.decorator.test.d.ts +2 -0
- package/dist/__tests__/routing/version.decorator.test.d.ts.map +1 -0
- package/dist/__tests__/routing/version.decorator.test.js +298 -0
- package/dist/__tests__/service.test.d.ts +2 -0
- package/dist/__tests__/service.test.d.ts.map +1 -0
- package/dist/__tests__/service.test.js +121 -0
- package/dist/__tests__/shutdown.test.d.ts +2 -0
- package/dist/__tests__/shutdown.test.d.ts.map +1 -0
- package/dist/__tests__/shutdown.test.js +250 -0
- package/dist/__tests__/testing/testing.module.test.d.ts +2 -0
- package/dist/__tests__/testing/testing.module.test.d.ts.map +1 -0
- package/dist/__tests__/testing/testing.module.test.js +370 -0
- package/dist/__tests__/upload/file-upload.test.d.ts +2 -0
- package/dist/__tests__/upload/file-upload.test.d.ts.map +1 -0
- package/dist/__tests__/upload/file-upload.test.js +498 -0
- package/dist/__tests__/utils/sanitize.test.d.ts +2 -0
- package/dist/__tests__/utils/sanitize.test.d.ts.map +1 -0
- package/dist/__tests__/utils/sanitize.test.js +291 -0
- package/dist/__tests__/validator.test.d.ts +2 -0
- package/dist/__tests__/validator.test.d.ts.map +1 -0
- package/dist/__tests__/validator.test.js +300 -0
- package/dist/container.d.ts +80 -0
- package/dist/container.d.ts.map +1 -0
- package/dist/container.js +271 -0
- package/dist/decorators.d.ts +92 -0
- package/dist/decorators.d.ts.map +1 -0
- package/dist/decorators.js +343 -0
- package/dist/errors/http.error.d.ts +31 -0
- package/dist/errors/http.error.d.ts.map +1 -0
- package/dist/errors/http.error.js +62 -0
- package/dist/filters/exception-filter.d.ts +39 -0
- package/dist/filters/exception-filter.d.ts.map +1 -0
- package/dist/filters/exception-filter.js +38 -0
- package/dist/filters/http-exception.filter.d.ts +9 -0
- package/dist/filters/http-exception.filter.d.ts.map +1 -0
- package/dist/filters/http-exception.filter.js +42 -0
- package/dist/hazel-app.d.ts +78 -0
- package/dist/hazel-app.d.ts.map +1 -0
- package/dist/hazel-app.js +453 -0
- package/dist/hazel-module.d.ts +20 -0
- package/dist/hazel-module.d.ts.map +1 -0
- package/dist/hazel-module.js +109 -0
- package/dist/hazel-response.d.ts +20 -0
- package/dist/hazel-response.d.ts.map +1 -0
- package/dist/hazel-response.js +68 -0
- package/dist/health.d.ts +73 -0
- package/dist/health.d.ts.map +1 -0
- package/dist/health.js +174 -0
- package/dist/index.d.ts +41 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +140 -0
- package/dist/interceptors/interceptor.d.ts +22 -0
- package/dist/interceptors/interceptor.d.ts.map +1 -0
- package/dist/interceptors/interceptor.js +46 -0
- package/dist/logger.d.ts +8 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +238 -0
- package/dist/middleware/cors.middleware.d.ts +44 -0
- package/dist/middleware/cors.middleware.d.ts.map +1 -0
- package/dist/middleware/cors.middleware.js +118 -0
- package/dist/middleware/csrf.middleware.d.ts +82 -0
- package/dist/middleware/csrf.middleware.d.ts.map +1 -0
- package/dist/middleware/csrf.middleware.js +183 -0
- package/dist/middleware/global-middleware.d.ts +111 -0
- package/dist/middleware/global-middleware.d.ts.map +1 -0
- package/dist/middleware/global-middleware.js +179 -0
- package/dist/middleware/rate-limit.middleware.d.ts +73 -0
- package/dist/middleware/rate-limit.middleware.d.ts.map +1 -0
- package/dist/middleware/rate-limit.middleware.js +124 -0
- package/dist/middleware/security-headers.middleware.d.ts +76 -0
- package/dist/middleware/security-headers.middleware.d.ts.map +1 -0
- package/dist/middleware/security-headers.middleware.js +123 -0
- package/dist/middleware/timeout.middleware.d.ts +25 -0
- package/dist/middleware/timeout.middleware.d.ts.map +1 -0
- package/dist/middleware/timeout.middleware.js +74 -0
- package/dist/middleware.d.ts +13 -0
- package/dist/middleware.d.ts.map +1 -0
- package/dist/middleware.js +47 -0
- package/dist/pipes/pipe.d.ts +50 -0
- package/dist/pipes/pipe.d.ts.map +1 -0
- package/dist/pipes/pipe.js +96 -0
- package/dist/pipes/validation.pipe.d.ts +6 -0
- package/dist/pipes/validation.pipe.d.ts.map +1 -0
- package/dist/pipes/validation.pipe.js +61 -0
- package/dist/request-context.d.ts +17 -0
- package/dist/request-context.d.ts.map +1 -0
- package/dist/request-context.js +2 -0
- package/dist/request-parser.d.ts +7 -0
- package/dist/request-parser.d.ts.map +1 -0
- package/dist/request-parser.js +60 -0
- package/dist/router.d.ts +33 -0
- package/dist/router.d.ts.map +1 -0
- package/dist/router.js +426 -0
- package/dist/routing/route-matcher.d.ts +39 -0
- package/dist/routing/route-matcher.d.ts.map +1 -0
- package/dist/routing/route-matcher.js +93 -0
- package/dist/routing/version.decorator.d.ts +36 -0
- package/dist/routing/version.decorator.d.ts.map +1 -0
- package/dist/routing/version.decorator.js +89 -0
- package/dist/service.d.ts +9 -0
- package/dist/service.d.ts.map +1 -0
- package/dist/service.js +39 -0
- package/dist/shutdown.d.ts +32 -0
- package/dist/shutdown.d.ts.map +1 -0
- package/dist/shutdown.js +109 -0
- package/dist/testing/testing.module.d.ts +83 -0
- package/dist/testing/testing.module.d.ts.map +1 -0
- package/dist/testing/testing.module.js +164 -0
- package/dist/types.d.ts +76 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/upload/file-upload.d.ts +75 -0
- package/dist/upload/file-upload.d.ts.map +1 -0
- package/dist/upload/file-upload.js +261 -0
- package/dist/utils/sanitize.d.ts +45 -0
- package/dist/utils/sanitize.d.ts.map +1 -0
- package/dist/utils/sanitize.js +165 -0
- package/dist/validator.d.ts +7 -0
- package/dist/validator.d.ts.map +1 -0
- package/dist/validator.js +119 -0
- package/package.json +65 -0
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SecurityHeadersMiddleware = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Security Headers Middleware
|
|
6
|
+
* Adds security headers to HTTP responses
|
|
7
|
+
*/
|
|
8
|
+
class SecurityHeadersMiddleware {
|
|
9
|
+
constructor(options = {}) {
|
|
10
|
+
this.options = options;
|
|
11
|
+
// Set secure defaults
|
|
12
|
+
this.options = {
|
|
13
|
+
noSniff: true,
|
|
14
|
+
frameOptions: 'DENY',
|
|
15
|
+
xssProtection: true,
|
|
16
|
+
hsts: {
|
|
17
|
+
maxAge: 31536000, // 1 year
|
|
18
|
+
includeSubDomains: true,
|
|
19
|
+
preload: false,
|
|
20
|
+
},
|
|
21
|
+
hidePoweredBy: true,
|
|
22
|
+
...options,
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
use(req, res, next) {
|
|
26
|
+
// Remove X-Powered-By header
|
|
27
|
+
if (this.options.hidePoweredBy) {
|
|
28
|
+
res.setHeader('X-Powered-By', '');
|
|
29
|
+
}
|
|
30
|
+
// X-Content-Type-Options
|
|
31
|
+
if (this.options.noSniff) {
|
|
32
|
+
res.setHeader('X-Content-Type-Options', 'nosniff');
|
|
33
|
+
}
|
|
34
|
+
// X-Frame-Options
|
|
35
|
+
if (this.options.frameOptions) {
|
|
36
|
+
res.setHeader('X-Frame-Options', this.options.frameOptions);
|
|
37
|
+
}
|
|
38
|
+
// X-XSS-Protection
|
|
39
|
+
if (this.options.xssProtection) {
|
|
40
|
+
res.setHeader('X-XSS-Protection', '1; mode=block');
|
|
41
|
+
}
|
|
42
|
+
// Strict-Transport-Security
|
|
43
|
+
if (this.options.hsts && (req.headers?.['x-forwarded-proto'] === 'https' || process.env.NODE_ENV === 'production')) {
|
|
44
|
+
const hstsValue = [
|
|
45
|
+
`max-age=${this.options.hsts.maxAge || 31536000}`,
|
|
46
|
+
this.options.hsts.includeSubDomains ? 'includeSubDomains' : '',
|
|
47
|
+
this.options.hsts.preload ? 'preload' : '',
|
|
48
|
+
]
|
|
49
|
+
.filter(Boolean)
|
|
50
|
+
.join('; ');
|
|
51
|
+
res.setHeader('Strict-Transport-Security', hstsValue);
|
|
52
|
+
}
|
|
53
|
+
// Content-Security-Policy
|
|
54
|
+
if (this.options.contentSecurityPolicy) {
|
|
55
|
+
const csp = typeof this.options.contentSecurityPolicy === 'string'
|
|
56
|
+
? this.options.contentSecurityPolicy
|
|
57
|
+
: this.buildCSP(this.options.contentSecurityPolicy);
|
|
58
|
+
res.setHeader('Content-Security-Policy', csp);
|
|
59
|
+
}
|
|
60
|
+
// Referrer-Policy
|
|
61
|
+
if (this.options.referrerPolicy) {
|
|
62
|
+
res.setHeader('Referrer-Policy', this.options.referrerPolicy);
|
|
63
|
+
}
|
|
64
|
+
// Permissions-Policy
|
|
65
|
+
if (this.options.permissionsPolicy) {
|
|
66
|
+
const permissions = Object.entries(this.options.permissionsPolicy)
|
|
67
|
+
.map(([feature, allowlist]) => {
|
|
68
|
+
const value = allowlist.length === 0 ? '()' : `(${allowlist.join(' ')})`;
|
|
69
|
+
return `${feature}=${value}`;
|
|
70
|
+
})
|
|
71
|
+
.join(', ');
|
|
72
|
+
res.setHeader('Permissions-Policy', permissions);
|
|
73
|
+
}
|
|
74
|
+
next();
|
|
75
|
+
}
|
|
76
|
+
buildCSP(policy) {
|
|
77
|
+
if (typeof policy === 'string') {
|
|
78
|
+
return policy;
|
|
79
|
+
}
|
|
80
|
+
const directives = [];
|
|
81
|
+
if (policy.defaultSrc) {
|
|
82
|
+
directives.push(`default-src ${policy.defaultSrc.join(' ')}`);
|
|
83
|
+
}
|
|
84
|
+
if (policy.scriptSrc) {
|
|
85
|
+
directives.push(`script-src ${policy.scriptSrc.join(' ')}`);
|
|
86
|
+
}
|
|
87
|
+
if (policy.styleSrc) {
|
|
88
|
+
directives.push(`style-src ${policy.styleSrc.join(' ')}`);
|
|
89
|
+
}
|
|
90
|
+
if (policy.imgSrc) {
|
|
91
|
+
directives.push(`img-src ${policy.imgSrc.join(' ')}`);
|
|
92
|
+
}
|
|
93
|
+
if (policy.connectSrc) {
|
|
94
|
+
directives.push(`connect-src ${policy.connectSrc.join(' ')}`);
|
|
95
|
+
}
|
|
96
|
+
if (policy.fontSrc) {
|
|
97
|
+
directives.push(`font-src ${policy.fontSrc.join(' ')}`);
|
|
98
|
+
}
|
|
99
|
+
if (policy.objectSrc) {
|
|
100
|
+
directives.push(`object-src ${policy.objectSrc.join(' ')}`);
|
|
101
|
+
}
|
|
102
|
+
if (policy.mediaSrc) {
|
|
103
|
+
directives.push(`media-src ${policy.mediaSrc.join(' ')}`);
|
|
104
|
+
}
|
|
105
|
+
if (policy.frameSrc) {
|
|
106
|
+
directives.push(`frame-src ${policy.frameSrc.join(' ')}`);
|
|
107
|
+
}
|
|
108
|
+
if (policy.baseUri) {
|
|
109
|
+
directives.push(`base-uri ${policy.baseUri.join(' ')}`);
|
|
110
|
+
}
|
|
111
|
+
if (policy.formAction) {
|
|
112
|
+
directives.push(`form-action ${policy.formAction.join(' ')}`);
|
|
113
|
+
}
|
|
114
|
+
if (policy.frameAncestors) {
|
|
115
|
+
directives.push(`frame-ancestors ${policy.frameAncestors.join(' ')}`);
|
|
116
|
+
}
|
|
117
|
+
if (policy.upgradeInsecureRequests) {
|
|
118
|
+
directives.push('upgrade-insecure-requests');
|
|
119
|
+
}
|
|
120
|
+
return directives.join('; ');
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
exports.SecurityHeadersMiddleware = SecurityHeadersMiddleware;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Request Timeout Middleware
|
|
3
|
+
* Prevents requests from hanging indefinitely
|
|
4
|
+
*/
|
|
5
|
+
import { Request, Response } from '../types';
|
|
6
|
+
export interface TimeoutOptions {
|
|
7
|
+
timeout?: number;
|
|
8
|
+
message?: string;
|
|
9
|
+
onTimeout?: (req: Request) => void;
|
|
10
|
+
}
|
|
11
|
+
export declare class TimeoutMiddleware {
|
|
12
|
+
private timeout;
|
|
13
|
+
private message;
|
|
14
|
+
private onTimeout?;
|
|
15
|
+
constructor(options?: TimeoutOptions);
|
|
16
|
+
/**
|
|
17
|
+
* Create timeout handler for a request
|
|
18
|
+
*/
|
|
19
|
+
handle(req: Request, res: Response, next: () => void): void;
|
|
20
|
+
/**
|
|
21
|
+
* Create middleware function
|
|
22
|
+
*/
|
|
23
|
+
static create(options?: TimeoutOptions): (req: Request, res: Response, next: () => void) => void;
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=timeout.middleware.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"timeout.middleware.d.ts","sourceRoot":"","sources":["../../src/middleware/timeout.middleware.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AAG7C,MAAM,WAAW,cAAc;IAC7B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,IAAI,CAAC;CACpC;AAED,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,SAAS,CAAC,CAAyB;gBAE/B,OAAO,GAAE,cAAmB;IAMxC;;OAEG;IACH,MAAM,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,IAAI,GAAG,IAAI;IAmD3D;;OAEG;IACH,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,cAAc,GAAG,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,IAAI,KAAK,IAAI;CAIjG"}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Request Timeout Middleware
|
|
4
|
+
* Prevents requests from hanging indefinitely
|
|
5
|
+
*/
|
|
6
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
7
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
8
|
+
};
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
exports.TimeoutMiddleware = void 0;
|
|
11
|
+
const logger_1 = __importDefault(require("../logger"));
|
|
12
|
+
class TimeoutMiddleware {
|
|
13
|
+
constructor(options = {}) {
|
|
14
|
+
this.timeout = options.timeout || 30000; // 30 seconds default
|
|
15
|
+
this.message = options.message || 'Request timeout';
|
|
16
|
+
this.onTimeout = options.onTimeout;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Create timeout handler for a request
|
|
20
|
+
*/
|
|
21
|
+
handle(req, res, next) {
|
|
22
|
+
let timeoutId = null;
|
|
23
|
+
let timedOut = false;
|
|
24
|
+
// Set timeout
|
|
25
|
+
timeoutId = setTimeout(() => {
|
|
26
|
+
timedOut = true;
|
|
27
|
+
// Call custom timeout handler if provided
|
|
28
|
+
if (this.onTimeout) {
|
|
29
|
+
try {
|
|
30
|
+
this.onTimeout(req);
|
|
31
|
+
}
|
|
32
|
+
catch (error) {
|
|
33
|
+
logger_1.default.error('Error in timeout callback:', error);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
// Log timeout
|
|
37
|
+
logger_1.default.warn('Request timeout:', {
|
|
38
|
+
method: req.method,
|
|
39
|
+
url: req.url,
|
|
40
|
+
timeout: this.timeout,
|
|
41
|
+
});
|
|
42
|
+
// Send timeout response
|
|
43
|
+
// Note: We can't check headersSent on our Response interface,
|
|
44
|
+
// but the status/json methods will handle this internally
|
|
45
|
+
res.status(408).json({
|
|
46
|
+
statusCode: 408,
|
|
47
|
+
message: this.message,
|
|
48
|
+
error: 'Request Timeout',
|
|
49
|
+
});
|
|
50
|
+
}, this.timeout);
|
|
51
|
+
// Clear timeout when response finishes
|
|
52
|
+
// Store original end method
|
|
53
|
+
const originalEnd = res.end.bind(res);
|
|
54
|
+
res.end = function () {
|
|
55
|
+
if (timeoutId) {
|
|
56
|
+
clearTimeout(timeoutId);
|
|
57
|
+
timeoutId = null;
|
|
58
|
+
}
|
|
59
|
+
return originalEnd();
|
|
60
|
+
};
|
|
61
|
+
// Continue to next middleware
|
|
62
|
+
if (!timedOut) {
|
|
63
|
+
next();
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Create middleware function
|
|
68
|
+
*/
|
|
69
|
+
static create(options) {
|
|
70
|
+
const middleware = new TimeoutMiddleware(options);
|
|
71
|
+
return (req, res, next) => middleware.handle(req, res, next);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
exports.TimeoutMiddleware = TimeoutMiddleware;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { Type } from './types';
|
|
2
|
+
import { RequestContext } from './types';
|
|
3
|
+
import { Container } from './container';
|
|
4
|
+
export interface Middleware {
|
|
5
|
+
use(context: RequestContext, next: () => Promise<unknown>): Promise<unknown>;
|
|
6
|
+
}
|
|
7
|
+
export type MiddlewareFunction = (context: RequestContext, next: () => Promise<unknown>) => Promise<unknown>;
|
|
8
|
+
export declare class MiddlewareHandler {
|
|
9
|
+
private container;
|
|
10
|
+
constructor(container: Container);
|
|
11
|
+
executeMiddlewareChain(middlewares: Type<Middleware>[], context: RequestContext, finalHandler: () => Promise<unknown>): Promise<unknown>;
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=middleware.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"middleware.d.ts","sourceRoot":"","sources":["../src/middleware.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AAC/B,OAAO,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AACzC,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAExC,MAAM,WAAW,UAAU;IACzB,GAAG,CAAC,OAAO,EAAE,cAAc,EAAE,IAAI,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CAC9E;AAED,MAAM,MAAM,kBAAkB,GAAG,CAC/B,OAAO,EAAE,cAAc,EACvB,IAAI,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,KACzB,OAAO,CAAC,OAAO,CAAC,CAAC;AAEtB,qBAAa,iBAAiB;IAChB,OAAO,CAAC,SAAS;gBAAT,SAAS,EAAE,SAAS;IAElC,sBAAsB,CAC1B,WAAW,EAAE,IAAI,CAAC,UAAU,CAAC,EAAE,EAC/B,OAAO,EAAE,cAAc,EACvB,YAAY,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,GACnC,OAAO,CAAC,OAAO,CAAC;CAwCpB"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.MiddlewareHandler = void 0;
|
|
4
|
+
class MiddlewareHandler {
|
|
5
|
+
constructor(container) {
|
|
6
|
+
this.container = container;
|
|
7
|
+
}
|
|
8
|
+
async executeMiddlewareChain(middlewares, context, finalHandler) {
|
|
9
|
+
const next = async () => {
|
|
10
|
+
if (!middlewares || middlewares.length === 0) {
|
|
11
|
+
try {
|
|
12
|
+
return await finalHandler();
|
|
13
|
+
}
|
|
14
|
+
catch (error) {
|
|
15
|
+
const err = error;
|
|
16
|
+
if (!err.status) {
|
|
17
|
+
err.status = 500;
|
|
18
|
+
}
|
|
19
|
+
throw error;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
const middleware = this.container.resolve(middlewares[0]);
|
|
23
|
+
const remainingMiddlewares = middlewares.slice(1);
|
|
24
|
+
try {
|
|
25
|
+
return await middleware.use(context, () => this.executeMiddlewareChain(remainingMiddlewares, context, finalHandler));
|
|
26
|
+
}
|
|
27
|
+
catch (error) {
|
|
28
|
+
const err = error;
|
|
29
|
+
if (!err.status) {
|
|
30
|
+
err.status = 500;
|
|
31
|
+
}
|
|
32
|
+
throw error;
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
try {
|
|
36
|
+
return await next();
|
|
37
|
+
}
|
|
38
|
+
catch (error) {
|
|
39
|
+
const err = error;
|
|
40
|
+
if (!err.status) {
|
|
41
|
+
err.status = 500;
|
|
42
|
+
}
|
|
43
|
+
throw error;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
exports.MiddlewareHandler = MiddlewareHandler;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { RequestContext } from '../request-context';
|
|
2
|
+
export interface PipeTransform<T = unknown, R = unknown> {
|
|
3
|
+
transform(value: T, context: RequestContext): R | Promise<R>;
|
|
4
|
+
}
|
|
5
|
+
export interface PipeMetadata {
|
|
6
|
+
type: Type<PipeTransform>;
|
|
7
|
+
options?: unknown;
|
|
8
|
+
}
|
|
9
|
+
export interface ValidationPipeOptions {
|
|
10
|
+
whitelist?: boolean;
|
|
11
|
+
forbidNonWhitelisted?: boolean;
|
|
12
|
+
transform?: boolean;
|
|
13
|
+
validateCustomDecorators?: boolean;
|
|
14
|
+
}
|
|
15
|
+
export declare class ValidationError extends Error {
|
|
16
|
+
errors: Array<{
|
|
17
|
+
property: string;
|
|
18
|
+
constraints: Record<string, string>;
|
|
19
|
+
value?: unknown;
|
|
20
|
+
}>;
|
|
21
|
+
constructor(message: string, errors: Array<{
|
|
22
|
+
property: string;
|
|
23
|
+
constraints: Record<string, string>;
|
|
24
|
+
value?: unknown;
|
|
25
|
+
}>);
|
|
26
|
+
toJSON(): {
|
|
27
|
+
message: string;
|
|
28
|
+
errors: Array<{
|
|
29
|
+
field: string;
|
|
30
|
+
messages: string[];
|
|
31
|
+
value: unknown;
|
|
32
|
+
}>;
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
export declare class ParseIntPipe implements PipeTransform<string, number> {
|
|
36
|
+
transform(value: string, context: RequestContext): number;
|
|
37
|
+
}
|
|
38
|
+
export declare class ParseFloatPipe implements PipeTransform<string, number> {
|
|
39
|
+
transform(value: string, context: RequestContext): number;
|
|
40
|
+
}
|
|
41
|
+
export declare class ParseBoolPipe implements PipeTransform<string, boolean> {
|
|
42
|
+
transform(value: string, context: RequestContext): boolean;
|
|
43
|
+
}
|
|
44
|
+
export declare class DefaultValuePipe<T = unknown> implements PipeTransform<T | undefined, T> {
|
|
45
|
+
private defaultValue;
|
|
46
|
+
constructor(defaultValue: T);
|
|
47
|
+
transform(value: T | undefined): T;
|
|
48
|
+
}
|
|
49
|
+
export type Type<T = unknown> = new (...args: unknown[]) => T;
|
|
50
|
+
//# sourceMappingURL=pipe.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pipe.d.ts","sourceRoot":"","sources":["../../src/pipes/pipe.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAGpD,MAAM,WAAW,aAAa,CAAC,CAAC,GAAG,OAAO,EAAE,CAAC,GAAG,OAAO;IACrD,SAAS,CAAC,KAAK,EAAE,CAAC,EAAE,OAAO,EAAE,cAAc,GAAG,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;CAC9D;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,IAAI,CAAC,aAAa,CAAC,CAAC;IAC1B,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,qBAAqB;IACpC,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,wBAAwB,CAAC,EAAE,OAAO,CAAC;CACpC;AAED,qBAAa,eAAgB,SAAQ,KAAK;IAG/B,MAAM,EAAE,KAAK,CAAC;QACnB,QAAQ,EAAE,MAAM,CAAC;QACjB,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QACpC,KAAK,CAAC,EAAE,OAAO,CAAC;KACjB,CAAC;gBALF,OAAO,EAAE,MAAM,EACR,MAAM,EAAE,KAAK,CAAC;QACnB,QAAQ,EAAE,MAAM,CAAC;QACjB,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QACpC,KAAK,CAAC,EAAE,OAAO,CAAC;KACjB,CAAC;IAMJ,MAAM,IAAI;QACR,OAAO,EAAE,MAAM,CAAC;QAChB,MAAM,EAAE,KAAK,CAAC;YACZ,KAAK,EAAE,MAAM,CAAC;YACd,QAAQ,EAAE,MAAM,EAAE,CAAC;YACnB,KAAK,EAAE,OAAO,CAAC;SAChB,CAAC,CAAC;KACJ;CAUF;AAED,qBAAa,YAAa,YAAW,aAAa,CAAC,MAAM,EAAE,MAAM,CAAC;IAChE,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,cAAc,GAAG,MAAM;CAwB1D;AAED,qBAAa,cAAe,YAAW,aAAa,CAAC,MAAM,EAAE,MAAM,CAAC;IAClE,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,cAAc,GAAG,MAAM;CAc1D;AAED,qBAAa,aAAc,YAAW,aAAa,CAAC,MAAM,EAAE,OAAO,CAAC;IAClE,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,cAAc,GAAG,OAAO;CAY3D;AAED,qBAAa,gBAAgB,CAAC,CAAC,GAAG,OAAO,CAAE,YAAW,aAAa,CAAC,CAAC,GAAG,SAAS,EAAE,CAAC,CAAC;IACvE,OAAO,CAAC,YAAY;gBAAZ,YAAY,EAAE,CAAC;IAEnC,SAAS,CAAC,KAAK,EAAE,CAAC,GAAG,SAAS,GAAG,CAAC;CAInC;AAED,MAAM,MAAM,IAAI,CAAC,CAAC,GAAG,OAAO,IAAI,KAAK,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,CAAC,CAAC"}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.DefaultValuePipe = exports.ParseBoolPipe = exports.ParseFloatPipe = exports.ParseIntPipe = exports.ValidationError = void 0;
|
|
7
|
+
const logger_1 = __importDefault(require("../logger"));
|
|
8
|
+
class ValidationError extends Error {
|
|
9
|
+
constructor(message, errors) {
|
|
10
|
+
super(message);
|
|
11
|
+
this.errors = errors;
|
|
12
|
+
this.name = 'ValidationError';
|
|
13
|
+
}
|
|
14
|
+
toJSON() {
|
|
15
|
+
return {
|
|
16
|
+
message: this.message,
|
|
17
|
+
errors: this.errors.map((error) => ({
|
|
18
|
+
field: error.property,
|
|
19
|
+
messages: Object.values(error.constraints),
|
|
20
|
+
value: error.value,
|
|
21
|
+
})),
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
exports.ValidationError = ValidationError;
|
|
26
|
+
class ParseIntPipe {
|
|
27
|
+
transform(value, context) {
|
|
28
|
+
logger_1.default.debug(`ParseIntPipe received value: ${value}`);
|
|
29
|
+
if (!value) {
|
|
30
|
+
throw new ValidationError('Value is required', [
|
|
31
|
+
{
|
|
32
|
+
value,
|
|
33
|
+
property: context.params?.id ? 'id' : 'value',
|
|
34
|
+
constraints: { isNotEmpty: 'value must not be empty' },
|
|
35
|
+
},
|
|
36
|
+
]);
|
|
37
|
+
}
|
|
38
|
+
const parsed = parseInt(value, 10);
|
|
39
|
+
logger_1.default.debug(`ParseIntPipe parsed value: ${parsed}`);
|
|
40
|
+
if (isNaN(parsed)) {
|
|
41
|
+
throw new ValidationError('Invalid integer value', [
|
|
42
|
+
{
|
|
43
|
+
value,
|
|
44
|
+
property: context.params?.id ? 'id' : 'value',
|
|
45
|
+
constraints: { isInt: 'value must be an integer' },
|
|
46
|
+
},
|
|
47
|
+
]);
|
|
48
|
+
}
|
|
49
|
+
return parsed;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
exports.ParseIntPipe = ParseIntPipe;
|
|
53
|
+
class ParseFloatPipe {
|
|
54
|
+
transform(value, context) {
|
|
55
|
+
logger_1.default.debug('Parsing float:', value);
|
|
56
|
+
const parsed = parseFloat(value);
|
|
57
|
+
if (isNaN(parsed)) {
|
|
58
|
+
throw new ValidationError('Invalid float value', [
|
|
59
|
+
{
|
|
60
|
+
value,
|
|
61
|
+
property: context.params?.id ? 'id' : 'value',
|
|
62
|
+
constraints: { isFloat: 'value must be a float' },
|
|
63
|
+
},
|
|
64
|
+
]);
|
|
65
|
+
}
|
|
66
|
+
return parsed;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
exports.ParseFloatPipe = ParseFloatPipe;
|
|
70
|
+
class ParseBoolPipe {
|
|
71
|
+
transform(value, context) {
|
|
72
|
+
logger_1.default.debug('Parsing boolean:', value);
|
|
73
|
+
if (value === 'true')
|
|
74
|
+
return true;
|
|
75
|
+
if (value === 'false')
|
|
76
|
+
return false;
|
|
77
|
+
throw new ValidationError('Invalid boolean value', [
|
|
78
|
+
{
|
|
79
|
+
value,
|
|
80
|
+
property: context.params?.id ? 'id' : 'value',
|
|
81
|
+
constraints: { isBoolean: 'value must be a boolean' },
|
|
82
|
+
},
|
|
83
|
+
]);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
exports.ParseBoolPipe = ParseBoolPipe;
|
|
87
|
+
class DefaultValuePipe {
|
|
88
|
+
constructor(defaultValue) {
|
|
89
|
+
this.defaultValue = defaultValue;
|
|
90
|
+
}
|
|
91
|
+
transform(value) {
|
|
92
|
+
logger_1.default.debug('Applying default value:', { value, defaultValue: this.defaultValue });
|
|
93
|
+
return value === undefined ? this.defaultValue : value;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
exports.DefaultValuePipe = DefaultValuePipe;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { PipeTransform } from './pipe';
|
|
2
|
+
import { RequestContext } from '../request-context';
|
|
3
|
+
export declare class ValidationPipe implements PipeTransform {
|
|
4
|
+
transform(value: unknown, context: RequestContext): Promise<unknown>;
|
|
5
|
+
}
|
|
6
|
+
//# sourceMappingURL=validation.pipe.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validation.pipe.d.ts","sourceRoot":"","sources":["../../src/pipes/validation.pipe.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAmB,MAAM,QAAQ,CAAC;AAIxD,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAMpD,qBAAa,cAAe,YAAW,aAAa;IAC5C,SAAS,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,OAAO,CAAC;CAkD3E"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.ValidationPipe = void 0;
|
|
7
|
+
const pipe_1 = require("./pipe");
|
|
8
|
+
const logger_1 = __importDefault(require("../logger"));
|
|
9
|
+
const class_transformer_1 = require("class-transformer");
|
|
10
|
+
const class_validator_1 = require("class-validator");
|
|
11
|
+
function isConstructor(f) {
|
|
12
|
+
return typeof f === 'function';
|
|
13
|
+
}
|
|
14
|
+
class ValidationPipe {
|
|
15
|
+
async transform(value, context) {
|
|
16
|
+
logger_1.default.debug('ValidationPipe.transform - Input value:', value);
|
|
17
|
+
logger_1.default.debug('ValidationPipe.transform - Context:', context);
|
|
18
|
+
// Get DTO type from context
|
|
19
|
+
const dtoType = context.dtoType;
|
|
20
|
+
logger_1.default.debug('ValidationPipe.transform - DTO type:', dtoType?.name);
|
|
21
|
+
if (!isConstructor(dtoType)) {
|
|
22
|
+
logger_1.default.debug('ValidationPipe.transform - No valid DTO type provided, returning value as is');
|
|
23
|
+
return value;
|
|
24
|
+
}
|
|
25
|
+
try {
|
|
26
|
+
// Ensure value is an object before transformation
|
|
27
|
+
if (typeof value !== 'object' || value === null) {
|
|
28
|
+
throw new pipe_1.ValidationError('Invalid input: expected an object', [
|
|
29
|
+
{
|
|
30
|
+
property: 'body',
|
|
31
|
+
constraints: { isObject: 'Input must be an object' },
|
|
32
|
+
value,
|
|
33
|
+
},
|
|
34
|
+
]);
|
|
35
|
+
}
|
|
36
|
+
// Transform plain object to class instance
|
|
37
|
+
const instance = (0, class_transformer_1.plainToClass)(dtoType, value);
|
|
38
|
+
logger_1.default.debug('ValidationPipe.transform - Transformed instance:', instance);
|
|
39
|
+
// Validate instance
|
|
40
|
+
const errors = await (0, class_validator_1.validate)(instance);
|
|
41
|
+
if (errors.length > 0) {
|
|
42
|
+
logger_1.default.error('ValidationPipe.transform - Validation failed:', errors);
|
|
43
|
+
const formattedErrors = errors.map((error) => ({
|
|
44
|
+
property: error.property,
|
|
45
|
+
constraints: error.constraints || {},
|
|
46
|
+
value: error.value,
|
|
47
|
+
}));
|
|
48
|
+
const validationError = new pipe_1.ValidationError('Validation failed', formattedErrors);
|
|
49
|
+
logger_1.default.error('ValidationPipe.transform - Throwing validation error:', validationError);
|
|
50
|
+
throw validationError;
|
|
51
|
+
}
|
|
52
|
+
logger_1.default.debug('ValidationPipe.transform - Validation successful');
|
|
53
|
+
return instance;
|
|
54
|
+
}
|
|
55
|
+
catch (error) {
|
|
56
|
+
logger_1.default.error('ValidationPipe.transform - Error during transformation:', error);
|
|
57
|
+
throw error;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
exports.ValidationPipe = ValidationPipe;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { Type } from './types';
|
|
2
|
+
export interface RequestContext {
|
|
3
|
+
method: string;
|
|
4
|
+
url: string;
|
|
5
|
+
headers: Record<string, string>;
|
|
6
|
+
params: Record<string, string>;
|
|
7
|
+
query: Record<string, string>;
|
|
8
|
+
body: unknown;
|
|
9
|
+
dtoType?: Type<unknown>;
|
|
10
|
+
user?: {
|
|
11
|
+
id: string | number;
|
|
12
|
+
username?: string;
|
|
13
|
+
role: string;
|
|
14
|
+
[key: string]: unknown;
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=request-context.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"request-context.d.ts","sourceRoot":"","sources":["../src/request-context.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AAE/B,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/B,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC9B,IAAI,EAAE,OAAO,CAAC;IACd,OAAO,CAAC,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;IACxB,IAAI,CAAC,EAAE;QACL,EAAE,EAAE,MAAM,GAAG,MAAM,CAAC;QACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,IAAI,EAAE,MAAM,CAAC;QACb,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;KACxB,CAAC;CACH"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { RequestContext, Request } from './types';
|
|
2
|
+
export declare class RequestParser {
|
|
3
|
+
static parseRequest(req: Request): Promise<RequestContext>;
|
|
4
|
+
private static normalizeHeaders;
|
|
5
|
+
private static parseQueryString;
|
|
6
|
+
}
|
|
7
|
+
//# sourceMappingURL=request-parser.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"request-parser.d.ts","sourceRoot":"","sources":["../src/request-parser.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAIlD,qBAAa,aAAa;WACX,YAAY,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC,cAAc,CAAC;IAqChE,OAAO,CAAC,MAAM,CAAC,gBAAgB;IAW/B,OAAO,CAAC,MAAM,CAAC,gBAAgB;CAQhC"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.RequestParser = void 0;
|
|
7
|
+
const logger_1 = __importDefault(require("./logger"));
|
|
8
|
+
class RequestParser {
|
|
9
|
+
static async parseRequest(req) {
|
|
10
|
+
try {
|
|
11
|
+
const { method, url, headers } = req;
|
|
12
|
+
const context = {
|
|
13
|
+
method: method || 'GET',
|
|
14
|
+
url: url || '/',
|
|
15
|
+
headers: this.normalizeHeaders(headers),
|
|
16
|
+
params: { ...req.params },
|
|
17
|
+
query: {},
|
|
18
|
+
body: req.body || {},
|
|
19
|
+
};
|
|
20
|
+
// Parse query parameters
|
|
21
|
+
if (url) {
|
|
22
|
+
const queryString = url.split('?')[1];
|
|
23
|
+
if (queryString) {
|
|
24
|
+
context.query = this.parseQueryString(queryString);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
logger_1.default.debug('Parsed request context:', context);
|
|
28
|
+
return context;
|
|
29
|
+
}
|
|
30
|
+
catch (error) {
|
|
31
|
+
const err = error;
|
|
32
|
+
logger_1.default.error(`[${req.method}] ${req.url} - Request parsing error: ${err.message} (status: ${err.status || 400})`);
|
|
33
|
+
if (process.env.NODE_ENV === 'development' && err.stack) {
|
|
34
|
+
logger_1.default.debug(err.stack);
|
|
35
|
+
}
|
|
36
|
+
if (!err.status) {
|
|
37
|
+
err.status = 500;
|
|
38
|
+
}
|
|
39
|
+
throw error;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
static normalizeHeaders(headers) {
|
|
43
|
+
const normalized = {};
|
|
44
|
+
if (!headers)
|
|
45
|
+
return normalized;
|
|
46
|
+
for (const [key, value] of Object.entries(headers)) {
|
|
47
|
+
normalized[key.toLowerCase()] = Array.isArray(value) ? value.join(', ') : value || '';
|
|
48
|
+
}
|
|
49
|
+
return normalized;
|
|
50
|
+
}
|
|
51
|
+
static parseQueryString(queryString) {
|
|
52
|
+
const params = {};
|
|
53
|
+
const searchParams = new URLSearchParams(queryString);
|
|
54
|
+
for (const [key, value] of searchParams.entries()) {
|
|
55
|
+
params[key] = value;
|
|
56
|
+
}
|
|
57
|
+
return params;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
exports.RequestParser = RequestParser;
|
package/dist/router.d.ts
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { Type } from './types';
|
|
2
|
+
import { RequestContext, Request, Response } from './types';
|
|
3
|
+
import { Container } from './container';
|
|
4
|
+
import 'reflect-metadata';
|
|
5
|
+
interface RouteMatch {
|
|
6
|
+
handler: RouteHandler;
|
|
7
|
+
context: RequestContext;
|
|
8
|
+
}
|
|
9
|
+
type RouteHandler = (req: Request, res: Response, context?: RequestContext) => void;
|
|
10
|
+
export declare class Router {
|
|
11
|
+
private container;
|
|
12
|
+
private routes;
|
|
13
|
+
private routesByMethod;
|
|
14
|
+
private middlewareHandler;
|
|
15
|
+
constructor(container: Container);
|
|
16
|
+
registerController(controller: Type<unknown>): void;
|
|
17
|
+
private applyPipes;
|
|
18
|
+
private applyInterceptors;
|
|
19
|
+
private createRouteHandler;
|
|
20
|
+
private extractParams;
|
|
21
|
+
private matchPath;
|
|
22
|
+
private createRoutePattern;
|
|
23
|
+
private normalizePath;
|
|
24
|
+
match(method: string, url: string, context: RequestContext): Promise<RouteMatch | null>;
|
|
25
|
+
get(path: string, handlers: RouteHandler[]): void;
|
|
26
|
+
post(path: string, handlers: RouteHandler[]): void;
|
|
27
|
+
put(path: string, handlers: RouteHandler[]): void;
|
|
28
|
+
delete(path: string, handlers: RouteHandler[]): void;
|
|
29
|
+
private addRoute;
|
|
30
|
+
handleRequest(req: Request, res: Response): Promise<void>;
|
|
31
|
+
}
|
|
32
|
+
export {};
|
|
33
|
+
//# sourceMappingURL=router.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"router.d.ts","sourceRoot":"","sources":["../src/router.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AAC/B,OAAO,EAAE,cAAc,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAC5D,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAKxC,OAAO,kBAAkB,CAAC;AAY1B,UAAU,UAAU;IAClB,OAAO,EAAE,YAAY,CAAC;IACtB,OAAO,EAAE,cAAc,CAAC;CACzB;AAED,KAAK,YAAY,GAAG,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,OAAO,CAAC,EAAE,cAAc,KAAK,IAAI,CAAC;AAEpF,qBAAa,MAAM;IAKL,OAAO,CAAC,SAAS;IAJ7B,OAAO,CAAC,MAAM,CAA0C;IACxD,OAAO,CAAC,cAAc,CAAuD;IAC7E,OAAO,CAAC,iBAAiB,CAAoB;gBAEzB,SAAS,EAAE,SAAS;IAUxC,kBAAkB,CAAC,UAAU,EAAE,IAAI,CAAC,OAAO,CAAC,GAAG,IAAI;YA4DrC,UAAU;YAsBV,iBAAiB;IAe/B,OAAO,CAAC,kBAAkB;IA+N1B,OAAO,CAAC,aAAa;IAiBrB,OAAO,CAAC,SAAS;IAiBjB,OAAO,CAAC,kBAAkB;IAW1B,OAAO,CAAC,aAAa;IAOf,KAAK,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC;IA0C7F,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,YAAY,EAAE,GAAG,IAAI;IAIjD,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,YAAY,EAAE,GAAG,IAAI;IAIlD,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,YAAY,EAAE,GAAG,IAAI;IAIjD,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,YAAY,EAAE,GAAG,IAAI;IAIpD,OAAO,CAAC,QAAQ;IAKV,aAAa,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC;CAkChE"}
|