clear-router 2.1.7 → 2.1.9

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,5 +1,11 @@
1
1
  # Clear Router
2
2
 
3
+ [![NPM Downloads](https://img.shields.io/npm/dt/clear-router.svg)](https://www.npmjs.com/package/clear-router)
4
+ [![npm version](https://img.shields.io/npm/v/clear-router.svg)](https://www.npmjs.com/package/clear-router)
5
+ [![License](https://img.shields.io/npm/l/clear-router.svg)](https://github.com/toneflix/clear-router/blob/main/LICENSE)
6
+ [![Publish to NPM](https://github.com/arkstack-hq/clear-router/actions/workflows/npm-publish.yml/badge.svg)](https://github.com/arkstack-hq/clear-router/actions/workflows/npm-publish.yml)
7
+ [![Run Tests](https://github.com/arkstack-hq/clear-router/actions/workflows/ci.yml/badge.svg)](https://github.com/arkstack-hq/clear-router/actions/workflows/ci.yml)
8
+
3
9
  Laravel-style routing system for H3 and Express.js, with clean route definitions, middleware support, controller bindings and full TypeScript support.
4
10
 
5
11
  ## Installation
@@ -1,24 +1,6 @@
1
- import { H3, H3Event, Middleware as Middleware$1, TypedServerRequest } from "h3";
2
1
  import { NextFunction, Request, Response as Response$1 } from "express";
2
+ import { H3, H3Event, Middleware as Middleware$1, TypedServerRequest } from "h3";
3
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
- * Common controller action names
15
- */
16
- type ControllerAction = 'index' | 'show' | 'create' | 'update' | 'destroy';
17
- /**
18
- * Generic Object type for request data
19
- */
20
- type RequestData = Record<string, any>;
21
- //#endregion
22
4
  //#region types/h3.d.ts
23
5
  type H3App = Omit<H3['fetch'], 'fetch'> & {
24
6
  fetch: (request: TypedServerRequest) => Promise<Response>;
@@ -115,4 +97,23 @@ type Handler = RouteHandler | ControllerHandler;
115
97
  */
116
98
  type Middleware = (req: Request, res: Response$1, next: NextFunction) => any | Promise<any>;
117
99
  //#endregion
118
- export { H3App as a, Middleware$1 as c, 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 };
100
+ //#region types/basic.d.ts
101
+ /**
102
+ * Controller method reference
103
+ */
104
+ type ControllerHandler = [any, string];
105
+ /**
106
+ * HTTP methods supported by the router
107
+ */
108
+ type HttpMethod = 'get' | 'post' | 'put' | 'delete' | 'patch' | 'options' | 'head';
109
+ /**
110
+ * Common controller action names
111
+ */
112
+ type ControllerAction = 'index' | 'show' | 'create' | 'update' | 'destroy';
113
+ /**
114
+ * Generic Object type for request data
115
+ */
116
+ type RequestData = Record<string, any>;
117
+ type ApiResourceMiddleware<M extends Middleware | Middleware$1> = M | M[] | { [K in ControllerAction]?: M | M[] };
118
+ //#endregion
119
+ export { HttpContext as a, H3App as c, Middleware$1 as d, Handler as i, Handler$1 as l, ControllerAction as n, Middleware as o, HttpMethod as r, Route as s, ApiResourceMiddleware as t, HttpContext$1 as u };
@@ -1,24 +1,6 @@
1
- import { NextFunction, Request, Response as Response$1 } from "express";
2
1
  import { H3, H3Event, Middleware as Middleware$1, TypedServerRequest } from "h3";
2
+ import { NextFunction, Request, Response as Response$1 } from "express";
3
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
- * Common controller action names
15
- */
16
- type ControllerAction = 'index' | 'show' | 'create' | 'update' | 'destroy';
17
- /**
18
- * Generic Object type for request data
19
- */
20
- type RequestData = Record<string, any>;
21
- //#endregion
22
4
  //#region types/h3.d.ts
23
5
  type H3App = Omit<H3['fetch'], 'fetch'> & {
24
6
  fetch: (request: TypedServerRequest) => Promise<Response>;
@@ -115,4 +97,23 @@ type Handler = RouteHandler | ControllerHandler;
115
97
  */
116
98
  type Middleware = (req: Request, res: Response$1, next: NextFunction) => any | Promise<any>;
117
99
  //#endregion
118
- export { H3App as a, Middleware$1 as c, 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 };
100
+ //#region types/basic.d.ts
101
+ /**
102
+ * Controller method reference
103
+ */
104
+ type ControllerHandler = [any, string];
105
+ /**
106
+ * HTTP methods supported by the router
107
+ */
108
+ type HttpMethod = 'get' | 'post' | 'put' | 'delete' | 'patch' | 'options' | 'head';
109
+ /**
110
+ * Common controller action names
111
+ */
112
+ type ControllerAction = 'index' | 'show' | 'create' | 'update' | 'destroy';
113
+ /**
114
+ * Generic Object type for request data
115
+ */
116
+ type RequestData = Record<string, any>;
117
+ type ApiResourceMiddleware<M extends Middleware | Middleware$1> = M | M[] | { [K in ControllerAction]?: M | M[] };
118
+ //#endregion
119
+ export { HttpContext as a, H3App as c, Middleware$1 as d, Handler as i, Handler$1 as l, ControllerAction as n, Middleware as o, HttpMethod as r, Route as s, ApiResourceMiddleware as t, HttpContext$1 as u };
@@ -1,5 +1,6 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
2
  const require_Route = require('../Route-DhC4kNPX.cjs');
3
+ let node_async_hooks = require("node:async_hooks");
3
4
 
4
5
  //#region src/express/router.ts
5
6
  /**
@@ -10,6 +11,7 @@ const require_Route = require('../Route-DhC4kNPX.cjs');
10
11
  * @repository https://github.com/toneflix/clear-router
11
12
  */
12
13
  var Router = class Router {
14
+ static groupContext = new node_async_hooks.AsyncLocalStorage();
13
15
  /**
14
16
  * All registered routes
15
17
  */
@@ -50,11 +52,15 @@ var Router = class Router {
50
52
  * @param middlewares - Array of middleware functions
51
53
  */
52
54
  static add(methods, path, handler, middlewares) {
55
+ const context = this.groupContext.getStore();
56
+ const activePrefix = context?.prefix ?? this.prefix;
57
+ const activeGroupMiddlewares = context?.groupMiddlewares ?? this.groupMiddlewares;
53
58
  methods = Array.isArray(methods) ? methods : [methods];
54
- const fullPath = this.normalizePath(`${this.prefix}/${path}`);
59
+ middlewares = middlewares ? Array.isArray(middlewares) ? middlewares : [middlewares] : void 0;
60
+ const fullPath = this.normalizePath(`${activePrefix}/${path}`);
55
61
  const route = new require_Route.Route(methods.includes("options") ? methods : methods.concat("options"), fullPath, handler, [
56
62
  ...this.globalMiddlewares,
57
- ...this.groupMiddlewares,
63
+ ...activeGroupMiddlewares,
58
64
  ...middlewares || []
59
65
  ]);
60
66
  if (!methods.includes("options") && !this.routesByPathMethod[`OPTIONS ${fullPath}`]) this.options(path, ({ res }) => {
@@ -105,7 +111,8 @@ var Router = class Router {
105
111
  if (except.includes(action)) continue;
106
112
  if (typeof preController[action] === "function") {
107
113
  const { method, path } = actions[action];
108
- this.add(method, `${basePath}${path}`, [controller, action]);
114
+ const actionMiddlewares = typeof options?.middlewares === "object" && !Array.isArray(options.middlewares) ? options.middlewares[action] : options?.middlewares;
115
+ this.add(method, `${basePath}${path}`, [controller, action], Array.isArray(actionMiddlewares) ? actionMiddlewares : actionMiddlewares ? [actionMiddlewares] : void 0);
109
116
  }
110
117
  }
111
118
  }
@@ -179,17 +186,17 @@ var Router = class Router {
179
186
  * @param middlewares - Middleware functions applied to all routes in group
180
187
  */
181
188
  static async group(prefix, callback, middlewares) {
182
- const previousPrefix = this.prefix;
183
- const previousMiddlewares = this.groupMiddlewares;
189
+ const context = this.groupContext.getStore();
190
+ const previousPrefix = context?.prefix ?? this.prefix;
191
+ const previousMiddlewares = context?.groupMiddlewares ?? this.groupMiddlewares;
184
192
  const fullPrefix = [previousPrefix, prefix].filter(Boolean).join("/");
185
- this.prefix = this.normalizePath(fullPrefix);
186
- this.groupMiddlewares = [...previousMiddlewares, ...middlewares || []];
187
- try {
193
+ const nextContext = {
194
+ prefix: this.normalizePath(fullPrefix),
195
+ groupMiddlewares: [...previousMiddlewares, ...middlewares || []]
196
+ };
197
+ await this.groupContext.run(nextContext, async () => {
188
198
  await Promise.resolve(callback());
189
- } finally {
190
- this.prefix = previousPrefix;
191
- this.groupMiddlewares = previousMiddlewares;
192
- }
199
+ });
193
200
  }
194
201
  /**
195
202
  * Apply global middlewares for the duration of the callback
@@ -1,4 +1,4 @@
1
- import { i as Route, l as ControllerAction, n as HttpContext, r as Middleware, t as Handler, u as HttpMethod } from "../express-JHxK-EqQ.cjs";
1
+ import { a as HttpContext, i as Handler, n as ControllerAction, o as Middleware, r as HttpMethod, s as Route, t as ApiResourceMiddleware } from "../basic-Chn8OGPD.cjs";
2
2
  import { Router as Router$1 } from "express";
3
3
 
4
4
  //#region src/express/router.d.ts
@@ -10,6 +10,7 @@ import { Router as Router$1 } from "express";
10
10
  * @repository https://github.com/toneflix/clear-router
11
11
  */
12
12
  declare class Router {
13
+ private static readonly groupContext;
13
14
  /**
14
15
  * All registered routes
15
16
  */
@@ -47,7 +48,7 @@ declare class Router {
47
48
  * @param handler - Route handler function or controller reference
48
49
  * @param middlewares - Array of middleware functions
49
50
  */
50
- static add(methods: HttpMethod | HttpMethod[], path: string, handler: Handler, middlewares?: Middleware[]): void;
51
+ static add(methods: HttpMethod | HttpMethod[], path: string, handler: Handler, middlewares?: Middleware[] | Middleware): void;
51
52
  /**
52
53
  * Register RESTful API resource routes for a controller with optional action filtering
53
54
  *
@@ -58,6 +59,7 @@ declare class Router {
58
59
  static apiResource(basePath: string, controller: any, options?: {
59
60
  only?: ControllerAction[];
60
61
  except?: ControllerAction[];
62
+ middlewares?: ApiResourceMiddleware<Middleware>;
61
63
  }): void;
62
64
  /**
63
65
  * Register a GET route
@@ -65,49 +67,49 @@ declare class Router {
65
67
  * @param handler - Route handler
66
68
  * @param middlewares - Middleware functions
67
69
  */
68
- static get(path: string, handler: Handler, middlewares?: Middleware[]): void;
70
+ static get(path: string, handler: Handler, middlewares?: Middleware[] | Middleware): void;
69
71
  /**
70
72
  * Register a POST route
71
73
  * @param path - Route path
72
74
  * @param handler - Route handler
73
75
  * @param middlewares - Middleware functions
74
76
  */
75
- static post(path: string, handler: Handler, middlewares?: Middleware[]): void;
77
+ static post(path: string, handler: Handler, middlewares?: Middleware[] | Middleware): void;
76
78
  /**
77
79
  * Register a PUT route
78
80
  * @param path - Route path
79
81
  * @param handler - Route handler
80
82
  * @param middlewares - Middleware functions
81
83
  */
82
- static put(path: string, handler: Handler, middlewares?: Middleware[]): void;
84
+ static put(path: string, handler: Handler, middlewares?: Middleware[] | Middleware): void;
83
85
  /**
84
86
  * Register a DELETE route
85
87
  * @param path - Route path
86
88
  * @param handler - Route handler
87
89
  * @param middlewares - Middleware functions
88
90
  */
89
- static delete(path: string, handler: Handler, middlewares?: Middleware[]): void;
91
+ static delete(path: string, handler: Handler, middlewares?: Middleware[] | Middleware): void;
90
92
  /**
91
93
  * Register a PATCH route
92
94
  * @param path - Route path
93
95
  * @param handler - Route handler
94
96
  * @param middlewares - Middleware functions
95
97
  */
96
- static patch(path: string, handler: Handler, middlewares?: Middleware[]): void;
98
+ static patch(path: string, handler: Handler, middlewares?: Middleware[] | Middleware): void;
97
99
  /**
98
100
  * Register an OPTIONS route
99
101
  * @param path - Route path
100
102
  * @param handler - Route handler
101
103
  * @param middlewares - Middleware functions
102
104
  */
103
- static options(path: string, handler: Handler, middlewares?: Middleware[]): void;
105
+ static options(path: string, handler: Handler, middlewares?: Middleware[] | Middleware): void;
104
106
  /**
105
107
  * Register a HEAD route
106
108
  * @param path - Route path
107
109
  * @param handler - Route handler
108
110
  * @param middlewares - Middleware functions
109
111
  */
110
- static head(path: string, handler: Handler, middlewares?: Middleware[]): void;
112
+ static head(path: string, handler: Handler, middlewares?: Middleware[] | Middleware): void;
111
113
  /**
112
114
  * Group routes with a common prefix and middlewares
113
115
  * @param prefix - URL prefix for grouped routes
@@ -1,4 +1,4 @@
1
- import { i as Route, l as ControllerAction, n as HttpContext, r as Middleware, t as Handler, u as HttpMethod } from "../express-D9GR9yTH.mjs";
1
+ import { a as HttpContext, i as Handler, n as ControllerAction, o as Middleware, r as HttpMethod, s as Route, t as ApiResourceMiddleware } from "../basic-DJmmZq1h.mjs";
2
2
  import { Router as Router$1 } from "express";
3
3
 
4
4
  //#region src/express/router.d.ts
@@ -10,6 +10,7 @@ import { Router as Router$1 } from "express";
10
10
  * @repository https://github.com/toneflix/clear-router
11
11
  */
12
12
  declare class Router {
13
+ private static readonly groupContext;
13
14
  /**
14
15
  * All registered routes
15
16
  */
@@ -47,7 +48,7 @@ declare class Router {
47
48
  * @param handler - Route handler function or controller reference
48
49
  * @param middlewares - Array of middleware functions
49
50
  */
50
- static add(methods: HttpMethod | HttpMethod[], path: string, handler: Handler, middlewares?: Middleware[]): void;
51
+ static add(methods: HttpMethod | HttpMethod[], path: string, handler: Handler, middlewares?: Middleware[] | Middleware): void;
51
52
  /**
52
53
  * Register RESTful API resource routes for a controller with optional action filtering
53
54
  *
@@ -58,6 +59,7 @@ declare class Router {
58
59
  static apiResource(basePath: string, controller: any, options?: {
59
60
  only?: ControllerAction[];
60
61
  except?: ControllerAction[];
62
+ middlewares?: ApiResourceMiddleware<Middleware>;
61
63
  }): void;
62
64
  /**
63
65
  * Register a GET route
@@ -65,49 +67,49 @@ declare class Router {
65
67
  * @param handler - Route handler
66
68
  * @param middlewares - Middleware functions
67
69
  */
68
- static get(path: string, handler: Handler, middlewares?: Middleware[]): void;
70
+ static get(path: string, handler: Handler, middlewares?: Middleware[] | Middleware): void;
69
71
  /**
70
72
  * Register a POST route
71
73
  * @param path - Route path
72
74
  * @param handler - Route handler
73
75
  * @param middlewares - Middleware functions
74
76
  */
75
- static post(path: string, handler: Handler, middlewares?: Middleware[]): void;
77
+ static post(path: string, handler: Handler, middlewares?: Middleware[] | Middleware): void;
76
78
  /**
77
79
  * Register a PUT route
78
80
  * @param path - Route path
79
81
  * @param handler - Route handler
80
82
  * @param middlewares - Middleware functions
81
83
  */
82
- static put(path: string, handler: Handler, middlewares?: Middleware[]): void;
84
+ static put(path: string, handler: Handler, middlewares?: Middleware[] | Middleware): void;
83
85
  /**
84
86
  * Register a DELETE route
85
87
  * @param path - Route path
86
88
  * @param handler - Route handler
87
89
  * @param middlewares - Middleware functions
88
90
  */
89
- static delete(path: string, handler: Handler, middlewares?: Middleware[]): void;
91
+ static delete(path: string, handler: Handler, middlewares?: Middleware[] | Middleware): void;
90
92
  /**
91
93
  * Register a PATCH route
92
94
  * @param path - Route path
93
95
  * @param handler - Route handler
94
96
  * @param middlewares - Middleware functions
95
97
  */
96
- static patch(path: string, handler: Handler, middlewares?: Middleware[]): void;
98
+ static patch(path: string, handler: Handler, middlewares?: Middleware[] | Middleware): void;
97
99
  /**
98
100
  * Register an OPTIONS route
99
101
  * @param path - Route path
100
102
  * @param handler - Route handler
101
103
  * @param middlewares - Middleware functions
102
104
  */
103
- static options(path: string, handler: Handler, middlewares?: Middleware[]): void;
105
+ static options(path: string, handler: Handler, middlewares?: Middleware[] | Middleware): void;
104
106
  /**
105
107
  * Register a HEAD route
106
108
  * @param path - Route path
107
109
  * @param handler - Route handler
108
110
  * @param middlewares - Middleware functions
109
111
  */
110
- static head(path: string, handler: Handler, middlewares?: Middleware[]): void;
112
+ static head(path: string, handler: Handler, middlewares?: Middleware[] | Middleware): void;
111
113
  /**
112
114
  * Group routes with a common prefix and middlewares
113
115
  * @param prefix - URL prefix for grouped routes
@@ -1,4 +1,5 @@
1
1
  import { n as ClearRequest, t as Route } from "../Route-BbPXcDGX.mjs";
2
+ import { AsyncLocalStorage } from "node:async_hooks";
2
3
 
3
4
  //#region src/express/router.ts
4
5
  /**
@@ -9,6 +10,7 @@ import { n as ClearRequest, t as Route } from "../Route-BbPXcDGX.mjs";
9
10
  * @repository https://github.com/toneflix/clear-router
10
11
  */
11
12
  var Router = class Router {
13
+ static groupContext = new AsyncLocalStorage();
12
14
  /**
13
15
  * All registered routes
14
16
  */
@@ -49,11 +51,15 @@ var Router = class Router {
49
51
  * @param middlewares - Array of middleware functions
50
52
  */
51
53
  static add(methods, path, handler, middlewares) {
54
+ const context = this.groupContext.getStore();
55
+ const activePrefix = context?.prefix ?? this.prefix;
56
+ const activeGroupMiddlewares = context?.groupMiddlewares ?? this.groupMiddlewares;
52
57
  methods = Array.isArray(methods) ? methods : [methods];
53
- const fullPath = this.normalizePath(`${this.prefix}/${path}`);
58
+ middlewares = middlewares ? Array.isArray(middlewares) ? middlewares : [middlewares] : void 0;
59
+ const fullPath = this.normalizePath(`${activePrefix}/${path}`);
54
60
  const route = new Route(methods.includes("options") ? methods : methods.concat("options"), fullPath, handler, [
55
61
  ...this.globalMiddlewares,
56
- ...this.groupMiddlewares,
62
+ ...activeGroupMiddlewares,
57
63
  ...middlewares || []
58
64
  ]);
59
65
  if (!methods.includes("options") && !this.routesByPathMethod[`OPTIONS ${fullPath}`]) this.options(path, ({ res }) => {
@@ -104,7 +110,8 @@ var Router = class Router {
104
110
  if (except.includes(action)) continue;
105
111
  if (typeof preController[action] === "function") {
106
112
  const { method, path } = actions[action];
107
- this.add(method, `${basePath}${path}`, [controller, action]);
113
+ const actionMiddlewares = typeof options?.middlewares === "object" && !Array.isArray(options.middlewares) ? options.middlewares[action] : options?.middlewares;
114
+ this.add(method, `${basePath}${path}`, [controller, action], Array.isArray(actionMiddlewares) ? actionMiddlewares : actionMiddlewares ? [actionMiddlewares] : void 0);
108
115
  }
109
116
  }
110
117
  }
@@ -178,17 +185,17 @@ var Router = class Router {
178
185
  * @param middlewares - Middleware functions applied to all routes in group
179
186
  */
180
187
  static async group(prefix, callback, middlewares) {
181
- const previousPrefix = this.prefix;
182
- const previousMiddlewares = this.groupMiddlewares;
188
+ const context = this.groupContext.getStore();
189
+ const previousPrefix = context?.prefix ?? this.prefix;
190
+ const previousMiddlewares = context?.groupMiddlewares ?? this.groupMiddlewares;
183
191
  const fullPrefix = [previousPrefix, prefix].filter(Boolean).join("/");
184
- this.prefix = this.normalizePath(fullPrefix);
185
- this.groupMiddlewares = [...previousMiddlewares, ...middlewares || []];
186
- try {
192
+ const nextContext = {
193
+ prefix: this.normalizePath(fullPrefix),
194
+ groupMiddlewares: [...previousMiddlewares, ...middlewares || []]
195
+ };
196
+ await this.groupContext.run(nextContext, async () => {
187
197
  await Promise.resolve(callback());
188
- } finally {
189
- this.prefix = previousPrefix;
190
- this.groupMiddlewares = previousMiddlewares;
191
- }
198
+ });
192
199
  }
193
200
  /**
194
201
  * Apply global middlewares for the duration of the callback
package/dist/h3/index.cjs CHANGED
@@ -1,5 +1,6 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
2
  const require_Route = require('../Route-DhC4kNPX.cjs');
3
+ let node_async_hooks = require("node:async_hooks");
3
4
  let h3 = require("h3");
4
5
 
5
6
  //#region src/h3/router.ts
@@ -10,6 +11,7 @@ let h3 = require("h3");
10
11
  * @repository https://github.com/toneflix/clear-router
11
12
  */
12
13
  var Router = class Router {
14
+ static groupContext = new node_async_hooks.AsyncLocalStorage();
13
15
  /**
14
16
  * All registered routes
15
17
  */
@@ -50,11 +52,15 @@ var Router = class Router {
50
52
  * @param middlewares - Array of middleware functions
51
53
  */
52
54
  static add(methods, path, handler, middlewares) {
55
+ const context = this.groupContext.getStore();
56
+ const activePrefix = context?.prefix ?? this.prefix;
57
+ const activeGroupMiddlewares = context?.groupMiddlewares ?? this.groupMiddlewares;
53
58
  methods = Array.isArray(methods) ? methods : [methods];
54
- const fullPath = this.normalizePath(`${this.prefix}/${path}`);
59
+ middlewares = middlewares ? Array.isArray(middlewares) ? middlewares : [middlewares] : void 0;
60
+ const fullPath = this.normalizePath(`${activePrefix}/${path}`);
55
61
  const route = new require_Route.Route(methods.includes("options") ? methods : methods.concat("options"), fullPath, handler, [
56
62
  ...this.globalMiddlewares,
57
- ...this.groupMiddlewares,
63
+ ...activeGroupMiddlewares,
58
64
  ...middlewares || []
59
65
  ]);
60
66
  if (!methods.includes("options") && !this.routesByPathMethod[`OPTIONS ${fullPath}`]) this.options(path, ({ res }) => {
@@ -105,7 +111,8 @@ var Router = class Router {
105
111
  if (except.includes(action)) continue;
106
112
  if (typeof preController[action] === "function") {
107
113
  const { method, path } = actions[action];
108
- this.add(method, `${basePath}${path}`, [controller, action]);
114
+ const actionMiddlewares = typeof options?.middlewares === "object" && !Array.isArray(options.middlewares) ? options.middlewares[action] : options?.middlewares;
115
+ this.add(method, `${basePath}${path}`, [controller, action], Array.isArray(actionMiddlewares) ? actionMiddlewares : actionMiddlewares ? [actionMiddlewares] : void 0);
109
116
  }
110
117
  }
111
118
  }
@@ -179,17 +186,17 @@ var Router = class Router {
179
186
  * @param middlewares - Middleware functions applied to all routes in group
180
187
  */
181
188
  static async group(prefix, callback, middlewares) {
182
- const previousPrefix = this.prefix;
183
- const previousMiddlewares = this.groupMiddlewares;
189
+ const context = this.groupContext.getStore();
190
+ const previousPrefix = context?.prefix ?? this.prefix;
191
+ const previousMiddlewares = context?.groupMiddlewares ?? this.groupMiddlewares;
184
192
  const fullPrefix = [previousPrefix, prefix].filter(Boolean).join("/");
185
- this.prefix = this.normalizePath(fullPrefix);
186
- this.groupMiddlewares = [...previousMiddlewares, ...middlewares || []];
187
- try {
193
+ const nextContext = {
194
+ prefix: this.normalizePath(fullPrefix),
195
+ groupMiddlewares: [...previousMiddlewares, ...middlewares || []]
196
+ };
197
+ await this.groupContext.run(nextContext, async () => {
188
198
  await Promise.resolve(callback());
189
- } finally {
190
- this.prefix = previousPrefix;
191
- this.groupMiddlewares = previousMiddlewares;
192
- }
199
+ });
193
200
  }
194
201
  /**
195
202
  * Apply global middlewares for the duration of the callback
@@ -1,4 +1,4 @@
1
- import { a as H3App, c as Middleware, i as Route, l as ControllerAction, o as Handler, s as HttpContext, u as HttpMethod } from "../express-JHxK-EqQ.cjs";
1
+ import { c as H3App, d as Middleware, l as Handler, n as ControllerAction, r as HttpMethod, s as Route, t as ApiResourceMiddleware, u as HttpContext } from "../basic-Chn8OGPD.cjs";
2
2
  import { H3 } from "h3";
3
3
 
4
4
  //#region src/h3/router.d.ts
@@ -9,6 +9,7 @@ import { H3 } from "h3";
9
9
  * @repository https://github.com/toneflix/clear-router
10
10
  */
11
11
  declare class Router {
12
+ private static readonly groupContext;
12
13
  /**
13
14
  * All registered routes
14
15
  */
@@ -46,7 +47,7 @@ declare class Router {
46
47
  * @param handler - Route handler function or controller reference
47
48
  * @param middlewares - Array of middleware functions
48
49
  */
49
- static add(methods: HttpMethod | HttpMethod[], path: string, handler: Handler, middlewares?: Middleware[]): void;
50
+ static add(methods: HttpMethod | HttpMethod[], path: string, handler: Handler, middlewares?: Middleware[] | Middleware): void;
50
51
  /**
51
52
  * Register RESTful API resource routes for a controller with optional action filtering
52
53
  *
@@ -57,6 +58,7 @@ declare class Router {
57
58
  static apiResource(basePath: string, controller: any, options?: {
58
59
  only?: ControllerAction[];
59
60
  except?: ControllerAction[];
61
+ middlewares?: ApiResourceMiddleware<Middleware>;
60
62
  }): void;
61
63
  /**
62
64
  * Register a GET route
@@ -64,49 +66,49 @@ declare class Router {
64
66
  * @param handler - Route handler
65
67
  * @param middlewares - Middleware functions
66
68
  */
67
- static get(path: string, handler: Handler, middlewares?: Middleware[]): void;
69
+ static get(path: string, handler: Handler, middlewares?: Middleware[] | Middleware): void;
68
70
  /**
69
71
  * Register a POST route
70
72
  * @param path - Route path
71
73
  * @param handler - Route handler
72
74
  * @param middlewares - Middleware functions
73
75
  */
74
- static post(path: string, handler: Handler, middlewares?: Middleware[]): void;
76
+ static post(path: string, handler: Handler, middlewares?: Middleware[] | Middleware): void;
75
77
  /**
76
78
  * Register a PUT route
77
79
  * @param path - Route path
78
80
  * @param handler - Route handler
79
81
  * @param middlewares - Middleware functions
80
82
  */
81
- static put(path: string, handler: Handler, middlewares?: Middleware[]): void;
83
+ static put(path: string, handler: Handler, middlewares?: Middleware[] | Middleware): void;
82
84
  /**
83
85
  * Register a DELETE route
84
86
  * @param path - Route path
85
87
  * @param handler - Route handler
86
88
  * @param middlewares - Middleware functions
87
89
  */
88
- static delete(path: string, handler: Handler, middlewares?: Middleware[]): void;
90
+ static delete(path: string, handler: Handler, middlewares?: Middleware[] | Middleware): void;
89
91
  /**
90
92
  * Register a PATCH route
91
93
  * @param path - Route path
92
94
  * @param handler - Route handler
93
95
  * @param middlewares - Middleware functions
94
96
  */
95
- static patch(path: string, handler: Handler, middlewares?: Middleware[]): void;
97
+ static patch(path: string, handler: Handler, middlewares?: Middleware[] | Middleware): void;
96
98
  /**
97
99
  * Register an OPTIONS route
98
100
  * @param path - Route path
99
101
  * @param handler - Route handler
100
102
  * @param middlewares - Middleware functions
101
103
  */
102
- static options(path: string, handler: Handler, middlewares?: Middleware[]): void;
104
+ static options(path: string, handler: Handler, middlewares?: Middleware[] | Middleware): void;
103
105
  /**
104
106
  * Register a HEAD route
105
107
  * @param path - Route path
106
108
  * @param handler - Route handler
107
109
  * @param middlewares - Middleware functions
108
110
  */
109
- static head(path: string, handler: Handler, middlewares?: Middleware[]): void;
111
+ static head(path: string, handler: Handler, middlewares?: Middleware[] | Middleware): void;
110
112
  /**
111
113
  * Group routes with a common prefix and middlewares
112
114
  * @param prefix - URL prefix for grouped routes
@@ -1,4 +1,4 @@
1
- import { a as H3App, c as Middleware, i as Route, l as ControllerAction, o as Handler, s as HttpContext, u as HttpMethod } from "../express-D9GR9yTH.mjs";
1
+ import { c as H3App, d as Middleware, l as Handler, n as ControllerAction, r as HttpMethod, s as Route, t as ApiResourceMiddleware, u as HttpContext } from "../basic-DJmmZq1h.mjs";
2
2
  import { H3 } from "h3";
3
3
 
4
4
  //#region src/h3/router.d.ts
@@ -9,6 +9,7 @@ import { H3 } from "h3";
9
9
  * @repository https://github.com/toneflix/clear-router
10
10
  */
11
11
  declare class Router {
12
+ private static readonly groupContext;
12
13
  /**
13
14
  * All registered routes
14
15
  */
@@ -46,7 +47,7 @@ declare class Router {
46
47
  * @param handler - Route handler function or controller reference
47
48
  * @param middlewares - Array of middleware functions
48
49
  */
49
- static add(methods: HttpMethod | HttpMethod[], path: string, handler: Handler, middlewares?: Middleware[]): void;
50
+ static add(methods: HttpMethod | HttpMethod[], path: string, handler: Handler, middlewares?: Middleware[] | Middleware): void;
50
51
  /**
51
52
  * Register RESTful API resource routes for a controller with optional action filtering
52
53
  *
@@ -57,6 +58,7 @@ declare class Router {
57
58
  static apiResource(basePath: string, controller: any, options?: {
58
59
  only?: ControllerAction[];
59
60
  except?: ControllerAction[];
61
+ middlewares?: ApiResourceMiddleware<Middleware>;
60
62
  }): void;
61
63
  /**
62
64
  * Register a GET route
@@ -64,49 +66,49 @@ declare class Router {
64
66
  * @param handler - Route handler
65
67
  * @param middlewares - Middleware functions
66
68
  */
67
- static get(path: string, handler: Handler, middlewares?: Middleware[]): void;
69
+ static get(path: string, handler: Handler, middlewares?: Middleware[] | Middleware): void;
68
70
  /**
69
71
  * Register a POST route
70
72
  * @param path - Route path
71
73
  * @param handler - Route handler
72
74
  * @param middlewares - Middleware functions
73
75
  */
74
- static post(path: string, handler: Handler, middlewares?: Middleware[]): void;
76
+ static post(path: string, handler: Handler, middlewares?: Middleware[] | Middleware): void;
75
77
  /**
76
78
  * Register a PUT route
77
79
  * @param path - Route path
78
80
  * @param handler - Route handler
79
81
  * @param middlewares - Middleware functions
80
82
  */
81
- static put(path: string, handler: Handler, middlewares?: Middleware[]): void;
83
+ static put(path: string, handler: Handler, middlewares?: Middleware[] | Middleware): void;
82
84
  /**
83
85
  * Register a DELETE route
84
86
  * @param path - Route path
85
87
  * @param handler - Route handler
86
88
  * @param middlewares - Middleware functions
87
89
  */
88
- static delete(path: string, handler: Handler, middlewares?: Middleware[]): void;
90
+ static delete(path: string, handler: Handler, middlewares?: Middleware[] | Middleware): void;
89
91
  /**
90
92
  * Register a PATCH route
91
93
  * @param path - Route path
92
94
  * @param handler - Route handler
93
95
  * @param middlewares - Middleware functions
94
96
  */
95
- static patch(path: string, handler: Handler, middlewares?: Middleware[]): void;
97
+ static patch(path: string, handler: Handler, middlewares?: Middleware[] | Middleware): void;
96
98
  /**
97
99
  * Register an OPTIONS route
98
100
  * @param path - Route path
99
101
  * @param handler - Route handler
100
102
  * @param middlewares - Middleware functions
101
103
  */
102
- static options(path: string, handler: Handler, middlewares?: Middleware[]): void;
104
+ static options(path: string, handler: Handler, middlewares?: Middleware[] | Middleware): void;
103
105
  /**
104
106
  * Register a HEAD route
105
107
  * @param path - Route path
106
108
  * @param handler - Route handler
107
109
  * @param middlewares - Middleware functions
108
110
  */
109
- static head(path: string, handler: Handler, middlewares?: Middleware[]): void;
111
+ static head(path: string, handler: Handler, middlewares?: Middleware[] | Middleware): void;
110
112
  /**
111
113
  * Group routes with a common prefix and middlewares
112
114
  * @param prefix - URL prefix for grouped routes
package/dist/h3/index.mjs CHANGED
@@ -1,4 +1,5 @@
1
1
  import { n as ClearRequest, t as Route } from "../Route-BbPXcDGX.mjs";
2
+ import { AsyncLocalStorage } from "node:async_hooks";
2
3
  import { getQuery, getRouterParams, readBody } from "h3";
3
4
 
4
5
  //#region src/h3/router.ts
@@ -9,6 +10,7 @@ import { getQuery, getRouterParams, readBody } from "h3";
9
10
  * @repository https://github.com/toneflix/clear-router
10
11
  */
11
12
  var Router = class Router {
13
+ static groupContext = new AsyncLocalStorage();
12
14
  /**
13
15
  * All registered routes
14
16
  */
@@ -49,11 +51,15 @@ var Router = class Router {
49
51
  * @param middlewares - Array of middleware functions
50
52
  */
51
53
  static add(methods, path, handler, middlewares) {
54
+ const context = this.groupContext.getStore();
55
+ const activePrefix = context?.prefix ?? this.prefix;
56
+ const activeGroupMiddlewares = context?.groupMiddlewares ?? this.groupMiddlewares;
52
57
  methods = Array.isArray(methods) ? methods : [methods];
53
- const fullPath = this.normalizePath(`${this.prefix}/${path}`);
58
+ middlewares = middlewares ? Array.isArray(middlewares) ? middlewares : [middlewares] : void 0;
59
+ const fullPath = this.normalizePath(`${activePrefix}/${path}`);
54
60
  const route = new Route(methods.includes("options") ? methods : methods.concat("options"), fullPath, handler, [
55
61
  ...this.globalMiddlewares,
56
- ...this.groupMiddlewares,
62
+ ...activeGroupMiddlewares,
57
63
  ...middlewares || []
58
64
  ]);
59
65
  if (!methods.includes("options") && !this.routesByPathMethod[`OPTIONS ${fullPath}`]) this.options(path, ({ res }) => {
@@ -104,7 +110,8 @@ var Router = class Router {
104
110
  if (except.includes(action)) continue;
105
111
  if (typeof preController[action] === "function") {
106
112
  const { method, path } = actions[action];
107
- this.add(method, `${basePath}${path}`, [controller, action]);
113
+ const actionMiddlewares = typeof options?.middlewares === "object" && !Array.isArray(options.middlewares) ? options.middlewares[action] : options?.middlewares;
114
+ this.add(method, `${basePath}${path}`, [controller, action], Array.isArray(actionMiddlewares) ? actionMiddlewares : actionMiddlewares ? [actionMiddlewares] : void 0);
108
115
  }
109
116
  }
110
117
  }
@@ -178,17 +185,17 @@ var Router = class Router {
178
185
  * @param middlewares - Middleware functions applied to all routes in group
179
186
  */
180
187
  static async group(prefix, callback, middlewares) {
181
- const previousPrefix = this.prefix;
182
- const previousMiddlewares = this.groupMiddlewares;
188
+ const context = this.groupContext.getStore();
189
+ const previousPrefix = context?.prefix ?? this.prefix;
190
+ const previousMiddlewares = context?.groupMiddlewares ?? this.groupMiddlewares;
183
191
  const fullPrefix = [previousPrefix, prefix].filter(Boolean).join("/");
184
- this.prefix = this.normalizePath(fullPrefix);
185
- this.groupMiddlewares = [...previousMiddlewares, ...middlewares || []];
186
- try {
192
+ const nextContext = {
193
+ prefix: this.normalizePath(fullPrefix),
194
+ groupMiddlewares: [...previousMiddlewares, ...middlewares || []]
195
+ };
196
+ await this.groupContext.run(nextContext, async () => {
187
197
  await Promise.resolve(callback());
188
- } finally {
189
- this.prefix = previousPrefix;
190
- this.groupMiddlewares = previousMiddlewares;
191
- }
198
+ });
192
199
  }
193
200
  /**
194
201
  * Apply global middlewares for the duration of the callback
package/dist/index.d.cts CHANGED
@@ -1,26 +1,6 @@
1
1
  import { NextFunction, Request, Response as Response$1 } from "express";
2
- import { H3Event, Middleware } from "h3";
2
+ import { H3Event, Middleware as Middleware$1 } from "h3";
3
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
- * Generic Object type for request data
15
- */
16
- type RequestData = Record<string, any>;
17
- //#endregion
18
- //#region types/express.d.ts
19
- /**
20
- * Middleware function type
21
- */
22
- type Middleware$1 = (req: Request, res: Response$1, next: NextFunction) => any | Promise<any>;
23
- //#endregion
24
4
  //#region types/h3.d.ts
25
5
  /**
26
6
  * HTTP context passed to route handlers
@@ -33,20 +13,38 @@ type RouteHandler = (
33
13
  /**
34
14
  * H3 event context
35
15
  */
36
-
37
16
  ctx: HttpContext,
38
17
  /**
39
18
  * ClearRequest instance
40
19
  */
41
-
42
20
  req: ClearRequest) => any | Promise<any>;
43
21
  /**
44
22
  * Handler can be either a function or controller reference
45
23
  */
46
24
  type Handler = RouteHandler | ControllerHandler;
47
25
  //#endregion
26
+ //#region types/basic.d.ts
27
+ /**
28
+ * Controller method reference
29
+ */
30
+ type ControllerHandler = [any, string];
31
+ /**
32
+ * HTTP methods supported by the router
33
+ */
34
+ type HttpMethod = 'get' | 'post' | 'put' | 'delete' | 'patch' | 'options' | 'head';
35
+ /**
36
+ * Generic Object type for request data
37
+ */
38
+ type RequestData = Record<string, any>;
39
+ //#endregion
40
+ //#region types/express.d.ts
41
+ /**
42
+ * Middleware function type
43
+ */
44
+ type Middleware = (req: Request, res: Response$1, next: NextFunction) => any | Promise<any>;
45
+ //#endregion
48
46
  //#region src/Route.d.ts
49
- declare class Route<X = any, M = Middleware | Middleware$1> {
47
+ declare class Route<X = any, M = Middleware$1 | Middleware> {
50
48
  ctx: X;
51
49
  body: RequestData;
52
50
  query: RequestData;
@@ -64,7 +62,7 @@ declare class Route<X = any, M = Middleware | Middleware$1> {
64
62
  }
65
63
  //#endregion
66
64
  //#region src/ClearRequest.d.ts
67
- declare class ClearRequest<X = any, M = Middleware | Middleware$1> {
65
+ declare class ClearRequest<X = any, M = Middleware$1 | Middleware> {
68
66
  [key: string]: any;
69
67
  /**
70
68
  * @param body - Parsed request body
package/dist/index.d.mts CHANGED
@@ -1,26 +1,6 @@
1
1
  import { NextFunction, Request, Response as Response$1 } from "express";
2
- import { H3Event, Middleware } from "h3";
2
+ import { H3Event, Middleware as Middleware$1 } from "h3";
3
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
- * Generic Object type for request data
15
- */
16
- type RequestData = Record<string, any>;
17
- //#endregion
18
- //#region types/express.d.ts
19
- /**
20
- * Middleware function type
21
- */
22
- type Middleware$1 = (req: Request, res: Response$1, next: NextFunction) => any | Promise<any>;
23
- //#endregion
24
4
  //#region types/h3.d.ts
25
5
  /**
26
6
  * HTTP context passed to route handlers
@@ -33,20 +13,38 @@ type RouteHandler = (
33
13
  /**
34
14
  * H3 event context
35
15
  */
36
-
37
16
  ctx: HttpContext,
38
17
  /**
39
18
  * ClearRequest instance
40
19
  */
41
-
42
20
  req: ClearRequest) => any | Promise<any>;
43
21
  /**
44
22
  * Handler can be either a function or controller reference
45
23
  */
46
24
  type Handler = RouteHandler | ControllerHandler;
47
25
  //#endregion
26
+ //#region types/basic.d.ts
27
+ /**
28
+ * Controller method reference
29
+ */
30
+ type ControllerHandler = [any, string];
31
+ /**
32
+ * HTTP methods supported by the router
33
+ */
34
+ type HttpMethod = 'get' | 'post' | 'put' | 'delete' | 'patch' | 'options' | 'head';
35
+ /**
36
+ * Generic Object type for request data
37
+ */
38
+ type RequestData = Record<string, any>;
39
+ //#endregion
40
+ //#region types/express.d.ts
41
+ /**
42
+ * Middleware function type
43
+ */
44
+ type Middleware = (req: Request, res: Response$1, next: NextFunction) => any | Promise<any>;
45
+ //#endregion
48
46
  //#region src/Route.d.ts
49
- declare class Route<X = any, M = Middleware | Middleware$1> {
47
+ declare class Route<X = any, M = Middleware$1 | Middleware> {
50
48
  ctx: X;
51
49
  body: RequestData;
52
50
  query: RequestData;
@@ -64,7 +62,7 @@ declare class Route<X = any, M = Middleware | Middleware$1> {
64
62
  }
65
63
  //#endregion
66
64
  //#region src/ClearRequest.d.ts
67
- declare class ClearRequest<X = any, M = Middleware | Middleware$1> {
65
+ declare class ClearRequest<X = any, M = Middleware$1 | Middleware> {
68
66
  [key: string]: any;
69
67
  /**
70
68
  * @param body - Parsed request body
@@ -1,7 +1,7 @@
1
- import { RequestData } from "../types/basic.mjs";
2
1
  import { Middleware } from "../types/h3.mjs";
3
2
  import { Route } from "./Route.mjs";
4
3
  import { Middleware as Middleware$1 } from "../types/express.mjs";
4
+ import { RequestData } from "../types/basic.mjs";
5
5
 
6
6
  //#region src/ClearRequest.d.ts
7
7
  declare class ClearRequest<X = any, M = Middleware | Middleware$1> {
@@ -1,7 +1,7 @@
1
- import { HttpMethod, RequestData } from "../types/basic.mjs";
2
1
  import { Handler, Middleware } from "../types/h3.mjs";
3
2
  import { ClearRequest } from "./ClearRequest.mjs";
4
3
  import { Middleware as Middleware$1 } from "../types/express.mjs";
4
+ import { HttpMethod, RequestData } from "../types/basic.mjs";
5
5
 
6
6
  //#region src/Route.d.ts
7
7
  declare class Route<X = any, M = Middleware | Middleware$1> {
@@ -1,3 +1,6 @@
1
+ import { Middleware } from "./h3.mjs";
2
+ import { Middleware as Middleware$1 } from "./express.mjs";
3
+
1
4
  //#region types/basic.d.ts
2
5
  /**
3
6
  * Controller method reference
@@ -15,5 +18,6 @@ type ControllerAction = 'index' | 'show' | 'create' | 'update' | 'destroy';
15
18
  * Generic Object type for request data
16
19
  */
17
20
  type RequestData = Record<string, any>;
21
+ type ApiResourceMiddleware<M extends Middleware$1 | Middleware> = M | M[] | { [K in ControllerAction]?: M | M[] };
18
22
  //#endregion
19
- export { ControllerAction, ControllerHandler, HttpMethod, RequestData };
23
+ export { ApiResourceMiddleware, ControllerAction, ControllerHandler, HttpMethod, RequestData };
@@ -1,5 +1,5 @@
1
- import { ControllerHandler } from "./basic.mjs";
2
1
  import { ClearRequest } from "./ClearRequest.mjs";
2
+ import { ControllerHandler } from "./basic.mjs";
3
3
  import { NextFunction, Request, Response } from "express";
4
4
 
5
5
  //#region types/express.d.ts
@@ -1,5 +1,5 @@
1
- import { ControllerHandler } from "./basic.mjs";
2
1
  import { ClearRequest } from "./ClearRequest.mjs";
2
+ import { ControllerHandler } from "./basic.mjs";
3
3
  import { H3, H3Event, Middleware, TypedServerRequest } from "h3";
4
4
 
5
5
  //#region types/h3.d.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clear-router",
3
- "version": "2.1.7",
3
+ "version": "2.1.9",
4
4
  "description": "Laravel-style routing system for Express.js and H3, with CommonJS, ESM, and TypeScript support",
5
5
  "keywords": [
6
6
  "h3",
@@ -90,6 +90,7 @@
90
90
  "lint": "eslint",
91
91
  "test:esm": "vitest tests/esm.test.ts",
92
92
  "test:ts": "vitest tests/typescript.test.ts",
93
+ "test:coverage": "vitest run --coverage",
93
94
  "example": "tsx example/express/index.ts",
94
95
  "example:esm": "tsx example/express/esm.ts",
95
96
  "example:ts": "tsx example/express/typescript.ts",