clear-router 2.0.8 → 2.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
- # Toneflix Clear Router
1
+ # Clear Router
2
2
 
3
- Laravel-style routing system for Express.js in JavaScript. Clean route definitions, middleware support, and controller bindings with full TypeScript support.
3
+ Laravel-style routing system for H3 and Express.js, with clean route definitions, middleware support, controller bindings and full TypeScript support.
4
4
 
5
5
  ## Installation
6
6
 
@@ -19,7 +19,7 @@ pnpm add clear-router h3
19
19
 
20
20
  # OR
21
21
 
22
- pnpm add clear-router hexpress3
22
+ pnpm add clear-router express
23
23
  ```
24
24
 
25
25
  OR
@@ -39,10 +39,13 @@ yarn add clear-router express
39
39
  - Middleware stack: per-route and group-level
40
40
  - Controller-method pair as route handler
41
41
  - Supports HttpContext style handlers: { req, res, next }
42
+ - Function handlers always receive context as first argument
43
+ - Controller handlers receive hydrated `this.body`, `this.query`, `this.params`, and `this.clearRequest`
44
+ - `clearRequest` is passed as second handler argument for controller handlers
42
45
  - Auto-binds controller methods
43
46
  - Full CommonJS, ESM, and TypeScript support
44
- - Error handling delegated to Express
45
- - Route inspection with allRoutes method
47
+ - Error handling delegated to Express | H3
48
+ - Route inspection with the `allRoutes` method
46
49
  - Fully Express-compatible
47
50
  - Fully H3-compatible
48
51
 
@@ -60,20 +63,9 @@ See the [H3 documentation](./docs/H3.md) for details.
60
63
 
61
64
  See [API.md](./docs/API.md) for complete API documentation.
62
65
 
63
- ## Error Handling
64
-
65
- All errors during route execution are automatically passed to Express error handling middleware using `next(error)`. You can define your error handler:
66
-
67
- ```javascript
68
- app.use((err, req, res, next) => {
69
- console.error(err);
70
- res.status(500).json({ error: err.message });
71
- });
72
- ```
73
-
74
66
  ## Middleware Execution Order
75
67
 
76
- ```
68
+ ```txt
77
69
  [ Global Middleware ] → [ Group Middleware ] → [ Route Middleware ]
78
70
  ```
79
71
 
@@ -81,6 +73,8 @@ app.use((err, req, res, next) => {
81
73
 
82
74
  - If function: executed directly
83
75
  - If [Controller, 'method']: auto-instantiated (if needed), method is called
76
+ - First handler arg is always context (`{ req, res, next }` for Express, H3 event for H3)
77
+ - Second handler arg is `clearRequest` for controller handlers
84
78
 
85
79
  ## Testing
86
80
 
@@ -91,7 +85,7 @@ npm run test:esm # Test ESM
91
85
  npm run test:ts # Test TypeScript
92
86
  ```
93
87
 
94
- See [TESTING.md](./docs/TESTING.md) for detailed testing guide.
88
+ See [TESTING.md](./docs/TESTING.md) for a detailed testing guide.
95
89
 
96
90
  ## Examples
97
91
 
@@ -101,7 +95,7 @@ npm run example:esm # ESM example
101
95
  npm run example:ts # TypeScript example
102
96
  ```
103
97
 
104
- Check `example/` directory for full working demos.
98
+ Check the `example/` directory for full working demos.
105
99
 
106
100
  ## Changelog
107
101
 
@@ -0,0 +1,54 @@
1
+
2
+ //#region src/ClearRequest.ts
3
+ var ClearRequest = class {
4
+ /**
5
+ * @param body - Parsed request body
6
+ */
7
+ body;
8
+ /**
9
+ * @param query - Parsed query parameters
10
+ */
11
+ query;
12
+ /**
13
+ * @param params - Parsed route parameters
14
+ */
15
+ params;
16
+ route;
17
+ constructor(init) {
18
+ Object.assign(this, init);
19
+ }
20
+ };
21
+
22
+ //#endregion
23
+ //#region src/Route.ts
24
+ var Route = class {
25
+ ctx;
26
+ body = {};
27
+ query = {};
28
+ params = {};
29
+ clearRequest;
30
+ methods;
31
+ path;
32
+ handler;
33
+ middlewares;
34
+ constructor(methods, path, handler, middlewares = []) {
35
+ this.methods = methods;
36
+ this.path = path;
37
+ this.handler = handler;
38
+ this.middlewares = middlewares;
39
+ }
40
+ };
41
+
42
+ //#endregion
43
+ Object.defineProperty(exports, 'ClearRequest', {
44
+ enumerable: true,
45
+ get: function () {
46
+ return ClearRequest;
47
+ }
48
+ });
49
+ Object.defineProperty(exports, 'Route', {
50
+ enumerable: true,
51
+ get: function () {
52
+ return Route;
53
+ }
54
+ });
@@ -0,0 +1,42 @@
1
+ //#region src/ClearRequest.ts
2
+ var ClearRequest = class {
3
+ /**
4
+ * @param body - Parsed request body
5
+ */
6
+ body;
7
+ /**
8
+ * @param query - Parsed query parameters
9
+ */
10
+ query;
11
+ /**
12
+ * @param params - Parsed route parameters
13
+ */
14
+ params;
15
+ route;
16
+ constructor(init) {
17
+ Object.assign(this, init);
18
+ }
19
+ };
20
+
21
+ //#endregion
22
+ //#region src/Route.ts
23
+ var Route = class {
24
+ ctx;
25
+ body = {};
26
+ query = {};
27
+ params = {};
28
+ clearRequest;
29
+ methods;
30
+ path;
31
+ handler;
32
+ middlewares;
33
+ constructor(methods, path, handler, middlewares = []) {
34
+ this.methods = methods;
35
+ this.path = path;
36
+ this.handler = handler;
37
+ this.middlewares = middlewares;
38
+ }
39
+ };
40
+
41
+ //#endregion
42
+ export { ClearRequest as n, Route as t };
@@ -1,4 +1,5 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
+ const require_Route = require('../Route-97p-tEbS.cjs');
2
3
 
3
4
  //#region src/express/router.ts
4
5
  /**
@@ -8,7 +9,7 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
8
9
  * @author 3m1n3nc3
9
10
  * @repository https://github.com/toneflix/clear-router
10
11
  */
11
- var Router = class {
12
+ var Router = class Router {
12
13
  /**
13
14
  * All registered routes
14
15
  */
@@ -43,16 +44,11 @@ var Router = class {
43
44
  static add(methods, path, handler, middlewares) {
44
45
  const methodArray = Array.isArray(methods) ? methods : [methods];
45
46
  const fullPath = this.normalizePath(`${this.prefix}/${path}`);
46
- this.routes.push({
47
- methods: methodArray,
48
- path: fullPath,
49
- handler,
50
- middlewares: [
51
- ...this.globalMiddlewares,
52
- ...this.groupMiddlewares,
53
- ...middlewares || []
54
- ]
55
- });
47
+ this.routes.push(new require_Route.Route(methodArray, fullPath, handler, [
48
+ ...this.globalMiddlewares,
49
+ ...this.groupMiddlewares,
50
+ ...middlewares || []
51
+ ]));
56
52
  }
57
53
  /**
58
54
  * Register RESTful API resource routes for a controller with optional action filtering
@@ -200,13 +196,20 @@ var Router = class {
200
196
  static async apply(router) {
201
197
  for (const route of this.routes) {
202
198
  let handlerFunction = null;
199
+ let instance = null;
203
200
  try {
204
- if (typeof route.handler === "function") handlerFunction = route.handler;
201
+ if (typeof route.handler === "function")
202
+ /**
203
+ * Since we do not have a controller instance, we will call the handler function directly and the route instance will be the this argument. This allows for both controller-based and function-based handlers to work seamlessly.
204
+ */
205
+ handlerFunction = route.handler.bind(route);
205
206
  else if (Array.isArray(route.handler) && route.handler.length === 2) {
206
207
  const [Controller, method] = route.handler;
207
- if (typeof Controller === "function" && typeof Controller[method] === "function") handlerFunction = Controller[method].bind(Controller);
208
- else if (typeof Controller === "function") {
209
- const instance = new Controller();
208
+ if (["function", "object"].includes(typeof Controller) && typeof Controller[method] === "function") {
209
+ instance = Controller;
210
+ handlerFunction = Controller[method].bind(Controller);
211
+ } else if (typeof Controller === "function") {
212
+ instance = new Controller();
210
213
  if (typeof instance[method] === "function") handlerFunction = instance[method].bind(instance);
211
214
  else throw new Error(`Method "${method}" not found in controller instance "${Controller.name}"`);
212
215
  } else throw new Error(`Invalid controller type for route: ${route.path}`);
@@ -227,16 +230,19 @@ var Router = class {
227
230
  "head"
228
231
  ].includes(method)) {
229
232
  const error = /* @__PURE__ */ new Error(`Invalid HTTP method: ${method} for route: ${route.path}`);
230
- console.error(`[ROUTES]`, error.message);
233
+ console.error("[ROUTES]", error.message);
231
234
  throw error;
232
235
  }
233
236
  router[method](route.path, ...route.middlewares || [], async (req, res, next) => {
234
237
  try {
235
- const result = handlerFunction({
238
+ const ctx = {
236
239
  req,
237
240
  res,
238
241
  next
239
- });
242
+ };
243
+ const inst = instance ?? route;
244
+ await Router.bindRequestToInstance(ctx, inst, route);
245
+ const result = handlerFunction(ctx, inst.clearRequest);
240
246
  await Promise.resolve(result);
241
247
  } catch (error) {
242
248
  next(error);
@@ -245,6 +251,20 @@ var Router = class {
245
251
  }
246
252
  }
247
253
  }
254
+ static async bindRequestToInstance(ctx, instance, route) {
255
+ if (!instance) return;
256
+ instance.ctx = ctx;
257
+ instance.body = ctx.req.body;
258
+ instance.query = ctx.req.query;
259
+ instance.params = ctx.req.params;
260
+ instance.clearRequest = new require_Route.ClearRequest({
261
+ ctx,
262
+ route,
263
+ body: instance.body,
264
+ query: instance.query,
265
+ params: instance.params
266
+ });
267
+ }
248
268
  };
249
269
 
250
270
  //#endregion
@@ -1,28 +1,6 @@
1
- import { i as RouteInfo, n as ControllerHandler, r as HttpMethod, t as ControllerAction } from "../basic-BCxhZ1Wd.cjs";
2
- import { NextFunction, Request, Response, Router as Router$1 } from "express";
1
+ import { d as RouteInfo, i as Route, l as ControllerAction, n as HttpContext, r as Middleware, t as Handler, u as HttpMethod } from "../express-sjYra4oE.cjs";
2
+ import { Router as Router$1 } from "express";
3
3
 
4
- //#region types/express.d.ts
5
- /**
6
- * HTTP context passed to route handlers
7
- */
8
- interface HttpContext {
9
- req: Request;
10
- res: Response;
11
- next: NextFunction;
12
- }
13
- /**
14
- * Route handler function type
15
- */
16
- type RouteHandler = (ctx: HttpContext) => any | Promise<any>;
17
- /**
18
- * Handler can be either a function or controller reference
19
- */
20
- type Handler = RouteHandler | ControllerHandler;
21
- /**
22
- * Middleware function type
23
- */
24
- type Middleware = (req: Request, res: Response, next: NextFunction) => any | Promise<any>;
25
- //#endregion
26
4
  //#region src/express/router.d.ts
27
5
  /**
28
6
  * @class clear-router
@@ -35,12 +13,7 @@ declare class Router {
35
13
  /**
36
14
  * All registered routes
37
15
  */
38
- static routes: Array<{
39
- methods: HttpMethod[];
40
- path: string;
41
- handler: Handler;
42
- middlewares: Middleware[];
43
- }>;
16
+ static routes: Array<Route<HttpContext, Middleware>>;
44
17
  /**
45
18
  * Current route prefix
46
19
  */
@@ -154,6 +127,7 @@ declare class Router {
154
127
  */
155
128
  static apply(router: Router$1): void;
156
129
  static apply(router: Router$1): Promise<void>;
130
+ private static bindRequestToInstance;
157
131
  }
158
132
  //#endregion
159
133
  export { Router };
@@ -1,28 +1,6 @@
1
- import { i as RouteInfo, n as ControllerHandler, r as HttpMethod, t as ControllerAction } from "../basic-POMY6a76.mjs";
2
- import { NextFunction, Request, Response, Router as Router$1 } from "express";
1
+ import { d as RouteInfo, i as Route, l as ControllerAction, n as HttpContext, r as Middleware, t as Handler, u as HttpMethod } from "../express-WiUTZlHA.mjs";
2
+ import { Router as Router$1 } from "express";
3
3
 
4
- //#region types/express.d.ts
5
- /**
6
- * HTTP context passed to route handlers
7
- */
8
- interface HttpContext {
9
- req: Request;
10
- res: Response;
11
- next: NextFunction;
12
- }
13
- /**
14
- * Route handler function type
15
- */
16
- type RouteHandler = (ctx: HttpContext) => any | Promise<any>;
17
- /**
18
- * Handler can be either a function or controller reference
19
- */
20
- type Handler = RouteHandler | ControllerHandler;
21
- /**
22
- * Middleware function type
23
- */
24
- type Middleware = (req: Request, res: Response, next: NextFunction) => any | Promise<any>;
25
- //#endregion
26
4
  //#region src/express/router.d.ts
27
5
  /**
28
6
  * @class clear-router
@@ -35,12 +13,7 @@ declare class Router {
35
13
  /**
36
14
  * All registered routes
37
15
  */
38
- static routes: Array<{
39
- methods: HttpMethod[];
40
- path: string;
41
- handler: Handler;
42
- middlewares: Middleware[];
43
- }>;
16
+ static routes: Array<Route<HttpContext, Middleware>>;
44
17
  /**
45
18
  * Current route prefix
46
19
  */
@@ -154,6 +127,7 @@ declare class Router {
154
127
  */
155
128
  static apply(router: Router$1): void;
156
129
  static apply(router: Router$1): Promise<void>;
130
+ private static bindRequestToInstance;
157
131
  }
158
132
  //#endregion
159
133
  export { Router };
@@ -1,3 +1,5 @@
1
+ import { n as ClearRequest, t as Route } from "../Route-BtmoiYq5.mjs";
2
+
1
3
  //#region src/express/router.ts
2
4
  /**
3
5
  * @class clear-router
@@ -6,7 +8,7 @@
6
8
  * @author 3m1n3nc3
7
9
  * @repository https://github.com/toneflix/clear-router
8
10
  */
9
- var Router = class {
11
+ var Router = class Router {
10
12
  /**
11
13
  * All registered routes
12
14
  */
@@ -41,16 +43,11 @@ var Router = class {
41
43
  static add(methods, path, handler, middlewares) {
42
44
  const methodArray = Array.isArray(methods) ? methods : [methods];
43
45
  const fullPath = this.normalizePath(`${this.prefix}/${path}`);
44
- this.routes.push({
45
- methods: methodArray,
46
- path: fullPath,
47
- handler,
48
- middlewares: [
49
- ...this.globalMiddlewares,
50
- ...this.groupMiddlewares,
51
- ...middlewares || []
52
- ]
53
- });
46
+ this.routes.push(new Route(methodArray, fullPath, handler, [
47
+ ...this.globalMiddlewares,
48
+ ...this.groupMiddlewares,
49
+ ...middlewares || []
50
+ ]));
54
51
  }
55
52
  /**
56
53
  * Register RESTful API resource routes for a controller with optional action filtering
@@ -198,13 +195,20 @@ var Router = class {
198
195
  static async apply(router) {
199
196
  for (const route of this.routes) {
200
197
  let handlerFunction = null;
198
+ let instance = null;
201
199
  try {
202
- if (typeof route.handler === "function") handlerFunction = route.handler;
200
+ if (typeof route.handler === "function")
201
+ /**
202
+ * Since we do not have a controller instance, we will call the handler function directly and the route instance will be the this argument. This allows for both controller-based and function-based handlers to work seamlessly.
203
+ */
204
+ handlerFunction = route.handler.bind(route);
203
205
  else if (Array.isArray(route.handler) && route.handler.length === 2) {
204
206
  const [Controller, method] = route.handler;
205
- if (typeof Controller === "function" && typeof Controller[method] === "function") handlerFunction = Controller[method].bind(Controller);
206
- else if (typeof Controller === "function") {
207
- const instance = new Controller();
207
+ if (["function", "object"].includes(typeof Controller) && typeof Controller[method] === "function") {
208
+ instance = Controller;
209
+ handlerFunction = Controller[method].bind(Controller);
210
+ } else if (typeof Controller === "function") {
211
+ instance = new Controller();
208
212
  if (typeof instance[method] === "function") handlerFunction = instance[method].bind(instance);
209
213
  else throw new Error(`Method "${method}" not found in controller instance "${Controller.name}"`);
210
214
  } else throw new Error(`Invalid controller type for route: ${route.path}`);
@@ -225,16 +229,19 @@ var Router = class {
225
229
  "head"
226
230
  ].includes(method)) {
227
231
  const error = /* @__PURE__ */ new Error(`Invalid HTTP method: ${method} for route: ${route.path}`);
228
- console.error(`[ROUTES]`, error.message);
232
+ console.error("[ROUTES]", error.message);
229
233
  throw error;
230
234
  }
231
235
  router[method](route.path, ...route.middlewares || [], async (req, res, next) => {
232
236
  try {
233
- const result = handlerFunction({
237
+ const ctx = {
234
238
  req,
235
239
  res,
236
240
  next
237
- });
241
+ };
242
+ const inst = instance ?? route;
243
+ await Router.bindRequestToInstance(ctx, inst, route);
244
+ const result = handlerFunction(ctx, inst.clearRequest);
238
245
  await Promise.resolve(result);
239
246
  } catch (error) {
240
247
  next(error);
@@ -243,6 +250,20 @@ var Router = class {
243
250
  }
244
251
  }
245
252
  }
253
+ static async bindRequestToInstance(ctx, instance, route) {
254
+ if (!instance) return;
255
+ instance.ctx = ctx;
256
+ instance.body = ctx.req.body;
257
+ instance.query = ctx.req.query;
258
+ instance.params = ctx.req.params;
259
+ instance.clearRequest = new ClearRequest({
260
+ ctx,
261
+ route,
262
+ body: instance.body,
263
+ query: instance.query,
264
+ params: instance.params
265
+ });
266
+ }
246
267
  };
247
268
 
248
269
  //#endregion
@@ -0,0 +1,123 @@
1
+ import { H3, H3Event, Middleware as Middleware$1, TypedServerRequest } from "h3";
2
+ import { NextFunction, Request, Response as Response$1 } from "express";
3
+
4
+ //#region types/basic.d.ts
5
+ /**
6
+ * Controller method reference
7
+ */
8
+ type ControllerHandler = [any, string];
9
+ /**
10
+ * HTTP methods supported by the router
11
+ */
12
+ type HttpMethod = 'get' | 'post' | 'put' | 'delete' | 'patch' | 'options' | 'head';
13
+ /**
14
+ * Route information object
15
+ */
16
+ interface RouteInfo {
17
+ methods: HttpMethod[];
18
+ path: string;
19
+ middlewareCount: number;
20
+ handlerType: 'function' | 'controller';
21
+ }
22
+ /**
23
+ * Common controller action names
24
+ */
25
+ type ControllerAction = 'index' | 'show' | 'create' | 'update' | 'destroy';
26
+ /**
27
+ * Generic Object type for request data
28
+ */
29
+ type RequestData = Record<string, any>;
30
+ //#endregion
31
+ //#region types/h3.d.ts
32
+ type H3App = Omit<H3['fetch'], 'fetch'> & {
33
+ fetch: (request: TypedServerRequest) => Promise<Response>;
34
+ };
35
+ /**
36
+ * HTTP context passed to route handlers
37
+ */
38
+ type HttpContext$1 = H3Event & {};
39
+ /**
40
+ * Route handler function type
41
+ */
42
+ type RouteHandler$1 = (
43
+ /**
44
+ * H3 event context
45
+ */
46
+
47
+ ctx: HttpContext$1,
48
+ /**
49
+ * ClearRequest instance
50
+ */
51
+
52
+ req: ClearRequest) => any | Promise<any>;
53
+ /**
54
+ * Handler can be either a function or controller reference
55
+ */
56
+ type Handler$1 = RouteHandler$1 | ControllerHandler;
57
+ //#endregion
58
+ //#region src/Route.d.ts
59
+ declare class Route<X = any, M = Middleware$1 | Middleware> {
60
+ ctx: X;
61
+ body: RequestData;
62
+ query: RequestData;
63
+ params: RequestData;
64
+ clearRequest: ClearRequest;
65
+ methods: HttpMethod[];
66
+ path: string;
67
+ handler: Handler$1;
68
+ middlewares: M[];
69
+ constructor(methods: HttpMethod[], path: string, handler: Handler$1, middlewares?: M[]);
70
+ }
71
+ //#endregion
72
+ //#region src/ClearRequest.d.ts
73
+ declare class ClearRequest<X = any, M = Middleware$1 | Middleware> {
74
+ [key: string]: any;
75
+ /**
76
+ * @param body - Parsed request body
77
+ */
78
+ body: RequestData;
79
+ /**
80
+ * @param query - Parsed query parameters
81
+ */
82
+ query: RequestData;
83
+ /**
84
+ * @param params - Parsed route parameters
85
+ */
86
+ params: RequestData;
87
+ route: Route<X, M>;
88
+ constructor(init?: Partial<ClearRequest>);
89
+ }
90
+ //#endregion
91
+ //#region types/express.d.ts
92
+ /**
93
+ * HTTP context passed to route handlers
94
+ */
95
+ interface HttpContext {
96
+ req: Request;
97
+ res: Response$1;
98
+ next: NextFunction;
99
+ }
100
+ /**
101
+ * Route handler function type
102
+ */
103
+ type RouteHandler = (
104
+ /**
105
+ * Express context object containing req, res, and next
106
+ */
107
+
108
+ ctx: HttpContext,
109
+ /**
110
+ * ClearRequest instance
111
+ */
112
+
113
+ req: ClearRequest) => any | Promise<any>;
114
+ /**
115
+ * Handler can be either a function or controller reference
116
+ */
117
+ type Handler = RouteHandler | ControllerHandler;
118
+ /**
119
+ * Middleware function type
120
+ */
121
+ type Middleware = (req: Request, res: Response$1, next: NextFunction) => any | Promise<any>;
122
+ //#endregion
123
+ export { H3App as a, Middleware$1 as c, RouteInfo as d, Route as i, ControllerAction as l, HttpContext as n, Handler$1 as o, Middleware as r, HttpContext$1 as s, Handler as t, HttpMethod as u };
@@ -0,0 +1,123 @@
1
+ import { NextFunction, Request, Response as Response$1 } from "express";
2
+ import { H3, H3Event, Middleware as Middleware$1, TypedServerRequest } from "h3";
3
+
4
+ //#region types/basic.d.ts
5
+ /**
6
+ * Controller method reference
7
+ */
8
+ type ControllerHandler = [any, string];
9
+ /**
10
+ * HTTP methods supported by the router
11
+ */
12
+ type HttpMethod = 'get' | 'post' | 'put' | 'delete' | 'patch' | 'options' | 'head';
13
+ /**
14
+ * Route information object
15
+ */
16
+ interface RouteInfo {
17
+ methods: HttpMethod[];
18
+ path: string;
19
+ middlewareCount: number;
20
+ handlerType: 'function' | 'controller';
21
+ }
22
+ /**
23
+ * Common controller action names
24
+ */
25
+ type ControllerAction = 'index' | 'show' | 'create' | 'update' | 'destroy';
26
+ /**
27
+ * Generic Object type for request data
28
+ */
29
+ type RequestData = Record<string, any>;
30
+ //#endregion
31
+ //#region types/h3.d.ts
32
+ type H3App = Omit<H3['fetch'], 'fetch'> & {
33
+ fetch: (request: TypedServerRequest) => Promise<Response>;
34
+ };
35
+ /**
36
+ * HTTP context passed to route handlers
37
+ */
38
+ type HttpContext$1 = H3Event & {};
39
+ /**
40
+ * Route handler function type
41
+ */
42
+ type RouteHandler$1 = (
43
+ /**
44
+ * H3 event context
45
+ */
46
+
47
+ ctx: HttpContext$1,
48
+ /**
49
+ * ClearRequest instance
50
+ */
51
+
52
+ req: ClearRequest) => any | Promise<any>;
53
+ /**
54
+ * Handler can be either a function or controller reference
55
+ */
56
+ type Handler$1 = RouteHandler$1 | ControllerHandler;
57
+ //#endregion
58
+ //#region src/Route.d.ts
59
+ declare class Route<X = any, M = Middleware$1 | Middleware> {
60
+ ctx: X;
61
+ body: RequestData;
62
+ query: RequestData;
63
+ params: RequestData;
64
+ clearRequest: ClearRequest;
65
+ methods: HttpMethod[];
66
+ path: string;
67
+ handler: Handler$1;
68
+ middlewares: M[];
69
+ constructor(methods: HttpMethod[], path: string, handler: Handler$1, middlewares?: M[]);
70
+ }
71
+ //#endregion
72
+ //#region src/ClearRequest.d.ts
73
+ declare class ClearRequest<X = any, M = Middleware$1 | Middleware> {
74
+ [key: string]: any;
75
+ /**
76
+ * @param body - Parsed request body
77
+ */
78
+ body: RequestData;
79
+ /**
80
+ * @param query - Parsed query parameters
81
+ */
82
+ query: RequestData;
83
+ /**
84
+ * @param params - Parsed route parameters
85
+ */
86
+ params: RequestData;
87
+ route: Route<X, M>;
88
+ constructor(init?: Partial<ClearRequest>);
89
+ }
90
+ //#endregion
91
+ //#region types/express.d.ts
92
+ /**
93
+ * HTTP context passed to route handlers
94
+ */
95
+ interface HttpContext {
96
+ req: Request;
97
+ res: Response$1;
98
+ next: NextFunction;
99
+ }
100
+ /**
101
+ * Route handler function type
102
+ */
103
+ type RouteHandler = (
104
+ /**
105
+ * Express context object containing req, res, and next
106
+ */
107
+
108
+ ctx: HttpContext,
109
+ /**
110
+ * ClearRequest instance
111
+ */
112
+
113
+ req: ClearRequest) => any | Promise<any>;
114
+ /**
115
+ * Handler can be either a function or controller reference
116
+ */
117
+ type Handler = RouteHandler | ControllerHandler;
118
+ /**
119
+ * Middleware function type
120
+ */
121
+ type Middleware = (req: Request, res: Response$1, next: NextFunction) => any | Promise<any>;
122
+ //#endregion
123
+ export { H3App as a, Middleware$1 as c, RouteInfo as d, Route as i, ControllerAction as l, HttpContext as n, Handler$1 as o, Middleware as r, HttpContext$1 as s, Handler as t, HttpMethod as u };
package/dist/h3/index.cjs CHANGED
@@ -1,4 +1,6 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
+ const require_Route = require('../Route-97p-tEbS.cjs');
3
+ let h3 = require("h3");
2
4
 
3
5
  //#region src/h3/router.ts
4
6
  /**
@@ -7,7 +9,7 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
7
9
  * @author 3m1n3nc3
8
10
  * @repository https://github.com/toneflix/clear-router
9
11
  */
10
- var Router = class {
12
+ var Router = class Router {
11
13
  /**
12
14
  * All registered routes
13
15
  */
@@ -42,16 +44,11 @@ var Router = class {
42
44
  static add(methods, path, handler, middlewares) {
43
45
  const methodArray = Array.isArray(methods) ? methods : [methods];
44
46
  const fullPath = this.normalizePath(`${this.prefix}/${path}`);
45
- this.routes.push({
46
- methods: methodArray,
47
- path: fullPath,
48
- handler,
49
- middlewares: [
50
- ...this.globalMiddlewares,
51
- ...this.groupMiddlewares,
52
- ...middlewares || []
53
- ]
54
- });
47
+ this.routes.push(new require_Route.Route(methodArray, fullPath, handler, [
48
+ ...this.globalMiddlewares,
49
+ ...this.groupMiddlewares,
50
+ ...middlewares || []
51
+ ]));
55
52
  }
56
53
  /**
57
54
  * Register RESTful API resource routes for a controller with optional action filtering
@@ -206,13 +203,20 @@ var Router = class {
206
203
  static apply(app) {
207
204
  for (const route of this.routes) {
208
205
  let handlerFunction = null;
206
+ let instance = null;
209
207
  try {
210
- if (typeof route.handler === "function") handlerFunction = route.handler;
208
+ if (typeof route.handler === "function")
209
+ /**
210
+ * Since we do not have a controller instance, we will call the handler function directly and the route instance will be the this argument. This allows for both controller-based and function-based handlers to work seamlessly.
211
+ */
212
+ handlerFunction = route.handler.bind(route);
211
213
  else if (Array.isArray(route.handler) && route.handler.length === 2) {
212
214
  const [Controller, method] = route.handler;
213
- if (typeof Controller === "function" && typeof Controller[method] === "function") handlerFunction = Controller[method].bind(Controller);
214
- else if (typeof Controller === "function") {
215
- const instance = new Controller();
215
+ if (["function", "object"].includes(typeof Controller) && typeof Controller[method] === "function") {
216
+ instance = Controller;
217
+ handlerFunction = Controller[method].bind(Controller);
218
+ } else if (typeof Controller === "function") {
219
+ instance = new Controller();
216
220
  if (typeof instance[method] === "function") handlerFunction = instance[method].bind(instance);
217
221
  else throw new Error(`Method "${method}" not found in controller instance "${Controller.name}"`);
218
222
  } else throw new Error(`Invalid controller type for route: ${route.path}`);
@@ -233,12 +237,15 @@ var Router = class {
233
237
  "head"
234
238
  ].includes(method)) {
235
239
  const error = /* @__PURE__ */ new Error(`Invalid HTTP method: ${method} for route: ${route.path}`);
236
- console.error(`[ROUTES]`, error.message);
240
+ console.error("[ROUTES]", error.message);
237
241
  throw error;
238
242
  }
239
243
  app[method](route.path, async (event) => {
240
244
  try {
241
- const result = handlerFunction(event);
245
+ const ctx = event;
246
+ const inst = instance ?? route;
247
+ await Router.bindRequestToInstance(ctx, inst, route);
248
+ const result = handlerFunction(ctx, inst.clearRequest);
242
249
  return await Promise.resolve(result);
243
250
  } catch (error) {
244
251
  return error;
@@ -248,6 +255,20 @@ var Router = class {
248
255
  }
249
256
  return app;
250
257
  }
258
+ static async bindRequestToInstance(ctx, instance, route) {
259
+ if (!instance) return;
260
+ instance.ctx = ctx;
261
+ instance.body = await (0, h3.readBody)(ctx) ?? {};
262
+ instance.query = (0, h3.getQuery)(ctx);
263
+ instance.params = (0, h3.getRouterParams)(ctx, { decode: true });
264
+ instance.clearRequest = new require_Route.ClearRequest({
265
+ ctx,
266
+ route,
267
+ body: instance.body,
268
+ query: instance.query,
269
+ params: instance.params
270
+ });
271
+ }
251
272
  };
252
273
 
253
274
  //#endregion
@@ -1,23 +1,6 @@
1
- import { i as RouteInfo, n as ControllerHandler, r as HttpMethod, t as ControllerAction } from "../basic-BCxhZ1Wd.cjs";
2
- import { H3, H3Event, Middleware, TypedServerRequest } from "h3";
1
+ import { a as H3App, c as Middleware, d as RouteInfo, i as Route, l as ControllerAction, o as Handler, s as HttpContext, u as HttpMethod } from "../express-sjYra4oE.cjs";
2
+ import { H3 } from "h3";
3
3
 
4
- //#region types/h3.d.ts
5
- type H3App = Omit<H3['fetch'], 'fetch'> & {
6
- fetch: (request: TypedServerRequest) => Promise<Response>;
7
- };
8
- /**
9
- * HTTP context passed to route handlers
10
- */
11
- interface HttpContext extends H3Event {}
12
- /**
13
- * Route handler function type
14
- */
15
- type RouteHandler = (ctx: HttpContext) => any | Promise<any>;
16
- /**
17
- * Handler can be either a function or controller reference
18
- */
19
- type Handler = RouteHandler | ControllerHandler;
20
- //#endregion
21
4
  //#region src/h3/router.d.ts
22
5
  /**
23
6
  * @class clear-router
@@ -29,12 +12,7 @@ declare class Router {
29
12
  /**
30
13
  * All registered routes
31
14
  */
32
- static routes: Array<{
33
- methods: HttpMethod[];
34
- path: string;
35
- handler: Handler;
36
- middlewares: Middleware[];
37
- }>;
15
+ static routes: Array<Route<HttpContext, Middleware>>;
38
16
  /**
39
17
  * Current route prefix
40
18
  */
@@ -147,6 +125,7 @@ declare class Router {
147
125
  * @param app - H3 app instance
148
126
  */
149
127
  static apply(app: H3): H3App;
128
+ private static bindRequestToInstance;
150
129
  }
151
130
  //#endregion
152
131
  export { Router };
@@ -1,23 +1,6 @@
1
- import { i as RouteInfo, n as ControllerHandler, r as HttpMethod, t as ControllerAction } from "../basic-POMY6a76.mjs";
2
- import { H3, H3Event, Middleware, TypedServerRequest } from "h3";
1
+ import { a as H3App, c as Middleware, d as RouteInfo, i as Route, l as ControllerAction, o as Handler, s as HttpContext, u as HttpMethod } from "../express-WiUTZlHA.mjs";
2
+ import { H3 } from "h3";
3
3
 
4
- //#region types/h3.d.ts
5
- type H3App = Omit<H3['fetch'], 'fetch'> & {
6
- fetch: (request: TypedServerRequest) => Promise<Response>;
7
- };
8
- /**
9
- * HTTP context passed to route handlers
10
- */
11
- interface HttpContext extends H3Event {}
12
- /**
13
- * Route handler function type
14
- */
15
- type RouteHandler = (ctx: HttpContext) => any | Promise<any>;
16
- /**
17
- * Handler can be either a function or controller reference
18
- */
19
- type Handler = RouteHandler | ControllerHandler;
20
- //#endregion
21
4
  //#region src/h3/router.d.ts
22
5
  /**
23
6
  * @class clear-router
@@ -29,12 +12,7 @@ declare class Router {
29
12
  /**
30
13
  * All registered routes
31
14
  */
32
- static routes: Array<{
33
- methods: HttpMethod[];
34
- path: string;
35
- handler: Handler;
36
- middlewares: Middleware[];
37
- }>;
15
+ static routes: Array<Route<HttpContext, Middleware>>;
38
16
  /**
39
17
  * Current route prefix
40
18
  */
@@ -147,6 +125,7 @@ declare class Router {
147
125
  * @param app - H3 app instance
148
126
  */
149
127
  static apply(app: H3): H3App;
128
+ private static bindRequestToInstance;
150
129
  }
151
130
  //#endregion
152
131
  export { Router };
package/dist/h3/index.mjs CHANGED
@@ -1,3 +1,6 @@
1
+ import { n as ClearRequest, t as Route } from "../Route-BtmoiYq5.mjs";
2
+ import { getQuery, getRouterParams, readBody } from "h3";
3
+
1
4
  //#region src/h3/router.ts
2
5
  /**
3
6
  * @class clear-router
@@ -5,7 +8,7 @@
5
8
  * @author 3m1n3nc3
6
9
  * @repository https://github.com/toneflix/clear-router
7
10
  */
8
- var Router = class {
11
+ var Router = class Router {
9
12
  /**
10
13
  * All registered routes
11
14
  */
@@ -40,16 +43,11 @@ var Router = class {
40
43
  static add(methods, path, handler, middlewares) {
41
44
  const methodArray = Array.isArray(methods) ? methods : [methods];
42
45
  const fullPath = this.normalizePath(`${this.prefix}/${path}`);
43
- this.routes.push({
44
- methods: methodArray,
45
- path: fullPath,
46
- handler,
47
- middlewares: [
48
- ...this.globalMiddlewares,
49
- ...this.groupMiddlewares,
50
- ...middlewares || []
51
- ]
52
- });
46
+ this.routes.push(new Route(methodArray, fullPath, handler, [
47
+ ...this.globalMiddlewares,
48
+ ...this.groupMiddlewares,
49
+ ...middlewares || []
50
+ ]));
53
51
  }
54
52
  /**
55
53
  * Register RESTful API resource routes for a controller with optional action filtering
@@ -204,13 +202,20 @@ var Router = class {
204
202
  static apply(app) {
205
203
  for (const route of this.routes) {
206
204
  let handlerFunction = null;
205
+ let instance = null;
207
206
  try {
208
- if (typeof route.handler === "function") handlerFunction = route.handler;
207
+ if (typeof route.handler === "function")
208
+ /**
209
+ * Since we do not have a controller instance, we will call the handler function directly and the route instance will be the this argument. This allows for both controller-based and function-based handlers to work seamlessly.
210
+ */
211
+ handlerFunction = route.handler.bind(route);
209
212
  else if (Array.isArray(route.handler) && route.handler.length === 2) {
210
213
  const [Controller, method] = route.handler;
211
- if (typeof Controller === "function" && typeof Controller[method] === "function") handlerFunction = Controller[method].bind(Controller);
212
- else if (typeof Controller === "function") {
213
- const instance = new Controller();
214
+ if (["function", "object"].includes(typeof Controller) && typeof Controller[method] === "function") {
215
+ instance = Controller;
216
+ handlerFunction = Controller[method].bind(Controller);
217
+ } else if (typeof Controller === "function") {
218
+ instance = new Controller();
214
219
  if (typeof instance[method] === "function") handlerFunction = instance[method].bind(instance);
215
220
  else throw new Error(`Method "${method}" not found in controller instance "${Controller.name}"`);
216
221
  } else throw new Error(`Invalid controller type for route: ${route.path}`);
@@ -231,12 +236,15 @@ var Router = class {
231
236
  "head"
232
237
  ].includes(method)) {
233
238
  const error = /* @__PURE__ */ new Error(`Invalid HTTP method: ${method} for route: ${route.path}`);
234
- console.error(`[ROUTES]`, error.message);
239
+ console.error("[ROUTES]", error.message);
235
240
  throw error;
236
241
  }
237
242
  app[method](route.path, async (event) => {
238
243
  try {
239
- const result = handlerFunction(event);
244
+ const ctx = event;
245
+ const inst = instance ?? route;
246
+ await Router.bindRequestToInstance(ctx, inst, route);
247
+ const result = handlerFunction(ctx, inst.clearRequest);
240
248
  return await Promise.resolve(result);
241
249
  } catch (error) {
242
250
  return error;
@@ -246,6 +254,20 @@ var Router = class {
246
254
  }
247
255
  return app;
248
256
  }
257
+ static async bindRequestToInstance(ctx, instance, route) {
258
+ if (!instance) return;
259
+ instance.ctx = ctx;
260
+ instance.body = await readBody(ctx) ?? {};
261
+ instance.query = getQuery(ctx);
262
+ instance.params = getRouterParams(ctx, { decode: true });
263
+ instance.clearRequest = new ClearRequest({
264
+ ctx,
265
+ route,
266
+ body: instance.body,
267
+ query: instance.query,
268
+ params: instance.params
269
+ });
270
+ }
249
271
  };
250
272
 
251
273
  //#endregion
@@ -0,0 +1,25 @@
1
+ import { RequestData } from "../types/basic.mjs";
2
+ import { Middleware } from "../types/h3.mjs";
3
+ import { Route } from "./Route.mjs";
4
+ import { Middleware as Middleware$1 } from "../types/express.mjs";
5
+
6
+ //#region src/ClearRequest.d.ts
7
+ declare class ClearRequest<X = any, M = Middleware | Middleware$1> {
8
+ [key: string]: any;
9
+ /**
10
+ * @param body - Parsed request body
11
+ */
12
+ body: RequestData;
13
+ /**
14
+ * @param query - Parsed query parameters
15
+ */
16
+ query: RequestData;
17
+ /**
18
+ * @param params - Parsed route parameters
19
+ */
20
+ params: RequestData;
21
+ route: Route<X, M>;
22
+ constructor(init?: Partial<ClearRequest>);
23
+ }
24
+ //#endregion
25
+ export { ClearRequest };
@@ -0,0 +1,20 @@
1
+ import { HttpMethod, RequestData } from "../types/basic.mjs";
2
+ import { Handler, Middleware } from "../types/h3.mjs";
3
+ import { ClearRequest } from "./ClearRequest.mjs";
4
+ import { Middleware as Middleware$1 } from "../types/express.mjs";
5
+
6
+ //#region src/Route.d.ts
7
+ declare class Route<X = any, M = Middleware | Middleware$1> {
8
+ ctx: X;
9
+ body: RequestData;
10
+ query: RequestData;
11
+ params: RequestData;
12
+ clearRequest: ClearRequest;
13
+ methods: HttpMethod[];
14
+ path: string;
15
+ handler: Handler;
16
+ middlewares: M[];
17
+ constructor(methods: HttpMethod[], path: string, handler: Handler, middlewares?: M[]);
18
+ }
19
+ //#endregion
20
+ export { Route };
@@ -20,5 +20,9 @@ interface RouteInfo {
20
20
  * Common controller action names
21
21
  */
22
22
  type ControllerAction = 'index' | 'show' | 'create' | 'update' | 'destroy';
23
+ /**
24
+ * Generic Object type for request data
25
+ */
26
+ type RequestData = Record<string, any>;
23
27
  //#endregion
24
- export { ControllerAction, ControllerHandler, HttpMethod, RouteInfo };
28
+ export { ControllerAction, ControllerHandler, HttpMethod, RequestData, RouteInfo };
@@ -1,4 +1,5 @@
1
1
  import { ControllerHandler } from "./basic.mjs";
2
+ import { ClearRequest } from "../src/ClearRequest.mjs";
2
3
  import { NextFunction, Request, Response } from "express";
3
4
 
4
5
  //#region types/express.d.ts
@@ -13,7 +14,17 @@ interface HttpContext {
13
14
  /**
14
15
  * Route handler function type
15
16
  */
16
- type RouteHandler = (ctx: HttpContext) => any | Promise<any>;
17
+ type RouteHandler = (
18
+ /**
19
+ * Express context object containing req, res, and next
20
+ */
21
+
22
+ ctx: HttpContext,
23
+ /**
24
+ * ClearRequest instance
25
+ */
26
+
27
+ req: ClearRequest) => any | Promise<any>;
17
28
  /**
18
29
  * Handler can be either a function or controller reference
19
30
  */
@@ -1,4 +1,5 @@
1
1
  import { ControllerHandler } from "./basic.mjs";
2
+ import { ClearRequest } from "../src/ClearRequest.mjs";
2
3
  import { H3, H3Event, Middleware, TypedServerRequest } from "h3";
3
4
 
4
5
  //#region types/h3.d.ts
@@ -9,11 +10,21 @@ type MaybePromise<T = unknown> = T | Promise<T>;
9
10
  /**
10
11
  * HTTP context passed to route handlers
11
12
  */
12
- interface HttpContext extends H3Event {}
13
+ type HttpContext = H3Event & {};
13
14
  /**
14
15
  * Route handler function type
15
16
  */
16
- type RouteHandler = (ctx: HttpContext) => any | Promise<any>;
17
+ type RouteHandler = (
18
+ /**
19
+ * H3 event context
20
+ */
21
+
22
+ ctx: HttpContext,
23
+ /**
24
+ * ClearRequest instance
25
+ */
26
+
27
+ req: ClearRequest) => any | Promise<any>;
17
28
  /**
18
29
  * Handler can be either a function or controller reference
19
30
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clear-router",
3
- "version": "2.0.8",
3
+ "version": "2.1.1",
4
4
  "description": "Laravel-style routing system for Express.js and H3, with CommonJS, ESM, and TypeScript support",
5
5
  "keywords": [
6
6
  "h3",
@@ -63,21 +63,26 @@
63
63
  }
64
64
  },
65
65
  "devDependencies": {
66
+ "@eslint/js": "^10.0.1",
67
+ "@eslint/markdown": "^7.5.1",
66
68
  "@types/express": "^4.17.21",
67
69
  "@types/node": "^20.10.6",
68
70
  "@types/supertest": "^6.0.3",
71
+ "eslint": "^10.0.2",
69
72
  "supertest": "^7.1.1",
70
73
  "tsdown": "^0.20.3",
71
74
  "tsx": "^4.21.0",
72
75
  "typescript": "^5.3.3",
76
+ "typescript-eslint": "^8.56.1",
73
77
  "vite-tsconfig-paths": "^6.1.1",
74
78
  "vitest": "^4.0.18"
75
79
  },
76
80
  "engines": {
77
- "node": ">=22.0.0"
81
+ "node": ">=20.0.0"
78
82
  },
79
83
  "scripts": {
80
- "test": "pnpm test:esm && pnpm test:ts",
84
+ "test": "vitest",
85
+ "lint": "eslint",
81
86
  "test:esm": "vitest tests/esm.test.ts",
82
87
  "test:ts": "vitest tests/typescript.test.ts",
83
88
  "example": "tsx example/express/index.ts",
@@ -1,24 +0,0 @@
1
- //#region types/basic.d.ts
2
- /**
3
- * Controller method reference
4
- */
5
- type ControllerHandler = [any, string];
6
- /**
7
- * HTTP methods supported by the router
8
- */
9
- type HttpMethod = 'get' | 'post' | 'put' | 'delete' | 'patch' | 'options' | 'head';
10
- /**
11
- * Route information object
12
- */
13
- interface RouteInfo {
14
- methods: HttpMethod[];
15
- path: string;
16
- middlewareCount: number;
17
- handlerType: 'function' | 'controller';
18
- }
19
- /**
20
- * Common controller action names
21
- */
22
- type ControllerAction = 'index' | 'show' | 'create' | 'update' | 'destroy';
23
- //#endregion
24
- export { RouteInfo as i, ControllerHandler as n, HttpMethod as r, ControllerAction as t };
@@ -1,24 +0,0 @@
1
- //#region types/basic.d.ts
2
- /**
3
- * Controller method reference
4
- */
5
- type ControllerHandler = [any, string];
6
- /**
7
- * HTTP methods supported by the router
8
- */
9
- type HttpMethod = 'get' | 'post' | 'put' | 'delete' | 'patch' | 'options' | 'head';
10
- /**
11
- * Route information object
12
- */
13
- interface RouteInfo {
14
- methods: HttpMethod[];
15
- path: string;
16
- middlewareCount: number;
17
- handlerType: 'function' | 'controller';
18
- }
19
- /**
20
- * Common controller action names
21
- */
22
- type ControllerAction = 'index' | 'show' | 'create' | 'update' | 'destroy';
23
- //#endregion
24
- export { RouteInfo as i, ControllerHandler as n, HttpMethod as r, ControllerAction as t };