vafast 0.1.13 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/middleware/component-renderer.js +10 -14
- package/dist/middleware/component-router.js +4 -1
- package/dist/middleware.d.ts +1 -1
- package/dist/middleware.js +8 -3
- package/dist/monitoring/types.d.ts +1 -1
- package/dist/router/index.d.ts +5 -0
- package/dist/router/index.js +7 -0
- package/dist/router/radix-tree.d.ts +51 -0
- package/dist/router/radix-tree.js +186 -0
- package/dist/router.d.ts +38 -10
- package/dist/router.js +55 -43
- package/dist/server/base-server.d.ts +0 -4
- package/dist/server/base-server.js +2 -24
- package/dist/server/index.d.ts +4 -4
- package/dist/server/index.js +8 -6
- package/dist/server/server.d.ts +30 -2
- package/dist/server/server.js +84 -60
- package/dist/types/index.d.ts +4 -2
- package/dist/types/index.js +3 -2
- package/dist/types/route.d.ts +2 -2
- package/dist/types/schema.d.ts +75 -0
- package/dist/types/schema.js +10 -0
- package/dist/types/types.d.ts +6 -2
- package/dist/utils/create-handler.d.ts +74 -0
- package/dist/utils/create-handler.js +234 -0
- package/dist/utils/dependency-manager.js +8 -2
- package/dist/utils/go-await.js +1 -4
- package/dist/utils/handle.d.ts +1 -0
- package/dist/utils/handle.js +5 -0
- package/dist/utils/index.d.ts +12 -10
- package/dist/utils/index.js +21 -11
- package/dist/utils/path-matcher.js +2 -1
- package/dist/utils/response.d.ts +5 -0
- package/dist/utils/response.js +41 -0
- package/package.json +30 -27
- package/dist/index.js.map +0 -1
- package/dist/types.d.ts +0 -18
- package/dist/types.js +0 -1
- package/dist/utils/route-handler-factory.d.ts +0 -50
- package/dist/utils/route-handler-factory.js +0 -182
package/dist/server/server.js
CHANGED
|
@@ -1,73 +1,97 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Vafast 核心服务器
|
|
3
|
+
*
|
|
4
|
+
* 基于 Radix Tree 的高性能路由匹配
|
|
5
|
+
* 时间复杂度: O(k),k 为路径段数
|
|
6
|
+
*/
|
|
7
|
+
import { flattenNestedRoutes } from "../router";
|
|
2
8
|
import { composeMiddleware } from "../middleware";
|
|
3
9
|
import { json } from "../utils/response";
|
|
4
10
|
import { BaseServer } from "./base-server";
|
|
5
|
-
import {
|
|
11
|
+
import { RadixRouter } from "../router/radix-tree";
|
|
12
|
+
/**
|
|
13
|
+
* Vafast 服务器
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```typescript
|
|
17
|
+
* const server = new Server([
|
|
18
|
+
* { method: "GET", path: "/", handler: () => new Response("Hello") },
|
|
19
|
+
* ]);
|
|
20
|
+
* export default { fetch: server.fetch };
|
|
21
|
+
* ```
|
|
22
|
+
*/
|
|
6
23
|
export class Server extends BaseServer {
|
|
24
|
+
router;
|
|
7
25
|
routes;
|
|
8
|
-
constructor(routes) {
|
|
26
|
+
constructor(routes = []) {
|
|
9
27
|
super();
|
|
10
|
-
|
|
11
|
-
this.routes =
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
this.
|
|
28
|
+
this.router = new RadixRouter();
|
|
29
|
+
this.routes = [];
|
|
30
|
+
if (routes.length > 0) {
|
|
31
|
+
this.registerRoutes(routes);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
registerRoutes(routes) {
|
|
35
|
+
const flattened = flattenNestedRoutes(routes);
|
|
36
|
+
this.routes.push(...flattened);
|
|
37
|
+
for (const route of flattened) {
|
|
38
|
+
this.router.register(route.method, route.fullPath, route.handler, route.middlewareChain || []);
|
|
39
|
+
}
|
|
40
|
+
this.detectRouteConflicts(flattened);
|
|
41
|
+
this.logFlattenedRoutes(flattened);
|
|
19
42
|
}
|
|
43
|
+
/** 快速提取 pathname */
|
|
44
|
+
extractPathname(url) {
|
|
45
|
+
let start = url.indexOf("://");
|
|
46
|
+
start = start === -1 ? 0 : start + 3;
|
|
47
|
+
const pathStart = url.indexOf("/", start);
|
|
48
|
+
if (pathStart === -1)
|
|
49
|
+
return "/";
|
|
50
|
+
let end = url.indexOf("?", pathStart);
|
|
51
|
+
if (end === -1)
|
|
52
|
+
end = url.indexOf("#", pathStart);
|
|
53
|
+
if (end === -1)
|
|
54
|
+
end = url.length;
|
|
55
|
+
return url.substring(pathStart, end) || "/";
|
|
56
|
+
}
|
|
57
|
+
/** 处理请求 */
|
|
20
58
|
fetch = async (req) => {
|
|
21
|
-
const
|
|
59
|
+
const pathname = this.extractPathname(req.url);
|
|
22
60
|
const method = req.method;
|
|
23
|
-
|
|
24
|
-
if (
|
|
25
|
-
|
|
61
|
+
const match = this.router.match(method, pathname);
|
|
62
|
+
if (match) {
|
|
63
|
+
req.params = match.params;
|
|
64
|
+
// 组合全局中间件 + 路由中间件(mapResponse 在 composeMiddleware 内部处理)
|
|
65
|
+
const allMiddleware = [...this.globalMiddleware, ...match.middleware];
|
|
66
|
+
const handler = composeMiddleware(allMiddleware, match.handler);
|
|
67
|
+
return handler(req);
|
|
26
68
|
}
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
break;
|
|
37
|
-
}
|
|
38
|
-
else {
|
|
39
|
-
// 路径匹配但方法不匹配,收集可用方法
|
|
40
|
-
availableMethods.push(route.method);
|
|
41
|
-
}
|
|
42
|
-
}
|
|
69
|
+
// 405 Method Not Allowed
|
|
70
|
+
const allowedMethods = this.router.getAllowedMethods(pathname);
|
|
71
|
+
if (allowedMethods.length > 0) {
|
|
72
|
+
return json({
|
|
73
|
+
success: false,
|
|
74
|
+
error: "Method Not Allowed",
|
|
75
|
+
message: `Method ${method} not allowed for this endpoint`,
|
|
76
|
+
allowedMethods,
|
|
77
|
+
}, 405, { Allow: allowedMethods.join(", ") });
|
|
43
78
|
}
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
// 将路径参数设置到 req 对象上,以便 TypedRoute 处理器能够访问
|
|
47
|
-
req.params = params;
|
|
48
|
-
return await matched.handler(req);
|
|
49
|
-
}
|
|
50
|
-
else if (availableMethods.length > 0) {
|
|
51
|
-
// 路径存在但方法不匹配,返回 405 Method Not Allowed
|
|
52
|
-
return json({
|
|
53
|
-
success: false,
|
|
54
|
-
error: "Method Not Allowed",
|
|
55
|
-
message: `Method ${method} not allowed for this endpoint`,
|
|
56
|
-
allowedMethods: availableMethods,
|
|
57
|
-
}, 405, {
|
|
58
|
-
Allow: availableMethods.join(", "),
|
|
59
|
-
});
|
|
60
|
-
}
|
|
61
|
-
else {
|
|
62
|
-
// 路径不存在,返回 404 Not Found
|
|
63
|
-
return json({ success: false, error: "Not Found" }, 404);
|
|
64
|
-
}
|
|
65
|
-
};
|
|
66
|
-
const middlewareChain = matched?.middlewareChain
|
|
67
|
-
? [...this.globalMiddleware, ...matched.middlewareChain]
|
|
68
|
-
: this.globalMiddleware;
|
|
69
|
-
// 使用 composeMiddleware 来确保错误处理中间件被应用
|
|
70
|
-
const composedHandler = composeMiddleware(middlewareChain, handler);
|
|
71
|
-
return await composedHandler(req);
|
|
79
|
+
// 404 Not Found
|
|
80
|
+
return json({ success: false, error: "Not Found" }, 404);
|
|
72
81
|
};
|
|
82
|
+
addRoute(route) {
|
|
83
|
+
const flattenedRoute = {
|
|
84
|
+
...route,
|
|
85
|
+
fullPath: route.path,
|
|
86
|
+
middlewareChain: route.middleware || [],
|
|
87
|
+
};
|
|
88
|
+
this.routes.push(flattenedRoute);
|
|
89
|
+
this.router.register(route.method, route.path, route.handler, route.middleware || []);
|
|
90
|
+
}
|
|
91
|
+
addRoutes(routes) {
|
|
92
|
+
this.registerRoutes(routes);
|
|
93
|
+
}
|
|
94
|
+
getRoutes() {
|
|
95
|
+
return this.router.getRoutes();
|
|
96
|
+
}
|
|
73
97
|
}
|
package/dist/types/index.d.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
-
export
|
|
1
|
+
export type { Method, Handler, Route, NestedRoute, Middleware, FlattenedRoute, ResponseBody, } from "./types";
|
|
2
|
+
export type { BaseRouteConfig, ExtendedRouteConfig, NestedRouteConfig, TypedRoute, CompatibleRoute, } from "./route";
|
|
3
|
+
export { createTypedRoute, isTypedRoute } from "./route";
|
|
2
4
|
export * from "./component-route";
|
|
3
|
-
export
|
|
5
|
+
export * from "./schema";
|
package/dist/types/index.js
CHANGED
package/dist/types/route.d.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { Route } from "./types";
|
|
1
|
+
import { Route, ResponseBody } from "./types";
|
|
2
2
|
export interface Middleware {
|
|
3
3
|
(req: Request, next: () => Promise<Response>): Promise<Response>;
|
|
4
4
|
}
|
|
5
5
|
export interface BaseRouteConfig {
|
|
6
6
|
method: "GET" | "POST" | "PUT" | "DELETE" | "PATCH" | "OPTIONS" | "HEAD";
|
|
7
7
|
path: string;
|
|
8
|
-
handler: (req: Request) =>
|
|
8
|
+
handler: (req: Request) => ResponseBody | Promise<ResponseBody>;
|
|
9
9
|
}
|
|
10
10
|
export interface ExtendedRouteConfig extends BaseRouteConfig {
|
|
11
11
|
middleware?: Middleware[];
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Schema 类型定义
|
|
3
|
+
*
|
|
4
|
+
* 使用 TSchema 约束替代 any,提供完整的类型安全
|
|
5
|
+
*
|
|
6
|
+
* @author Framework Team
|
|
7
|
+
* @version 1.0.0
|
|
8
|
+
* @license MIT
|
|
9
|
+
*/
|
|
10
|
+
import type { TSchema, Static } from "@sinclair/typebox";
|
|
11
|
+
/**
|
|
12
|
+
* 路由 Schema 配置
|
|
13
|
+
* 所有 schema 字段使用 TSchema 约束
|
|
14
|
+
*/
|
|
15
|
+
export interface RouteSchema {
|
|
16
|
+
body?: TSchema;
|
|
17
|
+
query?: TSchema;
|
|
18
|
+
params?: TSchema;
|
|
19
|
+
headers?: TSchema;
|
|
20
|
+
cookies?: TSchema;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* 从 Schema 配置推导出具体类型
|
|
24
|
+
*/
|
|
25
|
+
export type InferSchema<T extends RouteSchema> = {
|
|
26
|
+
body: T["body"] extends TSchema ? Static<T["body"]> : unknown;
|
|
27
|
+
query: T["query"] extends TSchema ? Static<T["query"]> : Record<string, string>;
|
|
28
|
+
params: T["params"] extends TSchema ? Static<T["params"]> : Record<string, string>;
|
|
29
|
+
headers: T["headers"] extends TSchema ? Static<T["headers"]> : Record<string, string>;
|
|
30
|
+
cookies: T["cookies"] extends TSchema ? Static<T["cookies"]> : Record<string, string>;
|
|
31
|
+
};
|
|
32
|
+
/**
|
|
33
|
+
* Handler 上下文类型
|
|
34
|
+
*/
|
|
35
|
+
export interface HandlerContext<T extends RouteSchema = RouteSchema> {
|
|
36
|
+
/** 原始请求对象 */
|
|
37
|
+
req: Request;
|
|
38
|
+
/** 请求体 (经过 schema 验证) */
|
|
39
|
+
body: InferSchema<T>["body"];
|
|
40
|
+
/** 查询参数 (经过 schema 验证) */
|
|
41
|
+
query: InferSchema<T>["query"];
|
|
42
|
+
/** 路径参数 (经过 schema 验证) */
|
|
43
|
+
params: InferSchema<T>["params"];
|
|
44
|
+
/** 请求头 (经过 schema 验证) */
|
|
45
|
+
headers: InferSchema<T>["headers"];
|
|
46
|
+
/** Cookie (经过 schema 验证) */
|
|
47
|
+
cookies: InferSchema<T>["cookies"];
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* 带额外上下文的 Handler 上下文类型
|
|
51
|
+
* 用于中间件注入额外数据
|
|
52
|
+
*/
|
|
53
|
+
export type HandlerContextWithExtra<T extends RouteSchema = RouteSchema, TExtra extends Record<string, unknown> = Record<string, never>> = HandlerContext<T> & TExtra;
|
|
54
|
+
/**
|
|
55
|
+
* Handler 函数类型
|
|
56
|
+
*/
|
|
57
|
+
export type TypedHandler<T extends RouteSchema = RouteSchema, TExtra extends Record<string, unknown> = Record<string, never>, TReturn = unknown> = (ctx: HandlerContextWithExtra<T, TExtra>) => TReturn | Promise<TReturn>;
|
|
58
|
+
/**
|
|
59
|
+
* 扩展的路由配置 (包含 schema)
|
|
60
|
+
*/
|
|
61
|
+
export interface TypedRouteConfig<T extends RouteSchema = RouteSchema> {
|
|
62
|
+
method: "GET" | "POST" | "PUT" | "DELETE" | "PATCH" | "OPTIONS" | "HEAD";
|
|
63
|
+
path: string;
|
|
64
|
+
schema?: T;
|
|
65
|
+
handler: (req: Request) => Response | Promise<Response>;
|
|
66
|
+
middleware?: Array<(req: Request, next: () => Promise<Response>) => Promise<Response>>;
|
|
67
|
+
docs?: {
|
|
68
|
+
description?: string;
|
|
69
|
+
tags?: string[];
|
|
70
|
+
security?: unknown[];
|
|
71
|
+
responses?: Record<string, unknown>;
|
|
72
|
+
};
|
|
73
|
+
timeout?: number;
|
|
74
|
+
maxBodySize?: string;
|
|
75
|
+
}
|
package/dist/types/types.d.ts
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
export type Method = "GET" | "POST" | "PUT" | "DELETE" | "PATCH" | "OPTIONS" | "HEAD";
|
|
2
|
-
|
|
3
|
-
export type
|
|
2
|
+
/** 支持的响应类型 - 由 mapResponse 自动转换 */
|
|
3
|
+
export type ResponseBody = Response | string | number | boolean | object | null | undefined | ReadableStream | Blob | ArrayBuffer;
|
|
4
|
+
/** Handler 返回值(支持同步/异步,任意类型) */
|
|
5
|
+
export type Handler = (req: Request, params?: Record<string, string>, user?: Record<string, any>) => ResponseBody | Promise<ResponseBody>;
|
|
6
|
+
/** 中间件(返回值必须是 Response 或 Promise<Response>) */
|
|
7
|
+
export type Middleware = (req: Request, next: () => Promise<Response>) => Response | Promise<Response>;
|
|
4
8
|
export interface Route {
|
|
5
9
|
method: Method;
|
|
6
10
|
path: string;
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 类型安全的路由处理器工厂
|
|
3
|
+
*
|
|
4
|
+
* 非柯里化设计,API 更简洁
|
|
5
|
+
*
|
|
6
|
+
* @author Framework Team
|
|
7
|
+
* @version 3.0.0
|
|
8
|
+
* @license MIT
|
|
9
|
+
*/
|
|
10
|
+
import type { RouteSchema, HandlerContext, HandlerContextWithExtra } from "../types/schema";
|
|
11
|
+
/** 空 schema 的上下文类型 */
|
|
12
|
+
type EmptySchemaContext = {
|
|
13
|
+
req: Request;
|
|
14
|
+
body: unknown;
|
|
15
|
+
query: Record<string, string>;
|
|
16
|
+
params: Record<string, string>;
|
|
17
|
+
headers: Record<string, string>;
|
|
18
|
+
cookies: Record<string, string>;
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* 创建类型安全的路由处理器
|
|
22
|
+
*
|
|
23
|
+
* @example
|
|
24
|
+
* ```typescript
|
|
25
|
+
* // 无 schema - 直接传入 handler
|
|
26
|
+
* createHandler(({ params }) => `User: ${params.id}`)
|
|
27
|
+
*
|
|
28
|
+
* // 有 schema - 传入 schema 和 handler
|
|
29
|
+
* createHandler(
|
|
30
|
+
* { body: Type.Object({ name: Type.String() }) },
|
|
31
|
+
* ({ body }) => ({ hello: body.name })
|
|
32
|
+
* )
|
|
33
|
+
* ```
|
|
34
|
+
*/
|
|
35
|
+
export declare function createHandler<R>(handler: (ctx: EmptySchemaContext) => R | Promise<R>): (req: Request) => Promise<Response>;
|
|
36
|
+
export declare function createHandler<const T extends RouteSchema, R>(schema: T, handler: (ctx: HandlerContext<T>) => R | Promise<R>): (req: Request) => Promise<Response>;
|
|
37
|
+
/**
|
|
38
|
+
* 创建带额外上下文的路由处理器
|
|
39
|
+
*
|
|
40
|
+
* 用于中间件注入额外数据的场景
|
|
41
|
+
*
|
|
42
|
+
* @example
|
|
43
|
+
* ```typescript
|
|
44
|
+
* // 定义中间件注入的类型
|
|
45
|
+
* type AuthContext = { user: { id: string; role: string } };
|
|
46
|
+
*
|
|
47
|
+
* // 无 schema
|
|
48
|
+
* createHandlerWithExtra<AuthContext>(({ user }) => {
|
|
49
|
+
* return { userId: user.id };
|
|
50
|
+
* })
|
|
51
|
+
*
|
|
52
|
+
* // 有 schema
|
|
53
|
+
* createHandlerWithExtra<AuthContext>(
|
|
54
|
+
* { body: Type.Object({ action: Type.String() }) },
|
|
55
|
+
* ({ body, user }) => ({ success: true, userId: user.id })
|
|
56
|
+
* )
|
|
57
|
+
* ```
|
|
58
|
+
*/
|
|
59
|
+
export declare function createHandlerWithExtra<TExtra extends Record<string, unknown> = Record<string, never>, R = unknown>(handler: (ctx: EmptySchemaContext & TExtra) => R | Promise<R>): (req: Request) => Promise<Response>;
|
|
60
|
+
export declare function createHandlerWithExtra<TExtra extends Record<string, unknown> = Record<string, never>, const T extends RouteSchema = RouteSchema, R = unknown>(schema: T, handler: (ctx: HandlerContextWithExtra<T, TExtra>) => R | Promise<R>): (req: Request) => Promise<Response>;
|
|
61
|
+
/**
|
|
62
|
+
* 简单的路由处理器 (无 schema 验证,只有 req)
|
|
63
|
+
*
|
|
64
|
+
* @example
|
|
65
|
+
* ```typescript
|
|
66
|
+
* simpleHandler(({ req }) => {
|
|
67
|
+
* return { message: "Hello World" };
|
|
68
|
+
* })
|
|
69
|
+
* ```
|
|
70
|
+
*/
|
|
71
|
+
export declare function simpleHandler<R>(handler: (ctx: {
|
|
72
|
+
req: Request;
|
|
73
|
+
}) => R | Promise<R>): (req: Request) => Promise<Response>;
|
|
74
|
+
export {};
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 类型安全的路由处理器工厂
|
|
3
|
+
*
|
|
4
|
+
* 非柯里化设计,API 更简洁
|
|
5
|
+
*
|
|
6
|
+
* @author Framework Team
|
|
7
|
+
* @version 3.0.0
|
|
8
|
+
* @license MIT
|
|
9
|
+
*/
|
|
10
|
+
import { parseBody, parseQuery, parseHeaders, parseCookies } from "./parsers";
|
|
11
|
+
import { goAwait } from "./go-await";
|
|
12
|
+
import { json } from "./response";
|
|
13
|
+
import { validateAllSchemasUltra, precompileSchemasUltra, } from "./validators/schema-validators-ultra";
|
|
14
|
+
/**
|
|
15
|
+
* 自动响应转换
|
|
16
|
+
* 将各种返回值类型转换为 Response 对象
|
|
17
|
+
*/
|
|
18
|
+
function autoResponse(result) {
|
|
19
|
+
// 已经是 Response
|
|
20
|
+
if (result instanceof Response) {
|
|
21
|
+
return result;
|
|
22
|
+
}
|
|
23
|
+
// null/undefined -> 204
|
|
24
|
+
if (result === null || result === undefined) {
|
|
25
|
+
return new Response(null, { status: 204 });
|
|
26
|
+
}
|
|
27
|
+
// 字符串 -> text/plain
|
|
28
|
+
if (typeof result === "string") {
|
|
29
|
+
return new Response(result, {
|
|
30
|
+
headers: { "Content-Type": "text/plain; charset=utf-8" },
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
// 数字/布尔 -> text/plain
|
|
34
|
+
if (typeof result === "number" || typeof result === "boolean") {
|
|
35
|
+
return new Response(String(result), {
|
|
36
|
+
headers: { "Content-Type": "text/plain; charset=utf-8" },
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
// 对象 -> 检查是否是 { data, status, headers } 格式
|
|
40
|
+
if (typeof result === "object") {
|
|
41
|
+
const obj = result;
|
|
42
|
+
if ("data" in obj && ("status" in obj || "headers" in obj)) {
|
|
43
|
+
const { data, status = 200, headers = {} } = obj;
|
|
44
|
+
if (data === null || data === undefined) {
|
|
45
|
+
return new Response(null, {
|
|
46
|
+
status: status === 200 ? 204 : status,
|
|
47
|
+
headers: headers,
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
if (typeof data === "string" ||
|
|
51
|
+
typeof data === "number" ||
|
|
52
|
+
typeof data === "boolean") {
|
|
53
|
+
return new Response(String(data), {
|
|
54
|
+
status: status,
|
|
55
|
+
headers: {
|
|
56
|
+
"Content-Type": "text/plain; charset=utf-8",
|
|
57
|
+
...headers,
|
|
58
|
+
},
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
return json(data, status, headers);
|
|
62
|
+
}
|
|
63
|
+
// 普通对象 -> JSON
|
|
64
|
+
return json(result);
|
|
65
|
+
}
|
|
66
|
+
// 其他类型 -> 204
|
|
67
|
+
return new Response(null, { status: 204 });
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* 处理验证错误
|
|
71
|
+
*/
|
|
72
|
+
function handleValidationError(error) {
|
|
73
|
+
return json({
|
|
74
|
+
success: false,
|
|
75
|
+
error: "Validation Error",
|
|
76
|
+
message: error.message,
|
|
77
|
+
timestamp: new Date().toISOString(),
|
|
78
|
+
}, 400);
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* 处理内部错误
|
|
82
|
+
*/
|
|
83
|
+
function handleInternalError(error) {
|
|
84
|
+
return json({
|
|
85
|
+
success: false,
|
|
86
|
+
error: "Internal Error",
|
|
87
|
+
message: error instanceof Error ? error.message : "未知错误",
|
|
88
|
+
}, 500);
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* 判断是否为 handler 函数
|
|
92
|
+
*/
|
|
93
|
+
function isHandler(value) {
|
|
94
|
+
return typeof value === "function";
|
|
95
|
+
}
|
|
96
|
+
// 实现
|
|
97
|
+
export function createHandler(schemaOrHandler, maybeHandler) {
|
|
98
|
+
// 判断调用方式
|
|
99
|
+
const hasSchema = !isHandler(schemaOrHandler);
|
|
100
|
+
const schema = hasSchema ? schemaOrHandler : {};
|
|
101
|
+
const handler = hasSchema
|
|
102
|
+
? maybeHandler
|
|
103
|
+
: schemaOrHandler;
|
|
104
|
+
// 预编译 schema
|
|
105
|
+
if (schema.body ||
|
|
106
|
+
schema.query ||
|
|
107
|
+
schema.params ||
|
|
108
|
+
schema.headers ||
|
|
109
|
+
schema.cookies) {
|
|
110
|
+
precompileSchemasUltra(schema);
|
|
111
|
+
}
|
|
112
|
+
return async (req) => {
|
|
113
|
+
try {
|
|
114
|
+
// 解析请求数据
|
|
115
|
+
const query = parseQuery(req);
|
|
116
|
+
const headers = parseHeaders(req);
|
|
117
|
+
const cookies = parseCookies(req);
|
|
118
|
+
const params = req.params || {};
|
|
119
|
+
// 解析请求体
|
|
120
|
+
let body = undefined;
|
|
121
|
+
if (req.method !== "GET" && req.method !== "HEAD") {
|
|
122
|
+
const [, parsedBody] = await goAwait(parseBody(req));
|
|
123
|
+
body = parsedBody;
|
|
124
|
+
}
|
|
125
|
+
// 验证 schema
|
|
126
|
+
const data = { body, query, params, headers, cookies };
|
|
127
|
+
if (schema.body ||
|
|
128
|
+
schema.query ||
|
|
129
|
+
schema.params ||
|
|
130
|
+
schema.headers ||
|
|
131
|
+
schema.cookies) {
|
|
132
|
+
validateAllSchemasUltra(schema, data);
|
|
133
|
+
}
|
|
134
|
+
// 调用 handler
|
|
135
|
+
const result = await handler({
|
|
136
|
+
req,
|
|
137
|
+
body: body,
|
|
138
|
+
query: query,
|
|
139
|
+
params: params,
|
|
140
|
+
headers: headers,
|
|
141
|
+
cookies: cookies,
|
|
142
|
+
});
|
|
143
|
+
return autoResponse(result);
|
|
144
|
+
}
|
|
145
|
+
catch (error) {
|
|
146
|
+
if (error instanceof Error && error.message.includes("验证失败")) {
|
|
147
|
+
return handleValidationError(error);
|
|
148
|
+
}
|
|
149
|
+
return handleInternalError(error);
|
|
150
|
+
}
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
// 实现
|
|
154
|
+
export function createHandlerWithExtra(schemaOrHandler, maybeHandler) {
|
|
155
|
+
// 判断调用方式
|
|
156
|
+
const hasSchema = !isHandler(schemaOrHandler);
|
|
157
|
+
const schema = hasSchema ? schemaOrHandler : {};
|
|
158
|
+
const handler = hasSchema
|
|
159
|
+
? maybeHandler
|
|
160
|
+
: schemaOrHandler;
|
|
161
|
+
// 预编译 schema
|
|
162
|
+
if (schema.body ||
|
|
163
|
+
schema.query ||
|
|
164
|
+
schema.params ||
|
|
165
|
+
schema.headers ||
|
|
166
|
+
schema.cookies) {
|
|
167
|
+
precompileSchemasUltra(schema);
|
|
168
|
+
}
|
|
169
|
+
return async (req) => {
|
|
170
|
+
try {
|
|
171
|
+
// 解析请求数据
|
|
172
|
+
const query = parseQuery(req);
|
|
173
|
+
const headers = parseHeaders(req);
|
|
174
|
+
const cookies = parseCookies(req);
|
|
175
|
+
const params = req.params || {};
|
|
176
|
+
// 解析请求体
|
|
177
|
+
let body = undefined;
|
|
178
|
+
if (req.method !== "GET" && req.method !== "HEAD") {
|
|
179
|
+
const [, parsedBody] = await goAwait(parseBody(req));
|
|
180
|
+
body = parsedBody;
|
|
181
|
+
}
|
|
182
|
+
// 验证 schema
|
|
183
|
+
const data = { body, query, params, headers, cookies };
|
|
184
|
+
if (schema.body ||
|
|
185
|
+
schema.query ||
|
|
186
|
+
schema.params ||
|
|
187
|
+
schema.headers ||
|
|
188
|
+
schema.cookies) {
|
|
189
|
+
validateAllSchemasUltra(schema, data);
|
|
190
|
+
}
|
|
191
|
+
// 获取中间件注入的额外数据
|
|
192
|
+
const extras = (req.__locals ??
|
|
193
|
+
{});
|
|
194
|
+
// 调用 handler
|
|
195
|
+
const result = await handler({
|
|
196
|
+
req,
|
|
197
|
+
body: body,
|
|
198
|
+
query: query,
|
|
199
|
+
params: params,
|
|
200
|
+
headers: headers,
|
|
201
|
+
cookies: cookies,
|
|
202
|
+
...extras,
|
|
203
|
+
});
|
|
204
|
+
return autoResponse(result);
|
|
205
|
+
}
|
|
206
|
+
catch (error) {
|
|
207
|
+
if (error instanceof Error && error.message.includes("验证失败")) {
|
|
208
|
+
return handleValidationError(error);
|
|
209
|
+
}
|
|
210
|
+
return handleInternalError(error);
|
|
211
|
+
}
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* 简单的路由处理器 (无 schema 验证,只有 req)
|
|
216
|
+
*
|
|
217
|
+
* @example
|
|
218
|
+
* ```typescript
|
|
219
|
+
* simpleHandler(({ req }) => {
|
|
220
|
+
* return { message: "Hello World" };
|
|
221
|
+
* })
|
|
222
|
+
* ```
|
|
223
|
+
*/
|
|
224
|
+
export function simpleHandler(handler) {
|
|
225
|
+
return async (req) => {
|
|
226
|
+
try {
|
|
227
|
+
const result = await handler({ req });
|
|
228
|
+
return autoResponse(result);
|
|
229
|
+
}
|
|
230
|
+
catch (error) {
|
|
231
|
+
return handleInternalError(error);
|
|
232
|
+
}
|
|
233
|
+
};
|
|
234
|
+
}
|
|
@@ -16,10 +16,16 @@ export class DependencyManager {
|
|
|
16
16
|
let deps;
|
|
17
17
|
switch (framework) {
|
|
18
18
|
case "vue":
|
|
19
|
-
deps = await Promise.all([
|
|
19
|
+
deps = await Promise.all([
|
|
20
|
+
import("vue"),
|
|
21
|
+
import("@vue/server-renderer"),
|
|
22
|
+
]);
|
|
20
23
|
break;
|
|
21
24
|
case "react":
|
|
22
|
-
deps = await Promise.all([
|
|
25
|
+
deps = await Promise.all([
|
|
26
|
+
import("react"),
|
|
27
|
+
import("react-dom/server"),
|
|
28
|
+
]);
|
|
23
29
|
break;
|
|
24
30
|
default:
|
|
25
31
|
throw new Error(`不支持的框架: ${framework}`);
|
package/dist/utils/go-await.js
CHANGED
|
@@ -26,8 +26,5 @@
|
|
|
26
26
|
export function goAwait(promise) {
|
|
27
27
|
return promise
|
|
28
28
|
.then((data) => [null, data])
|
|
29
|
-
.catch((err) => [
|
|
30
|
-
err instanceof Error ? err : new Error(String(err)),
|
|
31
|
-
undefined,
|
|
32
|
-
]);
|
|
29
|
+
.catch((err) => [err instanceof Error ? err : new Error(String(err)), undefined]);
|
|
33
30
|
}
|
package/dist/utils/handle.d.ts
CHANGED
package/dist/utils/handle.js
CHANGED
package/dist/utils/index.d.ts
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
export
|
|
5
|
-
export
|
|
6
|
-
export
|
|
7
|
-
export
|
|
8
|
-
export
|
|
9
|
-
export
|
|
10
|
-
export
|
|
1
|
+
/**
|
|
2
|
+
* 工具函数模块导出
|
|
3
|
+
*/
|
|
4
|
+
export { createHandler, createHandlerWithExtra, simpleHandler, } from "./create-handler";
|
|
5
|
+
export { parseBody, parseQuery, parseHeaders, parseCookies } from "./parsers";
|
|
6
|
+
export { json, text, html, redirect, empty, stream } from "./response";
|
|
7
|
+
export { goAwait } from "./go-await";
|
|
8
|
+
export { base64urlEncode, base64urlDecode } from "./base64url";
|
|
9
|
+
export { setLocals, getLocals } from "./handle";
|
|
10
|
+
export { parseRequest, validateRequest, parseAndValidateRequest, createRequestValidator, } from "./request-validator";
|
|
11
|
+
export { HtmlRenderer } from "./html-renderer";
|
|
12
|
+
export { DependencyManager } from "./dependency-manager";
|