clear-router 2.5.4 → 2.5.5

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 (56) hide show
  1. package/README.md +4 -0
  2. package/dist/bindings-BL1d0dFY.d.cts +20 -0
  3. package/dist/bindings-DIanvIVd.mjs +211 -0
  4. package/dist/bindings-DqvZ9-YW.d.mts +20 -0
  5. package/dist/bindings-DvV2DXWi.cjs +253 -0
  6. package/dist/core/index.cjs +5 -2
  7. package/dist/core/index.d.cts +2 -2
  8. package/dist/core/index.d.mts +2 -2
  9. package/dist/core/index.mjs +3 -2
  10. package/dist/decorators/index.cjs +5 -0
  11. package/dist/decorators/index.d.cts +2 -0
  12. package/dist/decorators/index.d.mts +2 -0
  13. package/dist/decorators/index.mjs +3 -0
  14. package/dist/decorators/setup.cjs +15 -0
  15. package/dist/decorators/setup.d.cts +2 -0
  16. package/dist/decorators/setup.d.mts +2 -0
  17. package/dist/decorators/setup.mjs +13 -0
  18. package/dist/express/index.cjs +26 -9
  19. package/dist/express/index.d.cts +1 -1
  20. package/dist/express/index.d.mts +1 -1
  21. package/dist/express/index.mjs +26 -9
  22. package/dist/fastify/index.cjs +24 -8
  23. package/dist/fastify/index.d.cts +4 -2
  24. package/dist/fastify/index.d.mts +4 -2
  25. package/dist/fastify/index.mjs +24 -8
  26. package/dist/h3/index.cjs +26 -9
  27. package/dist/h3/index.d.cts +1 -1
  28. package/dist/h3/index.d.mts +1 -1
  29. package/dist/h3/index.mjs +26 -9
  30. package/dist/hono/index.cjs +28 -11
  31. package/dist/hono/index.d.cts +4 -2
  32. package/dist/hono/index.d.mts +4 -2
  33. package/dist/hono/index.mjs +28 -11
  34. package/dist/index.cjs +271 -27
  35. package/dist/index.d.cts +70 -18
  36. package/dist/index.d.mts +70 -18
  37. package/dist/index.mjs +270 -28
  38. package/dist/koa/index.cjs +24 -8
  39. package/dist/koa/index.d.cts +4 -2
  40. package/dist/koa/index.d.mts +4 -2
  41. package/dist/koa/index.mjs +24 -8
  42. package/dist/{responses-B-UirWFf.cjs → responses-JzXstGU5.cjs} +18 -1
  43. package/dist/{responses-CJaD0ugv.mjs → responses-_II3dOJ5.mjs} +19 -1
  44. package/dist/{router-CS_2XQ7I.d.cts → router-78fDk87B.d.cts} +68 -12
  45. package/dist/{router-BReOXz-F.mjs → router-BAWdklDQ.mjs} +130 -48
  46. package/dist/{router-_w2VzMQF.d.mts → router-BdfAh1me.d.mts} +67 -11
  47. package/dist/{router-CHg0pZUO.cjs → router-CCUF5FE7.cjs} +130 -48
  48. package/dist/types/basic.d.mts +7 -0
  49. package/dist/types/core/Request.d.mts +25 -0
  50. package/dist/types/core/Response.d.mts +21 -0
  51. package/dist/types/express.d.mts +5 -2
  52. package/dist/types/fastify.d.mts +5 -2
  53. package/dist/types/h3.d.mts +5 -2
  54. package/dist/types/hono.d.mts +5 -2
  55. package/dist/types/koa.d.mts +5 -2
  56. package/package.json +16 -2
@@ -1,26 +1,6 @@
1
+ const require_bindings = require('./bindings-DvV2DXWi.cjs');
1
2
  let node_async_hooks = require("node:async_hooks");
2
3
 
3
- //#region src/ClearRequest.ts
4
- var ClearRequest = class {
5
- /**
6
- * @param body - Parsed request body
7
- */
8
- body;
9
- /**
10
- * @param query - Parsed query parameters
11
- */
12
- query;
13
- /**
14
- * @param params - Parsed route parameters
15
- */
16
- params;
17
- route;
18
- constructor(init) {
19
- Object.assign(this, init);
20
- }
21
- };
22
-
23
- //#endregion
24
4
  //#region src/Route.ts
25
5
  var Route = class {
26
6
  ctx;
@@ -60,6 +40,40 @@ var CoreRouter = class {
60
40
  static routerStateNamespace = "clear-router:core";
61
41
  static stateStoreKey = Symbol.for("clear-router:router-state");
62
42
  static stateBoundKey = Symbol.for("clear-router:router-state-bound");
43
+ static defaultConfigKey = Symbol.for("clear-router:default-config");
44
+ static createBaseConfig() {
45
+ return {
46
+ methodOverride: {
47
+ enabled: true,
48
+ bodyKeys: ["_method"],
49
+ headerKeys: ["x-http-method"]
50
+ },
51
+ container: {
52
+ enabled: false,
53
+ autoDiscover: false
54
+ }
55
+ };
56
+ }
57
+ static mergeConfig(target, source) {
58
+ if (!source) return target;
59
+ if (source.methodOverride) target.methodOverride = {
60
+ ...target.methodOverride || {},
61
+ ...source.methodOverride
62
+ };
63
+ if (source.container) target.container = {
64
+ ...target.container || {},
65
+ ...source.container
66
+ };
67
+ return target;
68
+ }
69
+ static getDefaultConfig() {
70
+ const g = globalThis;
71
+ if (!g[this.defaultConfigKey]) g[this.defaultConfigKey] = this.createBaseConfig();
72
+ return {
73
+ methodOverride: { ...g[this.defaultConfigKey].methodOverride },
74
+ container: { ...g[this.defaultConfigKey].container }
75
+ };
76
+ }
63
77
  static resolveStateNamespace() {
64
78
  return String(this.routerStateNamespace || this.name || "clear-router:core");
65
79
  }
@@ -70,11 +84,7 @@ var CoreRouter = class {
70
84
  }
71
85
  static createDefaultState() {
72
86
  return {
73
- config: { methodOverride: {
74
- enabled: true,
75
- bodyKeys: ["_method"],
76
- headerKeys: ["x-http-method"]
77
- } },
87
+ config: this.getDefaultConfig(),
78
88
  groupContext: new node_async_hooks.AsyncLocalStorage(),
79
89
  routes: [],
80
90
  routesByPathMethod: {},
@@ -151,11 +161,24 @@ var CoreRouter = class {
151
161
  }
152
162
  };
153
163
  }
154
- static config = { methodOverride: {
155
- enabled: true,
156
- bodyKeys: ["_method"],
157
- headerKeys: ["x-http-method"]
158
- } };
164
+ static config = {
165
+ methodOverride: {
166
+ enabled: true,
167
+ bodyKeys: ["_method"],
168
+ headerKeys: ["x-http-method"]
169
+ },
170
+ container: {
171
+ enabled: false,
172
+ autoDiscover: false
173
+ }
174
+ };
175
+ static configureDefaults(options) {
176
+ const g = globalThis;
177
+ const defaults = this.mergeConfig(g[this.defaultConfigKey] || this.createBaseConfig(), options);
178
+ g[this.defaultConfigKey] = defaults;
179
+ const store = this.getStateStore();
180
+ for (const state of Object.values(store)) state.config = this.mergeConfig(state.config || this.createBaseConfig(), options);
181
+ }
159
182
  static groupContext = new node_async_hooks.AsyncLocalStorage();
160
183
  static routes = [];
161
184
  static routesByPathMethod = {};
@@ -197,11 +220,12 @@ var CoreRouter = class {
197
220
  */
198
221
  static configure(options) {
199
222
  this.ensureState();
200
- if (!this.config.methodOverride) this.config.methodOverride = {
201
- enabled: true,
202
- bodyKeys: ["_method"],
203
- headerKeys: ["x-http-method"]
204
- };
223
+ this.config = this.mergeConfig(this.getDefaultConfig(), this.config);
224
+ const container = options?.container;
225
+ if (container) {
226
+ if (typeof container.enabled === "boolean") this.config.container.enabled = container.enabled;
227
+ if (typeof container.autoDiscover === "boolean") this.config.container.autoDiscover = container.autoDiscover;
228
+ }
205
229
  const override = options?.methodOverride;
206
230
  if (!override) return;
207
231
  if (typeof override.enabled === "boolean") this.config.methodOverride.enabled = override.enabled;
@@ -438,36 +462,94 @@ var CoreRouter = class {
438
462
  static resolveHandler(route) {
439
463
  let handlerFunction;
440
464
  let instance = null;
441
- if (typeof route.handler === "function") handlerFunction = route.handler.bind(route);
442
- else if (Array.isArray(route.handler) && route.handler.length === 2) {
465
+ let bindingTarget;
466
+ let bindingMethod;
467
+ let bindingHandler;
468
+ let bindingMetadata;
469
+ if (typeof route.handler === "function") {
470
+ handlerFunction = route.handler.bind(route);
471
+ bindingTarget = route.handler;
472
+ bindingHandler = route.handler;
473
+ } else if (Array.isArray(route.handler) && route.handler.length === 2) {
443
474
  const [ControllerType, method] = route.handler;
444
475
  if (["function", "object"].includes(typeof ControllerType) && typeof ControllerType[method] === "function") {
445
476
  instance = ControllerType;
446
477
  handlerFunction = ControllerType[method].bind(ControllerType);
478
+ bindingTarget = ControllerType;
479
+ bindingMethod = method;
480
+ bindingHandler = ControllerType[method];
481
+ bindingMetadata = ControllerType[Symbol.metadata];
447
482
  } else if (typeof ControllerType === "function") {
448
483
  instance = new ControllerType();
449
- if (typeof instance[method] === "function") handlerFunction = instance[method].bind(instance);
450
- else throw new Error(`Method "${method}" not found in controller instance "${ControllerType.name}"`);
484
+ if (typeof instance[method] === "function") {
485
+ handlerFunction = instance[method].bind(instance);
486
+ bindingTarget = ControllerType.prototype;
487
+ bindingMethod = method;
488
+ bindingHandler = instance[method];
489
+ bindingMetadata = ControllerType[Symbol.metadata];
490
+ } else throw new Error(`Method "${method}" not found in controller instance "${ControllerType.name}"`);
451
491
  } else throw new Error(`Invalid controller type for route: ${route.path}`);
452
492
  } else throw new Error(`Invalid handler format for route: ${route.path}`);
453
493
  return {
454
494
  handlerFunction,
455
- instance
495
+ instance,
496
+ bindingTarget,
497
+ bindingMethod,
498
+ bindingHandler,
499
+ bindingMetadata
456
500
  };
457
501
  }
502
+ static async callHandler(handlerFunction, ctx, bindingTarget, bindingMethod, bindingHandler, bindingMetadata) {
503
+ if (!this.config.container?.enabled) return handlerFunction(ctx, ctx.clearRequest);
504
+ const metadata = require_bindings.getBindingMetadataFromTargets([
505
+ {
506
+ target: bindingTarget,
507
+ propertyKey: bindingMethod
508
+ },
509
+ { target: bindingHandler },
510
+ {
511
+ target: bindingTarget,
512
+ propertyKey: "__class__"
513
+ }
514
+ ]) ?? require_bindings.getStandardMetadata(bindingMetadata, bindingMethod) ?? require_bindings.getStandardMetadata(bindingMetadata, "__class__");
515
+ if (!metadata) return handlerFunction(ctx, ctx.clearRequest);
516
+ const designTokens = [...bindingTarget ? require_bindings.getDesignParamTypes(bindingTarget, bindingMethod) : [], ...bindingHandler ? require_bindings.getDesignParamTypes(bindingHandler) : []];
517
+ const tokens = metadata.tokens?.length ? metadata.tokens : designTokens;
518
+ if (!tokens.length) return handlerFunction(ctx, ctx.clearRequest);
519
+ const args = [];
520
+ for (const token of tokens) {
521
+ const resolved = await require_bindings.Container.resolve(token, ctx, Boolean(this.config.container?.autoDiscover));
522
+ if (typeof resolved === "undefined") return handlerFunction(ctx, ctx.clearRequest);
523
+ args.push(resolved);
524
+ }
525
+ return handlerFunction(...args);
526
+ }
458
527
  static bindRequestToInstance(ctx, instance, route, payload) {
528
+ const clearRequest = ctx.clearRequest instanceof require_bindings.Request ? ctx.clearRequest : new require_bindings.Request({
529
+ ctx,
530
+ route,
531
+ body: payload.body,
532
+ query: payload.query,
533
+ params: payload.params,
534
+ method: String(payload.method || ctx.req?.method || ctx.method || "GET").toUpperCase(),
535
+ path: String(ctx.path || ctx.req?.path || ctx.req?.url || route.path),
536
+ url: String(ctx.url || ctx.req?.url || ctx.req?.originalUrl || route.path),
537
+ headers: ctx.req?.headers || ctx.headers || {},
538
+ original: ctx.req || ctx.request || ctx
539
+ });
540
+ clearRequest.ctx = ctx;
541
+ clearRequest.route = route;
542
+ clearRequest.body = payload.body;
543
+ clearRequest.query = payload.query;
544
+ clearRequest.params = payload.params;
545
+ ctx.clearRequest = clearRequest;
546
+ if (!(ctx.clearResponse instanceof require_bindings.Response)) ctx.clearResponse = new require_bindings.Response();
459
547
  if (!instance) return;
460
548
  instance.ctx = ctx;
461
549
  instance.body = payload.body;
462
550
  instance.query = payload.query;
463
551
  instance.params = payload.params;
464
- instance.clearRequest = new ClearRequest({
465
- ctx,
466
- route,
467
- body: instance.body,
468
- query: instance.query,
469
- params: instance.params
470
- });
552
+ instance.clearRequest = clearRequest;
471
553
  }
472
554
  };
473
555
 
@@ -26,6 +26,13 @@ interface RouterConfig {
26
26
  bodyKeys?: string[] | string; /** Keys in the request headers to check for method override */
27
27
  headerKeys?: string[] | string;
28
28
  };
29
+ /**
30
+ * Optional method binding / container resolution support. Disabled by default.
31
+ */
32
+ container?: {
33
+ /** Whether decorated handler parameter binding is enabled */enabled?: boolean; /** Whether unknown constructor tokens should be instantiated automatically */
34
+ autoDiscover?: boolean;
35
+ };
29
36
  }
30
37
  //#endregion
31
38
  export { ApiResourceMiddleware, ControllerAction, ControllerHandler, HttpMethod, RequestData, RouterConfig };
@@ -0,0 +1,25 @@
1
+ import { HttpMethod, RequestData } from "../../types/basic.mjs";
2
+ import { Route } from "../Route.mjs";
3
+ import { ClearRequest } from "../ClearRequest.mjs";
4
+
5
+ //#region src/core/Request.d.ts
6
+ declare class Request<X = any, M = any> extends ClearRequest<X, M> {
7
+ original?: any;
8
+ method: string;
9
+ path: string;
10
+ url: string;
11
+ headers: Headers | Record<string, any>;
12
+ constructor(init?: Partial<Request<X, M>> & {
13
+ body?: RequestData;
14
+ query?: RequestData;
15
+ params?: RequestData;
16
+ route?: Route<X, M>;
17
+ });
18
+ getBody(): RequestData;
19
+ header(name: string): string;
20
+ param(name: string): any;
21
+ input(name: string): any;
22
+ is(method: HttpMethod | string): boolean;
23
+ }
24
+ //#endregion
25
+ export { Request };
@@ -0,0 +1,21 @@
1
+ //#region src/core/Response.d.ts
2
+ declare class Response {
3
+ body: any;
4
+ headers: Headers;
5
+ sent: boolean;
6
+ statusCode: number;
7
+ constructor(init?: Partial<Response>);
8
+ status(code: number): this;
9
+ code(code: number): this;
10
+ setHeader(name: string, value: string): this;
11
+ header(name: string, value: string): this;
12
+ set(name: string, value: string): this;
13
+ type(contentType: string): this;
14
+ send(body?: any): this;
15
+ json(body: any): this;
16
+ html(body: string): this;
17
+ text(body: string): this;
18
+ noContent(): this;
19
+ }
20
+ //#endregion
21
+ export { Response };
@@ -1,5 +1,6 @@
1
1
  import { ControllerHandler } from "./basic.mjs";
2
- import { ClearRequest } from "./ClearRequest.mjs";
2
+ import { Response as Response$1 } from "./core/Response.mjs";
3
+ import { Request as Request$1 } from "./core/Request.mjs";
3
4
  import { NextFunction, Request, Response } from "express";
4
5
 
5
6
  //#region types/express.d.ts
@@ -13,6 +14,8 @@ interface HttpContext {
13
14
  req: RequestWithGetBody;
14
15
  res: Response;
15
16
  next: NextFunction;
17
+ clearRequest: Request$1;
18
+ clearResponse: Response$1;
16
19
  }
17
20
  /**
18
21
  * Route handler function type
@@ -27,7 +30,7 @@ ctx: HttpContext,
27
30
  * ClearRequest instance
28
31
  */
29
32
 
30
- req: ClearRequest) => any | Promise<any>;
33
+ req: Request$1) => any | Promise<any>;
31
34
  /**
32
35
  * Handler can be either a function or controller reference
33
36
  */
@@ -1,5 +1,6 @@
1
1
  import { ControllerHandler } from "./basic.mjs";
2
- import { ClearRequest } from "./ClearRequest.mjs";
2
+ import { Response } from "./core/Response.mjs";
3
+ import { Request } from "./core/Request.mjs";
3
4
  import { FastifyInstance, FastifyReply, FastifyRequest } from "fastify";
4
5
 
5
6
  //#region types/fastify.d.ts
@@ -9,8 +10,10 @@ interface RequestWithGetBody extends FastifyRequest {
9
10
  interface HttpContext {
10
11
  req: RequestWithGetBody;
11
12
  reply: FastifyReply;
13
+ clearRequest: Request;
14
+ clearResponse: Response;
12
15
  }
13
- type RouteHandler = (ctx: HttpContext, req: ClearRequest) => any | Promise<any>;
16
+ type RouteHandler = (ctx: HttpContext, req: Request) => any | Promise<any>;
14
17
  type Handler = RouteHandler | ControllerHandler;
15
18
  type NextFunction = (err?: Error) => void;
16
19
  type Middleware = (req: RequestWithGetBody, reply: FastifyReply, next: NextFunction) => any | Promise<any>;
@@ -1,5 +1,6 @@
1
1
  import { ControllerHandler } from "./basic.mjs";
2
- import { ClearRequest } from "./ClearRequest.mjs";
2
+ import { Response as Response$1 } from "./core/Response.mjs";
3
+ import { Request } from "./core/Request.mjs";
3
4
  import { EventHandlerRequest, H3, H3Event, Middleware, TypedServerRequest } from "h3";
4
5
 
5
6
  //#region types/h3.d.ts
@@ -16,6 +17,8 @@ type RequestlessH3Event = Omit<H3Event, 'req'>;
16
17
  */
17
18
  interface HttpContext extends RequestlessH3Event {
18
19
  req: HttpRequest;
20
+ clearRequest: Request;
21
+ clearResponse: Response$1;
19
22
  }
20
23
  /**
21
24
  * Route handler function type
@@ -30,7 +33,7 @@ ctx: HttpContext,
30
33
  * ClearRequest instance
31
34
  */
32
35
 
33
- req: ClearRequest) => any | Promise<any>;
36
+ req: Request) => any | Promise<any>;
34
37
  /**
35
38
  * Handler can be either a function or controller reference
36
39
  */
@@ -1,5 +1,6 @@
1
1
  import { ControllerHandler } from "./basic.mjs";
2
- import { ClearRequest } from "./ClearRequest.mjs";
2
+ import { Response } from "./core/Response.mjs";
3
+ import { Request } from "./core/Request.mjs";
3
4
  import { Context, HonoRequest, MiddlewareHandler } from "hono";
4
5
 
5
6
  //#region types/hono.d.ts
@@ -8,8 +9,10 @@ type RequestWithGetBody = HonoRequest & {
8
9
  };
9
10
  type HttpContext = Context & {
10
11
  req: RequestWithGetBody;
12
+ clearRequest: Request;
13
+ clearResponse: Response;
11
14
  };
12
- type RouteHandler = (ctx: HttpContext, req: ClearRequest) => any | Promise<any>;
15
+ type RouteHandler = (ctx: HttpContext, req: Request) => any | Promise<any>;
13
16
  type Handler = RouteHandler | ControllerHandler;
14
17
  type NextFunction = () => Promise<void>;
15
18
  type Middleware = MiddlewareHandler;
@@ -1,5 +1,6 @@
1
1
  import { ControllerHandler } from "./basic.mjs";
2
- import { ClearRequest } from "./ClearRequest.mjs";
2
+ import { Response } from "./core/Response.mjs";
3
+ import { Request } from "./core/Request.mjs";
3
4
  import Koa from "koa";
4
5
  import Router from "@koa/router";
5
6
 
@@ -10,10 +11,12 @@ interface RequestWithGetBody extends Koa.Request {
10
11
  }
11
12
  interface HttpContext extends Koa.Context {
12
13
  request: RequestWithGetBody;
14
+ clearRequest: Request;
15
+ clearResponse: Response;
13
16
  params: Record<string, any>;
14
17
  query: Record<string, any>;
15
18
  }
16
- type RouteHandler = (ctx: HttpContext, req: ClearRequest) => any | Promise<any>;
19
+ type RouteHandler = (ctx: HttpContext, req: Request) => any | Promise<any>;
17
20
  type Handler = RouteHandler | ControllerHandler;
18
21
  type NextFunction = Koa.Next;
19
22
  type Middleware = Koa.Middleware<any, any>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clear-router",
3
- "version": "2.5.4",
3
+ "version": "2.5.5",
4
4
  "description": "Laravel-style routing for Node.js with support for Express, H3, Fastify, Hono, and Koa, including CommonJS, ESM, and TypeScript support.",
5
5
  "keywords": [
6
6
  "h3",
@@ -40,6 +40,14 @@
40
40
  "import": "./dist/core/index.mjs",
41
41
  "require": "./dist/core/index.cjs"
42
42
  },
43
+ "./decorators": {
44
+ "import": "./dist/decorators/index.mjs",
45
+ "require": "./dist/decorators/index.cjs"
46
+ },
47
+ "./decorators/setup": {
48
+ "import": "./dist/decorators/setup.mjs",
49
+ "require": "./dist/decorators/setup.cjs"
50
+ },
43
51
  "./express": {
44
52
  "import": "./dist/express/index.mjs",
45
53
  "require": "./dist/express/index.cjs"
@@ -79,7 +87,8 @@
79
87
  "fastify": "^5.0.0",
80
88
  "h3": "^2.0.1-rc.16",
81
89
  "hono": "^4.0.0",
82
- "koa": "^3.0.0"
90
+ "koa": "^3.0.0",
91
+ "reflect-metadata": "^0.2.2"
83
92
  },
84
93
  "peerDependenciesMeta": {
85
94
  "@koa/router": {
@@ -99,6 +108,9 @@
99
108
  },
100
109
  "koa": {
101
110
  "optional": true
111
+ },
112
+ "reflect-metadata": {
113
+ "optional": true
102
114
  }
103
115
  },
104
116
  "devDependencies": {
@@ -115,10 +127,12 @@
115
127
  "hono": "^4.12.8",
116
128
  "koa": "^3.2.0",
117
129
  "parasito": "^0.1.6",
130
+ "reflect-metadata": "^0.2.2",
118
131
  "tsdown": "^0.20.3",
119
132
  "tsx": "^4.21.0",
120
133
  "typescript": "^5.3.3",
121
134
  "typescript-eslint": "^8.56.1",
135
+ "unrun": "0.2.27",
122
136
  "vite-tsconfig-paths": "^6.1.1",
123
137
  "vitepress": "^1.6.4",
124
138
  "vitest": "^4.0.18"