@hazeljs/core 0.2.0-beta.8 → 0.2.0-beta.80

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/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;
@@ -31,7 +33,7 @@ class Router {
31
33
  this.routesByMethod.set('PATCH', new Map());
32
34
  }
33
35
  registerController(controller) {
34
- logger_1.default.info(`Registering controller: ${controller.name}`);
36
+ logger_1.default.debug(`Registering controller: ${controller.name}`);
35
37
  const controllerMetadata = Reflect.getMetadata(CONTROLLER_METADATA_KEY, controller) || {};
36
38
  const routes = Reflect.getMetadata(ROUTE_METADATA_KEY, controller) || [];
37
39
  logger_1.default.debug('Controller metadata:', controllerMetadata);
@@ -41,7 +43,7 @@ class Router {
41
43
  const basePath = controllerMetadata.path || '';
42
44
  const routePath = path || '';
43
45
  const fullPath = this.normalizePath(`${basePath}${routePath}`);
44
- logger_1.default.info(`Registering route: ${method} ${fullPath} (handler: ${String(propertyKey)})`);
46
+ logger_1.default.debug(`Registering route: ${method} ${fullPath} (handler: ${String(propertyKey)})`);
45
47
  // Get parameter types from TypeScript metadata
46
48
  const paramTypes = Reflect.getMetadata('design:paramtypes', controller.prototype, propertyKey) || [];
47
49
  logger_1.default.debug('Parameter types:', paramTypes.map((t) => t?.name || 'undefined'));
@@ -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) || [];
@@ -142,6 +145,7 @@ class Router {
142
145
  switchToHttp: () => ({
143
146
  getRequest: () => req,
144
147
  getResponse: () => res,
148
+ getContext: () => context,
145
149
  }),
146
150
  };
147
151
  for (const guardType of allGuards) {
@@ -195,6 +199,10 @@ class Router {
195
199
  args[i] = context.headers;
196
200
  }
197
201
  }
202
+ else if (injection.type === 'request') {
203
+ // Handle @Req() / @Request() decorator - raw request for multipart, etc.
204
+ args[i] = req;
205
+ }
198
206
  else if (injection.type === 'response') {
199
207
  // Handle @Res decorator
200
208
  args[i] = new hazel_response_1.HazelExpressResponse(res);
@@ -231,6 +239,39 @@ class Router {
231
239
  args[i] = queryValue;
232
240
  }
233
241
  }
242
+ else if (injection.type === 'user') {
243
+ // Handle @CurrentUser() decorator — reads from context.user (set by a guard)
244
+ const user = context.user ?? req.user;
245
+ args[i] = injection.field ? user?.[injection.field] : user;
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
+ }
264
+ else if (injection.type === 'custom' && typeof injection.resolve === 'function') {
265
+ // Handle custom parameter decorators (e.g. @Ability() from @hazeljs/casl).
266
+ // The decorator stores a resolver function; call it with request, context, container.
267
+ args[i] = await injection.resolve(req, context, this.container);
268
+ }
269
+ }
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;
234
275
  }
235
276
  }
236
277
  // Auto-inject RequestContext for undecorated parameters
@@ -253,10 +294,19 @@ class Router {
253
294
  }
254
295
  // Get the controller method
255
296
  const method = controller[methodName];
256
- // Execute the controller method with interceptors
257
- 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 () => {
258
299
  return method.apply(controller, args);
259
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;
260
310
  // Apply @Redirect metadata
261
311
  const redirectMeta = Reflect.getMetadata(REDIRECT_METADATA_KEY, controllerClass.prototype, methodName);
262
312
  if (redirectMeta) {
@@ -376,7 +426,7 @@ class Router {
376
426
  return path.startsWith('/') ? path : `/${path}`;
377
427
  }
378
428
  async match(method, url, context) {
379
- const path = url.split('?')[0];
429
+ const path = this.normalizePath(url.split('?')[0] || '/');
380
430
  logger_1.default.debug(`Matching route: ${method} ${path}`);
381
431
  // Use method-specific cache for O(1) method lookup instead of O(n)
382
432
  const methodRoutes = this.routesByMethod.get(method);
@@ -432,7 +482,7 @@ class Router {
432
482
  // Match the request method and URL
433
483
  const match = await this.match(req.method || 'GET', req.url || '/', context);
434
484
  if (!match) {
435
- logger_1.default.warn(`No route found for ${req.method} ${req.url}`);
485
+ logger_1.default.debug(`No route found for ${req.method} ${req.url}`);
436
486
  res.status(404).json({ error: 'Not Found' });
437
487
  return;
438
488
  }
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.8",
3
+ "version": "0.2.0-beta.80",
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",
@@ -19,6 +19,8 @@
19
19
  },
20
20
  "dependencies": {
21
21
  "chalk": "^4.1.2",
22
+ "class-transformer": "^0.5.1",
23
+ "class-validator": "^0.14.1",
22
24
  "dotenv": "^16.5.0",
23
25
  "reflect-metadata": "^0.2.2",
24
26
  "winston": "^3.17.0"
@@ -55,11 +57,11 @@
55
57
  "decorators",
56
58
  "routing"
57
59
  ],
58
- "author": "Muhammad Arslan <marslan@hazeljs.com>",
59
- "license": "MIT",
60
+ "author": "Muhammad Arslan <muhammad.arslan@hazeljs.com>",
61
+ "license": "Apache-2.0",
60
62
  "bugs": {
61
63
  "url": "https://github.com/hazeljs/hazel-js/issues"
62
64
  },
63
65
  "homepage": "https://hazeljs.com",
64
- "gitHead": "b5cc0bf7d43739cad25220a611490b1d175acd62"
66
+ "gitHead": "083332d9cc6b634265382e7d1e0439236d30fedd"
65
67
  }