@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.
Files changed (194) hide show
  1. package/README.md +522 -0
  2. package/dist/__tests__/container.test.d.ts +2 -0
  3. package/dist/__tests__/container.test.d.ts.map +1 -0
  4. package/dist/__tests__/container.test.js +454 -0
  5. package/dist/__tests__/decorators.test.d.ts +2 -0
  6. package/dist/__tests__/decorators.test.d.ts.map +1 -0
  7. package/dist/__tests__/decorators.test.js +693 -0
  8. package/dist/__tests__/errors/http.error.test.d.ts +2 -0
  9. package/dist/__tests__/errors/http.error.test.d.ts.map +1 -0
  10. package/dist/__tests__/errors/http.error.test.js +117 -0
  11. package/dist/__tests__/filters/exception-filter.test.d.ts +2 -0
  12. package/dist/__tests__/filters/exception-filter.test.d.ts.map +1 -0
  13. package/dist/__tests__/filters/exception-filter.test.js +135 -0
  14. package/dist/__tests__/filters/http-exception.filter.test.d.ts +2 -0
  15. package/dist/__tests__/filters/http-exception.filter.test.d.ts.map +1 -0
  16. package/dist/__tests__/filters/http-exception.filter.test.js +119 -0
  17. package/dist/__tests__/hazel-app.test.d.ts +2 -0
  18. package/dist/__tests__/hazel-app.test.d.ts.map +1 -0
  19. package/dist/__tests__/hazel-app.test.js +682 -0
  20. package/dist/__tests__/hazel-module.test.d.ts +2 -0
  21. package/dist/__tests__/hazel-module.test.d.ts.map +1 -0
  22. package/dist/__tests__/hazel-module.test.js +408 -0
  23. package/dist/__tests__/hazel-response.test.d.ts +2 -0
  24. package/dist/__tests__/hazel-response.test.d.ts.map +1 -0
  25. package/dist/__tests__/hazel-response.test.js +138 -0
  26. package/dist/__tests__/health.test.d.ts +2 -0
  27. package/dist/__tests__/health.test.d.ts.map +1 -0
  28. package/dist/__tests__/health.test.js +147 -0
  29. package/dist/__tests__/index.test.d.ts +2 -0
  30. package/dist/__tests__/index.test.d.ts.map +1 -0
  31. package/dist/__tests__/index.test.js +239 -0
  32. package/dist/__tests__/interceptors/interceptor.test.d.ts +2 -0
  33. package/dist/__tests__/interceptors/interceptor.test.d.ts.map +1 -0
  34. package/dist/__tests__/interceptors/interceptor.test.js +166 -0
  35. package/dist/__tests__/logger.test.d.ts +2 -0
  36. package/dist/__tests__/logger.test.d.ts.map +1 -0
  37. package/dist/__tests__/logger.test.js +141 -0
  38. package/dist/__tests__/middleware/cors.test.d.ts +2 -0
  39. package/dist/__tests__/middleware/cors.test.d.ts.map +1 -0
  40. package/dist/__tests__/middleware/cors.test.js +129 -0
  41. package/dist/__tests__/middleware/csrf.test.d.ts +2 -0
  42. package/dist/__tests__/middleware/csrf.test.d.ts.map +1 -0
  43. package/dist/__tests__/middleware/csrf.test.js +247 -0
  44. package/dist/__tests__/middleware/global-middleware.test.d.ts +2 -0
  45. package/dist/__tests__/middleware/global-middleware.test.d.ts.map +1 -0
  46. package/dist/__tests__/middleware/global-middleware.test.js +259 -0
  47. package/dist/__tests__/middleware/rate-limit.test.d.ts +2 -0
  48. package/dist/__tests__/middleware/rate-limit.test.d.ts.map +1 -0
  49. package/dist/__tests__/middleware/rate-limit.test.js +264 -0
  50. package/dist/__tests__/middleware/security-headers.test.d.ts +2 -0
  51. package/dist/__tests__/middleware/security-headers.test.d.ts.map +1 -0
  52. package/dist/__tests__/middleware/security-headers.test.js +229 -0
  53. package/dist/__tests__/middleware/timeout.test.d.ts +2 -0
  54. package/dist/__tests__/middleware/timeout.test.d.ts.map +1 -0
  55. package/dist/__tests__/middleware/timeout.test.js +132 -0
  56. package/dist/__tests__/middleware.test.d.ts +2 -0
  57. package/dist/__tests__/middleware.test.d.ts.map +1 -0
  58. package/dist/__tests__/middleware.test.js +180 -0
  59. package/dist/__tests__/pipes/pipe.test.d.ts +2 -0
  60. package/dist/__tests__/pipes/pipe.test.d.ts.map +1 -0
  61. package/dist/__tests__/pipes/pipe.test.js +245 -0
  62. package/dist/__tests__/pipes/validation.pipe.test.d.ts +2 -0
  63. package/dist/__tests__/pipes/validation.pipe.test.d.ts.map +1 -0
  64. package/dist/__tests__/pipes/validation.pipe.test.js +297 -0
  65. package/dist/__tests__/request-parser.test.d.ts +2 -0
  66. package/dist/__tests__/request-parser.test.d.ts.map +1 -0
  67. package/dist/__tests__/request-parser.test.js +182 -0
  68. package/dist/__tests__/router.test.d.ts +2 -0
  69. package/dist/__tests__/router.test.d.ts.map +1 -0
  70. package/dist/__tests__/router.test.js +680 -0
  71. package/dist/__tests__/routing/route-matcher.test.d.ts +2 -0
  72. package/dist/__tests__/routing/route-matcher.test.d.ts.map +1 -0
  73. package/dist/__tests__/routing/route-matcher.test.js +219 -0
  74. package/dist/__tests__/routing/version.decorator.test.d.ts +2 -0
  75. package/dist/__tests__/routing/version.decorator.test.d.ts.map +1 -0
  76. package/dist/__tests__/routing/version.decorator.test.js +298 -0
  77. package/dist/__tests__/service.test.d.ts +2 -0
  78. package/dist/__tests__/service.test.d.ts.map +1 -0
  79. package/dist/__tests__/service.test.js +121 -0
  80. package/dist/__tests__/shutdown.test.d.ts +2 -0
  81. package/dist/__tests__/shutdown.test.d.ts.map +1 -0
  82. package/dist/__tests__/shutdown.test.js +250 -0
  83. package/dist/__tests__/testing/testing.module.test.d.ts +2 -0
  84. package/dist/__tests__/testing/testing.module.test.d.ts.map +1 -0
  85. package/dist/__tests__/testing/testing.module.test.js +370 -0
  86. package/dist/__tests__/upload/file-upload.test.d.ts +2 -0
  87. package/dist/__tests__/upload/file-upload.test.d.ts.map +1 -0
  88. package/dist/__tests__/upload/file-upload.test.js +498 -0
  89. package/dist/__tests__/utils/sanitize.test.d.ts +2 -0
  90. package/dist/__tests__/utils/sanitize.test.d.ts.map +1 -0
  91. package/dist/__tests__/utils/sanitize.test.js +291 -0
  92. package/dist/__tests__/validator.test.d.ts +2 -0
  93. package/dist/__tests__/validator.test.d.ts.map +1 -0
  94. package/dist/__tests__/validator.test.js +300 -0
  95. package/dist/container.d.ts +80 -0
  96. package/dist/container.d.ts.map +1 -0
  97. package/dist/container.js +271 -0
  98. package/dist/decorators.d.ts +92 -0
  99. package/dist/decorators.d.ts.map +1 -0
  100. package/dist/decorators.js +343 -0
  101. package/dist/errors/http.error.d.ts +31 -0
  102. package/dist/errors/http.error.d.ts.map +1 -0
  103. package/dist/errors/http.error.js +62 -0
  104. package/dist/filters/exception-filter.d.ts +39 -0
  105. package/dist/filters/exception-filter.d.ts.map +1 -0
  106. package/dist/filters/exception-filter.js +38 -0
  107. package/dist/filters/http-exception.filter.d.ts +9 -0
  108. package/dist/filters/http-exception.filter.d.ts.map +1 -0
  109. package/dist/filters/http-exception.filter.js +42 -0
  110. package/dist/hazel-app.d.ts +78 -0
  111. package/dist/hazel-app.d.ts.map +1 -0
  112. package/dist/hazel-app.js +453 -0
  113. package/dist/hazel-module.d.ts +20 -0
  114. package/dist/hazel-module.d.ts.map +1 -0
  115. package/dist/hazel-module.js +109 -0
  116. package/dist/hazel-response.d.ts +20 -0
  117. package/dist/hazel-response.d.ts.map +1 -0
  118. package/dist/hazel-response.js +68 -0
  119. package/dist/health.d.ts +73 -0
  120. package/dist/health.d.ts.map +1 -0
  121. package/dist/health.js +174 -0
  122. package/dist/index.d.ts +41 -0
  123. package/dist/index.d.ts.map +1 -0
  124. package/dist/index.js +140 -0
  125. package/dist/interceptors/interceptor.d.ts +22 -0
  126. package/dist/interceptors/interceptor.d.ts.map +1 -0
  127. package/dist/interceptors/interceptor.js +46 -0
  128. package/dist/logger.d.ts +8 -0
  129. package/dist/logger.d.ts.map +1 -0
  130. package/dist/logger.js +238 -0
  131. package/dist/middleware/cors.middleware.d.ts +44 -0
  132. package/dist/middleware/cors.middleware.d.ts.map +1 -0
  133. package/dist/middleware/cors.middleware.js +118 -0
  134. package/dist/middleware/csrf.middleware.d.ts +82 -0
  135. package/dist/middleware/csrf.middleware.d.ts.map +1 -0
  136. package/dist/middleware/csrf.middleware.js +183 -0
  137. package/dist/middleware/global-middleware.d.ts +111 -0
  138. package/dist/middleware/global-middleware.d.ts.map +1 -0
  139. package/dist/middleware/global-middleware.js +179 -0
  140. package/dist/middleware/rate-limit.middleware.d.ts +73 -0
  141. package/dist/middleware/rate-limit.middleware.d.ts.map +1 -0
  142. package/dist/middleware/rate-limit.middleware.js +124 -0
  143. package/dist/middleware/security-headers.middleware.d.ts +76 -0
  144. package/dist/middleware/security-headers.middleware.d.ts.map +1 -0
  145. package/dist/middleware/security-headers.middleware.js +123 -0
  146. package/dist/middleware/timeout.middleware.d.ts +25 -0
  147. package/dist/middleware/timeout.middleware.d.ts.map +1 -0
  148. package/dist/middleware/timeout.middleware.js +74 -0
  149. package/dist/middleware.d.ts +13 -0
  150. package/dist/middleware.d.ts.map +1 -0
  151. package/dist/middleware.js +47 -0
  152. package/dist/pipes/pipe.d.ts +50 -0
  153. package/dist/pipes/pipe.d.ts.map +1 -0
  154. package/dist/pipes/pipe.js +96 -0
  155. package/dist/pipes/validation.pipe.d.ts +6 -0
  156. package/dist/pipes/validation.pipe.d.ts.map +1 -0
  157. package/dist/pipes/validation.pipe.js +61 -0
  158. package/dist/request-context.d.ts +17 -0
  159. package/dist/request-context.d.ts.map +1 -0
  160. package/dist/request-context.js +2 -0
  161. package/dist/request-parser.d.ts +7 -0
  162. package/dist/request-parser.d.ts.map +1 -0
  163. package/dist/request-parser.js +60 -0
  164. package/dist/router.d.ts +33 -0
  165. package/dist/router.d.ts.map +1 -0
  166. package/dist/router.js +426 -0
  167. package/dist/routing/route-matcher.d.ts +39 -0
  168. package/dist/routing/route-matcher.d.ts.map +1 -0
  169. package/dist/routing/route-matcher.js +93 -0
  170. package/dist/routing/version.decorator.d.ts +36 -0
  171. package/dist/routing/version.decorator.d.ts.map +1 -0
  172. package/dist/routing/version.decorator.js +89 -0
  173. package/dist/service.d.ts +9 -0
  174. package/dist/service.d.ts.map +1 -0
  175. package/dist/service.js +39 -0
  176. package/dist/shutdown.d.ts +32 -0
  177. package/dist/shutdown.d.ts.map +1 -0
  178. package/dist/shutdown.js +109 -0
  179. package/dist/testing/testing.module.d.ts +83 -0
  180. package/dist/testing/testing.module.d.ts.map +1 -0
  181. package/dist/testing/testing.module.js +164 -0
  182. package/dist/types.d.ts +76 -0
  183. package/dist/types.d.ts.map +1 -0
  184. package/dist/types.js +2 -0
  185. package/dist/upload/file-upload.d.ts +75 -0
  186. package/dist/upload/file-upload.d.ts.map +1 -0
  187. package/dist/upload/file-upload.js +261 -0
  188. package/dist/utils/sanitize.d.ts +45 -0
  189. package/dist/utils/sanitize.d.ts.map +1 -0
  190. package/dist/utils/sanitize.js +165 -0
  191. package/dist/validator.d.ts +7 -0
  192. package/dist/validator.d.ts.map +1 -0
  193. package/dist/validator.js +119 -0
  194. package/package.json +65 -0
package/dist/logger.js ADDED
@@ -0,0 +1,238 @@
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.requestLogger = void 0;
7
+ const winston_1 = __importDefault(require("winston"));
8
+ const dotenv_1 = __importDefault(require("dotenv"));
9
+ const path_1 = __importDefault(require("path"));
10
+ const fs_1 = __importDefault(require("fs"));
11
+ const chalk_1 = __importDefault(require("chalk"));
12
+ // Load environment variables
13
+ dotenv_1.default.config();
14
+ const logLevel = process.env.LOG_LEVEL || 'info';
15
+ const logDir = process.env.LOG_DIR || 'logs';
16
+ // Ensure log directory exists
17
+ if (!fs_1.default.existsSync(logDir)) {
18
+ fs_1.default.mkdirSync(logDir, { recursive: true });
19
+ }
20
+ // Professional color scheme for different log levels
21
+ const colors = {
22
+ error: chalk_1.default.red,
23
+ warn: chalk_1.default.yellow,
24
+ info: chalk_1.default.blue,
25
+ http: chalk_1.default.magenta,
26
+ debug: chalk_1.default.gray,
27
+ silly: chalk_1.default.gray,
28
+ };
29
+ // Professional category-based coloring
30
+ const categoryColors = {
31
+ // Module operations
32
+ 'Initializing HazelModule': chalk_1.default.blue.bold,
33
+ 'Initializing module': chalk_1.default.blue.bold,
34
+ 'Initializing imported modules': chalk_1.default.blue,
35
+ 'Initializing HazelApp': chalk_1.default.cyan.bold,
36
+ // Provider operations
37
+ 'Registering providers': chalk_1.default.green.bold,
38
+ 'Registering provider': chalk_1.default.green,
39
+ 'Created instance of': chalk_1.default.green,
40
+ // Controller operations
41
+ 'Registering controllers': chalk_1.default.yellow.bold,
42
+ 'Registering controller': chalk_1.default.yellow,
43
+ // Route operations
44
+ 'Registering route': chalk_1.default.magenta.bold,
45
+ // Server operations
46
+ 'Server listening on': chalk_1.default.green.bold,
47
+ 'Application started': chalk_1.default.cyan.bold,
48
+ 'Connected to database': chalk_1.default.green.bold,
49
+ 'Disconnected from database': chalk_1.default.yellow,
50
+ // Cache operations
51
+ 'Cache service initialized': chalk_1.default.blue,
52
+ 'Cache registered': chalk_1.default.green,
53
+ 'Configuring cache module': chalk_1.default.blue,
54
+ // Configuration
55
+ 'Configuration loaded': chalk_1.default.green,
56
+ Configuring: chalk_1.default.blue,
57
+ // Container
58
+ 'Container initialized': chalk_1.default.cyan.bold,
59
+ };
60
+ // Helper to detect log category and apply appropriate color
61
+ const getCategoryColor = (message) => {
62
+ for (const [category, colorFn] of Object.entries(categoryColors)) {
63
+ if (message.includes(category)) {
64
+ return colorFn;
65
+ }
66
+ }
67
+ return chalk_1.default.white;
68
+ };
69
+ // Custom format for better readability with enhanced colors
70
+ const customFormat = winston_1.default.format.printf(({ level, message, timestamp, ...metadata }) => {
71
+ // Get the appropriate color for the log level
72
+ const levelColor = colors[level] || chalk_1.default.white;
73
+ // Format the timestamp with subtle color
74
+ const time = chalk_1.default.gray.dim(timestamp);
75
+ // Format the level with color and padding (no icons for professional look)
76
+ const levelStr = levelColor.bold(`[${level.toUpperCase()}]`.padEnd(9));
77
+ // Convert message to string
78
+ const messageStr = String(message);
79
+ // Detect category and apply appropriate color to message
80
+ const categoryColor = getCategoryColor(messageStr);
81
+ let msg = categoryColor(messageStr);
82
+ // Special formatting for important messages
83
+ if (messageStr.includes('Server listening on')) {
84
+ msg = chalk_1.default.green.bold(messageStr);
85
+ }
86
+ else if (messageStr.includes('Application started')) {
87
+ msg = chalk_1.default.cyan.bold(messageStr);
88
+ }
89
+ else if (messageStr.includes('→ Local:') || messageStr.includes('→ Network:')) {
90
+ msg = chalk_1.default.green(messageStr);
91
+ }
92
+ // Format metadata if present with enhanced colors
93
+ let metaStr = '';
94
+ if (Object.keys(metadata).length > 0) {
95
+ metaStr = Object.entries(metadata)
96
+ .map(([key, value]) => {
97
+ // Color keys based on their type
98
+ let keyColor = chalk_1.default.cyan;
99
+ if (key.includes('module') || key.includes('Module')) {
100
+ keyColor = chalk_1.default.blue;
101
+ }
102
+ else if (key.includes('provider') ||
103
+ key.includes('Provider') ||
104
+ key.includes('Service')) {
105
+ keyColor = chalk_1.default.green;
106
+ }
107
+ else if (key.includes('controller') || key.includes('Controller')) {
108
+ keyColor = chalk_1.default.yellow;
109
+ }
110
+ else if (key.includes('route') || key.includes('Route')) {
111
+ keyColor = chalk_1.default.magenta;
112
+ }
113
+ const formattedKey = keyColor.bold(key);
114
+ // Use a custom replacer function to handle circular references
115
+ const seen = new WeakSet();
116
+ const safeValue = JSON.stringify(value, (key, val) => {
117
+ // Skip known circular references
118
+ if (key === 'socket' || key === 'parser' || key === 'res' || key === 'req') {
119
+ return '[Circular]';
120
+ }
121
+ // Skip Node.js internal objects
122
+ if (key === '_idlePrev' || key === '_idleNext' || key === 'cleanupInterval') {
123
+ return '[Internal]';
124
+ }
125
+ // Handle circular references
126
+ if (typeof val === 'object' && val !== null) {
127
+ if (seen.has(val)) {
128
+ return '[Circular]';
129
+ }
130
+ seen.add(val);
131
+ }
132
+ return val;
133
+ });
134
+ // Color values based on their content
135
+ let valueColor = chalk_1.default.yellow;
136
+ if (typeof value === 'string') {
137
+ if (value.includes('Module') ||
138
+ value.includes('Service') ||
139
+ value.includes('Controller')) {
140
+ valueColor = chalk_1.default.cyan;
141
+ }
142
+ else if (value.includes('http://') || value.includes('localhost')) {
143
+ valueColor = chalk_1.default.green;
144
+ }
145
+ }
146
+ const formattedValue = valueColor(safeValue);
147
+ return `${formattedKey}=${formattedValue}`;
148
+ })
149
+ .join(' ');
150
+ metaStr = chalk_1.default.gray.dim(' | ') + metaStr;
151
+ }
152
+ return `${time} ${levelStr} ${msg}${metaStr}`;
153
+ });
154
+ // Create logger instance
155
+ const logger = winston_1.default.createLogger({
156
+ level: logLevel,
157
+ format: winston_1.default.format.combine(winston_1.default.format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss' }), customFormat),
158
+ transports: [
159
+ // Console transport with colors
160
+ new winston_1.default.transports.Console({
161
+ format: winston_1.default.format.combine(customFormat),
162
+ }),
163
+ // File transport for all logs (without colors)
164
+ new winston_1.default.transports.File({
165
+ filename: path_1.default.join(logDir, 'combined.log'),
166
+ format: winston_1.default.format.combine(winston_1.default.format.timestamp(), winston_1.default.format.printf(({ level, message, timestamp, ...metadata }) => {
167
+ let msg = `${timestamp} [${level.toUpperCase()}] ${message}`;
168
+ if (Object.keys(metadata).length > 0) {
169
+ msg += ` | ${JSON.stringify(metadata, (key, val) => {
170
+ if (key === 'socket' || key === 'parser' || key === 'res' || key === 'req') {
171
+ return '[Circular]';
172
+ }
173
+ return val;
174
+ })}`;
175
+ }
176
+ return msg;
177
+ })),
178
+ }),
179
+ // File transport for errors only (without colors)
180
+ new winston_1.default.transports.File({
181
+ filename: path_1.default.join(logDir, 'error.log'),
182
+ level: 'error',
183
+ format: winston_1.default.format.combine(winston_1.default.format.timestamp(), winston_1.default.format.printf(({ level, message, timestamp, ...metadata }) => {
184
+ let msg = `${timestamp} [${level.toUpperCase()}] ${message}`;
185
+ if (Object.keys(metadata).length > 0) {
186
+ msg += ` | ${JSON.stringify(metadata, (key, val) => {
187
+ if (key === 'socket' || key === 'parser' || key === 'res' || key === 'req') {
188
+ return '[Circular]';
189
+ }
190
+ return val;
191
+ })}`;
192
+ }
193
+ return msg;
194
+ })),
195
+ }),
196
+ ],
197
+ });
198
+ // Log application info when starting
199
+ const appInfo = {
200
+ service: process.env.APP_NAME || 'hazeljs',
201
+ version: process.env.APP_VERSION || '0.1.0',
202
+ environment: process.env.NODE_ENV || 'development',
203
+ };
204
+ logger.info(chalk_1.default.cyan.bold('Application started'), appInfo);
205
+ // Add request logging middleware with beautiful formatting
206
+ const requestLogger = (req, res, next) => {
207
+ const start = Date.now();
208
+ res.on('finish', () => {
209
+ const duration = Date.now() - start;
210
+ const statusColor = res.statusCode >= 500
211
+ ? chalk_1.default.red
212
+ : res.statusCode >= 400
213
+ ? chalk_1.default.yellow
214
+ : res.statusCode >= 300
215
+ ? chalk_1.default.cyan
216
+ : res.statusCode >= 200
217
+ ? chalk_1.default.green
218
+ : chalk_1.default.white;
219
+ logger.info(`${chalk_1.default.bold(req.method)} ${req.url}`, {
220
+ status: statusColor(res.statusCode),
221
+ duration: chalk_1.default.yellow(`${duration}ms`),
222
+ userAgent: chalk_1.default.gray(req.headers['user-agent']),
223
+ ip: chalk_1.default.gray(req.socket.remoteAddress),
224
+ });
225
+ });
226
+ next();
227
+ };
228
+ exports.requestLogger = requestLogger;
229
+ // Add helper method to check if debug is enabled
230
+ const isDebugEnabled = () => {
231
+ return logger.isLevelEnabled('debug');
232
+ };
233
+ // Extend logger with helper method
234
+ const enhancedLogger = Object.assign(logger, {
235
+ isDebugEnabled,
236
+ });
237
+ // Export logger instance
238
+ exports.default = enhancedLogger;
@@ -0,0 +1,44 @@
1
+ /**
2
+ * CORS Middleware
3
+ * Handles Cross-Origin Resource Sharing
4
+ */
5
+ import { Request, Response } from '../types';
6
+ export interface CorsOptions {
7
+ origin?: string | string[] | ((origin: string) => boolean);
8
+ methods?: string[];
9
+ allowedHeaders?: string[];
10
+ exposedHeaders?: string[];
11
+ credentials?: boolean;
12
+ maxAge?: number;
13
+ preflightContinue?: boolean;
14
+ optionsSuccessStatus?: number;
15
+ }
16
+ export declare class CorsMiddleware {
17
+ private options;
18
+ constructor(options?: CorsOptions);
19
+ /**
20
+ * Check if origin is allowed
21
+ */
22
+ private isOriginAllowed;
23
+ /**
24
+ * Set CORS headers
25
+ */
26
+ private setCorsHeaders;
27
+ /**
28
+ * Handle preflight request
29
+ */
30
+ private handlePreflight;
31
+ /**
32
+ * Handle CORS request
33
+ */
34
+ handle(req: Request, res: Response, next: () => void): void;
35
+ /**
36
+ * Create middleware function
37
+ */
38
+ static create(options?: CorsOptions): (req: Request, res: Response, next: () => void) => void;
39
+ }
40
+ /**
41
+ * Simple CORS helper for common use cases
42
+ */
43
+ export declare function enableCors(options?: CorsOptions): (req: Request, res: Response, next: () => void) => void;
44
+ //# sourceMappingURL=cors.middleware.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cors.middleware.d.ts","sourceRoot":"","sources":["../../src/middleware/cors.middleware.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AAE7C,MAAM,WAAW,WAAW;IAC1B,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,CAAC,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,CAAC;IAC3D,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,oBAAoB,CAAC,EAAE,MAAM,CAAC;CAC/B;AAED,qBAAa,cAAc;IACzB,OAAO,CAAC,OAAO,CAAwB;gBAE3B,OAAO,GAAE,WAAgB;IAarC;;OAEG;IACH,OAAO,CAAC,eAAe;IAsBvB;;OAEG;IACH,OAAO,CAAC,cAAc;IAsBtB;;OAEG;IACH,OAAO,CAAC,eAAe;IA+BvB;;OAEG;IACH,MAAM,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,IAAI,GAAG,IAAI;IAa3D;;OAEG;IACH,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,WAAW,GAAG,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,IAAI,KAAK,IAAI;CAI9F;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,OAAO,CAAC,EAAE,WAAW,GAAG,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,IAAI,KAAK,IAAI,CAEzG"}
@@ -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;