@hazeljs/core 0.2.0-beta.72 → 0.2.0-beta.74

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -215,6 +215,28 @@ export class UserController {
215
215
  }
216
216
  ```
217
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
+
218
240
  ## Middleware
219
241
 
220
242
  ### Global Middleware
@@ -504,10 +526,13 @@ Startup and registration logs (controllers, routes, providers) are at `debug` le
504
526
  - `@Controller(path)` - Define a controller
505
527
  - `@Injectable(options?)` - Mark class as injectable
506
528
  - `@Get(path?)`, `@Post(path?)`, `@Put(path?)`, `@Delete(path?)`, `@Patch(path?)` - HTTP methods
507
- - `@Param(name)`, `@Query(name)`, `@Body()`, `@Headers(name)` - Parameter extraction
508
- - `@UseMiddleware(middleware)` - Apply middleware
509
- - `@UseGuard(guard)` - Apply guard
510
- - `@UseInterceptor(interceptor)` - Apply interceptor
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)`
511
536
 
512
537
  ### Classes
513
538
 
@@ -1 +1 @@
1
- {"version":3,"file":"decorators.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/decorators.test.ts"],"names":[],"mappings":"AAyBA,OAAO,kBAAkB,CAAC"}
1
+ {"version":3,"file":"decorators.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/decorators.test.ts"],"names":[],"mappings":"AAuCA,OAAO,kBAAkB,CAAC"}
@@ -958,4 +958,280 @@ describe('Decorators', () => {
958
958
  expect(guardMeta).toContain(RoleGuard);
959
959
  });
960
960
  });
961
+ describe('Ip', () => {
962
+ it('should register ip injection type', () => {
963
+ class TestController {
964
+ get(ip) {
965
+ return { ip };
966
+ }
967
+ }
968
+ __decorate([
969
+ (0, decorators_1.Get)('/'),
970
+ __param(0, (0, decorators_1.Ip)()),
971
+ __metadata("design:type", Function),
972
+ __metadata("design:paramtypes", [String]),
973
+ __metadata("design:returntype", void 0)
974
+ ], TestController.prototype, "get", null);
975
+ const injections = Reflect.getMetadata('hazel:inject', TestController, 'get');
976
+ expect(injections[0]).toEqual({ type: 'ip' });
977
+ });
978
+ it('should throw when used outside a method parameter', () => {
979
+ class Ctrl {
980
+ get() { }
981
+ }
982
+ expect(() => (0, decorators_1.Ip)()(Ctrl.prototype, undefined, 0)).toThrow('Ip decorator must be used on a method parameter');
983
+ });
984
+ });
985
+ describe('Host', () => {
986
+ it('should register host injection type', () => {
987
+ class TestController {
988
+ get(host) {
989
+ return { host };
990
+ }
991
+ }
992
+ __decorate([
993
+ (0, decorators_1.Get)('/'),
994
+ __param(0, (0, decorators_1.Host)()),
995
+ __metadata("design:type", Function),
996
+ __metadata("design:paramtypes", [String]),
997
+ __metadata("design:returntype", void 0)
998
+ ], TestController.prototype, "get", null);
999
+ const injections = Reflect.getMetadata('hazel:inject', TestController, 'get');
1000
+ expect(injections[0]).toEqual({ type: 'host' });
1001
+ });
1002
+ it('should throw when used outside a method parameter', () => {
1003
+ class Ctrl {
1004
+ get() { }
1005
+ }
1006
+ expect(() => (0, decorators_1.Host)()(Ctrl.prototype, undefined, 0)).toThrow('Host decorator must be used on a method parameter');
1007
+ });
1008
+ });
1009
+ describe('Public', () => {
1010
+ it('should set public metadata on class', () => {
1011
+ let TestController = class TestController {
1012
+ };
1013
+ TestController = __decorate([
1014
+ (0, decorators_1.Public)()
1015
+ ], TestController);
1016
+ expect(Reflect.getMetadata('hazel:public', TestController)).toBe(true);
1017
+ });
1018
+ it('should set public metadata on method', () => {
1019
+ class TestController {
1020
+ login() {
1021
+ return {};
1022
+ }
1023
+ }
1024
+ __decorate([
1025
+ (0, decorators_1.Public)(),
1026
+ (0, decorators_1.Get)('/login'),
1027
+ __metadata("design:type", Function),
1028
+ __metadata("design:paramtypes", []),
1029
+ __metadata("design:returntype", void 0)
1030
+ ], TestController.prototype, "login", null);
1031
+ expect(Reflect.getMetadata('hazel:public', TestController.prototype, 'login')).toBe(true);
1032
+ });
1033
+ });
1034
+ describe('SkipAuth', () => {
1035
+ it('should be an alias for Public', () => {
1036
+ expect(decorators_1.SkipAuth).toBe(decorators_1.Public);
1037
+ });
1038
+ });
1039
+ describe('Timeout', () => {
1040
+ it('should store timeout in metadata', () => {
1041
+ class TestController {
1042
+ get() {
1043
+ return {};
1044
+ }
1045
+ }
1046
+ __decorate([
1047
+ (0, decorators_1.Timeout)(5000),
1048
+ (0, decorators_1.Get)('/'),
1049
+ __metadata("design:type", Function),
1050
+ __metadata("design:paramtypes", []),
1051
+ __metadata("design:returntype", void 0)
1052
+ ], TestController.prototype, "get", null);
1053
+ expect(Reflect.getMetadata('hazel:timeout', TestController.prototype, 'get')).toBe(5000);
1054
+ });
1055
+ });
1056
+ describe('Optional', () => {
1057
+ it('should add parameter index to optional indices', () => {
1058
+ class TestController {
1059
+ get(q) {
1060
+ return { q };
1061
+ }
1062
+ }
1063
+ __decorate([
1064
+ (0, decorators_1.Get)('/'),
1065
+ __param(0, (0, decorators_1.Optional)()),
1066
+ __param(0, (0, decorators_1.Query)('q')),
1067
+ __metadata("design:type", Function),
1068
+ __metadata("design:paramtypes", [String]),
1069
+ __metadata("design:returntype", void 0)
1070
+ ], TestController.prototype, "get", null);
1071
+ const indices = Reflect.getMetadata('hazel:optional-indices', TestController, 'get');
1072
+ expect(indices).toContain(0);
1073
+ });
1074
+ it('should throw when used outside a method parameter', () => {
1075
+ class Ctrl {
1076
+ get() { }
1077
+ }
1078
+ expect(() => (0, decorators_1.Optional)()(Ctrl.prototype, undefined, 0)).toThrow('Optional decorator must be used on a method parameter');
1079
+ });
1080
+ });
1081
+ describe('Session', () => {
1082
+ it('should register session injection type', () => {
1083
+ class TestController {
1084
+ get(session) {
1085
+ return { session };
1086
+ }
1087
+ }
1088
+ __decorate([
1089
+ (0, decorators_1.Get)('/'),
1090
+ __param(0, (0, decorators_1.Session)()),
1091
+ __metadata("design:type", Function),
1092
+ __metadata("design:paramtypes", [Object]),
1093
+ __metadata("design:returntype", void 0)
1094
+ ], TestController.prototype, "get", null);
1095
+ const injections = Reflect.getMetadata('hazel:inject', TestController, 'get');
1096
+ expect(injections[0]).toEqual({ type: 'session' });
1097
+ });
1098
+ it('should throw when used outside a method parameter', () => {
1099
+ class Ctrl {
1100
+ get() { }
1101
+ }
1102
+ expect(() => (0, decorators_1.Session)()(Ctrl.prototype, undefined, 0)).toThrow('Session decorator must be used on a method parameter');
1103
+ });
1104
+ });
1105
+ describe('Retry', () => {
1106
+ it('should store retry options and add RetryInterceptor to route', () => {
1107
+ class TestController {
1108
+ get() {
1109
+ return {};
1110
+ }
1111
+ }
1112
+ __decorate([
1113
+ (0, decorators_1.Retry)({ count: 3, delay: 100 }),
1114
+ (0, decorators_1.Get)('/'),
1115
+ __metadata("design:type", Function),
1116
+ __metadata("design:paramtypes", []),
1117
+ __metadata("design:returntype", void 0)
1118
+ ], TestController.prototype, "get", null);
1119
+ expect(Reflect.getMetadata('hazel:retry', TestController.prototype, 'get')).toEqual({
1120
+ count: 3,
1121
+ delay: 100,
1122
+ });
1123
+ const routes = Reflect.getMetadata('hazel:routes', TestController);
1124
+ const route = routes.find((r) => r.propertyKey === 'get');
1125
+ expect(route?.interceptors?.[0]?.type?.name).toBe('RetryInterceptor');
1126
+ });
1127
+ });
1128
+ describe('ApiTags', () => {
1129
+ it('should set api tags on class', () => {
1130
+ let TestController = class TestController {
1131
+ };
1132
+ TestController = __decorate([
1133
+ (0, decorators_1.ApiTags)('users', 'admin')
1134
+ ], TestController);
1135
+ expect(Reflect.getMetadata('hazel:api:tags', TestController)).toEqual(['users', 'admin']);
1136
+ });
1137
+ it('should set api tags on method', () => {
1138
+ class TestController {
1139
+ login() {
1140
+ return {};
1141
+ }
1142
+ }
1143
+ __decorate([
1144
+ (0, decorators_1.ApiTags)('auth'),
1145
+ (0, decorators_1.Get)('/login'),
1146
+ __metadata("design:type", Function),
1147
+ __metadata("design:paramtypes", []),
1148
+ __metadata("design:returntype", void 0)
1149
+ ], TestController.prototype, "login", null);
1150
+ expect(Reflect.getMetadata('hazel:api:tags', TestController.prototype, 'login')).toEqual(['auth']);
1151
+ });
1152
+ });
1153
+ describe('ApiOperation', () => {
1154
+ it('should store operation options when given object', () => {
1155
+ class TestController {
1156
+ get() {
1157
+ return {};
1158
+ }
1159
+ }
1160
+ __decorate([
1161
+ (0, decorators_1.ApiOperation)({ summary: 'Get user', description: 'Returns a user', operationId: 'getUser' }),
1162
+ (0, decorators_1.Get)('/'),
1163
+ __metadata("design:type", Function),
1164
+ __metadata("design:paramtypes", []),
1165
+ __metadata("design:returntype", void 0)
1166
+ ], TestController.prototype, "get", null);
1167
+ expect(Reflect.getMetadata('hazel:api:operation', TestController.prototype, 'get')).toEqual({
1168
+ summary: 'Get user',
1169
+ description: 'Returns a user',
1170
+ operationId: 'getUser',
1171
+ });
1172
+ });
1173
+ it('should accept string as summary', () => {
1174
+ class TestController {
1175
+ get() {
1176
+ return {};
1177
+ }
1178
+ }
1179
+ __decorate([
1180
+ (0, decorators_1.ApiOperation)('List users'),
1181
+ (0, decorators_1.Get)('/'),
1182
+ __metadata("design:type", Function),
1183
+ __metadata("design:paramtypes", []),
1184
+ __metadata("design:returntype", void 0)
1185
+ ], TestController.prototype, "get", null);
1186
+ expect(Reflect.getMetadata('hazel:api:operation', TestController.prototype, 'get')).toEqual({
1187
+ summary: 'List users',
1188
+ });
1189
+ });
1190
+ });
1191
+ describe('SetMetadata and getMetadata', () => {
1192
+ it('should set and get class-level metadata', () => {
1193
+ let AdminController = class AdminController {
1194
+ };
1195
+ AdminController = __decorate([
1196
+ (0, decorators_1.SetMetadata)('roles', ['admin'])
1197
+ ], AdminController);
1198
+ expect((0, decorators_1.getMetadata)('roles', AdminController)).toEqual(['admin']);
1199
+ expect(Reflect.getMetadata(`${decorators_1.CUSTOM_METADATA_PREFIX}roles`, AdminController)).toEqual(['admin']);
1200
+ });
1201
+ it('should set and get method-level metadata', () => {
1202
+ class TestController {
1203
+ get() {
1204
+ return {};
1205
+ }
1206
+ }
1207
+ __decorate([
1208
+ (0, decorators_1.SetMetadata)('roles', ['user']),
1209
+ (0, decorators_1.Get)('/'),
1210
+ __metadata("design:type", Function),
1211
+ __metadata("design:paramtypes", []),
1212
+ __metadata("design:returntype", void 0)
1213
+ ], TestController.prototype, "get", null);
1214
+ expect((0, decorators_1.getMetadata)('roles', TestController.prototype, 'get')).toEqual(['user']);
1215
+ });
1216
+ });
1217
+ describe('createParamDecorator', () => {
1218
+ it('should register custom inject metadata', () => {
1219
+ const MyParam = (0, decorators_1.createParamDecorator)((_req, ctx) => ctx.query?.foo);
1220
+ class TestController {
1221
+ get(foo) {
1222
+ return { foo };
1223
+ }
1224
+ }
1225
+ __decorate([
1226
+ (0, decorators_1.Get)('/'),
1227
+ __param(0, MyParam),
1228
+ __metadata("design:type", Function),
1229
+ __metadata("design:paramtypes", [String]),
1230
+ __metadata("design:returntype", void 0)
1231
+ ], TestController.prototype, "get", null);
1232
+ const injections = Reflect.getMetadata('hazel:inject', TestController, 'get');
1233
+ expect(injections).toBeDefined();
1234
+ expect(injections[0]).toEqual({ type: 'custom', resolve: expect.any(Function) });
1235
+ });
1236
+ });
961
1237
  });
@@ -1,8 +1,10 @@
1
1
  import 'reflect-metadata';
2
- import { Type, RequestContext } from './types';
2
+ import { Type, RequestContext, Request } from './types';
3
3
  import { PipeTransform, PipeMetadata } from './pipes/pipe';
4
4
  import { Interceptor, InterceptorMetadata } from './interceptors/interceptor';
5
5
  import { HazelApp } from './hazel-app';
6
+ import type { Container } from './container';
7
+ export declare const CUSTOM_METADATA_PREFIX = "hazel:meta:";
6
8
  export interface ControllerMetadata {
7
9
  path: string;
8
10
  interceptors?: InterceptorMetadata[];
@@ -95,5 +97,70 @@ export declare function HttpCode(statusCode: number): MethodDecorator;
95
97
  export declare function Header(name: string, value: string): MethodDecorator;
96
98
  export declare function Redirect(url: string, statusCode?: number): MethodDecorator;
97
99
  export declare function Res(): ParameterDecorator;
100
+ export declare function Ip(): ParameterDecorator;
101
+ export declare function Host(): ParameterDecorator;
102
+ /**
103
+ * Marks a controller or route as public (no auth required).
104
+ * Guards should check Reflect.getMetadata(PUBLIC_METADATA_KEY, target, propertyKey)
105
+ * or Reflect.getMetadata(PUBLIC_METADATA_KEY, target) and allow the request when true.
106
+ */
107
+ export declare function Public(): ClassDecorator & MethodDecorator;
108
+ /** Alias for @Public(). Use when you want to skip auth for specific routes. */
109
+ export declare const SkipAuth: typeof Public;
110
+ export declare function Timeout(ms: number): MethodDecorator;
111
+ export declare function Optional(): ParameterDecorator;
112
+ export declare function Session(): ParameterDecorator;
113
+ export interface RetryDecoratorOptions {
114
+ count: number;
115
+ delay?: number;
116
+ retryIf?: (err: Error) => boolean;
117
+ }
118
+ export declare function Retry(options: RetryDecoratorOptions): MethodDecorator;
119
+ export declare function ApiTags(...tags: string[]): ClassDecorator & MethodDecorator;
120
+ export interface ApiOperationOptions {
121
+ summary?: string;
122
+ description?: string;
123
+ operationId?: string;
124
+ }
125
+ export declare function ApiOperation(options: ApiOperationOptions | string): MethodDecorator;
126
+ /**
127
+ * Sets arbitrary metadata on a class or method.
128
+ * Guards, interceptors, and other components can read it via getMetadata(key, target, propertyKey?).
129
+ *
130
+ * @param key - Metadata key (stored under hazel:meta:<key> to avoid collisions)
131
+ * @param value - Value to store (any serializable or object)
132
+ * @example
133
+ * SetMetadata('roles', ['admin'])(MyController)
134
+ * SetMetadata('roles', ['user'])(MyController.prototype, 'getProfile')
135
+ */
136
+ export declare function SetMetadata(key: string, value: unknown): ClassDecorator & MethodDecorator;
137
+ /**
138
+ * Reads custom metadata set with SetMetadata.
139
+ *
140
+ * @param key - Key passed to SetMetadata(key, value)
141
+ * @param target - Class or prototype
142
+ * @param propertyKey - Optional method name (for method-level metadata)
143
+ */
144
+ export declare function getMetadata<T = unknown>(key: string, target: object, propertyKey?: string | symbol): T | undefined;
145
+ /**
146
+ * Context passed to custom parameter decorator resolvers.
147
+ * The router calls the resolver with (req, context, container) when invoking the handler.
148
+ */
149
+ export interface ParamDecoratorContext {
150
+ req: Request;
151
+ context: RequestContext;
152
+ container: Container;
153
+ }
154
+ /**
155
+ * Creates a custom parameter decorator that injects a value computed from the request.
156
+ * The resolver receives the raw request, parsed request context, and the DI container.
157
+ * Return value can be a Promise for async resolution (e.g. loading the current user from DB).
158
+ *
159
+ * @param resolve - Function (req, context, container) => value | Promise<value>
160
+ * @example
161
+ * const CurrentUser = createParamDecorator(async (req, ctx, container) => ctx.user ?? req.user);
162
+ * // In controller: getProfile(@CurrentUser() user: User) { ... }
163
+ */
164
+ export declare function createParamDecorator<T = unknown>(resolve: (req: Request, context: RequestContext, container: Container) => T | Promise<T>): ParameterDecorator;
98
165
  export { HazelApp };
99
166
  //# sourceMappingURL=decorators.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"decorators.d.ts","sourceRoot":"","sources":["../src/decorators.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"decorators.d.ts","sourceRoot":"","sources":["../src/decorators.ts"],"names":[],"mappings":"AAAA,OAAO,kBAAkB,CAAC;AAE1B,OAAO,EAAE,IAAI,EAAE,cAAc,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AACxD,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC3D,OAAO,EAAE,WAAW,EAAE,mBAAmB,EAAoB,MAAM,4BAA4B,CAAC;AAChG,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACvC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAmB7C,eAAO,MAAM,sBAAsB,gBAAgB,CAAC;AAEpD,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,CAAC,EAAE,mBAAmB,EAAE,CAAC;CACtC;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,GAAG,MAAM,CAAC;IACzB,WAAW,CAAC,EAAE,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;IAC9B,KAAK,CAAC,EAAE,YAAY,EAAE,CAAC;CACxB;AAED,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;IAC9B,KAAK,CAAC,EAAE,YAAY,EAAE,CAAC;IACvB,YAAY,CAAC,EAAE,mBAAmB,EAAE,CAAC;CACtC;AAID,MAAM,WAAW,cAAc;IAC7B,KAAK,CAAC,EAAE,WAAW,GAAG,WAAW,GAAG,SAAS,CAAC;CAC/C;AAED,MAAM,WAAW,iBAAiB;IAChC,KAAK,CAAC,EAAE,WAAW,GAAG,WAAW,GAAG,SAAS,CAAC;CAC/C;AAED,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,WAAW,GAAG,WAAW,GAAG,SAAS,CAAC;CAC/C;AAED,MAAM,WAAW,YAAY;IAC3B,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CAC/B;AAED,MAAM,WAAW,eAAe;IAC9B,eAAe,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CAClC;AAED,MAAM,WAAW,gBAAgB;IAC/B,YAAY,IAAI;QACd,UAAU,IAAI,OAAO,CAAC;QACtB,WAAW,IAAI,OAAO,CAAC;QACvB;;;;WAIG;QACH,UAAU,IAAI,cAAc,CAAC;KAC9B,CAAC;CACH;AAED,MAAM,WAAW,WAAW;IAC1B,WAAW,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC;CACpE;AAGD,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAC;AAErD,wBAAgB,UAAU,CAAC,OAAO,EAAE,iBAAiB,GAAG,MAAM,GAAG,cAAc,CAQ9E;AAED,wBAAgB,UAAU,CAAC,OAAO,GAAE,iBAAsB,GAAG,cAAc,CAS1E;AAED,wBAAgB,GAAG,CAAC,OAAO,CAAC,EAAE;IAAE,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,MAAM,GAAG,eAAe,CAGzE;AAED,wBAAgB,IAAI,CAAC,OAAO,CAAC,EAAE;IAAE,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,MAAM,GAAG,eAAe,CAG1E;AAED,wBAAgB,GAAG,CAAC,OAAO,CAAC,EAAE;IAAE,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,MAAM,GAAG,eAAe,CAGzE;AAED,wBAAgB,MAAM,CAAC,OAAO,CAAC,EAAE;IAAE,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,MAAM,GAAG,eAAe,CAG5E;AAED,wBAAgB,KAAK,CAAC,OAAO,CAAC,EAAE;IAAE,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,MAAM,GAAG,eAAe,CAG3E;AAED,wBAAgB,MAAM,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,kBAAkB,CAalF;AAED,wBAAgB,OAAO,CAAC,OAAO,GAAE,cAAmB,GAAG,cAAc,CAyBpE;AAED,wBAAgB,IAAI,CAAC,OAAO,CAAC,EAAE,IAAI,CAAC,OAAO,CAAC,GAAG,kBAAkB,CA6BhE;AAED,wBAAgB,OAAO,IAAI,kBAAkB,CAgB5C;AAED,wBAAgB,KAAK,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,IAAI,CAAC,aAAa,CAAC,GAAG,kBAAkB,CAuBvF;AAED,wBAAgB,KAAK,CAAC,SAAS,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,IAAI,CAAC,aAAa,CAAC,GAAG,kBAAkB,CAuBxF;AAED,wBAAgB,QAAQ,CACtB,GAAG,KAAK,EAAE,CAAC,IAAI,CAAC,aAAa,CAAC,GAAG,YAAY,CAAC,EAAE,GAC/C,cAAc,GAAG,eAAe,CAgClC;AAED,wBAAgB,eAAe,CAC7B,GAAG,YAAY,EAAE,CAAC,IAAI,CAAC,WAAW,CAAC,GAAG,mBAAmB,CAAC,EAAE,GAC3D,cAAc,GAAG,eAAe,CA4BlC;AAED,wBAAgB,SAAS,CAAC,GAAG,MAAM,EAAE,IAAI,CAAC,WAAW,CAAC,EAAE,GAAG,cAAc,GAAG,eAAe,CAmB1F;AAED,wBAAgB,MAAM,CAAC,OAAO,EAAE;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;CACpB,GAAG,eAAe,CASlB;AAED,wBAAgB,GAAG,IAAI,kBAAkB,CAgBxC;AAED,wBAAgB,OAAO,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,kBAAkB,CAgB/D;AAED,wBAAgB,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,eAAe,CAS5D;AAED,wBAAgB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,eAAe,CAYnE;AAED,wBAAgB,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,UAAU,GAAE,MAAY,GAAG,eAAe,CAS/E;AAED,wBAAgB,GAAG,IAAI,kBAAkB,CAyBxC;AAED,wBAAgB,EAAE,IAAI,kBAAkB,CAevC;AAED,wBAAgB,IAAI,IAAI,kBAAkB,CAezC;AAED;;;;GAIG;AACH,wBAAgB,MAAM,IAAI,cAAc,GAAG,eAAe,CAoBzD;AAED,+EAA+E;AAC/E,eAAO,MAAM,QAAQ,eAAS,CAAC;AAE/B,wBAAgB,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,eAAe,CASnD;AAED,wBAAgB,QAAQ,IAAI,kBAAkB,CAiB7C;AAED,wBAAgB,OAAO,IAAI,kBAAkB,CAe5C;AAED,MAAM,WAAW,qBAAqB;IACpC,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,KAAK,KAAK,OAAO,CAAC;CACnC;AAED,wBAAgB,KAAK,CAAC,OAAO,EAAE,qBAAqB,GAAG,eAAe,CAgBrE;AAED,wBAAgB,OAAO,CAAC,GAAG,IAAI,EAAE,MAAM,EAAE,GAAG,cAAc,GAAG,eAAe,CAoB3E;AAED,MAAM,WAAW,mBAAmB;IAClC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,wBAAgB,YAAY,CAAC,OAAO,EAAE,mBAAmB,GAAG,MAAM,GAAG,eAAe,CAUnF;AAED;;;;;;;;;GASG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,cAAc,GAAG,eAAe,CAczF;AAED;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,CAAC,GAAG,OAAO,EACrC,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,MAAM,EACd,WAAW,CAAC,EAAE,MAAM,GAAG,MAAM,GAC5B,CAAC,GAAG,SAAS,CAMf;AAED;;;GAGG;AACH,MAAM,WAAW,qBAAqB;IACpC,GAAG,EAAE,OAAO,CAAC;IACb,OAAO,EAAE,cAAc,CAAC;IACxB,SAAS,EAAE,SAAS,CAAC;CACtB;AAED;;;;;;;;;GASG;AACH,wBAAgB,oBAAoB,CAAC,CAAC,GAAG,OAAO,EAC9C,OAAO,EAAE,CAAC,GAAG,EAAE,OAAO,EAAE,OAAO,EAAE,cAAc,EAAE,SAAS,EAAE,SAAS,KAAK,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,GACvF,kBAAkB,CAcpB;AA8CD,OAAO,EAAE,QAAQ,EAAE,CAAC"}
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.HazelApp = exports.Module = exports.HazelModule = void 0;
6
+ exports.HazelApp = exports.SkipAuth = exports.Module = exports.HazelModule = exports.CUSTOM_METADATA_PREFIX = void 0;
7
7
  exports.Controller = Controller;
8
8
  exports.Injectable = Injectable;
9
9
  exports.Get = Get;
@@ -27,8 +27,21 @@ exports.HttpCode = HttpCode;
27
27
  exports.Header = Header;
28
28
  exports.Redirect = Redirect;
29
29
  exports.Res = Res;
30
+ exports.Ip = Ip;
31
+ exports.Host = Host;
32
+ exports.Public = Public;
33
+ exports.Timeout = Timeout;
34
+ exports.Optional = Optional;
35
+ exports.Session = Session;
36
+ exports.Retry = Retry;
37
+ exports.ApiTags = ApiTags;
38
+ exports.ApiOperation = ApiOperation;
39
+ exports.SetMetadata = SetMetadata;
40
+ exports.getMetadata = getMetadata;
41
+ exports.createParamDecorator = createParamDecorator;
30
42
  require("reflect-metadata");
31
43
  const logger_1 = __importDefault(require("./logger"));
44
+ const interceptor_1 = require("./interceptors/interceptor");
32
45
  const hazel_app_1 = require("./hazel-app");
33
46
  Object.defineProperty(exports, "HazelApp", { enumerable: true, get: function () { return hazel_app_1.HazelApp; } });
34
47
  const CONTROLLER_METADATA_KEY = 'hazel:controller';
@@ -42,6 +55,13 @@ const CLASS_INTERCEPTOR_METADATA_KEY = 'hazel:class-interceptors';
42
55
  const HTTP_CODE_METADATA_KEY = 'hazel:http-code';
43
56
  const HEADER_METADATA_KEY = 'hazel:headers';
44
57
  const REDIRECT_METADATA_KEY = 'hazel:redirect';
58
+ const PUBLIC_METADATA_KEY = 'hazel:public';
59
+ const TIMEOUT_METADATA_KEY = 'hazel:timeout';
60
+ const OPTIONAL_INDICES_METADATA_KEY = 'hazel:optional-indices';
61
+ const RETRY_METADATA_KEY = 'hazel:retry';
62
+ const API_TAGS_METADATA_KEY = 'hazel:api:tags';
63
+ const API_OPERATION_METADATA_KEY = 'hazel:api:operation';
64
+ exports.CUSTOM_METADATA_PREFIX = 'hazel:meta:';
45
65
  // Re-export from hazel-module for backward compatibility
46
66
  var hazel_module_1 = require("./hazel-module");
47
67
  Object.defineProperty(exports, "HazelModule", { enumerable: true, get: function () { return hazel_module_1.HazelModule; } });
@@ -313,6 +333,181 @@ function Res() {
313
333
  logger_1.default.debug('Updated injections:', JSON.stringify(injections, null, 2));
314
334
  };
315
335
  }
336
+ function Ip() {
337
+ return (target, propertyKey, parameterIndex) => {
338
+ if (!propertyKey) {
339
+ throw new Error('Ip decorator must be used on a method parameter');
340
+ }
341
+ const constructor = target
342
+ .constructor;
343
+ const injections = Reflect.getMetadata(INJECT_METADATA_KEY, constructor, propertyKey) || [];
344
+ injections[parameterIndex] = { type: 'ip' };
345
+ Reflect.defineMetadata(INJECT_METADATA_KEY, injections, constructor, propertyKey);
346
+ };
347
+ }
348
+ function Host() {
349
+ return (target, propertyKey, parameterIndex) => {
350
+ if (!propertyKey) {
351
+ throw new Error('Host decorator must be used on a method parameter');
352
+ }
353
+ const constructor = target
354
+ .constructor;
355
+ const injections = Reflect.getMetadata(INJECT_METADATA_KEY, constructor, propertyKey) || [];
356
+ injections[parameterIndex] = { type: 'host' };
357
+ Reflect.defineMetadata(INJECT_METADATA_KEY, injections, constructor, propertyKey);
358
+ };
359
+ }
360
+ /**
361
+ * Marks a controller or route as public (no auth required).
362
+ * Guards should check Reflect.getMetadata(PUBLIC_METADATA_KEY, target, propertyKey)
363
+ * or Reflect.getMetadata(PUBLIC_METADATA_KEY, target) and allow the request when true.
364
+ */
365
+ function Public() {
366
+ const setPublic = (target, propertyKey) => {
367
+ if (propertyKey === undefined) {
368
+ Reflect.defineMetadata(PUBLIC_METADATA_KEY, true, target);
369
+ }
370
+ else {
371
+ Reflect.defineMetadata(PUBLIC_METADATA_KEY, true, target, propertyKey);
372
+ }
373
+ };
374
+ const decorator = (target, propertyKey, descriptor) => {
375
+ if (propertyKey !== undefined && descriptor !== undefined) {
376
+ setPublic(target, propertyKey);
377
+ return descriptor;
378
+ }
379
+ setPublic(target);
380
+ };
381
+ return decorator;
382
+ }
383
+ /** Alias for @Public(). Use when you want to skip auth for specific routes. */
384
+ exports.SkipAuth = Public;
385
+ function Timeout(ms) {
386
+ return (target, propertyKey, descriptor) => {
387
+ Reflect.defineMetadata(TIMEOUT_METADATA_KEY, ms, target, propertyKey);
388
+ return descriptor;
389
+ };
390
+ }
391
+ function Optional() {
392
+ return (target, propertyKey, parameterIndex) => {
393
+ if (!propertyKey) {
394
+ throw new Error('Optional decorator must be used on a method parameter');
395
+ }
396
+ const constructor = target
397
+ .constructor;
398
+ const indices = Reflect.getMetadata(OPTIONAL_INDICES_METADATA_KEY, constructor, propertyKey) || [];
399
+ if (!indices.includes(parameterIndex)) {
400
+ indices.push(parameterIndex);
401
+ }
402
+ Reflect.defineMetadata(OPTIONAL_INDICES_METADATA_KEY, indices, constructor, propertyKey);
403
+ };
404
+ }
405
+ function Session() {
406
+ return (target, propertyKey, parameterIndex) => {
407
+ if (!propertyKey) {
408
+ throw new Error('Session decorator must be used on a method parameter');
409
+ }
410
+ const constructor = target
411
+ .constructor;
412
+ const injections = Reflect.getMetadata(INJECT_METADATA_KEY, constructor, propertyKey) || [];
413
+ injections[parameterIndex] = { type: 'session' };
414
+ Reflect.defineMetadata(INJECT_METADATA_KEY, injections, constructor, propertyKey);
415
+ };
416
+ }
417
+ function Retry(options) {
418
+ return (target, propertyKey, descriptor) => {
419
+ Reflect.defineMetadata(RETRY_METADATA_KEY, options, target, propertyKey);
420
+ const routes = Reflect.getMetadata(ROUTE_METADATA_KEY, target.constructor) || [];
421
+ const route = routes.find((r) => r.propertyKey === propertyKey);
422
+ if (route) {
423
+ route.interceptors = route.interceptors || [];
424
+ route.interceptors.unshift({ type: interceptor_1.RetryInterceptor, options });
425
+ Reflect.defineMetadata(ROUTE_METADATA_KEY, routes, target.constructor);
426
+ }
427
+ return descriptor;
428
+ };
429
+ }
430
+ function ApiTags(...tags) {
431
+ const setTags = (target, propertyKey) => {
432
+ if (propertyKey === undefined) {
433
+ Reflect.defineMetadata(API_TAGS_METADATA_KEY, tags, target);
434
+ }
435
+ else {
436
+ Reflect.defineMetadata(API_TAGS_METADATA_KEY, tags, target, propertyKey);
437
+ }
438
+ };
439
+ const decorator = (target, propertyKey, descriptor) => {
440
+ if (propertyKey !== undefined && descriptor !== undefined) {
441
+ setTags(target, propertyKey);
442
+ return descriptor;
443
+ }
444
+ setTags(target);
445
+ };
446
+ return decorator;
447
+ }
448
+ function ApiOperation(options) {
449
+ const opts = typeof options === 'string' ? { summary: options } : options;
450
+ return (target, propertyKey, descriptor) => {
451
+ Reflect.defineMetadata(API_OPERATION_METADATA_KEY, opts, target, propertyKey);
452
+ return descriptor;
453
+ };
454
+ }
455
+ /**
456
+ * Sets arbitrary metadata on a class or method.
457
+ * Guards, interceptors, and other components can read it via getMetadata(key, target, propertyKey?).
458
+ *
459
+ * @param key - Metadata key (stored under hazel:meta:<key> to avoid collisions)
460
+ * @param value - Value to store (any serializable or object)
461
+ * @example
462
+ * SetMetadata('roles', ['admin'])(MyController)
463
+ * SetMetadata('roles', ['user'])(MyController.prototype, 'getProfile')
464
+ */
465
+ function SetMetadata(key, value) {
466
+ const metaKey = `${exports.CUSTOM_METADATA_PREFIX}${key}`;
467
+ const decorator = (target, propertyKey, descriptor) => {
468
+ if (propertyKey !== undefined && descriptor !== undefined) {
469
+ Reflect.defineMetadata(metaKey, value, target, propertyKey);
470
+ return descriptor;
471
+ }
472
+ Reflect.defineMetadata(metaKey, value, target);
473
+ };
474
+ return decorator;
475
+ }
476
+ /**
477
+ * Reads custom metadata set with SetMetadata.
478
+ *
479
+ * @param key - Key passed to SetMetadata(key, value)
480
+ * @param target - Class or prototype
481
+ * @param propertyKey - Optional method name (for method-level metadata)
482
+ */
483
+ function getMetadata(key, target, propertyKey) {
484
+ const metaKey = `${exports.CUSTOM_METADATA_PREFIX}${key}`;
485
+ if (propertyKey !== undefined) {
486
+ return Reflect.getMetadata(metaKey, target, propertyKey);
487
+ }
488
+ return Reflect.getMetadata(metaKey, target);
489
+ }
490
+ /**
491
+ * Creates a custom parameter decorator that injects a value computed from the request.
492
+ * The resolver receives the raw request, parsed request context, and the DI container.
493
+ * Return value can be a Promise for async resolution (e.g. loading the current user from DB).
494
+ *
495
+ * @param resolve - Function (req, context, container) => value | Promise<value>
496
+ * @example
497
+ * const CurrentUser = createParamDecorator(async (req, ctx, container) => ctx.user ?? req.user);
498
+ * // In controller: getProfile(@CurrentUser() user: User) { ... }
499
+ */
500
+ function createParamDecorator(resolve) {
501
+ return (target, propertyKey, parameterIndex) => {
502
+ if (!propertyKey) {
503
+ throw new Error('createParamDecorator must be used on a method parameter');
504
+ }
505
+ const constructor = target.constructor;
506
+ const injections = Reflect.getMetadata(INJECT_METADATA_KEY, constructor, propertyKey) || [];
507
+ injections[parameterIndex] = { type: 'custom', resolve };
508
+ Reflect.defineMetadata(INJECT_METADATA_KEY, injections, constructor, propertyKey);
509
+ };
510
+ }
316
511
  function createRouteDecorator(method, options) {
317
512
  return (target, propertyKey, descriptor) => {
318
513
  logger_1.default.debug(`Registering ${method} route: ${String(propertyKey)}`);
@@ -18,6 +18,9 @@ export declare class NotFoundError extends HttpError {
18
18
  export declare class ConflictError extends HttpError {
19
19
  constructor(message: string);
20
20
  }
21
+ export declare class RequestTimeoutError extends HttpError {
22
+ constructor(message?: string);
23
+ }
21
24
  export declare class InternalServerError extends HttpError {
22
25
  constructor(message?: string);
23
26
  }
@@ -1 +1 @@
1
- {"version":3,"file":"http.error.d.ts","sourceRoot":"","sources":["../../src/errors/http.error.ts"],"names":[],"mappings":"AAAA,qBAAa,SAAU,SAAQ,KAAK;aAEhB,UAAU,EAAE,MAAM;aAElB,MAAM,CAAC,EAAE,MAAM,EAAE;gBAFjB,UAAU,EAAE,MAAM,EAClC,OAAO,EAAE,MAAM,EACC,MAAM,CAAC,EAAE,MAAM,EAAE,YAAA;CAKpC;AAED,qBAAa,eAAgB,SAAQ,SAAS;gBAChC,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,EAAE;CAI/C;AAED,qBAAa,iBAAkB,SAAQ,SAAS;gBAClC,OAAO,GAAE,MAAuB;CAI7C;AAED,qBAAa,cAAe,SAAQ,SAAS;gBAC/B,OAAO,GAAE,MAAoB;CAI1C;AAED,qBAAa,aAAc,SAAQ,SAAS;gBAC9B,OAAO,GAAE,MAAoB;CAI1C;AAED,qBAAa,aAAc,SAAQ,SAAS;gBAC9B,OAAO,EAAE,MAAM;CAI5B;AAED,qBAAa,mBAAoB,SAAQ,SAAS;gBACpC,OAAO,GAAE,MAAgC;CAItD;AAGD,eAAO,MAAM,aAAa,kBAAY,CAAC;AACvC,eAAO,MAAM,mBAAmB,wBAAkB,CAAC;AACnD,eAAO,MAAM,qBAAqB,0BAAoB,CAAC;AACvD,eAAO,MAAM,kBAAkB,uBAAiB,CAAC;AACjD,eAAO,MAAM,iBAAiB,sBAAgB,CAAC;AAC/C,eAAO,MAAM,iBAAiB,sBAAgB,CAAC;AAC/C,eAAO,MAAM,4BAA4B,4BAAsB,CAAC"}
1
+ {"version":3,"file":"http.error.d.ts","sourceRoot":"","sources":["../../src/errors/http.error.ts"],"names":[],"mappings":"AAAA,qBAAa,SAAU,SAAQ,KAAK;aAEhB,UAAU,EAAE,MAAM;aAElB,MAAM,CAAC,EAAE,MAAM,EAAE;gBAFjB,UAAU,EAAE,MAAM,EAClC,OAAO,EAAE,MAAM,EACC,MAAM,CAAC,EAAE,MAAM,EAAE,YAAA;CAKpC;AAED,qBAAa,eAAgB,SAAQ,SAAS;gBAChC,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,EAAE;CAI/C;AAED,qBAAa,iBAAkB,SAAQ,SAAS;gBAClC,OAAO,GAAE,MAAuB;CAI7C;AAED,qBAAa,cAAe,SAAQ,SAAS;gBAC/B,OAAO,GAAE,MAAoB;CAI1C;AAED,qBAAa,aAAc,SAAQ,SAAS;gBAC9B,OAAO,GAAE,MAAoB;CAI1C;AAED,qBAAa,aAAc,SAAQ,SAAS;gBAC9B,OAAO,EAAE,MAAM;CAI5B;AAED,qBAAa,mBAAoB,SAAQ,SAAS;gBACpC,OAAO,GAAE,MAA0B;CAIhD;AAED,qBAAa,mBAAoB,SAAQ,SAAS;gBACpC,OAAO,GAAE,MAAgC;CAItD;AAGD,eAAO,MAAM,aAAa,kBAAY,CAAC;AACvC,eAAO,MAAM,mBAAmB,wBAAkB,CAAC;AACnD,eAAO,MAAM,qBAAqB,0BAAoB,CAAC;AACvD,eAAO,MAAM,kBAAkB,uBAAiB,CAAC;AACjD,eAAO,MAAM,iBAAiB,sBAAgB,CAAC;AAC/C,eAAO,MAAM,iBAAiB,sBAAgB,CAAC;AAC/C,eAAO,MAAM,4BAA4B,4BAAsB,CAAC"}
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.InternalServerErrorException = exports.ConflictException = exports.NotFoundException = exports.ForbiddenException = exports.UnauthorizedException = exports.BadRequestException = exports.HttpException = exports.InternalServerError = exports.ConflictError = exports.NotFoundError = exports.ForbiddenError = exports.UnauthorizedError = exports.BadRequestError = exports.HttpError = void 0;
3
+ exports.InternalServerErrorException = exports.ConflictException = exports.NotFoundException = exports.ForbiddenException = exports.UnauthorizedException = exports.BadRequestException = exports.HttpException = exports.InternalServerError = exports.RequestTimeoutError = exports.ConflictError = exports.NotFoundError = exports.ForbiddenError = exports.UnauthorizedError = exports.BadRequestError = exports.HttpError = void 0;
4
4
  class HttpError extends Error {
5
5
  constructor(statusCode, message, errors) {
6
6
  super(message);
@@ -45,6 +45,13 @@ class ConflictError extends HttpError {
45
45
  }
46
46
  }
47
47
  exports.ConflictError = ConflictError;
48
+ class RequestTimeoutError extends HttpError {
49
+ constructor(message = 'Request Timeout') {
50
+ super(408, message);
51
+ this.name = 'RequestTimeoutError';
52
+ }
53
+ }
54
+ exports.RequestTimeoutError = RequestTimeoutError;
48
55
  class InternalServerError extends HttpError {
49
56
  constructor(message = 'Internal Server Error') {
50
57
  super(500, message);
package/dist/index.d.ts CHANGED
@@ -12,13 +12,13 @@ export { HealthCheckManager, BuiltInHealthChecks } from './health';
12
12
  export type { HealthCheck, HealthCheckResult, HealthStatus } from './health';
13
13
  export { TimeoutMiddleware } from './middleware/timeout.middleware';
14
14
  export type { TimeoutOptions } from './middleware/timeout.middleware';
15
- export { Controller, Injectable, Service, Get, Post, Put, Delete, Patch, Body, Param, Query, Req, Res, Headers, HttpCode, Header, Redirect, Inject, UsePipes, UseInterceptors, UseGuards, type ControllerMetadata, type RouteMetadata, type ControllerOptions, type RouteOptions, type ServiceOptions, type InjectableOptions, type RepositoryOptions, type OnModuleInit, type OnModuleDestroy, type ExecutionContext, type CanActivate, } from './decorators';
15
+ export { Controller, Injectable, Service, Get, Post, Put, Delete, Patch, Body, Param, Query, Req, Res, Ip, Host, Headers, HttpCode, Header, Redirect, Inject, UsePipes, UseInterceptors, UseGuards, Public, SkipAuth, AITask, Timeout, Optional, Session, Retry, ApiTags, ApiOperation, SetMetadata, getMetadata, createParamDecorator, CUSTOM_METADATA_PREFIX, type ControllerMetadata, type RouteMetadata, type ControllerOptions, type RouteOptions, type ServiceOptions, type InjectableOptions, type RepositoryOptions, type OnModuleInit, type OnModuleDestroy, type ExecutionContext, type CanActivate, type RetryDecoratorOptions, type ApiOperationOptions, type ParamDecoratorContext, } from './decorators';
16
16
  export { Container, Scope, type InjectionToken, type Provider } from './container';
17
17
  export type { Type, Request, Response, RequestContext, ValidationSchema } from './types';
18
- export { HttpError, BadRequestError, UnauthorizedError, ForbiddenError, NotFoundError, ConflictError, InternalServerError, HttpException, BadRequestException, UnauthorizedException, ForbiddenException, NotFoundException, ConflictException, InternalServerErrorException, } from './errors/http.error';
18
+ export { HttpError, BadRequestError, UnauthorizedError, ForbiddenError, NotFoundError, ConflictError, InternalServerError, HttpException, BadRequestException, UnauthorizedException, ForbiddenException, NotFoundException, ConflictException, InternalServerErrorException, RequestTimeoutError, } from './errors/http.error';
19
19
  export { PipeTransform, ValidationError, ParseIntPipe, type PipeMetadata } from './pipes/pipe';
20
20
  export { ValidationPipe } from './pipes/validation.pipe';
21
- export { Interceptor, type InterceptorMetadata } from './interceptors/interceptor';
21
+ export { Interceptor, RetryInterceptor, type InterceptorMetadata, type RetryOptions, } from './interceptors/interceptor';
22
22
  export { type ExceptionFilter, type ArgumentsHost, ArgumentsHostImpl, Catch, getFilterExceptions, } from './filters/exception-filter';
23
23
  export { HttpExceptionFilter } from './filters/http-exception.filter';
24
24
  export { Test, TestingModule, TestingModuleBuilder, type TestingModuleMetadata, } from './testing/testing.module';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -7,8 +7,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
7
7
  return (mod && mod.__esModule) ? mod : { "default": mod };
8
8
  };
9
9
  Object.defineProperty(exports, "__esModule", { value: true });
10
- exports.ArgumentsHostImpl = exports.ValidationPipe = exports.ParseIntPipe = exports.ValidationError = exports.InternalServerErrorException = exports.ConflictException = exports.NotFoundException = exports.ForbiddenException = exports.UnauthorizedException = exports.BadRequestException = exports.HttpException = exports.InternalServerError = exports.ConflictError = exports.NotFoundError = exports.ForbiddenError = exports.UnauthorizedError = exports.BadRequestError = exports.HttpError = exports.Scope = exports.Container = exports.UseGuards = exports.UseInterceptors = exports.UsePipes = exports.Inject = exports.Redirect = exports.Header = exports.HttpCode = exports.Headers = exports.Res = exports.Req = exports.Query = exports.Param = exports.Body = exports.Patch = exports.Delete = exports.Put = exports.Post = exports.Get = exports.Service = exports.Injectable = exports.Controller = exports.TimeoutMiddleware = exports.BuiltInHealthChecks = exports.HealthCheckManager = exports.ShutdownManager = exports.getModuleMetadata = exports.HazelModuleInstance = exports.Module = exports.HazelModule = exports.HazelApp = void 0;
11
- exports.escapeHtml = exports.sanitizeObject = exports.sanitizeSql = exports.sanitizeEmail = exports.sanitizeUrl = exports.sanitizeString = exports.sanitizeHtml = exports.RequestParser = exports.Router = exports.Validator = exports.default = exports.logger = exports.UploadedFiles = exports.UploadedFile = exports.FileUploadInterceptor = exports.CsrfMiddleware = exports.RateLimitMiddleware = exports.SecurityHeadersMiddleware = exports.LoggerMiddleware = exports.CorsMiddleware = exports.GlobalMiddlewareManager = exports.extractVersion = exports.matchVersion = exports.getVersionMetadata = exports.VersioningType = exports.Version = exports.RouteMatcher = exports.TestingModuleBuilder = exports.TestingModule = exports.Test = exports.HttpExceptionFilter = exports.getFilterExceptions = exports.Catch = void 0;
10
+ exports.UnauthorizedError = exports.BadRequestError = exports.HttpError = exports.Scope = exports.Container = exports.CUSTOM_METADATA_PREFIX = exports.createParamDecorator = exports.getMetadata = exports.SetMetadata = exports.ApiOperation = exports.ApiTags = exports.Retry = exports.Session = exports.Optional = exports.Timeout = exports.AITask = exports.SkipAuth = exports.Public = exports.UseGuards = exports.UseInterceptors = exports.UsePipes = exports.Inject = exports.Redirect = exports.Header = exports.HttpCode = exports.Headers = exports.Host = exports.Ip = exports.Res = exports.Req = exports.Query = exports.Param = exports.Body = exports.Patch = exports.Delete = exports.Put = exports.Post = exports.Get = exports.Service = exports.Injectable = exports.Controller = exports.TimeoutMiddleware = exports.BuiltInHealthChecks = exports.HealthCheckManager = exports.ShutdownManager = exports.getModuleMetadata = exports.HazelModuleInstance = exports.Module = exports.HazelModule = exports.HazelApp = void 0;
11
+ exports.escapeHtml = exports.sanitizeObject = exports.sanitizeSql = exports.sanitizeEmail = exports.sanitizeUrl = exports.sanitizeString = exports.sanitizeHtml = exports.RequestParser = exports.Router = exports.Validator = exports.default = exports.logger = exports.UploadedFiles = exports.UploadedFile = exports.FileUploadInterceptor = exports.CsrfMiddleware = exports.RateLimitMiddleware = exports.SecurityHeadersMiddleware = exports.LoggerMiddleware = exports.CorsMiddleware = exports.GlobalMiddlewareManager = exports.extractVersion = exports.matchVersion = exports.getVersionMetadata = exports.VersioningType = exports.Version = exports.RouteMatcher = exports.TestingModuleBuilder = exports.TestingModule = exports.Test = exports.HttpExceptionFilter = exports.getFilterExceptions = exports.Catch = exports.ArgumentsHostImpl = exports.RetryInterceptor = exports.ValidationPipe = exports.ParseIntPipe = exports.ValidationError = exports.RequestTimeoutError = exports.InternalServerErrorException = exports.ConflictException = exports.NotFoundException = exports.ForbiddenException = exports.UnauthorizedException = exports.BadRequestException = exports.HttpException = exports.InternalServerError = exports.ConflictError = exports.NotFoundError = exports.ForbiddenError = void 0;
12
12
  // Import reflect-metadata to enable decorator metadata
13
13
  // Users don't need to import this manually
14
14
  require("reflect-metadata");
@@ -44,6 +44,8 @@ Object.defineProperty(exports, "Param", { enumerable: true, get: function () { r
44
44
  Object.defineProperty(exports, "Query", { enumerable: true, get: function () { return decorators_1.Query; } });
45
45
  Object.defineProperty(exports, "Req", { enumerable: true, get: function () { return decorators_1.Req; } });
46
46
  Object.defineProperty(exports, "Res", { enumerable: true, get: function () { return decorators_1.Res; } });
47
+ Object.defineProperty(exports, "Ip", { enumerable: true, get: function () { return decorators_1.Ip; } });
48
+ Object.defineProperty(exports, "Host", { enumerable: true, get: function () { return decorators_1.Host; } });
47
49
  Object.defineProperty(exports, "Headers", { enumerable: true, get: function () { return decorators_1.Headers; } });
48
50
  Object.defineProperty(exports, "HttpCode", { enumerable: true, get: function () { return decorators_1.HttpCode; } });
49
51
  Object.defineProperty(exports, "Header", { enumerable: true, get: function () { return decorators_1.Header; } });
@@ -52,6 +54,19 @@ Object.defineProperty(exports, "Inject", { enumerable: true, get: function () {
52
54
  Object.defineProperty(exports, "UsePipes", { enumerable: true, get: function () { return decorators_1.UsePipes; } });
53
55
  Object.defineProperty(exports, "UseInterceptors", { enumerable: true, get: function () { return decorators_1.UseInterceptors; } });
54
56
  Object.defineProperty(exports, "UseGuards", { enumerable: true, get: function () { return decorators_1.UseGuards; } });
57
+ Object.defineProperty(exports, "Public", { enumerable: true, get: function () { return decorators_1.Public; } });
58
+ Object.defineProperty(exports, "SkipAuth", { enumerable: true, get: function () { return decorators_1.SkipAuth; } });
59
+ Object.defineProperty(exports, "AITask", { enumerable: true, get: function () { return decorators_1.AITask; } });
60
+ Object.defineProperty(exports, "Timeout", { enumerable: true, get: function () { return decorators_1.Timeout; } });
61
+ Object.defineProperty(exports, "Optional", { enumerable: true, get: function () { return decorators_1.Optional; } });
62
+ Object.defineProperty(exports, "Session", { enumerable: true, get: function () { return decorators_1.Session; } });
63
+ Object.defineProperty(exports, "Retry", { enumerable: true, get: function () { return decorators_1.Retry; } });
64
+ Object.defineProperty(exports, "ApiTags", { enumerable: true, get: function () { return decorators_1.ApiTags; } });
65
+ Object.defineProperty(exports, "ApiOperation", { enumerable: true, get: function () { return decorators_1.ApiOperation; } });
66
+ Object.defineProperty(exports, "SetMetadata", { enumerable: true, get: function () { return decorators_1.SetMetadata; } });
67
+ Object.defineProperty(exports, "getMetadata", { enumerable: true, get: function () { return decorators_1.getMetadata; } });
68
+ Object.defineProperty(exports, "createParamDecorator", { enumerable: true, get: function () { return decorators_1.createParamDecorator; } });
69
+ Object.defineProperty(exports, "CUSTOM_METADATA_PREFIX", { enumerable: true, get: function () { return decorators_1.CUSTOM_METADATA_PREFIX; } });
55
70
  // Container & DI
56
71
  var container_1 = require("./container");
57
72
  Object.defineProperty(exports, "Container", { enumerable: true, get: function () { return container_1.Container; } });
@@ -72,12 +87,16 @@ Object.defineProperty(exports, "ForbiddenException", { enumerable: true, get: fu
72
87
  Object.defineProperty(exports, "NotFoundException", { enumerable: true, get: function () { return http_error_1.NotFoundException; } });
73
88
  Object.defineProperty(exports, "ConflictException", { enumerable: true, get: function () { return http_error_1.ConflictException; } });
74
89
  Object.defineProperty(exports, "InternalServerErrorException", { enumerable: true, get: function () { return http_error_1.InternalServerErrorException; } });
90
+ Object.defineProperty(exports, "RequestTimeoutError", { enumerable: true, get: function () { return http_error_1.RequestTimeoutError; } });
75
91
  // Pipes
76
92
  var pipe_1 = require("./pipes/pipe");
77
93
  Object.defineProperty(exports, "ValidationError", { enumerable: true, get: function () { return pipe_1.ValidationError; } });
78
94
  Object.defineProperty(exports, "ParseIntPipe", { enumerable: true, get: function () { return pipe_1.ParseIntPipe; } });
79
95
  var validation_pipe_1 = require("./pipes/validation.pipe");
80
96
  Object.defineProperty(exports, "ValidationPipe", { enumerable: true, get: function () { return validation_pipe_1.ValidationPipe; } });
97
+ // Interceptors
98
+ var interceptor_1 = require("./interceptors/interceptor");
99
+ Object.defineProperty(exports, "RetryInterceptor", { enumerable: true, get: function () { return interceptor_1.RetryInterceptor; } });
81
100
  // Filters
82
101
  var exception_filter_1 = require("./filters/exception-filter");
83
102
  Object.defineProperty(exports, "ArgumentsHostImpl", { enumerable: true, get: function () { return exception_filter_1.ArgumentsHostImpl; } });
@@ -18,5 +18,13 @@ export declare class CacheInterceptor implements Interceptor {
18
18
  constructor(options?: CacheOptions);
19
19
  intercept(context: RequestContext, next: () => Promise<unknown>): Promise<unknown>;
20
20
  }
21
+ export interface RetryOptions {
22
+ count: number;
23
+ delay?: number;
24
+ retryIf?: (err: Error) => boolean;
25
+ }
26
+ export declare class RetryInterceptor implements Interceptor {
27
+ intercept(context: RequestContext, next: () => Promise<unknown>): Promise<unknown>;
28
+ }
21
29
  export type Type<T = unknown> = new (...args: unknown[]) => T;
22
30
  //# sourceMappingURL=interceptor.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"interceptor.d.ts","sourceRoot":"","sources":["../../src/interceptors/interceptor.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAGpD,MAAM,WAAW,WAAW;IAC1B,SAAS,CAAC,OAAO,EAAE,cAAc,EAAE,IAAI,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CACpF;AAED,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,IAAI,CAAC,WAAW,CAAC,CAAC;IACxB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,YAAY;IAC3B,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,qBAAa,kBAAmB,YAAW,WAAW;IAC9C,SAAS,CAAC,OAAO,EAAE,cAAc,EAAE,IAAI,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC;CAgBzF;AAED,qBAAa,gBAAiB,YAAW,WAAW;IAClD,OAAO,CAAC,MAAM,CAAC,KAAK,CAA2D;IAC/E,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAS;gBAEjB,OAAO,CAAC,EAAE,YAAY;IAI5B,SAAS,CAAC,OAAO,EAAE,cAAc,EAAE,IAAI,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC;CAgBzF;AAED,MAAM,MAAM,IAAI,CAAC,CAAC,GAAG,OAAO,IAAI,KAAK,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,CAAC,CAAC"}
1
+ {"version":3,"file":"interceptor.d.ts","sourceRoot":"","sources":["../../src/interceptors/interceptor.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAGpD,MAAM,WAAW,WAAW;IAC1B,SAAS,CAAC,OAAO,EAAE,cAAc,EAAE,IAAI,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CACpF;AAED,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,IAAI,CAAC,WAAW,CAAC,CAAC;IACxB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,YAAY;IAC3B,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,qBAAa,kBAAmB,YAAW,WAAW;IAC9C,SAAS,CAAC,OAAO,EAAE,cAAc,EAAE,IAAI,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC;CAgBzF;AAED,qBAAa,gBAAiB,YAAW,WAAW;IAClD,OAAO,CAAC,MAAM,CAAC,KAAK,CAA2D;IAC/E,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAS;gBAEjB,OAAO,CAAC,EAAE,YAAY;IAI5B,SAAS,CAAC,OAAO,EAAE,cAAc,EAAE,IAAI,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC;CAgBzF;AAED,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,KAAK,KAAK,OAAO,CAAC;CACnC;AAED,qBAAa,gBAAiB,YAAW,WAAW;IAC5C,SAAS,CAAC,OAAO,EAAE,cAAc,EAAE,IAAI,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC;CAqBzF;AAED,MAAM,MAAM,IAAI,CAAC,CAAC,GAAG,OAAO,IAAI,KAAK,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,CAAC,CAAC"}
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.CacheInterceptor = exports.LoggingInterceptor = void 0;
6
+ exports.RetryInterceptor = exports.CacheInterceptor = exports.LoggingInterceptor = void 0;
7
7
  const logger_1 = __importDefault(require("../logger"));
8
8
  class LoggingInterceptor {
9
9
  async intercept(context, next) {
@@ -44,3 +44,28 @@ class CacheInterceptor {
44
44
  }
45
45
  exports.CacheInterceptor = CacheInterceptor;
46
46
  CacheInterceptor.cache = new Map();
47
+ class RetryInterceptor {
48
+ async intercept(context, next) {
49
+ const opts = context.retryOptions;
50
+ if (!opts || opts.count < 1) {
51
+ return next();
52
+ }
53
+ const delayMs = opts.delay ?? 100;
54
+ const shouldRetry = opts.retryIf ?? (() => true);
55
+ let lastError;
56
+ for (let attempt = 0; attempt <= opts.count; attempt++) {
57
+ try {
58
+ return await next();
59
+ }
60
+ catch (err) {
61
+ lastError = err instanceof Error ? err : new Error(String(err));
62
+ if (attempt === opts.count || !shouldRetry(lastError)) {
63
+ throw lastError;
64
+ }
65
+ await new Promise((r) => setTimeout(r, delayMs));
66
+ }
67
+ }
68
+ throw lastError;
69
+ }
70
+ }
71
+ exports.RetryInterceptor = RetryInterceptor;
@@ -13,5 +13,10 @@ export interface RequestContext {
13
13
  role: string;
14
14
  [key: string]: unknown;
15
15
  };
16
+ retryOptions?: {
17
+ count: number;
18
+ delay?: number;
19
+ retryIf?: (err: Error) => boolean;
20
+ };
16
21
  }
17
22
  //# sourceMappingURL=request-context.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"request-context.d.ts","sourceRoot":"","sources":["../src/request-context.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AAE/B,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/B,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC9B,IAAI,EAAE,OAAO,CAAC;IACd,OAAO,CAAC,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;IACxB,IAAI,CAAC,EAAE;QACL,EAAE,EAAE,MAAM,GAAG,MAAM,CAAC;QACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,IAAI,EAAE,MAAM,CAAC;QACb,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;KACxB,CAAC;CACH"}
1
+ {"version":3,"file":"request-context.d.ts","sourceRoot":"","sources":["../src/request-context.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AAE/B,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/B,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC9B,IAAI,EAAE,OAAO,CAAC;IACd,OAAO,CAAC,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;IACxB,IAAI,CAAC,EAAE;QACL,EAAE,EAAE,MAAM,GAAG,MAAM,CAAC;QACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,IAAI,EAAE,MAAM,CAAC;QACb,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;KACxB,CAAC;IACF,YAAY,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,KAAK,KAAK,OAAO,CAAA;KAAE,CAAC;CACrF"}
@@ -1 +1 @@
1
- {"version":3,"file":"router.d.ts","sourceRoot":"","sources":["../src/router.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AAC/B,OAAO,EAAE,cAAc,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAC5D,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAMxC,OAAO,kBAAkB,CAAC;AAY1B,UAAU,UAAU;IAClB,OAAO,EAAE,YAAY,CAAC;IACtB,OAAO,EAAE,cAAc,CAAC;CACzB;AAED,KAAK,YAAY,GAAG,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,OAAO,CAAC,EAAE,cAAc,KAAK,IAAI,CAAC;AAEpF,qBAAa,MAAM;IAKL,OAAO,CAAC,SAAS;IAJ7B,OAAO,CAAC,MAAM,CAA0C;IACxD,OAAO,CAAC,cAAc,CAAuD;IAC7E,OAAO,CAAC,iBAAiB,CAAoB;gBAEzB,SAAS,EAAE,SAAS;IAUxC,kBAAkB,CAAC,UAAU,EAAE,IAAI,CAAC,OAAO,CAAC,GAAG,IAAI;YA4DrC,UAAU;YAsBV,iBAAiB;IAe/B,OAAO,CAAC,kBAAkB;IAiR1B,OAAO,CAAC,aAAa;IAiBrB,OAAO,CAAC,SAAS;IAiBjB,OAAO,CAAC,kBAAkB;IAW1B,OAAO,CAAC,aAAa;IAOf,KAAK,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC;IA0C7F,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,YAAY,EAAE,GAAG,IAAI;IAIjD,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,YAAY,EAAE,GAAG,IAAI;IAIlD,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,YAAY,EAAE,GAAG,IAAI;IAIjD,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,YAAY,EAAE,GAAG,IAAI;IAIpD,OAAO,CAAC,QAAQ;IAKV,aAAa,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC;CAkChE"}
1
+ {"version":3,"file":"router.d.ts","sourceRoot":"","sources":["../src/router.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AAC/B,OAAO,EAAE,cAAc,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAC5D,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAMxC,OAAO,kBAAkB,CAAC;AAc1B,UAAU,UAAU;IAClB,OAAO,EAAE,YAAY,CAAC;IACtB,OAAO,EAAE,cAAc,CAAC;CACzB;AAED,KAAK,YAAY,GAAG,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,OAAO,CAAC,EAAE,cAAc,KAAK,IAAI,CAAC;AAEpF,qBAAa,MAAM;IAKL,OAAO,CAAC,SAAS;IAJ7B,OAAO,CAAC,MAAM,CAA0C;IACxD,OAAO,CAAC,cAAc,CAAuD;IAC7E,OAAO,CAAC,iBAAiB,CAAoB;gBAEzB,SAAS,EAAE,SAAS;IAUxC,kBAAkB,CAAC,UAAU,EAAE,IAAI,CAAC,OAAO,CAAC,GAAG,IAAI;YA4DrC,UAAU;YAsBV,iBAAiB;IAe/B,OAAO,CAAC,kBAAkB;IAgT1B,OAAO,CAAC,aAAa;IAiBrB,OAAO,CAAC,SAAS;IAiBjB,OAAO,CAAC,kBAAkB;IAW1B,OAAO,CAAC,aAAa;IAOf,KAAK,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC;IA0C7F,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,YAAY,EAAE,GAAG,IAAI;IAIjD,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,YAAY,EAAE,GAAG,IAAI;IAIlD,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,YAAY,EAAE,GAAG,IAAI;IAIjD,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,YAAY,EAAE,GAAG,IAAI;IAIpD,OAAO,CAAC,QAAQ;IAKV,aAAa,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC;CAkChE"}
package/dist/router.js CHANGED
@@ -17,6 +17,8 @@ const CONTROLLER_METADATA_KEY = 'hazel:controller';
17
17
  const HTTP_CODE_METADATA_KEY = 'hazel:http-code';
18
18
  const HEADER_METADATA_KEY = 'hazel:headers';
19
19
  const REDIRECT_METADATA_KEY = 'hazel:redirect';
20
+ const TIMEOUT_METADATA_KEY = 'hazel:timeout';
21
+ const OPTIONAL_INDICES_METADATA_KEY = 'hazel:optional-indices';
20
22
  class Router {
21
23
  constructor(container) {
22
24
  this.container = container;
@@ -133,6 +135,7 @@ class Router {
133
135
  method: req.method || 'GET',
134
136
  url: req.url || '/',
135
137
  };
138
+ context.retryOptions = Reflect.getMetadata('hazel:retry', controllerClass.prototype, methodName);
136
139
  // Execute guards (class-level + method-level)
137
140
  const classGuards = Reflect.getMetadata('hazel:guards', controllerClass) || [];
138
141
  const methodGuards = Reflect.getMetadata('hazel:guards', controllerClass.prototype, methodName) || [];
@@ -241,6 +244,23 @@ class Router {
241
244
  const user = context.user ?? req.user;
242
245
  args[i] = injection.field ? user?.[injection.field] : user;
243
246
  }
247
+ else if (injection.type === 'ip') {
248
+ const r = req;
249
+ const forwarded = r.headers?.['x-forwarded-for'];
250
+ const ip = typeof forwarded === 'string'
251
+ ? forwarded.split(',')[0].trim()
252
+ : Array.isArray(forwarded)
253
+ ? forwarded[0]?.trim()
254
+ : r.socket?.remoteAddress;
255
+ args[i] = ip ?? undefined;
256
+ }
257
+ else if (injection.type === 'host') {
258
+ const host = req.headers?.['host'];
259
+ args[i] = typeof host === 'string' ? host : Array.isArray(host) ? host[0] : undefined;
260
+ }
261
+ else if (injection.type === 'session') {
262
+ args[i] = req.session;
263
+ }
244
264
  else if (injection.type === 'custom' && typeof injection.resolve === 'function') {
245
265
  // Handle custom parameter decorators (e.g. @Ability() from @hazeljs/casl).
246
266
  // The decorator stores a resolver function; call it with request, context, container.
@@ -248,6 +268,12 @@ class Router {
248
268
  }
249
269
  }
250
270
  }
271
+ const optionalIndices = Reflect.getMetadata(OPTIONAL_INDICES_METADATA_KEY, controllerClass, methodName) || [];
272
+ for (const i of optionalIndices) {
273
+ if (i < args.length && (args[i] === undefined || args[i] === null)) {
274
+ args[i] = undefined;
275
+ }
276
+ }
251
277
  // Auto-inject RequestContext for undecorated parameters
252
278
  const paramTypes = Reflect.getMetadata('design:paramtypes', controllerClass.prototype, methodName) || [];
253
279
  for (let i = 0; i < paramTypes.length; i++) {
@@ -268,10 +294,19 @@ class Router {
268
294
  }
269
295
  // Get the controller method
270
296
  const method = controller[methodName];
271
- // Execute the controller method with interceptors
272
- const result = await this.applyInterceptors(Reflect.getMetadata('hazel:interceptors', controllerClass, methodName) || [], context, async () => {
297
+ const timeoutMs = Reflect.getMetadata(TIMEOUT_METADATA_KEY, controllerClass.prototype, methodName);
298
+ let handlerPromise = this.applyInterceptors(Reflect.getMetadata('hazel:interceptors', controllerClass, methodName) || [], context, async () => {
273
299
  return method.apply(controller, args);
274
300
  });
301
+ if (timeoutMs != null && timeoutMs > 0) {
302
+ handlerPromise = Promise.race([
303
+ handlerPromise,
304
+ new Promise((_, reject) => {
305
+ setTimeout(() => reject(new http_error_1.RequestTimeoutError(`Request timed out after ${timeoutMs}ms`)), timeoutMs);
306
+ }),
307
+ ]);
308
+ }
309
+ const result = await handlerPromise;
275
310
  // Apply @Redirect metadata
276
311
  const redirectMeta = Reflect.getMetadata(REDIRECT_METADATA_KEY, controllerClass.prototype, methodName);
277
312
  if (redirectMeta) {
package/dist/types.d.ts CHANGED
@@ -56,6 +56,12 @@ export interface RequestContext {
56
56
  [key: string]: unknown;
57
57
  };
58
58
  req?: Request;
59
+ /** Set by router from @Retry() metadata; consumed by RetryInterceptor */
60
+ retryOptions?: {
61
+ count: number;
62
+ delay?: number;
63
+ retryIf?: (err: Error) => boolean;
64
+ };
59
65
  }
60
66
  export interface ValidationRule {
61
67
  type: 'string' | 'number' | 'boolean' | 'object' | 'array';
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hazeljs/core",
3
- "version": "0.2.0-beta.72",
3
+ "version": "0.2.0-beta.74",
4
4
  "description": "Core HazelJS framework - Dependency injection, routing, decorators, and base functionality",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -63,5 +63,5 @@
63
63
  "url": "https://github.com/hazeljs/hazel-js/issues"
64
64
  },
65
65
  "homepage": "https://hazeljs.com",
66
- "gitHead": "04468cb4dddc7a4faf829af5c5410a85cae14d74"
66
+ "gitHead": "f863c8fb663e52a87551bdb34a2df59ceed3d423"
67
67
  }