@hazeljs/core 0.2.0-alpha.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.
Files changed (195) hide show
  1. package/LICENSE +192 -0
  2. package/README.md +560 -0
  3. package/dist/__tests__/container.test.d.ts +2 -0
  4. package/dist/__tests__/container.test.d.ts.map +1 -0
  5. package/dist/__tests__/container.test.js +454 -0
  6. package/dist/__tests__/decorators.test.d.ts +2 -0
  7. package/dist/__tests__/decorators.test.d.ts.map +1 -0
  8. package/dist/__tests__/decorators.test.js +1237 -0
  9. package/dist/__tests__/errors/http.error.test.d.ts +2 -0
  10. package/dist/__tests__/errors/http.error.test.d.ts.map +1 -0
  11. package/dist/__tests__/errors/http.error.test.js +117 -0
  12. package/dist/__tests__/filters/exception-filter.test.d.ts +2 -0
  13. package/dist/__tests__/filters/exception-filter.test.d.ts.map +1 -0
  14. package/dist/__tests__/filters/exception-filter.test.js +135 -0
  15. package/dist/__tests__/filters/http-exception.filter.test.d.ts +2 -0
  16. package/dist/__tests__/filters/http-exception.filter.test.d.ts.map +1 -0
  17. package/dist/__tests__/filters/http-exception.filter.test.js +119 -0
  18. package/dist/__tests__/hazel-app.test.d.ts +2 -0
  19. package/dist/__tests__/hazel-app.test.d.ts.map +1 -0
  20. package/dist/__tests__/hazel-app.test.js +810 -0
  21. package/dist/__tests__/hazel-module.test.d.ts +2 -0
  22. package/dist/__tests__/hazel-module.test.d.ts.map +1 -0
  23. package/dist/__tests__/hazel-module.test.js +408 -0
  24. package/dist/__tests__/hazel-response.test.d.ts +2 -0
  25. package/dist/__tests__/hazel-response.test.d.ts.map +1 -0
  26. package/dist/__tests__/hazel-response.test.js +138 -0
  27. package/dist/__tests__/health.test.d.ts +2 -0
  28. package/dist/__tests__/health.test.d.ts.map +1 -0
  29. package/dist/__tests__/health.test.js +147 -0
  30. package/dist/__tests__/index.test.d.ts +2 -0
  31. package/dist/__tests__/index.test.d.ts.map +1 -0
  32. package/dist/__tests__/index.test.js +239 -0
  33. package/dist/__tests__/interceptors/interceptor.test.d.ts +2 -0
  34. package/dist/__tests__/interceptors/interceptor.test.d.ts.map +1 -0
  35. package/dist/__tests__/interceptors/interceptor.test.js +166 -0
  36. package/dist/__tests__/logger.test.d.ts +2 -0
  37. package/dist/__tests__/logger.test.d.ts.map +1 -0
  38. package/dist/__tests__/logger.test.js +141 -0
  39. package/dist/__tests__/middleware/cors.test.d.ts +2 -0
  40. package/dist/__tests__/middleware/cors.test.d.ts.map +1 -0
  41. package/dist/__tests__/middleware/cors.test.js +129 -0
  42. package/dist/__tests__/middleware/csrf.test.d.ts +2 -0
  43. package/dist/__tests__/middleware/csrf.test.d.ts.map +1 -0
  44. package/dist/__tests__/middleware/csrf.test.js +247 -0
  45. package/dist/__tests__/middleware/global-middleware.test.d.ts +2 -0
  46. package/dist/__tests__/middleware/global-middleware.test.d.ts.map +1 -0
  47. package/dist/__tests__/middleware/global-middleware.test.js +259 -0
  48. package/dist/__tests__/middleware/rate-limit.test.d.ts +2 -0
  49. package/dist/__tests__/middleware/rate-limit.test.d.ts.map +1 -0
  50. package/dist/__tests__/middleware/rate-limit.test.js +264 -0
  51. package/dist/__tests__/middleware/security-headers.test.d.ts +2 -0
  52. package/dist/__tests__/middleware/security-headers.test.d.ts.map +1 -0
  53. package/dist/__tests__/middleware/security-headers.test.js +229 -0
  54. package/dist/__tests__/middleware/timeout.test.d.ts +2 -0
  55. package/dist/__tests__/middleware/timeout.test.d.ts.map +1 -0
  56. package/dist/__tests__/middleware/timeout.test.js +132 -0
  57. package/dist/__tests__/middleware.test.d.ts +2 -0
  58. package/dist/__tests__/middleware.test.d.ts.map +1 -0
  59. package/dist/__tests__/middleware.test.js +180 -0
  60. package/dist/__tests__/pipes/pipe.test.d.ts +2 -0
  61. package/dist/__tests__/pipes/pipe.test.d.ts.map +1 -0
  62. package/dist/__tests__/pipes/pipe.test.js +245 -0
  63. package/dist/__tests__/pipes/validation.pipe.test.d.ts +2 -0
  64. package/dist/__tests__/pipes/validation.pipe.test.d.ts.map +1 -0
  65. package/dist/__tests__/pipes/validation.pipe.test.js +297 -0
  66. package/dist/__tests__/request-parser.test.d.ts +2 -0
  67. package/dist/__tests__/request-parser.test.d.ts.map +1 -0
  68. package/dist/__tests__/request-parser.test.js +182 -0
  69. package/dist/__tests__/router.test.d.ts +2 -0
  70. package/dist/__tests__/router.test.d.ts.map +1 -0
  71. package/dist/__tests__/router.test.js +1183 -0
  72. package/dist/__tests__/routing/route-matcher.test.d.ts +2 -0
  73. package/dist/__tests__/routing/route-matcher.test.d.ts.map +1 -0
  74. package/dist/__tests__/routing/route-matcher.test.js +219 -0
  75. package/dist/__tests__/routing/version.decorator.test.d.ts +2 -0
  76. package/dist/__tests__/routing/version.decorator.test.d.ts.map +1 -0
  77. package/dist/__tests__/routing/version.decorator.test.js +298 -0
  78. package/dist/__tests__/service.test.d.ts +2 -0
  79. package/dist/__tests__/service.test.d.ts.map +1 -0
  80. package/dist/__tests__/service.test.js +121 -0
  81. package/dist/__tests__/shutdown.test.d.ts +2 -0
  82. package/dist/__tests__/shutdown.test.d.ts.map +1 -0
  83. package/dist/__tests__/shutdown.test.js +250 -0
  84. package/dist/__tests__/testing/testing.module.test.d.ts +2 -0
  85. package/dist/__tests__/testing/testing.module.test.d.ts.map +1 -0
  86. package/dist/__tests__/testing/testing.module.test.js +370 -0
  87. package/dist/__tests__/upload/file-upload.test.d.ts +2 -0
  88. package/dist/__tests__/upload/file-upload.test.d.ts.map +1 -0
  89. package/dist/__tests__/upload/file-upload.test.js +498 -0
  90. package/dist/__tests__/utils/sanitize.test.d.ts +2 -0
  91. package/dist/__tests__/utils/sanitize.test.d.ts.map +1 -0
  92. package/dist/__tests__/utils/sanitize.test.js +291 -0
  93. package/dist/__tests__/validator.test.d.ts +2 -0
  94. package/dist/__tests__/validator.test.d.ts.map +1 -0
  95. package/dist/__tests__/validator.test.js +300 -0
  96. package/dist/container.d.ts +80 -0
  97. package/dist/container.d.ts.map +1 -0
  98. package/dist/container.js +271 -0
  99. package/dist/decorators.d.ts +166 -0
  100. package/dist/decorators.d.ts.map +1 -0
  101. package/dist/decorators.js +538 -0
  102. package/dist/errors/http.error.d.ts +34 -0
  103. package/dist/errors/http.error.d.ts.map +1 -0
  104. package/dist/errors/http.error.js +69 -0
  105. package/dist/filters/exception-filter.d.ts +39 -0
  106. package/dist/filters/exception-filter.d.ts.map +1 -0
  107. package/dist/filters/exception-filter.js +38 -0
  108. package/dist/filters/http-exception.filter.d.ts +9 -0
  109. package/dist/filters/http-exception.filter.d.ts.map +1 -0
  110. package/dist/filters/http-exception.filter.js +42 -0
  111. package/dist/hazel-app.d.ts +94 -0
  112. package/dist/hazel-app.d.ts.map +1 -0
  113. package/dist/hazel-app.js +516 -0
  114. package/dist/hazel-module.d.ts +29 -0
  115. package/dist/hazel-module.d.ts.map +1 -0
  116. package/dist/hazel-module.js +137 -0
  117. package/dist/hazel-response.d.ts +25 -0
  118. package/dist/hazel-response.d.ts.map +1 -0
  119. package/dist/hazel-response.js +89 -0
  120. package/dist/health.d.ts +73 -0
  121. package/dist/health.d.ts.map +1 -0
  122. package/dist/health.js +174 -0
  123. package/dist/index.d.ts +41 -0
  124. package/dist/index.d.ts.map +1 -0
  125. package/dist/index.js +159 -0
  126. package/dist/interceptors/interceptor.d.ts +30 -0
  127. package/dist/interceptors/interceptor.d.ts.map +1 -0
  128. package/dist/interceptors/interceptor.js +71 -0
  129. package/dist/logger.d.ts +8 -0
  130. package/dist/logger.d.ts.map +1 -0
  131. package/dist/logger.js +261 -0
  132. package/dist/middleware/cors.middleware.d.ts +44 -0
  133. package/dist/middleware/cors.middleware.d.ts.map +1 -0
  134. package/dist/middleware/cors.middleware.js +118 -0
  135. package/dist/middleware/csrf.middleware.d.ts +82 -0
  136. package/dist/middleware/csrf.middleware.d.ts.map +1 -0
  137. package/dist/middleware/csrf.middleware.js +183 -0
  138. package/dist/middleware/global-middleware.d.ts +111 -0
  139. package/dist/middleware/global-middleware.d.ts.map +1 -0
  140. package/dist/middleware/global-middleware.js +179 -0
  141. package/dist/middleware/rate-limit.middleware.d.ts +73 -0
  142. package/dist/middleware/rate-limit.middleware.d.ts.map +1 -0
  143. package/dist/middleware/rate-limit.middleware.js +124 -0
  144. package/dist/middleware/security-headers.middleware.d.ts +76 -0
  145. package/dist/middleware/security-headers.middleware.d.ts.map +1 -0
  146. package/dist/middleware/security-headers.middleware.js +123 -0
  147. package/dist/middleware/timeout.middleware.d.ts +25 -0
  148. package/dist/middleware/timeout.middleware.d.ts.map +1 -0
  149. package/dist/middleware/timeout.middleware.js +74 -0
  150. package/dist/middleware.d.ts +13 -0
  151. package/dist/middleware.d.ts.map +1 -0
  152. package/dist/middleware.js +47 -0
  153. package/dist/pipes/pipe.d.ts +50 -0
  154. package/dist/pipes/pipe.d.ts.map +1 -0
  155. package/dist/pipes/pipe.js +96 -0
  156. package/dist/pipes/validation.pipe.d.ts +6 -0
  157. package/dist/pipes/validation.pipe.d.ts.map +1 -0
  158. package/dist/pipes/validation.pipe.js +61 -0
  159. package/dist/request-context.d.ts +22 -0
  160. package/dist/request-context.d.ts.map +1 -0
  161. package/dist/request-context.js +2 -0
  162. package/dist/request-parser.d.ts +7 -0
  163. package/dist/request-parser.d.ts.map +1 -0
  164. package/dist/request-parser.js +60 -0
  165. package/dist/router.d.ts +33 -0
  166. package/dist/router.d.ts.map +1 -0
  167. package/dist/router.js +506 -0
  168. package/dist/routing/route-matcher.d.ts +39 -0
  169. package/dist/routing/route-matcher.d.ts.map +1 -0
  170. package/dist/routing/route-matcher.js +93 -0
  171. package/dist/routing/version.decorator.d.ts +36 -0
  172. package/dist/routing/version.decorator.d.ts.map +1 -0
  173. package/dist/routing/version.decorator.js +89 -0
  174. package/dist/service.d.ts +9 -0
  175. package/dist/service.d.ts.map +1 -0
  176. package/dist/service.js +39 -0
  177. package/dist/shutdown.d.ts +32 -0
  178. package/dist/shutdown.d.ts.map +1 -0
  179. package/dist/shutdown.js +109 -0
  180. package/dist/testing/testing.module.d.ts +83 -0
  181. package/dist/testing/testing.module.d.ts.map +1 -0
  182. package/dist/testing/testing.module.js +164 -0
  183. package/dist/types.d.ts +82 -0
  184. package/dist/types.d.ts.map +1 -0
  185. package/dist/types.js +2 -0
  186. package/dist/upload/file-upload.d.ts +75 -0
  187. package/dist/upload/file-upload.d.ts.map +1 -0
  188. package/dist/upload/file-upload.js +261 -0
  189. package/dist/utils/sanitize.d.ts +45 -0
  190. package/dist/utils/sanitize.d.ts.map +1 -0
  191. package/dist/utils/sanitize.js +165 -0
  192. package/dist/validator.d.ts +7 -0
  193. package/dist/validator.d.ts.map +1 -0
  194. package/dist/validator.js +119 -0
  195. package/package.json +67 -0
@@ -0,0 +1,118 @@
1
+ "use strict";
2
+ /**
3
+ * CORS Middleware
4
+ * Handles Cross-Origin Resource Sharing
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.CorsMiddleware = void 0;
8
+ exports.enableCors = enableCors;
9
+ class CorsMiddleware {
10
+ constructor(options = {}) {
11
+ this.options = {
12
+ origin: options.origin || '*',
13
+ methods: options.methods || ['GET', 'HEAD', 'PUT', 'PATCH', 'POST', 'DELETE'],
14
+ allowedHeaders: options.allowedHeaders || ['Content-Type', 'Authorization'],
15
+ exposedHeaders: options.exposedHeaders || [],
16
+ credentials: options.credentials || false,
17
+ maxAge: options.maxAge || 86400, // 24 hours
18
+ preflightContinue: options.preflightContinue || false,
19
+ optionsSuccessStatus: options.optionsSuccessStatus || 204,
20
+ };
21
+ }
22
+ /**
23
+ * Check if origin is allowed
24
+ */
25
+ isOriginAllowed(origin) {
26
+ const { origin: allowedOrigin } = this.options;
27
+ if (allowedOrigin === '*') {
28
+ return true;
29
+ }
30
+ if (typeof allowedOrigin === 'string') {
31
+ return origin === allowedOrigin;
32
+ }
33
+ if (Array.isArray(allowedOrigin)) {
34
+ return allowedOrigin.includes(origin);
35
+ }
36
+ if (typeof allowedOrigin === 'function') {
37
+ return allowedOrigin(origin);
38
+ }
39
+ return false;
40
+ }
41
+ /**
42
+ * Set CORS headers
43
+ */
44
+ setCorsHeaders(req, res) {
45
+ const origin = req.headers?.origin || req.headers?.referer || '';
46
+ // Set Access-Control-Allow-Origin
47
+ if (this.options.origin === '*') {
48
+ res.setHeader('Access-Control-Allow-Origin', '*');
49
+ }
50
+ else if (this.isOriginAllowed(origin)) {
51
+ res.setHeader('Access-Control-Allow-Origin', origin);
52
+ res.setHeader('Vary', 'Origin');
53
+ }
54
+ // Set Access-Control-Allow-Credentials
55
+ if (this.options.credentials) {
56
+ res.setHeader('Access-Control-Allow-Credentials', 'true');
57
+ }
58
+ // Set Access-Control-Expose-Headers
59
+ if (this.options.exposedHeaders.length > 0) {
60
+ res.setHeader('Access-Control-Expose-Headers', this.options.exposedHeaders.join(', '));
61
+ }
62
+ }
63
+ /**
64
+ * Handle preflight request
65
+ */
66
+ handlePreflight(req, res) {
67
+ if (req.method !== 'OPTIONS') {
68
+ return false;
69
+ }
70
+ // Set CORS headers
71
+ this.setCorsHeaders(req, res);
72
+ // Set Access-Control-Allow-Methods
73
+ res.setHeader('Access-Control-Allow-Methods', this.options.methods.join(', '));
74
+ // Set Access-Control-Allow-Headers
75
+ const requestHeaders = req.headers?.['access-control-request-headers'];
76
+ if (requestHeaders) {
77
+ res.setHeader('Access-Control-Allow-Headers', requestHeaders);
78
+ }
79
+ else if (this.options.allowedHeaders.length > 0) {
80
+ res.setHeader('Access-Control-Allow-Headers', this.options.allowedHeaders.join(', '));
81
+ }
82
+ // Set Access-Control-Max-Age
83
+ res.setHeader('Access-Control-Max-Age', this.options.maxAge.toString());
84
+ // Send response
85
+ if (!this.options.preflightContinue) {
86
+ res.status(this.options.optionsSuccessStatus).end();
87
+ return true;
88
+ }
89
+ return false;
90
+ }
91
+ /**
92
+ * Handle CORS request
93
+ */
94
+ handle(req, res, next) {
95
+ // Handle preflight request
96
+ if (this.handlePreflight(req, res)) {
97
+ return;
98
+ }
99
+ // Set CORS headers for actual request
100
+ this.setCorsHeaders(req, res);
101
+ // Continue to next middleware
102
+ next();
103
+ }
104
+ /**
105
+ * Create middleware function
106
+ */
107
+ static create(options) {
108
+ const middleware = new CorsMiddleware(options);
109
+ return (req, res, next) => middleware.handle(req, res, next);
110
+ }
111
+ }
112
+ exports.CorsMiddleware = CorsMiddleware;
113
+ /**
114
+ * Simple CORS helper for common use cases
115
+ */
116
+ function enableCors(options) {
117
+ return CorsMiddleware.create(options);
118
+ }
@@ -0,0 +1,82 @@
1
+ import { Request, Response } from '../types';
2
+ import { MiddlewareClass, NextFunction } from './global-middleware';
3
+ /**
4
+ * CSRF protection options
5
+ */
6
+ export interface CsrfOptions {
7
+ /**
8
+ * Cookie name for CSRF token
9
+ */
10
+ cookieName?: string;
11
+ /**
12
+ * Header name for CSRF token
13
+ */
14
+ headerName?: string;
15
+ /**
16
+ * Secret key for token generation
17
+ */
18
+ secret?: string;
19
+ /**
20
+ * Cookie options
21
+ */
22
+ cookieOptions?: {
23
+ httpOnly?: boolean;
24
+ secure?: boolean;
25
+ sameSite?: 'strict' | 'lax' | 'none';
26
+ path?: string;
27
+ domain?: string;
28
+ maxAge?: number;
29
+ };
30
+ /**
31
+ * Methods to protect (default: POST, PUT, PATCH, DELETE)
32
+ */
33
+ methods?: string[];
34
+ /**
35
+ * Paths to exclude from CSRF protection
36
+ */
37
+ excludePaths?: string[];
38
+ /**
39
+ * Token length in bytes
40
+ */
41
+ tokenLength?: number;
42
+ }
43
+ /**
44
+ * CSRF Protection Middleware
45
+ * Protects against Cross-Site Request Forgery attacks
46
+ */
47
+ export declare class CsrfMiddleware implements MiddlewareClass {
48
+ private options;
49
+ private secret;
50
+ private tokenStore;
51
+ constructor(options?: CsrfOptions);
52
+ use(req: Request, res: Response, next: NextFunction): void;
53
+ /**
54
+ * Get or create CSRF token
55
+ */
56
+ private getOrCreateToken;
57
+ /**
58
+ * Generate CSRF token
59
+ */
60
+ private generateToken;
61
+ /**
62
+ * Verify CSRF token
63
+ */
64
+ private verifyToken;
65
+ /**
66
+ * Get token from request
67
+ */
68
+ private getTokenFromRequest;
69
+ /**
70
+ * Get session ID from request
71
+ */
72
+ private getSessionId;
73
+ /**
74
+ * Build cookie string
75
+ */
76
+ private buildCookie;
77
+ /**
78
+ * Cleanup expired tokens
79
+ */
80
+ private cleanupTokens;
81
+ }
82
+ //# sourceMappingURL=csrf.middleware.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"csrf.middleware.d.ts","sourceRoot":"","sources":["../../src/middleware/csrf.middleware.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AAC7C,OAAO,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAKpE;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B;;OAEG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB;;OAEG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB;;OAEG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAEhB;;OAEG;IACH,aAAa,CAAC,EAAE;QACd,QAAQ,CAAC,EAAE,OAAO,CAAC;QACnB,MAAM,CAAC,EAAE,OAAO,CAAC;QACjB,QAAQ,CAAC,EAAE,QAAQ,GAAG,KAAK,GAAG,MAAM,CAAC;QACrC,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,MAAM,CAAC,EAAE,MAAM,CAAC;KACjB,CAAC;IAEF;;OAEG;IACH,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IAEnB;;OAEG;IACH,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IAExB;;OAEG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;;GAGG;AACH,qBAAa,cAAe,YAAW,eAAe;IAIxC,OAAO,CAAC,OAAO;IAH3B,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,UAAU,CAAkC;gBAEhC,OAAO,GAAE,WAAgB;IAyB7C,GAAG,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,YAAY,GAAG,IAAI;IA4B1D;;OAEG;IACH,OAAO,CAAC,gBAAgB;IAgBxB;;OAEG;IACH,OAAO,CAAC,aAAa;IAQrB;;OAEG;IACH,OAAO,CAAC,WAAW;IAenB;;OAEG;IACH,OAAO,CAAC,mBAAmB;IAwB3B;;OAEG;IACH,OAAO,CAAC,YAAY;IAsBpB;;OAEG;IACH,OAAO,CAAC,WAAW;IAcnB;;OAEG;IACH,OAAO,CAAC,aAAa;CAYtB"}
@@ -0,0 +1,183 @@
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.CsrfMiddleware = void 0;
7
+ const http_error_1 = require("../errors/http.error");
8
+ const crypto_1 = require("crypto");
9
+ const logger_1 = __importDefault(require("../logger"));
10
+ /**
11
+ * CSRF Protection Middleware
12
+ * Protects against Cross-Site Request Forgery attacks
13
+ */
14
+ class CsrfMiddleware {
15
+ constructor(options = {}) {
16
+ this.options = options;
17
+ this.tokenStore = new Map();
18
+ this.options = {
19
+ cookieName: '_csrf',
20
+ headerName: 'x-csrf-token',
21
+ secret: process.env.CSRF_SECRET || (0, crypto_1.randomBytes)(32).toString('hex'),
22
+ cookieOptions: {
23
+ httpOnly: true,
24
+ secure: process.env.NODE_ENV === 'production',
25
+ sameSite: 'strict',
26
+ path: '/',
27
+ maxAge: 3600, // 1 hour
28
+ },
29
+ methods: ['POST', 'PUT', 'PATCH', 'DELETE'],
30
+ tokenLength: 32,
31
+ ...options,
32
+ };
33
+ this.secret = this.options.secret;
34
+ // Cleanup expired tokens every 5 minutes
35
+ setInterval(() => {
36
+ this.cleanupTokens();
37
+ }, 300000);
38
+ }
39
+ use(req, res, next) {
40
+ const path = req.url?.split('?')[0] || '/';
41
+ // Skip excluded paths
42
+ if (this.options.excludePaths?.some(excluded => path.startsWith(excluded))) {
43
+ next();
44
+ return;
45
+ }
46
+ // Generate or retrieve token
47
+ const token = this.getOrCreateToken(req, res);
48
+ // Check if method requires CSRF protection
49
+ if (this.options.methods?.includes(req.method || '')) {
50
+ const providedToken = this.getTokenFromRequest(req);
51
+ if (!providedToken || !this.verifyToken(token, providedToken)) {
52
+ logger_1.default.warn(`CSRF token validation failed for ${req.method} ${path}`);
53
+ throw new http_error_1.HttpError(403, 'Invalid CSRF token');
54
+ }
55
+ }
56
+ // Add token to response for forms
57
+ res.setHeader('X-CSRF-Token', token);
58
+ next();
59
+ }
60
+ /**
61
+ * Get or create CSRF token
62
+ */
63
+ getOrCreateToken(req, res) {
64
+ const sessionId = this.getSessionId(req);
65
+ let token = this.tokenStore.get(sessionId);
66
+ if (!token) {
67
+ token = this.generateToken();
68
+ this.tokenStore.set(sessionId, token);
69
+ }
70
+ // Set cookie
71
+ const cookie = this.buildCookie(token);
72
+ res.setHeader('Set-Cookie', cookie);
73
+ return token;
74
+ }
75
+ /**
76
+ * Generate CSRF token
77
+ */
78
+ generateToken() {
79
+ const random = (0, crypto_1.randomBytes)(this.options.tokenLength || 32).toString('hex');
80
+ const hmac = (0, crypto_1.createHmac)('sha256', this.secret);
81
+ hmac.update(random);
82
+ const signature = hmac.digest('hex');
83
+ return `${random}.${signature}`;
84
+ }
85
+ /**
86
+ * Verify CSRF token
87
+ */
88
+ verifyToken(storedToken, providedToken) {
89
+ if (!storedToken || !providedToken)
90
+ return false;
91
+ if (storedToken === providedToken)
92
+ return true;
93
+ // Verify signature
94
+ const [random, signature] = providedToken.split('.');
95
+ if (!random || !signature)
96
+ return false;
97
+ const hmac = (0, crypto_1.createHmac)('sha256', this.secret);
98
+ hmac.update(random);
99
+ const expectedSignature = hmac.digest('hex');
100
+ return signature === expectedSignature;
101
+ }
102
+ /**
103
+ * Get token from request
104
+ */
105
+ getTokenFromRequest(req) {
106
+ // Try header first
107
+ if (req.headers) {
108
+ const headerToken = req.headers[this.options.headerName.toLowerCase()];
109
+ if (headerToken) {
110
+ return Array.isArray(headerToken) ? headerToken[0] : headerToken;
111
+ }
112
+ }
113
+ // Try body
114
+ if (req.body && typeof req.body === 'object' && this.options.cookieName in req.body) {
115
+ const bodyValue = req.body[this.options.cookieName];
116
+ return typeof bodyValue === 'string' ? bodyValue : null;
117
+ }
118
+ // Try query parameter
119
+ if (req.query && typeof req.query === 'object' && this.options.cookieName in req.query) {
120
+ const queryValue = req.query[this.options.cookieName];
121
+ return typeof queryValue === 'string' ? queryValue : null;
122
+ }
123
+ return null;
124
+ }
125
+ /**
126
+ * Get session ID from request
127
+ */
128
+ getSessionId(req) {
129
+ // Try to get from cookie
130
+ if (req.headers?.cookie) {
131
+ const sessionCookie = req.headers.cookie
132
+ .split(';')
133
+ .find(c => c.trim().startsWith('sessionId='));
134
+ if (sessionCookie) {
135
+ return sessionCookie.split('=')[1].trim();
136
+ }
137
+ }
138
+ // Fallback to IP + User-Agent hash
139
+ const ip = req.socket?.remoteAddress || 'unknown';
140
+ const ua = req.headers?.['user-agent'] || 'unknown';
141
+ const hash = (0, crypto_1.createHmac)('sha256', this.secret)
142
+ .update(`${ip}:${ua}`)
143
+ .digest('hex')
144
+ .substring(0, 16);
145
+ return hash;
146
+ }
147
+ /**
148
+ * Build cookie string
149
+ */
150
+ buildCookie(token) {
151
+ const opts = this.options.cookieOptions;
152
+ const parts = [`${this.options.cookieName}=${token}`];
153
+ if (opts.path)
154
+ parts.push(`Path=${opts.path}`);
155
+ if (opts.domain)
156
+ parts.push(`Domain=${opts.domain}`);
157
+ if (opts.maxAge)
158
+ parts.push(`Max-Age=${opts.maxAge}`);
159
+ if (opts.httpOnly)
160
+ parts.push('HttpOnly');
161
+ if (opts.secure)
162
+ parts.push('Secure');
163
+ if (opts.sameSite)
164
+ parts.push(`SameSite=${opts.sameSite}`);
165
+ return parts.join('; ');
166
+ }
167
+ /**
168
+ * Cleanup expired tokens
169
+ */
170
+ cleanupTokens() {
171
+ // Simple cleanup - remove old entries
172
+ // In production, use a proper session store with TTL
173
+ if (this.tokenStore.size > 10000) {
174
+ const entries = Array.from(this.tokenStore.entries());
175
+ this.tokenStore.clear();
176
+ // Keep last 5000 entries
177
+ entries.slice(-5000).forEach(([key, value]) => {
178
+ this.tokenStore.set(key, value);
179
+ });
180
+ }
181
+ }
182
+ }
183
+ exports.CsrfMiddleware = CsrfMiddleware;
@@ -0,0 +1,111 @@
1
+ import { Request, Response } from '../types';
2
+ /**
3
+ * Next function type for middleware chain
4
+ */
5
+ export type NextFunction = () => Promise<void> | void;
6
+ /**
7
+ * Middleware function type
8
+ */
9
+ export type MiddlewareFunction = (req: Request, res: Response, next: NextFunction) => Promise<void> | void;
10
+ /**
11
+ * Middleware interface
12
+ */
13
+ export interface MiddlewareConsumer {
14
+ apply(...middleware: Array<MiddlewareFunction | MiddlewareClass>): MiddlewareConfigProxy;
15
+ }
16
+ /**
17
+ * Middleware class interface
18
+ */
19
+ export interface MiddlewareClass {
20
+ use(req: Request, res: Response, next: NextFunction): Promise<void> | void;
21
+ }
22
+ /**
23
+ * Middleware configuration proxy
24
+ */
25
+ export interface MiddlewareConfigProxy {
26
+ forRoutes(...routes: Array<string | RouteInfo>): MiddlewareConfigProxy;
27
+ exclude(...routes: Array<string | RouteInfo>): MiddlewareConfigProxy;
28
+ }
29
+ /**
30
+ * Route information
31
+ */
32
+ export interface RouteInfo {
33
+ path: string;
34
+ method?: string;
35
+ }
36
+ /**
37
+ * Global middleware manager
38
+ */
39
+ export declare class GlobalMiddlewareManager {
40
+ private middleware;
41
+ /**
42
+ * Add global middleware
43
+ */
44
+ use(middleware: MiddlewareFunction | MiddlewareClass): void;
45
+ /**
46
+ * Add middleware for specific routes
47
+ */
48
+ useFor(middleware: MiddlewareFunction | MiddlewareClass, routes: Array<string | RouteInfo>): void;
49
+ /**
50
+ * Add middleware with exclusions
51
+ */
52
+ useExcept(middleware: MiddlewareFunction | MiddlewareClass, excludedRoutes: Array<string | RouteInfo>): void;
53
+ /**
54
+ * Execute middleware chain for a request
55
+ */
56
+ execute(req: Request, res: Response): Promise<void>;
57
+ /**
58
+ * Get middleware applicable to a request
59
+ */
60
+ private getApplicableMiddleware;
61
+ /**
62
+ * Check if request matches any route
63
+ */
64
+ private matchesAnyRoute;
65
+ /**
66
+ * Match path with pattern
67
+ */
68
+ private matchPath;
69
+ /**
70
+ * Normalize routes to RouteInfo array
71
+ */
72
+ private normalizeRoutes;
73
+ /**
74
+ * Clear all middleware
75
+ */
76
+ clear(): void;
77
+ /**
78
+ * Get all registered middleware
79
+ */
80
+ getMiddleware(): MiddlewareEntry[];
81
+ }
82
+ /**
83
+ * Middleware entry
84
+ */
85
+ interface MiddlewareEntry {
86
+ handler: MiddlewareFunction | MiddlewareClass;
87
+ routes: RouteInfo[];
88
+ excludedRoutes: RouteInfo[];
89
+ }
90
+ /**
91
+ * Built-in CORS middleware
92
+ */
93
+ export declare class CorsMiddleware implements MiddlewareClass {
94
+ private options;
95
+ constructor(options?: CorsOptions);
96
+ use(req: Request, res: Response, next: NextFunction): void;
97
+ }
98
+ export interface CorsOptions {
99
+ origin?: string;
100
+ methods?: string[];
101
+ allowedHeaders?: string[];
102
+ credentials?: boolean;
103
+ }
104
+ /**
105
+ * Built-in logging middleware
106
+ */
107
+ export declare class LoggerMiddleware implements MiddlewareClass {
108
+ use(req: Request, res: Response, next: NextFunction): Promise<void>;
109
+ }
110
+ export {};
111
+ //# sourceMappingURL=global-middleware.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"global-middleware.d.ts","sourceRoot":"","sources":["../../src/middleware/global-middleware.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,179 @@
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.LoggerMiddleware = exports.CorsMiddleware = exports.GlobalMiddlewareManager = void 0;
7
+ const logger_1 = __importDefault(require("../logger"));
8
+ /**
9
+ * Global middleware manager
10
+ */
11
+ class GlobalMiddlewareManager {
12
+ constructor() {
13
+ this.middleware = [];
14
+ }
15
+ /**
16
+ * Add global middleware
17
+ */
18
+ use(middleware) {
19
+ logger_1.default.info('Registering global middleware');
20
+ this.middleware.push({
21
+ handler: middleware,
22
+ routes: [],
23
+ excludedRoutes: [],
24
+ });
25
+ }
26
+ /**
27
+ * Add middleware for specific routes
28
+ */
29
+ useFor(middleware, routes) {
30
+ logger_1.default.info('Registering route-specific middleware');
31
+ this.middleware.push({
32
+ handler: middleware,
33
+ routes: this.normalizeRoutes(routes),
34
+ excludedRoutes: [],
35
+ });
36
+ }
37
+ /**
38
+ * Add middleware with exclusions
39
+ */
40
+ useExcept(middleware, excludedRoutes) {
41
+ logger_1.default.info('Registering middleware with exclusions');
42
+ this.middleware.push({
43
+ handler: middleware,
44
+ routes: [],
45
+ excludedRoutes: this.normalizeRoutes(excludedRoutes),
46
+ });
47
+ }
48
+ /**
49
+ * Execute middleware chain for a request
50
+ */
51
+ async execute(req, res) {
52
+ const applicableMiddleware = this.getApplicableMiddleware(req);
53
+ let index = 0;
54
+ const next = async () => {
55
+ if (index >= applicableMiddleware.length) {
56
+ return;
57
+ }
58
+ const entry = applicableMiddleware[index++];
59
+ const handler = entry.handler;
60
+ try {
61
+ if (typeof handler === 'function') {
62
+ await handler(req, res, next);
63
+ }
64
+ else {
65
+ await handler.use(req, res, next);
66
+ }
67
+ }
68
+ catch (error) {
69
+ logger_1.default.error('Middleware error:', error);
70
+ throw error;
71
+ }
72
+ };
73
+ await next();
74
+ }
75
+ /**
76
+ * Get middleware applicable to a request
77
+ */
78
+ getApplicableMiddleware(req) {
79
+ return this.middleware.filter((entry) => {
80
+ // Check if route is excluded
81
+ if (this.matchesAnyRoute(req, entry.excludedRoutes)) {
82
+ return false;
83
+ }
84
+ // If no specific routes, apply to all
85
+ if (entry.routes.length === 0) {
86
+ return true;
87
+ }
88
+ // Check if route matches
89
+ return this.matchesAnyRoute(req, entry.routes);
90
+ });
91
+ }
92
+ /**
93
+ * Check if request matches any route
94
+ */
95
+ matchesAnyRoute(req, routes) {
96
+ const requestPath = req.url?.split('?')[0] || '/';
97
+ const requestMethod = req.method || 'GET';
98
+ return routes.some((route) => {
99
+ const methodMatches = !route.method || route.method === requestMethod;
100
+ const pathMatches = this.matchPath(requestPath, route.path);
101
+ return methodMatches && pathMatches;
102
+ });
103
+ }
104
+ /**
105
+ * Match path with pattern
106
+ */
107
+ matchPath(path, pattern) {
108
+ // Simple wildcard matching
109
+ if (pattern === '*')
110
+ return true;
111
+ if (pattern.endsWith('*')) {
112
+ const prefix = pattern.slice(0, -1);
113
+ return path.startsWith(prefix);
114
+ }
115
+ return path === pattern;
116
+ }
117
+ /**
118
+ * Normalize routes to RouteInfo array
119
+ */
120
+ normalizeRoutes(routes) {
121
+ return routes.map((route) => {
122
+ if (typeof route === 'string') {
123
+ return { path: route };
124
+ }
125
+ return route;
126
+ });
127
+ }
128
+ /**
129
+ * Clear all middleware
130
+ */
131
+ clear() {
132
+ this.middleware = [];
133
+ }
134
+ /**
135
+ * Get all registered middleware
136
+ */
137
+ getMiddleware() {
138
+ return [...this.middleware];
139
+ }
140
+ }
141
+ exports.GlobalMiddlewareManager = GlobalMiddlewareManager;
142
+ /**
143
+ * Built-in CORS middleware
144
+ */
145
+ class CorsMiddleware {
146
+ constructor(options = {}) {
147
+ this.options = options;
148
+ }
149
+ use(req, res, next) {
150
+ const origin = this.options.origin || '*';
151
+ const methods = this.options.methods || ['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'OPTIONS'];
152
+ const headers = this.options.allowedHeaders || ['Content-Type', 'Authorization'];
153
+ res.setHeader('Access-Control-Allow-Origin', origin);
154
+ res.setHeader('Access-Control-Allow-Methods', methods.join(', '));
155
+ res.setHeader('Access-Control-Allow-Headers', headers.join(', '));
156
+ if (this.options.credentials) {
157
+ res.setHeader('Access-Control-Allow-Credentials', 'true');
158
+ }
159
+ if (req.method === 'OPTIONS') {
160
+ res.status(204).end();
161
+ return;
162
+ }
163
+ next();
164
+ }
165
+ }
166
+ exports.CorsMiddleware = CorsMiddleware;
167
+ /**
168
+ * Built-in logging middleware
169
+ */
170
+ class LoggerMiddleware {
171
+ async use(req, res, next) {
172
+ const start = Date.now();
173
+ logger_1.default.info(`→ ${req.method} ${req.url}`);
174
+ await next();
175
+ const duration = Date.now() - start;
176
+ logger_1.default.info(`← ${req.method} ${req.url} ${duration}ms`);
177
+ }
178
+ }
179
+ exports.LoggerMiddleware = LoggerMiddleware;