clear-router 2.4.0 → 2.5.0
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/core/index.cjs +1 -1
- package/dist/core/index.mjs +1 -1
- package/dist/express/index.cjs +1 -1
- package/dist/express/index.mjs +1 -1
- package/dist/fastify/index.cjs +1 -1
- package/dist/fastify/index.mjs +1 -1
- package/dist/h3/index.cjs +1 -1
- package/dist/h3/index.mjs +1 -1
- package/dist/hono/index.cjs +1 -1
- package/dist/hono/index.mjs +1 -1
- package/dist/index.cjs +5 -0
- package/dist/index.mjs +5 -0
- package/dist/koa/index.cjs +169 -0
- package/dist/koa/index.d.cts +63 -0
- package/dist/koa/index.d.mts +63 -0
- package/dist/koa/index.mjs +168 -0
- package/dist/{router-C-c43ybe.cjs → router-Cxi21bzi.cjs} +5 -0
- package/dist/{router-CHaZi7NS.mjs → router-Dxc8FQOl.mjs} +5 -0
- package/dist/types/koa.d.mts +22 -0
- package/dist/types/koa.mjs +1 -0
- package/package.json +21 -5
package/README.md
CHANGED
|
@@ -49,6 +49,7 @@ yarn add clear-router express
|
|
|
49
49
|
- Function handlers always receive context as first argument
|
|
50
50
|
- Controller handlers receive hydrated `this.body`, `this.query`, `this.params`, and `this.clearRequest`
|
|
51
51
|
- `clearRequest` is passed as second handler argument for controller handlers
|
|
52
|
+
- Route handlers can return response values directly across Express, Fastify, Hono, and H3
|
|
52
53
|
- Auto-binds controller methods
|
|
53
54
|
- Full CommonJS, ESM, and TypeScript support
|
|
54
55
|
- Error handling delegated to Express | H3 | Fastify | Hono
|
package/dist/core/index.cjs
CHANGED
package/dist/core/index.mjs
CHANGED
package/dist/express/index.cjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
2
|
-
const require_router = require('../router-
|
|
2
|
+
const require_router = require('../router-Cxi21bzi.cjs');
|
|
3
3
|
const require_responses = require('../responses-r-O3jS-S.cjs');
|
|
4
4
|
|
|
5
5
|
//#region src/express/router.ts
|
package/dist/express/index.mjs
CHANGED
package/dist/fastify/index.cjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
2
|
-
const require_router = require('../router-
|
|
2
|
+
const require_router = require('../router-Cxi21bzi.cjs');
|
|
3
3
|
const require_responses = require('../responses-r-O3jS-S.cjs');
|
|
4
4
|
|
|
5
5
|
//#region src/fastify/router.ts
|
package/dist/fastify/index.mjs
CHANGED
package/dist/h3/index.cjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
2
|
-
const require_router = require('../router-
|
|
2
|
+
const require_router = require('../router-Cxi21bzi.cjs');
|
|
3
3
|
const require_responses = require('../responses-r-O3jS-S.cjs');
|
|
4
4
|
let h3 = require("h3");
|
|
5
5
|
|
package/dist/h3/index.mjs
CHANGED
package/dist/hono/index.cjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
2
|
-
const require_router = require('../router-
|
|
2
|
+
const require_router = require('../router-Cxi21bzi.cjs');
|
|
3
3
|
const require_responses = require('../responses-r-O3jS-S.cjs');
|
|
4
4
|
|
|
5
5
|
//#region src/hono/router.ts
|
package/dist/hono/index.mjs
CHANGED
package/dist/index.cjs
CHANGED
package/dist/index.mjs
CHANGED
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
2
|
+
const require_router = require('../router-Cxi21bzi.cjs');
|
|
3
|
+
const require_responses = require('../responses-r-O3jS-S.cjs');
|
|
4
|
+
|
|
5
|
+
//#region src/koa/router.ts
|
|
6
|
+
/**
|
|
7
|
+
* @class clear-router Koa Router
|
|
8
|
+
* @description Laravel-style routing system for Koa using @koa/router and shared clear-router core
|
|
9
|
+
* @author 3m1n3nc3
|
|
10
|
+
* @repository https://github.com/toneflix/clear-router
|
|
11
|
+
*/
|
|
12
|
+
var Router = class Router extends require_router.CoreRouter {
|
|
13
|
+
static routerStateNamespace = "clear-router:koa";
|
|
14
|
+
static bodyCache = /* @__PURE__ */ new WeakMap();
|
|
15
|
+
static async readBodyCached(ctx) {
|
|
16
|
+
if (this.bodyCache.has(ctx)) {
|
|
17
|
+
const cached = this.bodyCache.get(ctx) || {};
|
|
18
|
+
ctx.request.getBody = () => cached;
|
|
19
|
+
return cached;
|
|
20
|
+
}
|
|
21
|
+
let body = ctx.request.body && typeof ctx.request.body === "object" ? ctx.request.body : {};
|
|
22
|
+
if (!Object.keys(body).length && !["GET", "HEAD"].includes(ctx.method.toUpperCase())) body = await this.readBody(ctx);
|
|
23
|
+
ctx.request.getBody = () => body;
|
|
24
|
+
this.bodyCache.set(ctx, body);
|
|
25
|
+
return body;
|
|
26
|
+
}
|
|
27
|
+
static async readBody(ctx) {
|
|
28
|
+
const contentType = String(ctx.get("content-type") || "").toLowerCase();
|
|
29
|
+
const chunks = [];
|
|
30
|
+
for await (const chunk of ctx.req) chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
|
31
|
+
const raw = Buffer.concat(chunks).toString("utf8");
|
|
32
|
+
if (!raw) return {};
|
|
33
|
+
if (contentType.includes("application/json")) return JSON.parse(raw);
|
|
34
|
+
if (contentType.includes("application/x-www-form-urlencoded")) return Object.fromEntries(new URLSearchParams(raw));
|
|
35
|
+
return {};
|
|
36
|
+
}
|
|
37
|
+
static async sendReturnValue(ctx, value, method, path) {
|
|
38
|
+
if (ctx.respond === false || ctx.headerSent) return value;
|
|
39
|
+
const meta = require_responses.resolveResponseMeta(value, {
|
|
40
|
+
headers: ctx.headers,
|
|
41
|
+
method,
|
|
42
|
+
path,
|
|
43
|
+
status: ctx.status && ctx.status !== 404 ? ctx.status : void 0
|
|
44
|
+
});
|
|
45
|
+
if (!meta) return void 0;
|
|
46
|
+
ctx.status = meta.status;
|
|
47
|
+
if (require_responses.isFetchResponse(meta.body)) {
|
|
48
|
+
meta.body.headers.forEach((headerValue, key) => {
|
|
49
|
+
ctx.set(key, headerValue);
|
|
50
|
+
});
|
|
51
|
+
ctx.status = meta.body.status;
|
|
52
|
+
ctx.body = Buffer.from(await meta.body.arrayBuffer());
|
|
53
|
+
return ctx.body;
|
|
54
|
+
}
|
|
55
|
+
if (meta.contentType) ctx.type = meta.contentType;
|
|
56
|
+
ctx.body = meta.isEmpty ? null : meta.body;
|
|
57
|
+
return ctx.body;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Adds a new route to the router with the specified HTTP methods, path, handler, and optional middlewares.
|
|
61
|
+
*/
|
|
62
|
+
static add(methods, path, handler, middlewares) {
|
|
63
|
+
super.add(methods, path, handler, middlewares);
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Adds a new API resource route to the router for the specified base path and controller.
|
|
67
|
+
*/
|
|
68
|
+
static apiResource(basePath, controller, options) {
|
|
69
|
+
super.apiResource(basePath, controller, options);
|
|
70
|
+
}
|
|
71
|
+
static get(path, handler, middlewares) {
|
|
72
|
+
super.get(path, handler, middlewares);
|
|
73
|
+
}
|
|
74
|
+
static post(path, handler, middlewares) {
|
|
75
|
+
super.post(path, handler, middlewares);
|
|
76
|
+
}
|
|
77
|
+
static put(path, handler, middlewares) {
|
|
78
|
+
super.put(path, handler, middlewares);
|
|
79
|
+
}
|
|
80
|
+
static delete(path, handler, middlewares) {
|
|
81
|
+
super.delete(path, handler, middlewares);
|
|
82
|
+
}
|
|
83
|
+
static patch(path, handler, middlewares) {
|
|
84
|
+
super.patch(path, handler, middlewares);
|
|
85
|
+
}
|
|
86
|
+
static options(path, handler, middlewares) {
|
|
87
|
+
super.options(path, handler, middlewares);
|
|
88
|
+
}
|
|
89
|
+
static head(path, handler, middlewares) {
|
|
90
|
+
super.head(path, handler, middlewares);
|
|
91
|
+
}
|
|
92
|
+
static async group(prefix, callback, middlewares) {
|
|
93
|
+
await super.group(prefix, callback, middlewares);
|
|
94
|
+
}
|
|
95
|
+
static middleware(middlewares, callback) {
|
|
96
|
+
super.middleware(middlewares, callback);
|
|
97
|
+
}
|
|
98
|
+
static allRoutes(type) {
|
|
99
|
+
return super.allRoutes(type);
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Applies the registered routes to a @koa/router instance.
|
|
103
|
+
*/
|
|
104
|
+
static apply(router) {
|
|
105
|
+
for (const route of this.routes) {
|
|
106
|
+
let handlerFunction = null;
|
|
107
|
+
let instance = null;
|
|
108
|
+
try {
|
|
109
|
+
const resolved = this.resolveHandler(route);
|
|
110
|
+
handlerFunction = resolved.handlerFunction;
|
|
111
|
+
instance = resolved.instance;
|
|
112
|
+
} catch (error) {
|
|
113
|
+
console.error(`[ROUTES] Error setting up route ${route.path}:`, error.message);
|
|
114
|
+
throw error;
|
|
115
|
+
}
|
|
116
|
+
if (!handlerFunction) continue;
|
|
117
|
+
for (const method of route.methods) {
|
|
118
|
+
const allowedMethods = [
|
|
119
|
+
"get",
|
|
120
|
+
"post",
|
|
121
|
+
"put",
|
|
122
|
+
"delete",
|
|
123
|
+
"patch",
|
|
124
|
+
"options",
|
|
125
|
+
"head"
|
|
126
|
+
];
|
|
127
|
+
if (method === "options" && route.methods.length > 1) continue;
|
|
128
|
+
if (!allowedMethods.includes(method)) throw new Error(`Invalid HTTP method: ${method} for route: ${route.path}`);
|
|
129
|
+
router[method](route.path, ...route.middlewares || [], async (context, next) => {
|
|
130
|
+
const ctx = context;
|
|
131
|
+
const reqBody = await Router.readBodyCached(ctx);
|
|
132
|
+
const override = Router.resolveMethodOverride(ctx.method, ctx.headers, reqBody);
|
|
133
|
+
if (method === "post" && override && override !== "post") return next();
|
|
134
|
+
const inst = instance ?? route;
|
|
135
|
+
Router.bindRequestToInstance(ctx, inst, route, {
|
|
136
|
+
body: reqBody,
|
|
137
|
+
query: ctx.query,
|
|
138
|
+
params: ctx.params ?? {}
|
|
139
|
+
});
|
|
140
|
+
const result = handlerFunction(ctx, inst.clearRequest);
|
|
141
|
+
const resolved = await Promise.resolve(result);
|
|
142
|
+
return Router.sendReturnValue(ctx, resolved, method, route.path);
|
|
143
|
+
});
|
|
144
|
+
if ([
|
|
145
|
+
"put",
|
|
146
|
+
"patch",
|
|
147
|
+
"delete"
|
|
148
|
+
].includes(method)) router.post(route.path, ...route.middlewares || [], async (context, next) => {
|
|
149
|
+
const ctx = context;
|
|
150
|
+
const reqBody = await Router.readBodyCached(ctx);
|
|
151
|
+
if (Router.resolveMethodOverride(ctx.method, ctx.headers, reqBody) !== method) return next();
|
|
152
|
+
const inst = instance ?? route;
|
|
153
|
+
Router.bindRequestToInstance(ctx, inst, route, {
|
|
154
|
+
body: reqBody,
|
|
155
|
+
query: ctx.query,
|
|
156
|
+
params: ctx.params ?? {}
|
|
157
|
+
});
|
|
158
|
+
const result = handlerFunction(ctx, inst.clearRequest);
|
|
159
|
+
const resolved = await Promise.resolve(result);
|
|
160
|
+
return Router.sendReturnValue(ctx, resolved, method, route.path);
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
return router;
|
|
165
|
+
}
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
//#endregion
|
|
169
|
+
exports.Router = Router;
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { d as ApiResourceMiddleware, f as ControllerAction, m as HttpMethod, n as ClearRequest, p as ControllerHandler, r as Route, t as CoreRouter } from "../router-A-9JuZPi.cjs";
|
|
2
|
+
import Koa from "koa";
|
|
3
|
+
import Router$1 from "@koa/router";
|
|
4
|
+
|
|
5
|
+
//#region types/koa.d.ts
|
|
6
|
+
interface RequestWithGetBody extends Koa.Request {
|
|
7
|
+
getBody: () => Record<string, any>;
|
|
8
|
+
body?: any;
|
|
9
|
+
}
|
|
10
|
+
interface HttpContext extends Koa.Context {
|
|
11
|
+
request: RequestWithGetBody;
|
|
12
|
+
params: Record<string, any>;
|
|
13
|
+
query: Record<string, any>;
|
|
14
|
+
}
|
|
15
|
+
type RouteHandler = (ctx: HttpContext, req: ClearRequest) => any | Promise<any>;
|
|
16
|
+
type Handler = RouteHandler | ControllerHandler;
|
|
17
|
+
type Middleware = Koa.Middleware<any, any>;
|
|
18
|
+
type KoaRouterApp = Router$1<any, any>;
|
|
19
|
+
//#endregion
|
|
20
|
+
//#region src/koa/router.d.ts
|
|
21
|
+
/**
|
|
22
|
+
* @class clear-router Koa Router
|
|
23
|
+
* @description Laravel-style routing system for Koa using @koa/router and shared clear-router core
|
|
24
|
+
* @author 3m1n3nc3
|
|
25
|
+
* @repository https://github.com/toneflix/clear-router
|
|
26
|
+
*/
|
|
27
|
+
declare class Router extends CoreRouter {
|
|
28
|
+
protected static routerStateNamespace: string;
|
|
29
|
+
private static readonly bodyCache;
|
|
30
|
+
private static readBodyCached;
|
|
31
|
+
private static readBody;
|
|
32
|
+
private static sendReturnValue;
|
|
33
|
+
/**
|
|
34
|
+
* Adds a new route to the router with the specified HTTP methods, path, handler, and optional middlewares.
|
|
35
|
+
*/
|
|
36
|
+
static add(methods: HttpMethod | HttpMethod[], path: string, handler: Handler, middlewares?: Middleware[] | Middleware): void;
|
|
37
|
+
/**
|
|
38
|
+
* Adds a new API resource route to the router for the specified base path and controller.
|
|
39
|
+
*/
|
|
40
|
+
static apiResource(basePath: string, controller: any, options?: {
|
|
41
|
+
only?: ControllerAction[];
|
|
42
|
+
except?: ControllerAction[];
|
|
43
|
+
middlewares?: ApiResourceMiddleware<Middleware>;
|
|
44
|
+
}): void;
|
|
45
|
+
static get(path: string, handler: Handler, middlewares?: Middleware[] | Middleware): void;
|
|
46
|
+
static post(path: string, handler: Handler, middlewares?: Middleware[] | Middleware): void;
|
|
47
|
+
static put(path: string, handler: Handler, middlewares?: Middleware[] | Middleware): void;
|
|
48
|
+
static delete(path: string, handler: Handler, middlewares?: Middleware[] | Middleware): void;
|
|
49
|
+
static patch(path: string, handler: Handler, middlewares?: Middleware[] | Middleware): void;
|
|
50
|
+
static options(path: string, handler: Handler, middlewares?: Middleware[] | Middleware): void;
|
|
51
|
+
static head(path: string, handler: Handler, middlewares?: Middleware[] | Middleware): void;
|
|
52
|
+
static group(prefix: string, callback: () => void | Promise<void>, middlewares?: Middleware[]): Promise<void>;
|
|
53
|
+
static middleware(middlewares: Middleware[], callback: () => void): void;
|
|
54
|
+
static allRoutes(): Array<Route<HttpContext, Middleware, Handler>>;
|
|
55
|
+
static allRoutes(type: 'path'): Record<string, Route<HttpContext, Middleware, Handler>>;
|
|
56
|
+
static allRoutes(type: 'method'): { [method in Uppercase<HttpMethod>]?: Array<Route<HttpContext, Middleware, Handler>> };
|
|
57
|
+
/**
|
|
58
|
+
* Applies the registered routes to a @koa/router instance.
|
|
59
|
+
*/
|
|
60
|
+
static apply(router: KoaRouterApp): KoaRouterApp;
|
|
61
|
+
}
|
|
62
|
+
//#endregion
|
|
63
|
+
export { Router };
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { d as ApiResourceMiddleware, f as ControllerAction, m as HttpMethod, n as ClearRequest, p as ControllerHandler, r as Route, t as CoreRouter } from "../router-CVeqd7Ro.mjs";
|
|
2
|
+
import Koa from "koa";
|
|
3
|
+
import Router$1 from "@koa/router";
|
|
4
|
+
|
|
5
|
+
//#region types/koa.d.ts
|
|
6
|
+
interface RequestWithGetBody extends Koa.Request {
|
|
7
|
+
getBody: () => Record<string, any>;
|
|
8
|
+
body?: any;
|
|
9
|
+
}
|
|
10
|
+
interface HttpContext extends Koa.Context {
|
|
11
|
+
request: RequestWithGetBody;
|
|
12
|
+
params: Record<string, any>;
|
|
13
|
+
query: Record<string, any>;
|
|
14
|
+
}
|
|
15
|
+
type RouteHandler = (ctx: HttpContext, req: ClearRequest) => any | Promise<any>;
|
|
16
|
+
type Handler = RouteHandler | ControllerHandler;
|
|
17
|
+
type Middleware = Koa.Middleware<any, any>;
|
|
18
|
+
type KoaRouterApp = Router$1<any, any>;
|
|
19
|
+
//#endregion
|
|
20
|
+
//#region src/koa/router.d.ts
|
|
21
|
+
/**
|
|
22
|
+
* @class clear-router Koa Router
|
|
23
|
+
* @description Laravel-style routing system for Koa using @koa/router and shared clear-router core
|
|
24
|
+
* @author 3m1n3nc3
|
|
25
|
+
* @repository https://github.com/toneflix/clear-router
|
|
26
|
+
*/
|
|
27
|
+
declare class Router extends CoreRouter {
|
|
28
|
+
protected static routerStateNamespace: string;
|
|
29
|
+
private static readonly bodyCache;
|
|
30
|
+
private static readBodyCached;
|
|
31
|
+
private static readBody;
|
|
32
|
+
private static sendReturnValue;
|
|
33
|
+
/**
|
|
34
|
+
* Adds a new route to the router with the specified HTTP methods, path, handler, and optional middlewares.
|
|
35
|
+
*/
|
|
36
|
+
static add(methods: HttpMethod | HttpMethod[], path: string, handler: Handler, middlewares?: Middleware[] | Middleware): void;
|
|
37
|
+
/**
|
|
38
|
+
* Adds a new API resource route to the router for the specified base path and controller.
|
|
39
|
+
*/
|
|
40
|
+
static apiResource(basePath: string, controller: any, options?: {
|
|
41
|
+
only?: ControllerAction[];
|
|
42
|
+
except?: ControllerAction[];
|
|
43
|
+
middlewares?: ApiResourceMiddleware<Middleware>;
|
|
44
|
+
}): void;
|
|
45
|
+
static get(path: string, handler: Handler, middlewares?: Middleware[] | Middleware): void;
|
|
46
|
+
static post(path: string, handler: Handler, middlewares?: Middleware[] | Middleware): void;
|
|
47
|
+
static put(path: string, handler: Handler, middlewares?: Middleware[] | Middleware): void;
|
|
48
|
+
static delete(path: string, handler: Handler, middlewares?: Middleware[] | Middleware): void;
|
|
49
|
+
static patch(path: string, handler: Handler, middlewares?: Middleware[] | Middleware): void;
|
|
50
|
+
static options(path: string, handler: Handler, middlewares?: Middleware[] | Middleware): void;
|
|
51
|
+
static head(path: string, handler: Handler, middlewares?: Middleware[] | Middleware): void;
|
|
52
|
+
static group(prefix: string, callback: () => void | Promise<void>, middlewares?: Middleware[]): Promise<void>;
|
|
53
|
+
static middleware(middlewares: Middleware[], callback: () => void): void;
|
|
54
|
+
static allRoutes(): Array<Route<HttpContext, Middleware, Handler>>;
|
|
55
|
+
static allRoutes(type: 'path'): Record<string, Route<HttpContext, Middleware, Handler>>;
|
|
56
|
+
static allRoutes(type: 'method'): { [method in Uppercase<HttpMethod>]?: Array<Route<HttpContext, Middleware, Handler>> };
|
|
57
|
+
/**
|
|
58
|
+
* Applies the registered routes to a @koa/router instance.
|
|
59
|
+
*/
|
|
60
|
+
static apply(router: KoaRouterApp): KoaRouterApp;
|
|
61
|
+
}
|
|
62
|
+
//#endregion
|
|
63
|
+
export { Router };
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import { t as CoreRouter } from "../router-Dxc8FQOl.mjs";
|
|
2
|
+
import { n as resolveResponseMeta, t as isFetchResponse } from "../responses-HOePPAl8.mjs";
|
|
3
|
+
|
|
4
|
+
//#region src/koa/router.ts
|
|
5
|
+
/**
|
|
6
|
+
* @class clear-router Koa Router
|
|
7
|
+
* @description Laravel-style routing system for Koa using @koa/router and shared clear-router core
|
|
8
|
+
* @author 3m1n3nc3
|
|
9
|
+
* @repository https://github.com/toneflix/clear-router
|
|
10
|
+
*/
|
|
11
|
+
var Router = class Router extends CoreRouter {
|
|
12
|
+
static routerStateNamespace = "clear-router:koa";
|
|
13
|
+
static bodyCache = /* @__PURE__ */ new WeakMap();
|
|
14
|
+
static async readBodyCached(ctx) {
|
|
15
|
+
if (this.bodyCache.has(ctx)) {
|
|
16
|
+
const cached = this.bodyCache.get(ctx) || {};
|
|
17
|
+
ctx.request.getBody = () => cached;
|
|
18
|
+
return cached;
|
|
19
|
+
}
|
|
20
|
+
let body = ctx.request.body && typeof ctx.request.body === "object" ? ctx.request.body : {};
|
|
21
|
+
if (!Object.keys(body).length && !["GET", "HEAD"].includes(ctx.method.toUpperCase())) body = await this.readBody(ctx);
|
|
22
|
+
ctx.request.getBody = () => body;
|
|
23
|
+
this.bodyCache.set(ctx, body);
|
|
24
|
+
return body;
|
|
25
|
+
}
|
|
26
|
+
static async readBody(ctx) {
|
|
27
|
+
const contentType = String(ctx.get("content-type") || "").toLowerCase();
|
|
28
|
+
const chunks = [];
|
|
29
|
+
for await (const chunk of ctx.req) chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
|
30
|
+
const raw = Buffer.concat(chunks).toString("utf8");
|
|
31
|
+
if (!raw) return {};
|
|
32
|
+
if (contentType.includes("application/json")) return JSON.parse(raw);
|
|
33
|
+
if (contentType.includes("application/x-www-form-urlencoded")) return Object.fromEntries(new URLSearchParams(raw));
|
|
34
|
+
return {};
|
|
35
|
+
}
|
|
36
|
+
static async sendReturnValue(ctx, value, method, path) {
|
|
37
|
+
if (ctx.respond === false || ctx.headerSent) return value;
|
|
38
|
+
const meta = resolveResponseMeta(value, {
|
|
39
|
+
headers: ctx.headers,
|
|
40
|
+
method,
|
|
41
|
+
path,
|
|
42
|
+
status: ctx.status && ctx.status !== 404 ? ctx.status : void 0
|
|
43
|
+
});
|
|
44
|
+
if (!meta) return void 0;
|
|
45
|
+
ctx.status = meta.status;
|
|
46
|
+
if (isFetchResponse(meta.body)) {
|
|
47
|
+
meta.body.headers.forEach((headerValue, key) => {
|
|
48
|
+
ctx.set(key, headerValue);
|
|
49
|
+
});
|
|
50
|
+
ctx.status = meta.body.status;
|
|
51
|
+
ctx.body = Buffer.from(await meta.body.arrayBuffer());
|
|
52
|
+
return ctx.body;
|
|
53
|
+
}
|
|
54
|
+
if (meta.contentType) ctx.type = meta.contentType;
|
|
55
|
+
ctx.body = meta.isEmpty ? null : meta.body;
|
|
56
|
+
return ctx.body;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Adds a new route to the router with the specified HTTP methods, path, handler, and optional middlewares.
|
|
60
|
+
*/
|
|
61
|
+
static add(methods, path, handler, middlewares) {
|
|
62
|
+
super.add(methods, path, handler, middlewares);
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Adds a new API resource route to the router for the specified base path and controller.
|
|
66
|
+
*/
|
|
67
|
+
static apiResource(basePath, controller, options) {
|
|
68
|
+
super.apiResource(basePath, controller, options);
|
|
69
|
+
}
|
|
70
|
+
static get(path, handler, middlewares) {
|
|
71
|
+
super.get(path, handler, middlewares);
|
|
72
|
+
}
|
|
73
|
+
static post(path, handler, middlewares) {
|
|
74
|
+
super.post(path, handler, middlewares);
|
|
75
|
+
}
|
|
76
|
+
static put(path, handler, middlewares) {
|
|
77
|
+
super.put(path, handler, middlewares);
|
|
78
|
+
}
|
|
79
|
+
static delete(path, handler, middlewares) {
|
|
80
|
+
super.delete(path, handler, middlewares);
|
|
81
|
+
}
|
|
82
|
+
static patch(path, handler, middlewares) {
|
|
83
|
+
super.patch(path, handler, middlewares);
|
|
84
|
+
}
|
|
85
|
+
static options(path, handler, middlewares) {
|
|
86
|
+
super.options(path, handler, middlewares);
|
|
87
|
+
}
|
|
88
|
+
static head(path, handler, middlewares) {
|
|
89
|
+
super.head(path, handler, middlewares);
|
|
90
|
+
}
|
|
91
|
+
static async group(prefix, callback, middlewares) {
|
|
92
|
+
await super.group(prefix, callback, middlewares);
|
|
93
|
+
}
|
|
94
|
+
static middleware(middlewares, callback) {
|
|
95
|
+
super.middleware(middlewares, callback);
|
|
96
|
+
}
|
|
97
|
+
static allRoutes(type) {
|
|
98
|
+
return super.allRoutes(type);
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Applies the registered routes to a @koa/router instance.
|
|
102
|
+
*/
|
|
103
|
+
static apply(router) {
|
|
104
|
+
for (const route of this.routes) {
|
|
105
|
+
let handlerFunction = null;
|
|
106
|
+
let instance = null;
|
|
107
|
+
try {
|
|
108
|
+
const resolved = this.resolveHandler(route);
|
|
109
|
+
handlerFunction = resolved.handlerFunction;
|
|
110
|
+
instance = resolved.instance;
|
|
111
|
+
} catch (error) {
|
|
112
|
+
console.error(`[ROUTES] Error setting up route ${route.path}:`, error.message);
|
|
113
|
+
throw error;
|
|
114
|
+
}
|
|
115
|
+
if (!handlerFunction) continue;
|
|
116
|
+
for (const method of route.methods) {
|
|
117
|
+
const allowedMethods = [
|
|
118
|
+
"get",
|
|
119
|
+
"post",
|
|
120
|
+
"put",
|
|
121
|
+
"delete",
|
|
122
|
+
"patch",
|
|
123
|
+
"options",
|
|
124
|
+
"head"
|
|
125
|
+
];
|
|
126
|
+
if (method === "options" && route.methods.length > 1) continue;
|
|
127
|
+
if (!allowedMethods.includes(method)) throw new Error(`Invalid HTTP method: ${method} for route: ${route.path}`);
|
|
128
|
+
router[method](route.path, ...route.middlewares || [], async (context, next) => {
|
|
129
|
+
const ctx = context;
|
|
130
|
+
const reqBody = await Router.readBodyCached(ctx);
|
|
131
|
+
const override = Router.resolveMethodOverride(ctx.method, ctx.headers, reqBody);
|
|
132
|
+
if (method === "post" && override && override !== "post") return next();
|
|
133
|
+
const inst = instance ?? route;
|
|
134
|
+
Router.bindRequestToInstance(ctx, inst, route, {
|
|
135
|
+
body: reqBody,
|
|
136
|
+
query: ctx.query,
|
|
137
|
+
params: ctx.params ?? {}
|
|
138
|
+
});
|
|
139
|
+
const result = handlerFunction(ctx, inst.clearRequest);
|
|
140
|
+
const resolved = await Promise.resolve(result);
|
|
141
|
+
return Router.sendReturnValue(ctx, resolved, method, route.path);
|
|
142
|
+
});
|
|
143
|
+
if ([
|
|
144
|
+
"put",
|
|
145
|
+
"patch",
|
|
146
|
+
"delete"
|
|
147
|
+
].includes(method)) router.post(route.path, ...route.middlewares || [], async (context, next) => {
|
|
148
|
+
const ctx = context;
|
|
149
|
+
const reqBody = await Router.readBodyCached(ctx);
|
|
150
|
+
if (Router.resolveMethodOverride(ctx.method, ctx.headers, reqBody) !== method) return next();
|
|
151
|
+
const inst = instance ?? route;
|
|
152
|
+
Router.bindRequestToInstance(ctx, inst, route, {
|
|
153
|
+
body: reqBody,
|
|
154
|
+
query: ctx.query,
|
|
155
|
+
params: ctx.params ?? {}
|
|
156
|
+
});
|
|
157
|
+
const result = handlerFunction(ctx, inst.clearRequest);
|
|
158
|
+
const resolved = await Promise.resolve(result);
|
|
159
|
+
return Router.sendReturnValue(ctx, resolved, method, route.path);
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
return router;
|
|
164
|
+
}
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
//#endregion
|
|
168
|
+
export { Router };
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { ControllerHandler } from "./basic.mjs";
|
|
2
|
+
import { ClearRequest } from "./ClearRequest.mjs";
|
|
3
|
+
import Koa from "koa";
|
|
4
|
+
import Router from "@koa/router";
|
|
5
|
+
|
|
6
|
+
//#region types/koa.d.ts
|
|
7
|
+
interface RequestWithGetBody extends Koa.Request {
|
|
8
|
+
getBody: () => Record<string, any>;
|
|
9
|
+
body?: any;
|
|
10
|
+
}
|
|
11
|
+
interface HttpContext extends Koa.Context {
|
|
12
|
+
request: RequestWithGetBody;
|
|
13
|
+
params: Record<string, any>;
|
|
14
|
+
query: Record<string, any>;
|
|
15
|
+
}
|
|
16
|
+
type RouteHandler = (ctx: HttpContext, req: ClearRequest) => any | Promise<any>;
|
|
17
|
+
type Handler = RouteHandler | ControllerHandler;
|
|
18
|
+
type NextFunction = Koa.Next;
|
|
19
|
+
type Middleware = Koa.Middleware<any, any>;
|
|
20
|
+
type KoaRouterApp = Router<any, any>;
|
|
21
|
+
//#endregion
|
|
22
|
+
export { Handler, HttpContext, KoaRouterApp, Middleware, NextFunction, RequestWithGetBody, RouteHandler };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { };
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "clear-router",
|
|
3
|
-
"version": "2.
|
|
4
|
-
"description": "Laravel-style routing for Node.js with support for Express, H3, Fastify, and
|
|
3
|
+
"version": "2.5.0",
|
|
4
|
+
"description": "Laravel-style routing for Node.js with support for Express, H3, Fastify, Hono, and Koa, including CommonJS, ESM, and TypeScript support.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"h3",
|
|
7
7
|
"srvx",
|
|
@@ -56,11 +56,16 @@
|
|
|
56
56
|
"import": "./dist/hono/index.mjs",
|
|
57
57
|
"require": "./dist/hono/index.cjs"
|
|
58
58
|
},
|
|
59
|
+
"./koa": {
|
|
60
|
+
"import": "./dist/koa/index.mjs",
|
|
61
|
+
"require": "./dist/koa/index.cjs"
|
|
62
|
+
},
|
|
59
63
|
"./types/basic": "./dist/types/basic.mjs",
|
|
60
64
|
"./types/express": "./dist/types/express.mjs",
|
|
61
65
|
"./types/fastify": "./dist/types/fastify.mjs",
|
|
62
66
|
"./types/h3": "./dist/types/h3.mjs",
|
|
63
67
|
"./types/hono": "./dist/types/hono.mjs",
|
|
68
|
+
"./types/koa": "./dist/types/koa.mjs",
|
|
64
69
|
"./package.json": "./package.json"
|
|
65
70
|
},
|
|
66
71
|
"files": [
|
|
@@ -69,12 +74,17 @@
|
|
|
69
74
|
"LICENSE"
|
|
70
75
|
],
|
|
71
76
|
"peerDependencies": {
|
|
77
|
+
"@koa/router": "^15.0.0",
|
|
72
78
|
"express": "^5.1.0",
|
|
73
79
|
"fastify": "^5.0.0",
|
|
74
80
|
"h3": "^2.0.1-rc.16",
|
|
75
|
-
"hono": "^4.0.0"
|
|
81
|
+
"hono": "^4.0.0",
|
|
82
|
+
"koa": "^3.0.0"
|
|
76
83
|
},
|
|
77
84
|
"peerDependenciesMeta": {
|
|
85
|
+
"@koa/router": {
|
|
86
|
+
"optional": true
|
|
87
|
+
},
|
|
78
88
|
"express": {
|
|
79
89
|
"optional": true
|
|
80
90
|
},
|
|
@@ -86,19 +96,25 @@
|
|
|
86
96
|
},
|
|
87
97
|
"hono": {
|
|
88
98
|
"optional": true
|
|
99
|
+
},
|
|
100
|
+
"koa": {
|
|
101
|
+
"optional": true
|
|
89
102
|
}
|
|
90
103
|
},
|
|
91
104
|
"devDependencies": {
|
|
92
105
|
"@eslint/js": "^10.0.1",
|
|
93
106
|
"@eslint/markdown": "^7.5.1",
|
|
107
|
+
"@koa/router": "^15.5.0",
|
|
94
108
|
"@types/express": "^4.17.21",
|
|
109
|
+
"@types/koa": "^3.0.2",
|
|
110
|
+
"@types/koa__router": "^12.0.5",
|
|
95
111
|
"@types/node": "^20.10.6",
|
|
96
|
-
"@types/supertest": "^6.0.3",
|
|
97
112
|
"@vitest/coverage-v8": "4.0.18",
|
|
98
113
|
"eslint": "^10.0.2",
|
|
99
114
|
"fastify": "^5.8.2",
|
|
100
115
|
"hono": "^4.12.8",
|
|
101
|
-
"
|
|
116
|
+
"koa": "^3.2.0",
|
|
117
|
+
"parasito": "^0.1.6",
|
|
102
118
|
"tsdown": "^0.20.3",
|
|
103
119
|
"tsx": "^4.21.0",
|
|
104
120
|
"typescript": "^5.3.3",
|