@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
package/README.md ADDED
@@ -0,0 +1,560 @@
1
+ # @hazeljs/core
2
+
3
+ **The foundation of HazelJS — DI, routing, and decorators that feel right.**
4
+
5
+ Stop wiring boilerplate. Build APIs with dependency injection, decorator-based routing, and middleware that just works. TypeScript-first, production-ready, zero Express dependency.
6
+
7
+ [![npm version](https://img.shields.io/npm/v/@hazeljs/core.svg)](https://www.npmjs.com/package/@hazeljs/core)
8
+ [![npm downloads](https://img.shields.io/npm/dm/@hazeljs/core)](https://www.npmjs.com/package/@hazeljs/core)
9
+ [![License: Apache-2.0](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://www.apache.org/licenses/LICENSE-2.0)
10
+
11
+ ## Features
12
+
13
+ - 🎯 **Dependency Injection** - Advanced DI with Singleton, Transient, and Request scopes
14
+ - 🎨 **Decorator-Based API** - Clean, intuitive programming model
15
+ - 🛣️ **Routing** - Express-based routing with parameter extraction
16
+ - 🔌 **Middleware Support** - Global and route-level middleware
17
+ - 🛡️ **Guards & Interceptors** - Request validation and transformation
18
+ - 🔧 **Pipes** - Data transformation and validation
19
+ - 🏥 **Health Checks** - Built-in liveness, readiness, and startup probes
20
+ - 🛑 **Graceful Shutdown** - Proper cleanup and connection draining
21
+ - 📊 **Logging** - Winston-based structured logging
22
+ - ✅ **Validation** - Automatic request validation with class-validator
23
+ - 🧪 **Testing Utilities** - Full testing support with TestingModule
24
+
25
+ ## Installation
26
+
27
+ ```bash
28
+ npm install @hazeljs/core
29
+ ```
30
+
31
+ ## Quick Start
32
+
33
+ ### 1. Create a Controller
34
+
35
+ ```typescript
36
+ import { Controller, Get, Post, Body, Param } from '@hazeljs/core';
37
+
38
+ @Controller('/users')
39
+ export class UserController {
40
+ @Get()
41
+ findAll() {
42
+ return { users: [] };
43
+ }
44
+
45
+ @Get('/:id')
46
+ findOne(@Param('id') id: string) {
47
+ return { id, name: 'John Doe' };
48
+ }
49
+
50
+ @Post()
51
+ create(@Body() createUserDto: CreateUserDto) {
52
+ return { message: 'User created', data: createUserDto };
53
+ }
54
+ }
55
+ ```
56
+
57
+ ### 2. Create a Service
58
+
59
+ ```typescript
60
+ import { Injectable } from '@hazeljs/core';
61
+
62
+ @Injectable()
63
+ export class UserService {
64
+ private users = [];
65
+
66
+ findAll() {
67
+ return this.users;
68
+ }
69
+
70
+ findOne(id: string) {
71
+ return this.users.find(user => user.id === id);
72
+ }
73
+
74
+ create(data: any) {
75
+ const user = { id: Date.now().toString(), ...data };
76
+ this.users.push(user);
77
+ return user;
78
+ }
79
+ }
80
+ ```
81
+
82
+ ### 3. Create a Module
83
+
84
+ ```typescript
85
+ import { HazelModule } from '@hazeljs/core';
86
+ import { UserController } from './user.controller';
87
+ import { UserService } from './user.service';
88
+
89
+ @HazelModule({
90
+ controllers: [UserController],
91
+ providers: [UserService],
92
+ })
93
+ export class AppModule {}
94
+ ```
95
+
96
+ ### 4. Bootstrap the Application
97
+
98
+ ```typescript
99
+ import { HazelApp, BuiltInHealthChecks } from '@hazeljs/core';
100
+ import { AppModule } from './app.module';
101
+
102
+ async function bootstrap() {
103
+ const app = await HazelApp.create(AppModule);
104
+
105
+ // Register health checks
106
+ app.registerHealthCheck(BuiltInHealthChecks.memoryCheck(500));
107
+ app.registerHealthCheck(BuiltInHealthChecks.eventLoopCheck(100));
108
+
109
+ // Register shutdown handlers
110
+ app.registerShutdownHandler({
111
+ name: 'cleanup',
112
+ handler: async () => {
113
+ console.log('Cleaning up resources...');
114
+ },
115
+ timeout: 5000,
116
+ });
117
+
118
+ await app.listen(3000);
119
+ }
120
+
121
+ bootstrap();
122
+ ```
123
+
124
+ ## Dependency Injection
125
+
126
+ ### Scopes
127
+
128
+ ```typescript
129
+ import { Injectable, Scope } from '@hazeljs/core';
130
+
131
+ // Singleton (default) - one instance for entire app
132
+ @Injectable()
133
+ export class SingletonService {}
134
+
135
+ // Transient - new instance every time
136
+ @Injectable({ scope: Scope.TRANSIENT })
137
+ export class TransientService {}
138
+
139
+ // Request - one instance per HTTP request
140
+ @Injectable({ scope: Scope.REQUEST })
141
+ export class RequestContextService {}
142
+ ```
143
+
144
+ ### Constructor Injection
145
+
146
+ ```typescript
147
+ @Injectable()
148
+ export class OrderService {
149
+ constructor(
150
+ private userService: UserService,
151
+ private paymentService: PaymentService
152
+ ) {}
153
+
154
+ async createOrder(userId: string) {
155
+ const user = await this.userService.findOne(userId);
156
+ const payment = await this.paymentService.process(user);
157
+ return { user, payment };
158
+ }
159
+ }
160
+ ```
161
+
162
+ ## Routing & Decorators
163
+
164
+ ### HTTP Methods
165
+
166
+ ```typescript
167
+ import { Controller, Get, Post, Put, Delete, Patch } from '@hazeljs/core';
168
+
169
+ @Controller('/api')
170
+ export class ApiController {
171
+ @Get('/items')
172
+ getItems() {}
173
+
174
+ @Post('/items')
175
+ createItem() {}
176
+
177
+ @Put('/items/:id')
178
+ updateItem() {}
179
+
180
+ @Patch('/items/:id')
181
+ patchItem() {}
182
+
183
+ @Delete('/items/:id')
184
+ deleteItem() {}
185
+ }
186
+ ```
187
+
188
+ ### Parameter Decorators
189
+
190
+ ```typescript
191
+ import { Controller, Get, Post, Param, Query, Body, Headers, Req, Res } from '@hazeljs/core';
192
+
193
+ @Controller('/users')
194
+ export class UserController {
195
+ @Get('/:id')
196
+ findOne(
197
+ @Param('id') id: string,
198
+ @Query('include') include?: string
199
+ ) {
200
+ return { id, include };
201
+ }
202
+
203
+ @Post()
204
+ create(
205
+ @Body() createUserDto: CreateUserDto,
206
+ @Headers('authorization') auth: string
207
+ ) {
208
+ return { data: createUserDto, auth };
209
+ }
210
+
211
+ @Get('/raw')
212
+ rawAccess(@Req() req: Request, @Res() res: Response) {
213
+ res.json({ message: 'Direct access to req/res' });
214
+ }
215
+ }
216
+ ```
217
+
218
+ ### Custom metadata and parameter decorators
219
+
220
+ Attach custom metadata for guards or other layers with **SetMetadata** / **getMetadata**, and build your own parameter decorators with **createParamDecorator**:
221
+
222
+ ```typescript
223
+ import { SetMetadata, getMetadata, createParamDecorator } from '@hazeljs/core';
224
+
225
+ // Custom metadata (e.g. for guards)
226
+ @SetMetadata('roles', ['admin'])
227
+ class AdminController {}
228
+
229
+ // Custom parameter decorator
230
+ const CurrentUser = createParamDecorator((_req, ctx) => ctx.user);
231
+
232
+ @Get('profile')
233
+ getProfile(@CurrentUser user: User) {
234
+ return user;
235
+ }
236
+ ```
237
+
238
+ Use the custom param decorator **without** parentheses: `@CurrentUser`. See the [full API reference](https://hazeljs.com/docs/api-reference) for `ParamDecoratorContext` and `CUSTOM_METADATA_PREFIX`.
239
+
240
+ ## Middleware
241
+
242
+ ### Global Middleware
243
+
244
+ ```typescript
245
+ import { Middleware, MiddlewareContext } from '@hazeljs/core';
246
+
247
+ @Injectable()
248
+ export class LoggerMiddleware implements Middleware {
249
+ async use(context: MiddlewareContext, next: () => Promise<void>) {
250
+ console.log(`${context.request.method} ${context.request.url}`);
251
+ await next();
252
+ }
253
+ }
254
+
255
+ // Register globally
256
+ const app = await HazelApp.create(AppModule);
257
+ app.useGlobalMiddleware(new LoggerMiddleware());
258
+ ```
259
+
260
+ ### Route-Level Middleware
261
+
262
+ ```typescript
263
+ import { Controller, Get, UseMiddleware } from '@hazeljs/core';
264
+
265
+ @Controller('/admin')
266
+ @UseMiddleware(AuthMiddleware)
267
+ export class AdminController {
268
+ @Get('/dashboard')
269
+ @UseMiddleware(RoleCheckMiddleware)
270
+ getDashboard() {
271
+ return { data: 'admin dashboard' };
272
+ }
273
+ }
274
+ ```
275
+
276
+ ## Guards
277
+
278
+ ```typescript
279
+ import { Guard, GuardContext } from '@hazeljs/core';
280
+
281
+ @Injectable()
282
+ export class AuthGuard implements Guard {
283
+ canActivate(context: GuardContext): boolean | Promise<boolean> {
284
+ const token = context.request.headers.authorization;
285
+ return this.validateToken(token);
286
+ }
287
+
288
+ private validateToken(token: string): boolean {
289
+ // Validate JWT token
290
+ return !!token;
291
+ }
292
+ }
293
+
294
+ // Use in controller
295
+ @Controller('/protected')
296
+ @UseGuard(AuthGuard)
297
+ export class ProtectedController {
298
+ @Get()
299
+ getData() {
300
+ return { message: 'Protected data' };
301
+ }
302
+ }
303
+ ```
304
+
305
+ ## Interceptors
306
+
307
+ ```typescript
308
+ import { Interceptor, InterceptorContext } from '@hazeljs/core';
309
+
310
+ @Injectable()
311
+ export class TransformInterceptor implements Interceptor {
312
+ async intercept(context: InterceptorContext, next: () => Promise<any>) {
313
+ const result = await next();
314
+
315
+ return {
316
+ data: result,
317
+ timestamp: new Date().toISOString(),
318
+ path: context.request.url,
319
+ };
320
+ }
321
+ }
322
+
323
+ // Use globally or per route
324
+ @Controller('/api')
325
+ @UseInterceptor(TransformInterceptor)
326
+ export class ApiController {}
327
+ ```
328
+
329
+ ## Pipes
330
+
331
+ ```typescript
332
+ import { Pipe, PipeContext } from '@hazeljs/core';
333
+
334
+ @Injectable()
335
+ export class ValidationPipe implements Pipe {
336
+ transform(value: any, context: PipeContext) {
337
+ if (!value) {
338
+ throw new Error('Value is required');
339
+ }
340
+ return value;
341
+ }
342
+ }
343
+
344
+ // Use in route
345
+ @Post()
346
+ create(@Body(ValidationPipe) createDto: CreateDto) {
347
+ return createDto;
348
+ }
349
+ ```
350
+
351
+ ## Validation
352
+
353
+ ```typescript
354
+ import { IsEmail, IsString, MinLength } from 'class-validator';
355
+
356
+ export class CreateUserDto {
357
+ @IsEmail()
358
+ email: string;
359
+
360
+ @IsString()
361
+ @MinLength(8)
362
+ password: string;
363
+
364
+ @IsString()
365
+ name: string;
366
+ }
367
+
368
+ @Controller('/users')
369
+ export class UserController {
370
+ @Post()
371
+ create(@Body() createUserDto: CreateUserDto) {
372
+ // Automatically validated before reaching here
373
+ return createUserDto;
374
+ }
375
+ }
376
+ ```
377
+
378
+ ## Health Checks
379
+
380
+ ```typescript
381
+ import { HazelApp, BuiltInHealthChecks } from '@hazeljs/core';
382
+
383
+ const app = await HazelApp.create(AppModule);
384
+
385
+ // Built-in checks
386
+ app.registerHealthCheck(BuiltInHealthChecks.memoryCheck(500)); // 500MB threshold
387
+ app.registerHealthCheck(BuiltInHealthChecks.eventLoopCheck(100)); // 100ms lag
388
+
389
+ // Custom health check
390
+ app.registerHealthCheck({
391
+ name: 'database',
392
+ check: async () => {
393
+ try {
394
+ await database.ping();
395
+ return { status: 'healthy' };
396
+ } catch (error) {
397
+ return {
398
+ status: 'unhealthy',
399
+ message: error.message
400
+ };
401
+ }
402
+ },
403
+ critical: true,
404
+ timeout: 3000,
405
+ });
406
+
407
+ // Endpoints available:
408
+ // GET /health - Liveness probe
409
+ // GET /ready - Readiness probe
410
+ // GET /startup - Startup probe
411
+ ```
412
+
413
+ ## Graceful Shutdown
414
+
415
+ ```typescript
416
+ const app = await HazelApp.create(AppModule);
417
+
418
+ app.registerShutdownHandler({
419
+ name: 'database',
420
+ handler: async () => {
421
+ await database.disconnect();
422
+ console.log('Database disconnected');
423
+ },
424
+ timeout: 5000,
425
+ });
426
+
427
+ app.registerShutdownHandler({
428
+ name: 'cache',
429
+ handler: async () => {
430
+ await redis.quit();
431
+ console.log('Redis connection closed');
432
+ },
433
+ timeout: 3000,
434
+ });
435
+
436
+ await app.listen(3000);
437
+
438
+ // On SIGTERM/SIGINT:
439
+ // 1. HTTP server stops accepting new connections
440
+ // 2. Existing requests complete (up to 10s)
441
+ // 3. Shutdown handlers execute in order
442
+ // 4. Process exits cleanly
443
+ ```
444
+
445
+ ## Exception Handling
446
+
447
+ ```typescript
448
+ import { ExceptionFilter, ExceptionContext } from '@hazeljs/core';
449
+
450
+ @Injectable()
451
+ export class HttpExceptionFilter implements ExceptionFilter {
452
+ catch(error: Error, context: ExceptionContext) {
453
+ const response = context.response;
454
+
455
+ response.status(500).json({
456
+ statusCode: 500,
457
+ message: error.message,
458
+ timestamp: new Date().toISOString(),
459
+ path: context.request.url,
460
+ });
461
+ }
462
+ }
463
+
464
+ // Use globally
465
+ app.useGlobalExceptionFilter(new HttpExceptionFilter());
466
+ ```
467
+
468
+ ## Testing
469
+
470
+ ```typescript
471
+ import { TestingModule } from '@hazeljs/core';
472
+ import { UserController } from './user.controller';
473
+ import { UserService } from './user.service';
474
+
475
+ describe('UserController', () => {
476
+ let module: TestingModule;
477
+ let controller: UserController;
478
+ let service: UserService;
479
+
480
+ beforeEach(async () => {
481
+ module = await TestingModule.create({
482
+ controllers: [UserController],
483
+ providers: [
484
+ {
485
+ provide: UserService,
486
+ useValue: {
487
+ findAll: jest.fn().mockResolvedValue([]),
488
+ },
489
+ },
490
+ ],
491
+ });
492
+
493
+ controller = module.get(UserController);
494
+ service = module.get(UserService);
495
+ });
496
+
497
+ afterEach(async () => {
498
+ await module.close();
499
+ });
500
+
501
+ it('should return all users', async () => {
502
+ const result = await controller.findAll();
503
+ expect(result).toEqual([]);
504
+ expect(service.findAll).toHaveBeenCalled();
505
+ });
506
+ });
507
+ ```
508
+
509
+ ## Logging
510
+
511
+ HTTP requests are logged in following format: `METHOD path status duration` (e.g. `GET /api/health 200 3ms`).
512
+
513
+ | Env var | Default | Description |
514
+ |---------|---------|-------------|
515
+ | `LOG_LEVEL` | `info` | Log level (error, warn, info, debug) |
516
+ | `LOG_HTTP` | `true` | Set to `false` to disable HTTP request logs |
517
+ | `LOG_ENABLED` | `true` | Set to `false` to disable all logging |
518
+
519
+ Startup and registration logs (controllers, routes, providers) are at `debug` level. Use `LOG_LEVEL=debug` for troubleshooting.
520
+
521
+ ## API Reference
522
+
523
+ ### Decorators
524
+
525
+ - `@HazelModule(options)` - Define a module
526
+ - `@Controller(path)` - Define a controller
527
+ - `@Injectable(options?)` - Mark class as injectable
528
+ - `@Get(path?)`, `@Post(path?)`, `@Put(path?)`, `@Delete(path?)`, `@Patch(path?)` - HTTP methods
529
+ - `@Param(name)`, `@Query(name)`, `@Body()`, `@Headers(name)`, `@Req()`, `@Res()`, `@Ip()`, `@Host()` - Parameter extraction
530
+ - `@UseGuards(...guards)`, `@UseInterceptors(...interceptors)`, `@UsePipes(...pipes)` - Apply guards, interceptors, pipes
531
+ - `@Public()` / `@SkipAuth()` - Mark route as public
532
+ - `@Timeout(ms)`, `@Retry(options)`, `@Optional()`, `@Session()` - Per-route behavior
533
+ - `@ApiTags(...tags)`, `@ApiOperation(options)` - OpenAPI metadata
534
+ - **SetMetadata(key, value)** - Attach custom metadata to a class or method (read with `getMetadata(key, target, propertyKey?)`)
535
+ - **createParamDecorator(resolve)** - Build custom parameter decorators that inject values from `(req, context, container)`
536
+
537
+ ### Classes
538
+
539
+ - `HazelApp` - Main application class
540
+ - `TestingModule` - Testing utilities
541
+ - `Logger` - Logging service
542
+
543
+ ## Examples
544
+
545
+ See the [examples](../../example) directory for complete working examples.
546
+
547
+ ## Contributing
548
+
549
+ Contributions are welcome! Please read our [Contributing Guide](../../CONTRIBUTING.md) for details.
550
+
551
+ ## License
552
+
553
+ Apache 2.0 © [HazelJS](https://hazeljs.com)
554
+
555
+ ## Links
556
+
557
+ - [Documentation](https://hazeljs.com/docs)
558
+ - [GitHub](https://github.com/hazel-js/hazeljs)
559
+ - [Issues](https://github.com/hazel-js/hazeljs/issues)
560
+ - [Discord](https://discord.com/channels/1448263814238965833/1448263814859456575)
@@ -0,0 +1,2 @@
1
+ import 'reflect-metadata';
2
+ //# sourceMappingURL=container.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"container.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/container.test.ts"],"names":[],"mappings":"AACA,OAAO,kBAAkB,CAAC"}