clear-router 2.1.9 → 2.1.11
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 +1 -0
- package/dist/{basic-Chn8OGPD.d.cts → basic-DXbqD6cP.d.cts} +12 -1
- package/dist/{basic-DJmmZq1h.d.mts → basic-vvrFwa_Y.d.mts} +12 -1
- package/dist/express/index.cjs +80 -1
- package/dist/express/index.d.cts +13 -4
- package/dist/express/index.d.mts +13 -4
- package/dist/express/index.mjs +80 -1
- package/dist/h3/index.cjs +76 -3
- package/dist/h3/index.d.cts +10 -5
- package/dist/h3/index.d.mts +10 -5
- package/dist/h3/index.mjs +76 -3
- package/dist/types/basic.d.mts +12 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -42,6 +42,7 @@ yarn add clear-router express
|
|
|
42
42
|
|
|
43
43
|
- Simple and clean route declarations (get, post, put, delete, patch, options, head)
|
|
44
44
|
- Grouped routes with prefix
|
|
45
|
+
- Method override support via body or header keys (configurable)
|
|
45
46
|
- Middleware stack: per-route and group-level
|
|
46
47
|
- Controller-method pair as route handler
|
|
47
48
|
- Supports HttpContext style handlers: { req, res, next }
|
|
@@ -115,5 +115,16 @@ type ControllerAction = 'index' | 'show' | 'create' | 'update' | 'destroy';
|
|
|
115
115
|
*/
|
|
116
116
|
type RequestData = Record<string, any>;
|
|
117
117
|
type ApiResourceMiddleware<M extends Middleware | Middleware$1> = M | M[] | { [K in ControllerAction]?: M | M[] };
|
|
118
|
+
interface RouterConfig {
|
|
119
|
+
/**
|
|
120
|
+
* Configuration for method override functionality, allowing clients to use a
|
|
121
|
+
* specific header or body parameter to override the HTTP method.
|
|
122
|
+
*/
|
|
123
|
+
methodOverride?: {
|
|
124
|
+
/** Whether method override is enabled */enabled?: boolean; /** Keys in the request body to check for method override */
|
|
125
|
+
bodyKeys?: string[] | string; /** Keys in the request headers to check for method override */
|
|
126
|
+
headerKeys?: string[] | string;
|
|
127
|
+
};
|
|
128
|
+
}
|
|
118
129
|
//#endregion
|
|
119
|
-
export {
|
|
130
|
+
export { Handler as a, Route as c, HttpContext$1 as d, Middleware$1 as f, RouterConfig as i, H3App as l, ControllerAction as n, HttpContext as o, HttpMethod as r, Middleware as s, ApiResourceMiddleware as t, Handler$1 as u };
|
|
@@ -115,5 +115,16 @@ type ControllerAction = 'index' | 'show' | 'create' | 'update' | 'destroy';
|
|
|
115
115
|
*/
|
|
116
116
|
type RequestData = Record<string, any>;
|
|
117
117
|
type ApiResourceMiddleware<M extends Middleware | Middleware$1> = M | M[] | { [K in ControllerAction]?: M | M[] };
|
|
118
|
+
interface RouterConfig {
|
|
119
|
+
/**
|
|
120
|
+
* Configuration for method override functionality, allowing clients to use a
|
|
121
|
+
* specific header or body parameter to override the HTTP method.
|
|
122
|
+
*/
|
|
123
|
+
methodOverride?: {
|
|
124
|
+
/** Whether method override is enabled */enabled?: boolean; /** Keys in the request body to check for method override */
|
|
125
|
+
bodyKeys?: string[] | string; /** Keys in the request headers to check for method override */
|
|
126
|
+
headerKeys?: string[] | string;
|
|
127
|
+
};
|
|
128
|
+
}
|
|
118
129
|
//#endregion
|
|
119
|
-
export {
|
|
130
|
+
export { Handler as a, Route as c, HttpContext$1 as d, Middleware$1 as f, RouterConfig as i, H3App as l, ControllerAction as n, HttpContext as o, HttpMethod as r, Middleware as s, ApiResourceMiddleware as t, Handler$1 as u };
|
package/dist/express/index.cjs
CHANGED
|
@@ -11,6 +11,11 @@ let node_async_hooks = require("node:async_hooks");
|
|
|
11
11
|
* @repository https://github.com/toneflix/clear-router
|
|
12
12
|
*/
|
|
13
13
|
var Router = class Router {
|
|
14
|
+
static config = { methodOverride: {
|
|
15
|
+
enabled: true,
|
|
16
|
+
bodyKeys: ["_method"],
|
|
17
|
+
headerKeys: ["x-http-method"]
|
|
18
|
+
} };
|
|
14
19
|
static groupContext = new node_async_hooks.AsyncLocalStorage();
|
|
15
20
|
/**
|
|
16
21
|
* All registered routes
|
|
@@ -45,6 +50,53 @@ var Router = class Router {
|
|
|
45
50
|
return "/" + path.split("/").filter(Boolean).join("/");
|
|
46
51
|
}
|
|
47
52
|
/**
|
|
53
|
+
* Configure router settings to modify behavior.
|
|
54
|
+
*
|
|
55
|
+
* @param options - Configuration options for the router
|
|
56
|
+
* @returns
|
|
57
|
+
*/
|
|
58
|
+
static configure(options) {
|
|
59
|
+
if (!this.config.methodOverride) this.config.methodOverride = {
|
|
60
|
+
enabled: true,
|
|
61
|
+
bodyKeys: ["_method"],
|
|
62
|
+
headerKeys: ["x-http-method"]
|
|
63
|
+
};
|
|
64
|
+
const override = options?.methodOverride;
|
|
65
|
+
if (!override) return;
|
|
66
|
+
if (typeof override.enabled === "boolean") this.config.methodOverride.enabled = override.enabled;
|
|
67
|
+
const bodyKeys = override.bodyKeys;
|
|
68
|
+
if (typeof bodyKeys !== "undefined") this.config.methodOverride.bodyKeys = (Array.isArray(bodyKeys) ? bodyKeys : [bodyKeys]).map((e) => String(e).trim()).filter(Boolean);
|
|
69
|
+
const headerKeys = override.headerKeys;
|
|
70
|
+
if (typeof headerKeys !== "undefined") this.config.methodOverride.headerKeys = (Array.isArray(headerKeys) ? headerKeys : [headerKeys]).map((e) => String(e).trim().toLowerCase()).filter(Boolean);
|
|
71
|
+
}
|
|
72
|
+
static resolveMethodOverride(method, headers, body) {
|
|
73
|
+
if (!this.config.methodOverride?.enabled || method.toLowerCase() !== "post") return null;
|
|
74
|
+
let override;
|
|
75
|
+
for (const key of this.config.methodOverride?.headerKeys || []) {
|
|
76
|
+
const value = headers?.[key];
|
|
77
|
+
if (Array.isArray(value) ? value[0] : value) {
|
|
78
|
+
override = Array.isArray(value) ? value[0] : value;
|
|
79
|
+
break;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
if (!override && body && typeof body === "object") for (const key of this.config.methodOverride?.bodyKeys || []) {
|
|
83
|
+
const value = body[key];
|
|
84
|
+
if (typeof value !== "undefined" && value !== null && value !== "") {
|
|
85
|
+
override = value;
|
|
86
|
+
break;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
const normalized = String(override || "").trim().toLowerCase();
|
|
90
|
+
if (!normalized) return null;
|
|
91
|
+
if ([
|
|
92
|
+
"put",
|
|
93
|
+
"patch",
|
|
94
|
+
"delete",
|
|
95
|
+
"post"
|
|
96
|
+
].includes(normalized)) return normalized;
|
|
97
|
+
return null;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
48
100
|
* Add a route with specified HTTP methods, path, handler, and middlewares
|
|
49
101
|
* @param methods - HTTP method(s) for the route
|
|
50
102
|
* @param path - Route path
|
|
@@ -256,7 +308,34 @@ var Router = class Router {
|
|
|
256
308
|
console.error("[ROUTES]", error.message);
|
|
257
309
|
throw error;
|
|
258
310
|
}
|
|
259
|
-
router[method](route.path,
|
|
311
|
+
router[method](route.path, (req, res, next) => {
|
|
312
|
+
const override = Router.resolveMethodOverride(req.method, req.headers, req.body);
|
|
313
|
+
if (method === "post" && override && override !== "post") return next("route");
|
|
314
|
+
return next();
|
|
315
|
+
}, ...route.middlewares || [], async (req, res, next) => {
|
|
316
|
+
try {
|
|
317
|
+
const ctx = {
|
|
318
|
+
req,
|
|
319
|
+
res,
|
|
320
|
+
next
|
|
321
|
+
};
|
|
322
|
+
const inst = instance ?? route;
|
|
323
|
+
await Router.bindRequestToInstance(ctx, inst, route);
|
|
324
|
+
const result = handlerFunction(ctx, inst.clearRequest);
|
|
325
|
+
await Promise.resolve(result);
|
|
326
|
+
} catch (error) {
|
|
327
|
+
next(error);
|
|
328
|
+
}
|
|
329
|
+
});
|
|
330
|
+
if ([
|
|
331
|
+
"put",
|
|
332
|
+
"patch",
|
|
333
|
+
"delete"
|
|
334
|
+
].includes(method)) router.post(route.path, (req, res, next) => {
|
|
335
|
+
if (Router.resolveMethodOverride(req.method, req.headers, req.body) !== method) return next("route");
|
|
336
|
+
req.method = method.toUpperCase();
|
|
337
|
+
return next();
|
|
338
|
+
}, ...route.middlewares || [], async (req, res, next) => {
|
|
260
339
|
try {
|
|
261
340
|
const ctx = {
|
|
262
341
|
req,
|
package/dist/express/index.d.cts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as
|
|
1
|
+
import { a as Handler, c as Route, i as RouterConfig, n as ControllerAction, o as HttpContext, r as HttpMethod, s as Middleware, t as ApiResourceMiddleware } from "../basic-DXbqD6cP.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
|
+
static config: RouterConfig;
|
|
13
14
|
private static readonly groupContext;
|
|
14
15
|
/**
|
|
15
16
|
* All registered routes
|
|
@@ -41,6 +42,14 @@ declare class Router {
|
|
|
41
42
|
* @returns Normalized path
|
|
42
43
|
*/
|
|
43
44
|
static normalizePath(path: string): string;
|
|
45
|
+
/**
|
|
46
|
+
* Configure router settings to modify behavior.
|
|
47
|
+
*
|
|
48
|
+
* @param options - Configuration options for the router
|
|
49
|
+
* @returns
|
|
50
|
+
*/
|
|
51
|
+
static configure(options?: RouterConfig): void;
|
|
52
|
+
private static resolveMethodOverride;
|
|
44
53
|
/**
|
|
45
54
|
* Add a route with specified HTTP methods, path, handler, and middlewares
|
|
46
55
|
* @param methods - HTTP method(s) for the route
|
|
@@ -127,9 +136,9 @@ declare class Router {
|
|
|
127
136
|
* Get all registered routes with their information
|
|
128
137
|
* @returns Array of route information objects
|
|
129
138
|
*/
|
|
130
|
-
static allRoutes(
|
|
131
|
-
static allRoutes(type
|
|
132
|
-
static allRoutes(type
|
|
139
|
+
static allRoutes(): Array<Route<HttpContext, Middleware>>;
|
|
140
|
+
static allRoutes(type: 'path'): Record<string, Route<HttpContext, Middleware>>;
|
|
141
|
+
static allRoutes(type: 'method'): { [method in Uppercase<HttpMethod>]?: Array<Route<HttpContext, Middleware>> };
|
|
133
142
|
/**
|
|
134
143
|
* Apply all registered routes to the provided Express Router instance
|
|
135
144
|
* Handles controller-method binding and middleware application
|
package/dist/express/index.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as
|
|
1
|
+
import { a as Handler, c as Route, i as RouterConfig, n as ControllerAction, o as HttpContext, r as HttpMethod, s as Middleware, t as ApiResourceMiddleware } from "../basic-vvrFwa_Y.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
|
+
static config: RouterConfig;
|
|
13
14
|
private static readonly groupContext;
|
|
14
15
|
/**
|
|
15
16
|
* All registered routes
|
|
@@ -41,6 +42,14 @@ declare class Router {
|
|
|
41
42
|
* @returns Normalized path
|
|
42
43
|
*/
|
|
43
44
|
static normalizePath(path: string): string;
|
|
45
|
+
/**
|
|
46
|
+
* Configure router settings to modify behavior.
|
|
47
|
+
*
|
|
48
|
+
* @param options - Configuration options for the router
|
|
49
|
+
* @returns
|
|
50
|
+
*/
|
|
51
|
+
static configure(options?: RouterConfig): void;
|
|
52
|
+
private static resolveMethodOverride;
|
|
44
53
|
/**
|
|
45
54
|
* Add a route with specified HTTP methods, path, handler, and middlewares
|
|
46
55
|
* @param methods - HTTP method(s) for the route
|
|
@@ -127,9 +136,9 @@ declare class Router {
|
|
|
127
136
|
* Get all registered routes with their information
|
|
128
137
|
* @returns Array of route information objects
|
|
129
138
|
*/
|
|
130
|
-
static allRoutes(
|
|
131
|
-
static allRoutes(type
|
|
132
|
-
static allRoutes(type
|
|
139
|
+
static allRoutes(): Array<Route<HttpContext, Middleware>>;
|
|
140
|
+
static allRoutes(type: 'path'): Record<string, Route<HttpContext, Middleware>>;
|
|
141
|
+
static allRoutes(type: 'method'): { [method in Uppercase<HttpMethod>]?: Array<Route<HttpContext, Middleware>> };
|
|
133
142
|
/**
|
|
134
143
|
* Apply all registered routes to the provided Express Router instance
|
|
135
144
|
* Handles controller-method binding and middleware application
|
package/dist/express/index.mjs
CHANGED
|
@@ -10,6 +10,11 @@ import { AsyncLocalStorage } from "node:async_hooks";
|
|
|
10
10
|
* @repository https://github.com/toneflix/clear-router
|
|
11
11
|
*/
|
|
12
12
|
var Router = class Router {
|
|
13
|
+
static config = { methodOverride: {
|
|
14
|
+
enabled: true,
|
|
15
|
+
bodyKeys: ["_method"],
|
|
16
|
+
headerKeys: ["x-http-method"]
|
|
17
|
+
} };
|
|
13
18
|
static groupContext = new AsyncLocalStorage();
|
|
14
19
|
/**
|
|
15
20
|
* All registered routes
|
|
@@ -44,6 +49,53 @@ var Router = class Router {
|
|
|
44
49
|
return "/" + path.split("/").filter(Boolean).join("/");
|
|
45
50
|
}
|
|
46
51
|
/**
|
|
52
|
+
* Configure router settings to modify behavior.
|
|
53
|
+
*
|
|
54
|
+
* @param options - Configuration options for the router
|
|
55
|
+
* @returns
|
|
56
|
+
*/
|
|
57
|
+
static configure(options) {
|
|
58
|
+
if (!this.config.methodOverride) this.config.methodOverride = {
|
|
59
|
+
enabled: true,
|
|
60
|
+
bodyKeys: ["_method"],
|
|
61
|
+
headerKeys: ["x-http-method"]
|
|
62
|
+
};
|
|
63
|
+
const override = options?.methodOverride;
|
|
64
|
+
if (!override) return;
|
|
65
|
+
if (typeof override.enabled === "boolean") this.config.methodOverride.enabled = override.enabled;
|
|
66
|
+
const bodyKeys = override.bodyKeys;
|
|
67
|
+
if (typeof bodyKeys !== "undefined") this.config.methodOverride.bodyKeys = (Array.isArray(bodyKeys) ? bodyKeys : [bodyKeys]).map((e) => String(e).trim()).filter(Boolean);
|
|
68
|
+
const headerKeys = override.headerKeys;
|
|
69
|
+
if (typeof headerKeys !== "undefined") this.config.methodOverride.headerKeys = (Array.isArray(headerKeys) ? headerKeys : [headerKeys]).map((e) => String(e).trim().toLowerCase()).filter(Boolean);
|
|
70
|
+
}
|
|
71
|
+
static resolveMethodOverride(method, headers, body) {
|
|
72
|
+
if (!this.config.methodOverride?.enabled || method.toLowerCase() !== "post") return null;
|
|
73
|
+
let override;
|
|
74
|
+
for (const key of this.config.methodOverride?.headerKeys || []) {
|
|
75
|
+
const value = headers?.[key];
|
|
76
|
+
if (Array.isArray(value) ? value[0] : value) {
|
|
77
|
+
override = Array.isArray(value) ? value[0] : value;
|
|
78
|
+
break;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
if (!override && body && typeof body === "object") for (const key of this.config.methodOverride?.bodyKeys || []) {
|
|
82
|
+
const value = body[key];
|
|
83
|
+
if (typeof value !== "undefined" && value !== null && value !== "") {
|
|
84
|
+
override = value;
|
|
85
|
+
break;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
const normalized = String(override || "").trim().toLowerCase();
|
|
89
|
+
if (!normalized) return null;
|
|
90
|
+
if ([
|
|
91
|
+
"put",
|
|
92
|
+
"patch",
|
|
93
|
+
"delete",
|
|
94
|
+
"post"
|
|
95
|
+
].includes(normalized)) return normalized;
|
|
96
|
+
return null;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
47
99
|
* Add a route with specified HTTP methods, path, handler, and middlewares
|
|
48
100
|
* @param methods - HTTP method(s) for the route
|
|
49
101
|
* @param path - Route path
|
|
@@ -255,7 +307,34 @@ var Router = class Router {
|
|
|
255
307
|
console.error("[ROUTES]", error.message);
|
|
256
308
|
throw error;
|
|
257
309
|
}
|
|
258
|
-
router[method](route.path,
|
|
310
|
+
router[method](route.path, (req, res, next) => {
|
|
311
|
+
const override = Router.resolveMethodOverride(req.method, req.headers, req.body);
|
|
312
|
+
if (method === "post" && override && override !== "post") return next("route");
|
|
313
|
+
return next();
|
|
314
|
+
}, ...route.middlewares || [], async (req, res, next) => {
|
|
315
|
+
try {
|
|
316
|
+
const ctx = {
|
|
317
|
+
req,
|
|
318
|
+
res,
|
|
319
|
+
next
|
|
320
|
+
};
|
|
321
|
+
const inst = instance ?? route;
|
|
322
|
+
await Router.bindRequestToInstance(ctx, inst, route);
|
|
323
|
+
const result = handlerFunction(ctx, inst.clearRequest);
|
|
324
|
+
await Promise.resolve(result);
|
|
325
|
+
} catch (error) {
|
|
326
|
+
next(error);
|
|
327
|
+
}
|
|
328
|
+
});
|
|
329
|
+
if ([
|
|
330
|
+
"put",
|
|
331
|
+
"patch",
|
|
332
|
+
"delete"
|
|
333
|
+
].includes(method)) router.post(route.path, (req, res, next) => {
|
|
334
|
+
if (Router.resolveMethodOverride(req.method, req.headers, req.body) !== method) return next("route");
|
|
335
|
+
req.method = method.toUpperCase();
|
|
336
|
+
return next();
|
|
337
|
+
}, ...route.middlewares || [], async (req, res, next) => {
|
|
259
338
|
try {
|
|
260
339
|
const ctx = {
|
|
261
340
|
req,
|
package/dist/h3/index.cjs
CHANGED
|
@@ -11,6 +11,12 @@ let h3 = require("h3");
|
|
|
11
11
|
* @repository https://github.com/toneflix/clear-router
|
|
12
12
|
*/
|
|
13
13
|
var Router = class Router {
|
|
14
|
+
static config = { methodOverride: {
|
|
15
|
+
enabled: true,
|
|
16
|
+
bodyKeys: ["_method"],
|
|
17
|
+
headerKeys: ["x-http-method"]
|
|
18
|
+
} };
|
|
19
|
+
static bodyCache = /* @__PURE__ */ new WeakMap();
|
|
14
20
|
static groupContext = new node_async_hooks.AsyncLocalStorage();
|
|
15
21
|
/**
|
|
16
22
|
* All registered routes
|
|
@@ -44,6 +50,53 @@ var Router = class Router {
|
|
|
44
50
|
static normalizePath(path) {
|
|
45
51
|
return "/" + path.split("/").filter(Boolean).join("/");
|
|
46
52
|
}
|
|
53
|
+
static configure(options) {
|
|
54
|
+
if (!this.config.methodOverride) this.config.methodOverride = {
|
|
55
|
+
enabled: true,
|
|
56
|
+
bodyKeys: ["_method"],
|
|
57
|
+
headerKeys: ["x-http-method"]
|
|
58
|
+
};
|
|
59
|
+
const override = options?.methodOverride;
|
|
60
|
+
if (!override) return;
|
|
61
|
+
if (typeof override.enabled === "boolean") this.config.methodOverride.enabled = override.enabled;
|
|
62
|
+
const bodyKeys = override.bodyKeys;
|
|
63
|
+
if (typeof bodyKeys !== "undefined") this.config.methodOverride.bodyKeys = (Array.isArray(bodyKeys) ? bodyKeys : [bodyKeys]).map((e) => String(e).trim()).filter(Boolean);
|
|
64
|
+
const headerKeys = override.headerKeys;
|
|
65
|
+
if (typeof headerKeys !== "undefined") this.config.methodOverride.headerKeys = (Array.isArray(headerKeys) ? headerKeys : [headerKeys]).map((e) => String(e).trim().toLowerCase()).filter(Boolean);
|
|
66
|
+
}
|
|
67
|
+
static async readBodyCached(ctx) {
|
|
68
|
+
if (this.bodyCache.has(ctx)) return this.bodyCache.get(ctx);
|
|
69
|
+
const body = await (0, h3.readBody)(ctx) ?? {};
|
|
70
|
+
this.bodyCache.set(ctx, body);
|
|
71
|
+
return body;
|
|
72
|
+
}
|
|
73
|
+
static resolveMethodOverride(method, headers, body) {
|
|
74
|
+
if (!this.config.methodOverride?.enabled || method.toLowerCase() !== "post") return null;
|
|
75
|
+
let override;
|
|
76
|
+
for (const key of this.config.methodOverride?.headerKeys || []) {
|
|
77
|
+
const value = headers.get(key);
|
|
78
|
+
if (value) {
|
|
79
|
+
override = value;
|
|
80
|
+
break;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
if (!override && body && typeof body === "object") for (const key of this.config.methodOverride?.bodyKeys || []) {
|
|
84
|
+
const value = body[key];
|
|
85
|
+
if (typeof value !== "undefined" && value !== null && value !== "") {
|
|
86
|
+
override = value;
|
|
87
|
+
break;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
const normalized = String(override || "").trim().toLowerCase();
|
|
91
|
+
if (!normalized) return null;
|
|
92
|
+
if ([
|
|
93
|
+
"put",
|
|
94
|
+
"patch",
|
|
95
|
+
"delete",
|
|
96
|
+
"post"
|
|
97
|
+
].includes(normalized)) return normalized;
|
|
98
|
+
return null;
|
|
99
|
+
}
|
|
47
100
|
/**
|
|
48
101
|
* Add a route with specified HTTP methods, path, handler, and middlewares
|
|
49
102
|
* @param methods - HTTP method(s) for the route
|
|
@@ -266,8 +319,28 @@ var Router = class Router {
|
|
|
266
319
|
app[method](route.path, async (event) => {
|
|
267
320
|
try {
|
|
268
321
|
const ctx = event;
|
|
322
|
+
const reqBody = await Router.readBodyCached(ctx);
|
|
323
|
+
const override = Router.resolveMethodOverride(ctx.req.method, ctx.req.headers, reqBody);
|
|
324
|
+
if (method === "post" && override && override !== "post") return;
|
|
325
|
+
const inst = instance ?? route;
|
|
326
|
+
await Router.bindRequestToInstance(ctx, inst, route, reqBody);
|
|
327
|
+
const result = handlerFunction(ctx, inst.clearRequest);
|
|
328
|
+
return await Promise.resolve(result);
|
|
329
|
+
} catch (error) {
|
|
330
|
+
return error;
|
|
331
|
+
}
|
|
332
|
+
}, { middleware: route.middlewares });
|
|
333
|
+
if ([
|
|
334
|
+
"put",
|
|
335
|
+
"patch",
|
|
336
|
+
"delete"
|
|
337
|
+
].includes(method)) app.post(route.path, async (event) => {
|
|
338
|
+
try {
|
|
339
|
+
const ctx = event;
|
|
340
|
+
const reqBody = await Router.readBodyCached(ctx);
|
|
341
|
+
if (Router.resolveMethodOverride(ctx.req.method, ctx.req.headers, reqBody) !== method) return;
|
|
269
342
|
const inst = instance ?? route;
|
|
270
|
-
await Router.bindRequestToInstance(ctx, inst, route);
|
|
343
|
+
await Router.bindRequestToInstance(ctx, inst, route, reqBody);
|
|
271
344
|
const result = handlerFunction(ctx, inst.clearRequest);
|
|
272
345
|
return await Promise.resolve(result);
|
|
273
346
|
} catch (error) {
|
|
@@ -278,10 +351,10 @@ var Router = class Router {
|
|
|
278
351
|
}
|
|
279
352
|
return app;
|
|
280
353
|
}
|
|
281
|
-
static async bindRequestToInstance(ctx, instance, route) {
|
|
354
|
+
static async bindRequestToInstance(ctx, instance, route, body) {
|
|
282
355
|
if (!instance) return;
|
|
283
356
|
instance.ctx = ctx;
|
|
284
|
-
instance.body = await
|
|
357
|
+
instance.body = body ?? await Router.readBodyCached(ctx);
|
|
285
358
|
instance.query = (0, h3.getQuery)(ctx);
|
|
286
359
|
instance.params = (0, h3.getRouterParams)(ctx, { decode: true });
|
|
287
360
|
instance.clearRequest = new require_Route.ClearRequest({
|
package/dist/h3/index.d.cts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { c as
|
|
1
|
+
import { c as Route, d as HttpContext, f as Middleware, i as RouterConfig, l as H3App, n as ControllerAction, r as HttpMethod, t as ApiResourceMiddleware, u as Handler } from "../basic-DXbqD6cP.cjs";
|
|
2
2
|
import { H3 } from "h3";
|
|
3
3
|
|
|
4
4
|
//#region src/h3/router.d.ts
|
|
@@ -9,6 +9,8 @@ import { H3 } from "h3";
|
|
|
9
9
|
* @repository https://github.com/toneflix/clear-router
|
|
10
10
|
*/
|
|
11
11
|
declare class Router {
|
|
12
|
+
static config: RouterConfig;
|
|
13
|
+
private static readonly bodyCache;
|
|
12
14
|
private static readonly groupContext;
|
|
13
15
|
/**
|
|
14
16
|
* All registered routes
|
|
@@ -40,6 +42,9 @@ declare class Router {
|
|
|
40
42
|
* @returns Normalized path
|
|
41
43
|
*/
|
|
42
44
|
static normalizePath(path: string): string;
|
|
45
|
+
static configure(options?: RouterConfig): void;
|
|
46
|
+
private static readBodyCached;
|
|
47
|
+
private static resolveMethodOverride;
|
|
43
48
|
/**
|
|
44
49
|
* Add a route with specified HTTP methods, path, handler, and middlewares
|
|
45
50
|
* @param methods - HTTP method(s) for the route
|
|
@@ -125,10 +130,10 @@ declare class Router {
|
|
|
125
130
|
/**
|
|
126
131
|
* Get all registered routes with their information
|
|
127
132
|
* @returns Array of route information objects
|
|
128
|
-
|
|
129
|
-
static allRoutes(
|
|
130
|
-
static allRoutes(type
|
|
131
|
-
static allRoutes(type
|
|
133
|
+
*/
|
|
134
|
+
static allRoutes(): Array<Route<HttpContext, Middleware>>;
|
|
135
|
+
static allRoutes(type: 'path'): Record<string, Route<HttpContext, Middleware>>;
|
|
136
|
+
static allRoutes(type: 'method'): { [method in Uppercase<HttpMethod>]?: Array<Route<HttpContext, Middleware>> };
|
|
132
137
|
/**
|
|
133
138
|
* Apply all registered routes to the provided H3 Router instance
|
|
134
139
|
* Handles controller-method binding and middleware application
|
package/dist/h3/index.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { c as
|
|
1
|
+
import { c as Route, d as HttpContext, f as Middleware, i as RouterConfig, l as H3App, n as ControllerAction, r as HttpMethod, t as ApiResourceMiddleware, u as Handler } from "../basic-vvrFwa_Y.mjs";
|
|
2
2
|
import { H3 } from "h3";
|
|
3
3
|
|
|
4
4
|
//#region src/h3/router.d.ts
|
|
@@ -9,6 +9,8 @@ import { H3 } from "h3";
|
|
|
9
9
|
* @repository https://github.com/toneflix/clear-router
|
|
10
10
|
*/
|
|
11
11
|
declare class Router {
|
|
12
|
+
static config: RouterConfig;
|
|
13
|
+
private static readonly bodyCache;
|
|
12
14
|
private static readonly groupContext;
|
|
13
15
|
/**
|
|
14
16
|
* All registered routes
|
|
@@ -40,6 +42,9 @@ declare class Router {
|
|
|
40
42
|
* @returns Normalized path
|
|
41
43
|
*/
|
|
42
44
|
static normalizePath(path: string): string;
|
|
45
|
+
static configure(options?: RouterConfig): void;
|
|
46
|
+
private static readBodyCached;
|
|
47
|
+
private static resolveMethodOverride;
|
|
43
48
|
/**
|
|
44
49
|
* Add a route with specified HTTP methods, path, handler, and middlewares
|
|
45
50
|
* @param methods - HTTP method(s) for the route
|
|
@@ -125,10 +130,10 @@ declare class Router {
|
|
|
125
130
|
/**
|
|
126
131
|
* Get all registered routes with their information
|
|
127
132
|
* @returns Array of route information objects
|
|
128
|
-
|
|
129
|
-
static allRoutes(
|
|
130
|
-
static allRoutes(type
|
|
131
|
-
static allRoutes(type
|
|
133
|
+
*/
|
|
134
|
+
static allRoutes(): Array<Route<HttpContext, Middleware>>;
|
|
135
|
+
static allRoutes(type: 'path'): Record<string, Route<HttpContext, Middleware>>;
|
|
136
|
+
static allRoutes(type: 'method'): { [method in Uppercase<HttpMethod>]?: Array<Route<HttpContext, Middleware>> };
|
|
132
137
|
/**
|
|
133
138
|
* Apply all registered routes to the provided H3 Router instance
|
|
134
139
|
* Handles controller-method binding and middleware application
|
package/dist/h3/index.mjs
CHANGED
|
@@ -10,6 +10,12 @@ import { getQuery, getRouterParams, readBody } from "h3";
|
|
|
10
10
|
* @repository https://github.com/toneflix/clear-router
|
|
11
11
|
*/
|
|
12
12
|
var Router = class Router {
|
|
13
|
+
static config = { methodOverride: {
|
|
14
|
+
enabled: true,
|
|
15
|
+
bodyKeys: ["_method"],
|
|
16
|
+
headerKeys: ["x-http-method"]
|
|
17
|
+
} };
|
|
18
|
+
static bodyCache = /* @__PURE__ */ new WeakMap();
|
|
13
19
|
static groupContext = new AsyncLocalStorage();
|
|
14
20
|
/**
|
|
15
21
|
* All registered routes
|
|
@@ -43,6 +49,53 @@ var Router = class Router {
|
|
|
43
49
|
static normalizePath(path) {
|
|
44
50
|
return "/" + path.split("/").filter(Boolean).join("/");
|
|
45
51
|
}
|
|
52
|
+
static configure(options) {
|
|
53
|
+
if (!this.config.methodOverride) this.config.methodOverride = {
|
|
54
|
+
enabled: true,
|
|
55
|
+
bodyKeys: ["_method"],
|
|
56
|
+
headerKeys: ["x-http-method"]
|
|
57
|
+
};
|
|
58
|
+
const override = options?.methodOverride;
|
|
59
|
+
if (!override) return;
|
|
60
|
+
if (typeof override.enabled === "boolean") this.config.methodOverride.enabled = override.enabled;
|
|
61
|
+
const bodyKeys = override.bodyKeys;
|
|
62
|
+
if (typeof bodyKeys !== "undefined") this.config.methodOverride.bodyKeys = (Array.isArray(bodyKeys) ? bodyKeys : [bodyKeys]).map((e) => String(e).trim()).filter(Boolean);
|
|
63
|
+
const headerKeys = override.headerKeys;
|
|
64
|
+
if (typeof headerKeys !== "undefined") this.config.methodOverride.headerKeys = (Array.isArray(headerKeys) ? headerKeys : [headerKeys]).map((e) => String(e).trim().toLowerCase()).filter(Boolean);
|
|
65
|
+
}
|
|
66
|
+
static async readBodyCached(ctx) {
|
|
67
|
+
if (this.bodyCache.has(ctx)) return this.bodyCache.get(ctx);
|
|
68
|
+
const body = await readBody(ctx) ?? {};
|
|
69
|
+
this.bodyCache.set(ctx, body);
|
|
70
|
+
return body;
|
|
71
|
+
}
|
|
72
|
+
static resolveMethodOverride(method, headers, body) {
|
|
73
|
+
if (!this.config.methodOverride?.enabled || method.toLowerCase() !== "post") return null;
|
|
74
|
+
let override;
|
|
75
|
+
for (const key of this.config.methodOverride?.headerKeys || []) {
|
|
76
|
+
const value = headers.get(key);
|
|
77
|
+
if (value) {
|
|
78
|
+
override = value;
|
|
79
|
+
break;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
if (!override && body && typeof body === "object") for (const key of this.config.methodOverride?.bodyKeys || []) {
|
|
83
|
+
const value = body[key];
|
|
84
|
+
if (typeof value !== "undefined" && value !== null && value !== "") {
|
|
85
|
+
override = value;
|
|
86
|
+
break;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
const normalized = String(override || "").trim().toLowerCase();
|
|
90
|
+
if (!normalized) return null;
|
|
91
|
+
if ([
|
|
92
|
+
"put",
|
|
93
|
+
"patch",
|
|
94
|
+
"delete",
|
|
95
|
+
"post"
|
|
96
|
+
].includes(normalized)) return normalized;
|
|
97
|
+
return null;
|
|
98
|
+
}
|
|
46
99
|
/**
|
|
47
100
|
* Add a route with specified HTTP methods, path, handler, and middlewares
|
|
48
101
|
* @param methods - HTTP method(s) for the route
|
|
@@ -265,8 +318,28 @@ var Router = class Router {
|
|
|
265
318
|
app[method](route.path, async (event) => {
|
|
266
319
|
try {
|
|
267
320
|
const ctx = event;
|
|
321
|
+
const reqBody = await Router.readBodyCached(ctx);
|
|
322
|
+
const override = Router.resolveMethodOverride(ctx.req.method, ctx.req.headers, reqBody);
|
|
323
|
+
if (method === "post" && override && override !== "post") return;
|
|
324
|
+
const inst = instance ?? route;
|
|
325
|
+
await Router.bindRequestToInstance(ctx, inst, route, reqBody);
|
|
326
|
+
const result = handlerFunction(ctx, inst.clearRequest);
|
|
327
|
+
return await Promise.resolve(result);
|
|
328
|
+
} catch (error) {
|
|
329
|
+
return error;
|
|
330
|
+
}
|
|
331
|
+
}, { middleware: route.middlewares });
|
|
332
|
+
if ([
|
|
333
|
+
"put",
|
|
334
|
+
"patch",
|
|
335
|
+
"delete"
|
|
336
|
+
].includes(method)) app.post(route.path, async (event) => {
|
|
337
|
+
try {
|
|
338
|
+
const ctx = event;
|
|
339
|
+
const reqBody = await Router.readBodyCached(ctx);
|
|
340
|
+
if (Router.resolveMethodOverride(ctx.req.method, ctx.req.headers, reqBody) !== method) return;
|
|
268
341
|
const inst = instance ?? route;
|
|
269
|
-
await Router.bindRequestToInstance(ctx, inst, route);
|
|
342
|
+
await Router.bindRequestToInstance(ctx, inst, route, reqBody);
|
|
270
343
|
const result = handlerFunction(ctx, inst.clearRequest);
|
|
271
344
|
return await Promise.resolve(result);
|
|
272
345
|
} catch (error) {
|
|
@@ -277,10 +350,10 @@ var Router = class Router {
|
|
|
277
350
|
}
|
|
278
351
|
return app;
|
|
279
352
|
}
|
|
280
|
-
static async bindRequestToInstance(ctx, instance, route) {
|
|
353
|
+
static async bindRequestToInstance(ctx, instance, route, body) {
|
|
281
354
|
if (!instance) return;
|
|
282
355
|
instance.ctx = ctx;
|
|
283
|
-
instance.body = await
|
|
356
|
+
instance.body = body ?? await Router.readBodyCached(ctx);
|
|
284
357
|
instance.query = getQuery(ctx);
|
|
285
358
|
instance.params = getRouterParams(ctx, { decode: true });
|
|
286
359
|
instance.clearRequest = new ClearRequest({
|
package/dist/types/basic.d.mts
CHANGED
|
@@ -19,5 +19,16 @@ type ControllerAction = 'index' | 'show' | 'create' | 'update' | 'destroy';
|
|
|
19
19
|
*/
|
|
20
20
|
type RequestData = Record<string, any>;
|
|
21
21
|
type ApiResourceMiddleware<M extends Middleware$1 | Middleware> = M | M[] | { [K in ControllerAction]?: M | M[] };
|
|
22
|
+
interface RouterConfig {
|
|
23
|
+
/**
|
|
24
|
+
* Configuration for method override functionality, allowing clients to use a
|
|
25
|
+
* specific header or body parameter to override the HTTP method.
|
|
26
|
+
*/
|
|
27
|
+
methodOverride?: {
|
|
28
|
+
/** Whether method override is enabled */enabled?: boolean; /** Keys in the request body to check for method override */
|
|
29
|
+
bodyKeys?: string[] | string; /** Keys in the request headers to check for method override */
|
|
30
|
+
headerKeys?: string[] | string;
|
|
31
|
+
};
|
|
32
|
+
}
|
|
22
33
|
//#endregion
|
|
23
|
-
export { ApiResourceMiddleware, ControllerAction, ControllerHandler, HttpMethod, RequestData };
|
|
34
|
+
export { ApiResourceMiddleware, ControllerAction, ControllerHandler, HttpMethod, RequestData, RouterConfig };
|