@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,73 @@
1
+ import { Request, Response } from '../types';
2
+ import { MiddlewareClass, NextFunction } from './global-middleware';
3
+ /**
4
+ * Rate limit storage interface
5
+ */
6
+ interface RateLimitStore {
7
+ get(key: string): Promise<number | null>;
8
+ set(key: string, value: number, ttl: number): Promise<void>;
9
+ increment(key: string, ttl: number): Promise<number>;
10
+ reset(key: string): Promise<void>;
11
+ }
12
+ /**
13
+ * Rate limit options
14
+ */
15
+ export interface RateLimitOptions {
16
+ /**
17
+ * Maximum number of requests
18
+ */
19
+ max: number;
20
+ /**
21
+ * Time window in seconds
22
+ */
23
+ windowMs: number;
24
+ /**
25
+ * Custom key generator function
26
+ */
27
+ keyGenerator?: (req: Request) => string;
28
+ /**
29
+ * Custom store (default: in-memory)
30
+ */
31
+ store?: RateLimitStore;
32
+ /**
33
+ * Skip successful requests
34
+ */
35
+ skipSuccessfulRequests?: boolean;
36
+ /**
37
+ * Skip failed requests
38
+ */
39
+ skipFailedRequests?: boolean;
40
+ /**
41
+ * Custom error message
42
+ */
43
+ message?: string;
44
+ /**
45
+ * Custom error status code
46
+ */
47
+ statusCode?: number;
48
+ /**
49
+ * Standard rate limit headers
50
+ */
51
+ standardHeaders?: boolean;
52
+ /**
53
+ * Legacy rate limit headers
54
+ */
55
+ legacyHeaders?: boolean;
56
+ }
57
+ /**
58
+ * Rate Limiting Middleware
59
+ * Prevents abuse by limiting the number of requests from a single IP
60
+ */
61
+ export declare class RateLimitMiddleware implements MiddlewareClass {
62
+ private options;
63
+ private store;
64
+ private cleanupInterval;
65
+ constructor(options: RateLimitOptions);
66
+ use(req: Request, res: Response, next: NextFunction): Promise<void>;
67
+ /**
68
+ * Cleanup resources
69
+ */
70
+ destroy(): void;
71
+ }
72
+ export {};
73
+ //# sourceMappingURL=rate-limit.middleware.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rate-limit.middleware.d.ts","sourceRoot":"","sources":["../../src/middleware/rate-limit.middleware.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AAC7C,OAAO,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAIpE;;GAEG;AACH,UAAU,cAAc;IACtB,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IACzC,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5D,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACrD,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACnC;AA0DD;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B;;OAEG;IACH,GAAG,EAAE,MAAM,CAAC;IAEZ;;OAEG;IACH,QAAQ,EAAE,MAAM,CAAC;IAEjB;;OAEG;IACH,YAAY,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,MAAM,CAAC;IAExC;;OAEG;IACH,KAAK,CAAC,EAAE,cAAc,CAAC;IAEvB;;OAEG;IACH,sBAAsB,CAAC,EAAE,OAAO,CAAC;IAEjC;;OAEG;IACH,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAE7B;;OAEG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB;;OAEG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB;;OAEG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;IAE1B;;OAEG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AAED;;;GAGG;AACH,qBAAa,mBAAoB,YAAW,eAAe;IAI7C,OAAO,CAAC,OAAO;IAH3B,OAAO,CAAC,KAAK,CAAiB;IAC9B,OAAO,CAAC,eAAe,CAA+B;gBAElC,OAAO,EAAE,gBAAgB;IA2BvC,GAAG,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC;IAkCzE;;OAEG;IACH,OAAO,IAAI,IAAI;CAMhB"}
@@ -0,0 +1,124 @@
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.RateLimitMiddleware = void 0;
7
+ const http_error_1 = require("../errors/http.error");
8
+ const logger_1 = __importDefault(require("../logger"));
9
+ /**
10
+ * In-memory rate limit store
11
+ */
12
+ class MemoryStore {
13
+ constructor() {
14
+ this.store = new Map();
15
+ }
16
+ async get(key) {
17
+ const entry = this.store.get(key);
18
+ if (!entry)
19
+ return null;
20
+ if (Date.now() > entry.resetTime) {
21
+ this.store.delete(key);
22
+ return null;
23
+ }
24
+ return entry.count;
25
+ }
26
+ async set(key, value, ttl) {
27
+ this.store.set(key, {
28
+ count: value,
29
+ resetTime: Date.now() + ttl * 1000,
30
+ });
31
+ }
32
+ async increment(key, ttl) {
33
+ const entry = this.store.get(key);
34
+ const now = Date.now();
35
+ if (!entry || now > entry.resetTime) {
36
+ await this.set(key, 1, ttl);
37
+ return 1;
38
+ }
39
+ entry.count++;
40
+ this.store.set(key, entry);
41
+ return entry.count;
42
+ }
43
+ async reset(key) {
44
+ this.store.delete(key);
45
+ }
46
+ /**
47
+ * Clean up expired entries
48
+ */
49
+ cleanup() {
50
+ const now = Date.now();
51
+ for (const [key, entry] of this.store.entries()) {
52
+ if (now > entry.resetTime) {
53
+ this.store.delete(key);
54
+ }
55
+ }
56
+ }
57
+ }
58
+ /**
59
+ * Rate Limiting Middleware
60
+ * Prevents abuse by limiting the number of requests from a single IP
61
+ */
62
+ class RateLimitMiddleware {
63
+ constructor(options) {
64
+ this.options = options;
65
+ this.cleanupInterval = null;
66
+ this.store = options.store || new MemoryStore();
67
+ // Set defaults
68
+ this.options = {
69
+ keyGenerator: (req) => {
70
+ const forwarded = req.headers?.['x-forwarded-for'];
71
+ const ip = forwarded
72
+ ? (Array.isArray(forwarded) ? forwarded[0] : forwarded.split(',')[0].trim())
73
+ : req.socket?.remoteAddress || 'unknown';
74
+ return ip;
75
+ },
76
+ message: 'Too many requests, please try again later.',
77
+ statusCode: 429,
78
+ standardHeaders: true,
79
+ legacyHeaders: true,
80
+ ...options,
81
+ };
82
+ // Cleanup expired entries every minute
83
+ if (this.store instanceof MemoryStore) {
84
+ this.cleanupInterval = setInterval(() => {
85
+ this.store.cleanup();
86
+ }, 60000);
87
+ }
88
+ }
89
+ async use(req, res, next) {
90
+ const key = this.options.keyGenerator(req);
91
+ const count = await this.store.increment(key, this.options.windowMs);
92
+ // Set rate limit headers
93
+ if (this.options.standardHeaders) {
94
+ res.setHeader('RateLimit-Limit', this.options.max.toString());
95
+ res.setHeader('RateLimit-Remaining', Math.max(0, this.options.max - count).toString());
96
+ res.setHeader('RateLimit-Reset', new Date(Date.now() + this.options.windowMs * 1000).toISOString());
97
+ }
98
+ if (this.options.legacyHeaders) {
99
+ res.setHeader('X-RateLimit-Limit', this.options.max.toString());
100
+ res.setHeader('X-RateLimit-Remaining', Math.max(0, this.options.max - count).toString());
101
+ res.setHeader('X-RateLimit-Reset', new Date(Date.now() + this.options.windowMs * 1000).toISOString());
102
+ }
103
+ // Check if limit exceeded
104
+ if (count > this.options.max) {
105
+ logger_1.default.warn(`Rate limit exceeded for ${key}: ${count}/${this.options.max}`);
106
+ throw new http_error_1.HttpError(this.options.statusCode, this.options.message);
107
+ }
108
+ await next();
109
+ // Handle skip options
110
+ // Note: Response statusCode is set via res.status() and checked via res.writeHead
111
+ // For now, we'll skip this check as Response interface doesn't expose statusCode directly
112
+ // In a real implementation, you'd track the status in a response interceptor
113
+ }
114
+ /**
115
+ * Cleanup resources
116
+ */
117
+ destroy() {
118
+ if (this.cleanupInterval) {
119
+ clearInterval(this.cleanupInterval);
120
+ this.cleanupInterval = null;
121
+ }
122
+ }
123
+ }
124
+ exports.RateLimitMiddleware = RateLimitMiddleware;
@@ -0,0 +1,76 @@
1
+ import { Request, Response } from '../types';
2
+ import { MiddlewareClass, NextFunction } from './global-middleware';
3
+ /**
4
+ * Security headers configuration
5
+ */
6
+ export interface SecurityHeadersOptions {
7
+ /**
8
+ * Enable X-Content-Type-Options header
9
+ * Prevents MIME type sniffing
10
+ */
11
+ noSniff?: boolean;
12
+ /**
13
+ * Enable X-Frame-Options header
14
+ * Prevents clickjacking attacks
15
+ */
16
+ frameOptions?: 'DENY' | 'SAMEORIGIN' | string;
17
+ /**
18
+ * Enable X-XSS-Protection header
19
+ * Enables browser XSS filter
20
+ */
21
+ xssProtection?: boolean;
22
+ /**
23
+ * Enable Strict-Transport-Security (HSTS) header
24
+ * Forces HTTPS connections
25
+ */
26
+ hsts?: {
27
+ maxAge?: number;
28
+ includeSubDomains?: boolean;
29
+ preload?: boolean;
30
+ };
31
+ /**
32
+ * Enable Content-Security-Policy header
33
+ * Controls resource loading
34
+ */
35
+ contentSecurityPolicy?: string | {
36
+ defaultSrc?: string[];
37
+ scriptSrc?: string[];
38
+ styleSrc?: string[];
39
+ imgSrc?: string[];
40
+ connectSrc?: string[];
41
+ fontSrc?: string[];
42
+ objectSrc?: string[];
43
+ mediaSrc?: string[];
44
+ frameSrc?: string[];
45
+ baseUri?: string[];
46
+ formAction?: string[];
47
+ frameAncestors?: string[];
48
+ upgradeInsecureRequests?: boolean;
49
+ };
50
+ /**
51
+ * Enable Referrer-Policy header
52
+ * Controls referrer information
53
+ */
54
+ referrerPolicy?: 'no-referrer' | 'no-referrer-when-downgrade' | 'origin' | 'origin-when-cross-origin' | 'same-origin' | 'strict-origin' | 'strict-origin-when-cross-origin' | 'unsafe-url';
55
+ /**
56
+ * Enable Permissions-Policy header
57
+ * Controls browser features
58
+ */
59
+ permissionsPolicy?: Record<string, string[]>;
60
+ /**
61
+ * Enable X-Powered-By removal
62
+ * Hides framework information
63
+ */
64
+ hidePoweredBy?: boolean;
65
+ }
66
+ /**
67
+ * Security Headers Middleware
68
+ * Adds security headers to HTTP responses
69
+ */
70
+ export declare class SecurityHeadersMiddleware implements MiddlewareClass {
71
+ private options;
72
+ constructor(options?: SecurityHeadersOptions);
73
+ use(req: Request, res: Response, next: NextFunction): void;
74
+ private buildCSP;
75
+ }
76
+ //# sourceMappingURL=security-headers.middleware.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"security-headers.middleware.d.ts","sourceRoot":"","sources":["../../src/middleware/security-headers.middleware.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AAC7C,OAAO,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAEpE;;GAEG;AACH,MAAM,WAAW,sBAAsB;IACrC;;;OAGG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAElB;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,GAAG,YAAY,GAAG,MAAM,CAAC;IAE9C;;;OAGG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC;IAExB;;;OAGG;IACH,IAAI,CAAC,EAAE;QACL,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,iBAAiB,CAAC,EAAE,OAAO,CAAC;QAC5B,OAAO,CAAC,EAAE,OAAO,CAAC;KACnB,CAAC;IAEF;;;OAGG;IACH,qBAAqB,CAAC,EAAE,MAAM,GAAG;QAC/B,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;QACtB,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;QACrB,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;QACpB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;QAClB,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;QACtB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;QACnB,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;QACrB,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;QACpB,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;QACpB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;QACnB,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;QACtB,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;QAC1B,uBAAuB,CAAC,EAAE,OAAO,CAAC;KACnC,CAAC;IAEF;;;OAGG;IACH,cAAc,CAAC,EAAE,aAAa,GAAG,4BAA4B,GAAG,QAAQ,GAAG,0BAA0B,GAAG,aAAa,GAAG,eAAe,GAAG,iCAAiC,GAAG,YAAY,CAAC;IAE3L;;;OAGG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;IAE7C;;;OAGG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AAED;;;GAGG;AACH,qBAAa,yBAA0B,YAAW,eAAe;IACnD,OAAO,CAAC,OAAO;gBAAP,OAAO,GAAE,sBAA2B;IAgBxD,GAAG,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,YAAY,GAAG,IAAI;IA+D1D,OAAO,CAAC,QAAQ;CAiDjB"}
@@ -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"}