vafast 0.6.0 → 0.6.2
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/{contract-C9wzCRMW.d.mts → contract-CuruqP6h.d.mts} +3 -1
- package/dist/{contract-CVd4v1jb.mjs → contract-Cwmmo-Nn.mjs} +3 -2
- package/dist/contract-Cwmmo-Nn.mjs.map +1 -0
- package/dist/defineRoute.mjs +4 -2
- package/dist/defineRoute.mjs.map +1 -1
- package/dist/index.d.mts +4 -4
- package/dist/index.mjs +6 -6
- package/dist/middleware-DXssDt1F.d.mts +51 -0
- package/dist/middleware.d.mts +2 -2
- package/dist/middleware.mjs +2 -2
- package/dist/{response-kuzDQDr9.mjs → response-Bs9GhJz-.mjs} +35 -10
- package/dist/response-Bs9GhJz-.mjs.map +1 -0
- package/dist/{response-C4l9MTGJ.d.mts → response-F-VxN-cB.d.mts} +2 -2
- package/dist/{route-registry-BzExlM2t.mjs → route-registry-DsPslV2b.mjs} +2 -1
- package/dist/route-registry-DsPslV2b.mjs.map +1 -0
- package/dist/server/index.mjs +3 -3
- package/dist/server/server-factory.mjs +3 -3
- package/dist/server/server.mjs +2 -2
- package/dist/{server-CTlKzxzm.mjs → server-CAhwnEPW.mjs} +2 -2
- package/dist/{server-CTlKzxzm.mjs.map → server-CAhwnEPW.mjs.map} +1 -1
- package/dist/{server-CzfQQrjf.mjs → server-DWndB63Z.mjs} +14 -6
- package/dist/server-DWndB63Z.mjs.map +1 -0
- package/dist/utils/contract.d.mts +1 -1
- package/dist/utils/contract.mjs +1 -1
- package/dist/utils/index.d.mts +2 -2
- package/dist/utils/index.mjs +3 -3
- package/dist/utils/response.d.mts +1 -1
- package/dist/utils/response.mjs +1 -1
- package/dist/utils/route-registry.mjs +1 -1
- package/package.json +1 -1
- package/dist/contract-CVd4v1jb.mjs.map +0 -1
- package/dist/middleware-CD1F3BEw.d.mts +0 -24
- package/dist/response-kuzDQDr9.mjs.map +0 -1
- package/dist/route-registry-BzExlM2t.mjs.map +0 -1
- package/dist/server-CzfQQrjf.mjs.map +0 -1
|
@@ -8,6 +8,8 @@ interface RouteSpec {
|
|
|
8
8
|
path: string;
|
|
9
9
|
name?: string;
|
|
10
10
|
description?: string;
|
|
11
|
+
/** 是否为 SSE 端点(Server-Sent Events) */
|
|
12
|
+
sse?: boolean;
|
|
11
13
|
schema?: {
|
|
12
14
|
body?: TSchema;
|
|
13
15
|
query?: TSchema;
|
|
@@ -69,4 +71,4 @@ declare function generateAITools(routes: readonly unknown[]): AITool[];
|
|
|
69
71
|
declare function getApiSpec(routesOrContext?: readonly unknown[] | Record<string, unknown>): ApiSpec;
|
|
70
72
|
//#endregion
|
|
71
73
|
export { generateAITools as n, getApiSpec as r, ApiSpec as t };
|
|
72
|
-
//# sourceMappingURL=contract-
|
|
74
|
+
//# sourceMappingURL=contract-CuruqP6h.d.mts.map
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { o as getRouteRegistry } from "./route-registry-
|
|
1
|
+
import { o as getRouteRegistry } from "./route-registry-DsPslV2b.mjs";
|
|
2
2
|
|
|
3
3
|
//#region src/utils/contract.ts
|
|
4
4
|
/**
|
|
@@ -16,6 +16,7 @@ function generateSpec(routes) {
|
|
|
16
16
|
};
|
|
17
17
|
if (r.name) spec.name = r.name;
|
|
18
18
|
if (r.description) spec.description = r.description;
|
|
19
|
+
if (r.sse === true || r.handler?.__sse?.__brand === "SSE") spec.sse = true;
|
|
19
20
|
if (r.schema) {
|
|
20
21
|
const schema = r.schema;
|
|
21
22
|
if (schema.body || schema.query || schema.params || schema.headers || schema.cookies || schema.response) {
|
|
@@ -112,4 +113,4 @@ function getRoutesFromRegistry() {
|
|
|
112
113
|
|
|
113
114
|
//#endregion
|
|
114
115
|
export { getApiSpec as n, generateAITools as t };
|
|
115
|
-
//# sourceMappingURL=contract-
|
|
116
|
+
//# sourceMappingURL=contract-Cwmmo-Nn.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"contract-Cwmmo-Nn.mjs","names":[],"sources":["../src/utils/contract.ts"],"sourcesContent":["/**\n * API Spec 生成器\n *\n * 用于生成 API 规范,支持:\n * - 跨仓库类型同步\n * - AI 工具函数生成\n * - Swagger/OpenAPI 文档生成\n *\n * @example\n * ```typescript\n * import { getApiSpec } from 'vafast'\n *\n * // 方式 1:直接作为 handler(推荐,自动从全局 Registry 获取)\n * const allRoutes = [\n * ...routes,\n * { method: 'GET', path: '/api-spec', handler: getApiSpec }\n * ]\n *\n * // 方式 2:显式传参(灵活场景,如只暴露公开 API)\n * { handler: () => getApiSpec(publicRoutes) }\n * ```\n */\n\nimport type { TSchema } from '@sinclair/typebox'\nimport type { RouteSchema } from '../defineRoute'\nimport { getRouteRegistry } from './route-registry'\n\n/** 路由规范信息 */\ninterface RouteSpec {\n method: string\n path: string\n name?: string\n description?: string\n /** 是否为 SSE 端点(Server-Sent Events) */\n sse?: boolean\n schema?: {\n body?: TSchema\n query?: TSchema\n params?: TSchema\n headers?: TSchema\n cookies?: TSchema\n response?: TSchema\n }\n}\n\n/** API 规范 */\nexport interface ApiSpec {\n version: string\n generatedAt: string\n routes: RouteSpec[]\n}\n\n/** AI 工具函数格式 */\ninterface AITool {\n name: string\n description?: string\n parameters?: TSchema\n}\n\n/**\n * 从路由数组生成 API 规范\n */\nfunction generateSpec(routes: readonly unknown[]): ApiSpec {\n const routeSpecs: RouteSpec[] = []\n\n for (const route of routes) {\n const r = route as {\n method?: string\n path?: string\n name?: string\n description?: string\n schema?: RouteSchema\n handler?: { __sse?: { readonly __brand: 'SSE' } }\n sse?: boolean // 从 Registry 获取时已经是布尔值\n }\n\n if (!r.method || !r.path) continue\n\n // 跳过 spec 接口本身\n if (r.path === '/api-spec' || r.path === '/__spec__') continue\n\n const spec: RouteSpec = {\n method: r.method,\n path: r.path,\n }\n\n // 直接从路由获取 name 和 description\n if (r.name) spec.name = r.name\n if (r.description) spec.description = r.description\n\n // 检测 SSE 标记(两种来源:handler.__sse 或直接的 sse 字段)\n if (r.sse === true || r.handler?.__sse?.__brand === 'SSE') {\n spec.sse = true\n }\n\n // 直接从路由获取 schema\n if (r.schema) {\n const schema = r.schema\n if (schema.body || schema.query || schema.params || schema.headers || schema.cookies || schema.response) {\n spec.schema = {}\n if (schema.body) spec.schema.body = schema.body\n if (schema.query) spec.schema.query = schema.query\n if (schema.params) spec.schema.params = schema.params\n if (schema.headers) spec.schema.headers = schema.headers\n if (schema.cookies) spec.schema.cookies = schema.cookies\n if (schema.response) spec.schema.response = schema.response\n }\n }\n\n routeSpecs.push(spec)\n }\n\n return {\n version: '1.0.0',\n generatedAt: new Date().toISOString(),\n routes: routeSpecs,\n }\n}\n\n/**\n * 从路由数组生成 AI 工具函数\n *\n * 可直接用于 OpenAI Function Calling / Claude Tools\n *\n * @example\n * ```typescript\n * const tools = generateAITools(routes)\n * // [{ name: 'get_users', description: '获取用户列表', parameters: {...} }]\n * ```\n */\nexport function generateAITools(routes: readonly unknown[]): AITool[] {\n const tools: AITool[] = []\n\n for (const route of routes) {\n const r = route as {\n method?: string\n path?: string\n name?: string\n description?: string\n schema?: RouteSchema\n }\n\n if (!r.method || !r.path) continue\n if (r.path === '/api-spec' || r.path === '/__spec__') continue\n\n // 使用 name 或从 path 生成\n const name = r.name || pathToToolName(r.method, r.path)\n\n const tool: AITool = { name }\n if (r.description) tool.description = r.description\n\n // GET 用 query,其他用 body\n if (r.schema) {\n if (r.method === 'GET' && r.schema.query) {\n tool.parameters = r.schema.query\n } else if (r.schema.body) {\n tool.parameters = r.schema.body\n }\n }\n\n tools.push(tool)\n }\n\n return tools\n}\n\n/** 从路径生成工具名 */\nfunction pathToToolName(method: string, path: string): string {\n const segments = path.split('/').filter(Boolean)\n const cleanSegments = segments\n .map(s => s.startsWith(':') ? '' : s)\n .filter(Boolean)\n\n const prefix = method.toLowerCase()\n const suffix = cleanSegments.join('_')\n\n return suffix ? `${prefix}_${suffix}` : prefix\n}\n\n/**\n * 获取 API 规范\n *\n * 支持多种调用方式:\n * 1. 直接作为 handler:自动从全局 Registry 获取(推荐)\n * 2. 无参调用:同上,用于 CLI/测试\n * 3. 有参调用:显式传递路由数组(灵活场景)\n *\n * @param routesOrContext - 可选,路由数组或 handler context。不传则从全局 Registry 获取\n * @returns ApiSpec 对象\n *\n * @example\n * ```typescript\n * import { getApiSpec } from 'vafast'\n *\n * // 方式 1:直接作为 handler(推荐,最简洁)\n * { method: 'GET', path: '/api-spec', handler: getApiSpec }\n *\n * // 方式 2:显式传参(只暴露公开 API)\n * { handler: () => getApiSpec(publicRoutes) }\n *\n * // 方式 3:本地使用(CLI、测试)\n * const spec = getApiSpec()\n * ```\n */\nexport function getApiSpec(routesOrContext?: readonly unknown[] | Record<string, unknown>): ApiSpec {\n // 智能检测:是路由数组还是 handler context\n // 路由数组:Array && 第一个元素有 method 属性\n const isRoutesArray = Array.isArray(routesOrContext) &&\n routesOrContext.length > 0 &&\n typeof (routesOrContext[0] as Record<string, unknown>)?.method === 'string'\n\n const routeList = isRoutesArray\n ? routesOrContext\n : getRoutesFromRegistry()\n\n return generateSpec(routeList)\n}\n\n/**\n * 从全局 Registry 获取路由列表\n * 包含 schema 信息(Registry 存储完整路由)\n */\nfunction getRoutesFromRegistry(): readonly unknown[] {\n try {\n const registry = getRouteRegistry()\n return registry.getAll()\n } catch {\n // Registry 未初始化时返回空数组\n return []\n }\n}\n"],"mappings":";;;;;;AA8DA,SAAS,aAAa,QAAqC;CACzD,MAAM,aAA0B,EAAE;AAElC,MAAK,MAAM,SAAS,QAAQ;EAC1B,MAAM,IAAI;AAUV,MAAI,CAAC,EAAE,UAAU,CAAC,EAAE,KAAM;AAG1B,MAAI,EAAE,SAAS,eAAe,EAAE,SAAS,YAAa;EAEtD,MAAM,OAAkB;GACtB,QAAQ,EAAE;GACV,MAAM,EAAE;GACT;AAGD,MAAI,EAAE,KAAM,MAAK,OAAO,EAAE;AAC1B,MAAI,EAAE,YAAa,MAAK,cAAc,EAAE;AAGxC,MAAI,EAAE,QAAQ,QAAQ,EAAE,SAAS,OAAO,YAAY,MAClD,MAAK,MAAM;AAIb,MAAI,EAAE,QAAQ;GACZ,MAAM,SAAS,EAAE;AACjB,OAAI,OAAO,QAAQ,OAAO,SAAS,OAAO,UAAU,OAAO,WAAW,OAAO,WAAW,OAAO,UAAU;AACvG,SAAK,SAAS,EAAE;AAChB,QAAI,OAAO,KAAM,MAAK,OAAO,OAAO,OAAO;AAC3C,QAAI,OAAO,MAAO,MAAK,OAAO,QAAQ,OAAO;AAC7C,QAAI,OAAO,OAAQ,MAAK,OAAO,SAAS,OAAO;AAC/C,QAAI,OAAO,QAAS,MAAK,OAAO,UAAU,OAAO;AACjD,QAAI,OAAO,QAAS,MAAK,OAAO,UAAU,OAAO;AACjD,QAAI,OAAO,SAAU,MAAK,OAAO,WAAW,OAAO;;;AAIvD,aAAW,KAAK,KAAK;;AAGvB,QAAO;EACL,SAAS;EACT,8BAAa,IAAI,MAAM,EAAC,aAAa;EACrC,QAAQ;EACT;;;;;;;;;;;;;AAcH,SAAgB,gBAAgB,QAAsC;CACpE,MAAM,QAAkB,EAAE;AAE1B,MAAK,MAAM,SAAS,QAAQ;EAC1B,MAAM,IAAI;AAQV,MAAI,CAAC,EAAE,UAAU,CAAC,EAAE,KAAM;AAC1B,MAAI,EAAE,SAAS,eAAe,EAAE,SAAS,YAAa;EAKtD,MAAM,OAAe,EAAE,MAFV,EAAE,QAAQ,eAAe,EAAE,QAAQ,EAAE,KAAK,EAE1B;AAC7B,MAAI,EAAE,YAAa,MAAK,cAAc,EAAE;AAGxC,MAAI,EAAE,QACJ;OAAI,EAAE,WAAW,SAAS,EAAE,OAAO,MACjC,MAAK,aAAa,EAAE,OAAO;YAClB,EAAE,OAAO,KAClB,MAAK,aAAa,EAAE,OAAO;;AAI/B,QAAM,KAAK,KAAK;;AAGlB,QAAO;;;AAIT,SAAS,eAAe,QAAgB,MAAsB;CAE5D,MAAM,gBADW,KAAK,MAAM,IAAI,CAAC,OAAO,QAAQ,CAE7C,KAAI,MAAK,EAAE,WAAW,IAAI,GAAG,KAAK,EAAE,CACpC,OAAO,QAAQ;CAElB,MAAM,SAAS,OAAO,aAAa;CACnC,MAAM,SAAS,cAAc,KAAK,IAAI;AAEtC,QAAO,SAAS,GAAG,OAAO,GAAG,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;AA4B1C,SAAgB,WAAW,iBAAyE;AAWlG,QAAO,aARe,MAAM,QAAQ,gBAAgB,IAClD,gBAAgB,SAAS,KACzB,OAAQ,gBAAgB,IAAgC,WAAW,WAGjE,kBACA,uBAAuB,CAEG;;;;;;AAOhC,SAAS,wBAA4C;AACnD,KAAI;AAEF,SADiB,kBAAkB,CACnB,QAAQ;SAClB;AAEN,SAAO,EAAE"}
|
package/dist/defineRoute.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { a as parseCookies, l as parseHeaders, r as parseBody, u as parseQuery } from "./parsers-BrG_mRLq.mjs";
|
|
2
2
|
import { a as validateAllSchemas, i as precompileSchemas } from "./validators-CkfvNBbK.mjs";
|
|
3
|
-
import { i as json,
|
|
3
|
+
import { i as json, u as VafastError } from "./response-Bs9GhJz-.mjs";
|
|
4
4
|
|
|
5
5
|
//#region src/defineRoute.ts
|
|
6
6
|
/**
|
|
@@ -170,16 +170,18 @@ function flattenRoutes(routes, parentPath = "", parentMiddleware = [], parent) {
|
|
|
170
170
|
const fullPath = parentPath + route.path;
|
|
171
171
|
const mergedMiddleware = [...parentMiddleware, ...route.middleware || []];
|
|
172
172
|
if (isLeafRoute(route)) {
|
|
173
|
+
const isSSE = route.handler?.__sse?.__brand === "SSE";
|
|
173
174
|
const processed = {
|
|
174
175
|
method: route.method,
|
|
175
176
|
path: fullPath,
|
|
176
177
|
name: route.name,
|
|
177
178
|
description: route.description,
|
|
178
179
|
schema: route.schema,
|
|
179
|
-
handler: wrapHandler(route.schema, route.handler),
|
|
180
|
+
handler: isSSE ? route.handler : wrapHandler(route.schema, route.handler),
|
|
180
181
|
middleware: mergedMiddleware.length > 0 ? mergedMiddleware : void 0,
|
|
181
182
|
docs: route.docs
|
|
182
183
|
};
|
|
184
|
+
if (isSSE) processed.handler.__sse = { __brand: "SSE" };
|
|
183
185
|
if (parent) processed.parent = parent;
|
|
184
186
|
const knownKeys = [
|
|
185
187
|
"method",
|
package/dist/defineRoute.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"defineRoute.mjs","names":[],"sources":["../src/defineRoute.ts"],"sourcesContent":["/**\n * 路由定义 - Schema 在路由级别定义,支持嵌套路由和中间件类型推断\n *\n * @example\n * ```typescript\n * // 定义带类型的中间件(函数式风格,通过 next 传递上下文)\n * const authMiddleware = defineMiddleware<{ user: User }>((req, next) => {\n * const user = getUser(req)\n * return next({ user }) // 通过 next 参数传递上下文\n * })\n *\n * // 路由自动推断中间件注入的类型\n * const routes = defineRoutes([\n * defineRoute({\n * path: '/api',\n * middleware: [authMiddleware],\n * children: [\n * defineRoute({\n * method: 'GET',\n * path: '/profile',\n * handler: ({ user }) => ({ name: user.name }) // ✅ user 有类型\n * })\n * ]\n * })\n * ])\n * ```\n */\n\nimport type { TSchema, Static } from \"@sinclair/typebox\";\nimport { parseBody, parseQuery, parseHeaders, parseCookies } from \"./utils/parsers\";\nimport { validateAllSchemas, precompileSchemas } from \"./utils/validators/validators\";\nimport { json } from \"./utils/response\";\nimport { VafastError } from \"./middleware\";\n\n// ============= Schema 类型 =============\n\n/** 路由 Schema 配置 */\nexport interface RouteSchema {\n body?: TSchema;\n query?: TSchema;\n params?: TSchema;\n headers?: TSchema;\n cookies?: TSchema;\n /** 响应类型 schema(用于类型同步,运行时不做校验) */\n response?: TSchema;\n}\n\n/** 从 Schema 推断类型 */\ntype InferSchemaType<T extends RouteSchema> = {\n body: T[\"body\"] extends TSchema ? Static<T[\"body\"]> : unknown;\n query: T[\"query\"] extends TSchema ? Static<T[\"query\"]> : Record<string, string>;\n params: T[\"params\"] extends TSchema ? Static<T[\"params\"]> : Record<string, string>;\n headers: T[\"headers\"] extends TSchema ? Static<T[\"headers\"]> : Record<string, string>;\n cookies: T[\"cookies\"] extends TSchema ? Static<T[\"cookies\"]> : Record<string, string>;\n};\n\n// ============= 中间件类型系统 =============\n\n/** 带类型标记的中间件 */\nexport interface TypedMiddleware<TContext extends object = object> {\n (req: Request, next: (ctx?: TContext) => Promise<Response>): Response | Promise<Response>;\n /** 类型标记(仅编译时使用) */\n __context?: TContext;\n}\n\n/** 普通中间件(无类型注入) */\ntype PlainMiddleware = (req: Request, next: () => Promise<Response>) => Response | Promise<Response>;\n\n/** 任意中间件类型 */\ntype AnyMiddleware = TypedMiddleware<object> | PlainMiddleware;\n\n/** 从中间件提取上下文类型 */\ntype ExtractMiddlewareContext<T> = T extends TypedMiddleware<infer C> ? C : object;\n\n/** 合并中间件数组的上下文类型 */\ntype MergeMiddlewareContexts<T extends readonly unknown[]> =\n T extends readonly [infer First, ...infer Rest]\n ? ExtractMiddlewareContext<First> & MergeMiddlewareContexts<Rest>\n : object;\n\n// ============= Handler 上下文 =============\n\n/** Handler 上下文(包含 schema 推断) */\nexport interface HandlerContext<TSchema extends RouteSchema = RouteSchema> {\n req: Request;\n body: InferSchemaType<TSchema>[\"body\"];\n query: InferSchemaType<TSchema>[\"query\"];\n params: InferSchemaType<TSchema>[\"params\"];\n headers: InferSchemaType<TSchema>[\"headers\"];\n cookies: InferSchemaType<TSchema>[\"cookies\"];\n}\n\n/** Handler 上下文(带中间件注入的额外类型) */\ntype HandlerContextWithExtra<TSchema extends RouteSchema, TExtra> =\n HandlerContext<TSchema> & TExtra;\n\n// ============= 路由配置类型 =============\n\n/** HTTP 方法 */\ntype HTTPMethod = \"GET\" | \"POST\" | \"PUT\" | \"DELETE\" | \"PATCH\" | \"OPTIONS\" | \"HEAD\";\n\n/** 叶子路由配置(有 method 和 handler) */\nexport interface LeafRouteConfig<\n TMethod extends HTTPMethod = HTTPMethod,\n TPath extends string = string,\n TSchema extends RouteSchema = RouteSchema,\n TReturn = unknown,\n TMiddleware extends readonly AnyMiddleware[] = readonly AnyMiddleware[]\n> {\n readonly method: TMethod;\n readonly path: TPath;\n readonly name?: string;\n readonly description?: string;\n readonly schema?: TSchema;\n readonly handler: (\n ctx: HandlerContextWithExtra<TSchema, MergeMiddlewareContexts<TMiddleware>>\n ) => TReturn | Promise<TReturn>;\n readonly middleware?: TMiddleware;\n readonly docs?: {\n tags?: string[];\n security?: unknown[];\n responses?: Record<string, unknown>;\n };\n}\n\n/** 嵌套路由配置(有 children,无 method 和 handler) */\nexport interface NestedRouteConfig<\n TPath extends string = string,\n TMiddleware extends readonly AnyMiddleware[] = readonly AnyMiddleware[]\n> {\n readonly path: TPath;\n readonly name?: string;\n readonly description?: string;\n readonly middleware?: TMiddleware;\n readonly children: ReadonlyArray<RouteConfigResult>;\n}\n\n/** defineRoute 返回的类型 */\ntype RouteConfigResult =\n | LeafRouteConfig<HTTPMethod, string, RouteSchema, unknown, readonly AnyMiddleware[]>\n | NestedRouteConfig<string, readonly AnyMiddleware[]>;\n\n/** 处理后的扁平路由 */\nexport interface ProcessedRoute {\n method: HTTPMethod;\n path: string;\n name?: string;\n description?: string;\n schema?: RouteSchema;\n handler: (req: Request) => Promise<Response>;\n middleware?: readonly AnyMiddleware[];\n docs?: {\n tags?: string[];\n security?: unknown[];\n responses?: Record<string, unknown>;\n };\n /** 允许任意扩展(兼容 Route 类型) */\n [key: string]: unknown;\n}\n\n/** Route 类型别名(便于用户使用) */\nexport type Route = ProcessedRoute;\n\n// ============= defineMiddleware =============\n\n/**\n * 定义带类型的中间件(函数式风格)\n *\n * 通过 next() 参数传递上下文,更符合函数式编程风格\n *\n * @example\n * ```typescript\n * type AuthContext = { user: { id: string; name: string } }\n *\n * const authMiddleware = defineMiddleware<AuthContext>((req, next) => {\n * const user = getUserFromToken(req)\n * return next({ user }) // 通过 next 传递上下文\n * })\n * ```\n */\nexport function defineMiddleware<TContext extends object = object>(\n handler: (\n req: Request,\n next: (ctx?: TContext) => Promise<Response>\n ) => Promise<Response>\n): TypedMiddleware<TContext> {\n // 包装成标准中间件签名\n const middleware = ((req: Request, originalNext: () => Promise<Response>) => {\n // 包装 next,接收上下文参数并存储到 req.__locals\n const nextWithContext = (ctx?: TContext): Promise<Response> => {\n if (ctx) {\n const target = req as unknown as { __locals?: object };\n target.__locals = { ...(target.__locals || {}), ...ctx };\n }\n return originalNext();\n };\n return handler(req, nextWithContext);\n }) as TypedMiddleware<TContext>;\n\n return middleware;\n}\n\n// ============= 响应处理 =============\n\n/** 自动转换返回值为 Response */\nfunction autoResponse(result: unknown): Response {\n if (result instanceof Response) return result;\n if (result === null || result === undefined) return new Response(null, { status: 204 });\n if (typeof result === \"string\") {\n return new Response(result, { headers: { \"Content-Type\": \"text/plain; charset=utf-8\" } });\n }\n if (typeof result === \"number\" || typeof result === \"boolean\") {\n return new Response(String(result), { headers: { \"Content-Type\": \"text/plain; charset=utf-8\" } });\n }\n if (typeof result === \"object\") {\n const obj = result as Record<string, unknown>;\n if (\"data\" in obj && (\"status\" in obj || \"headers\" in obj)) {\n const { data, status = 200, headers = {} } = obj;\n if (data === null || data === undefined) {\n return new Response(null, { status: status === 200 ? 204 : (status as number), headers: headers as HeadersInit });\n }\n return json(data, status as number, headers as Record<string, string>);\n }\n return json(result);\n }\n return new Response(null, { status: 204 });\n}\n\n/** 创建包装后的 handler */\nfunction wrapHandler<TSchema extends RouteSchema>(\n schema: TSchema | undefined,\n userHandler: (ctx: HandlerContext<TSchema>) => unknown | Promise<unknown>\n): (req: Request) => Promise<Response> {\n if (schema && (schema.body || schema.query || schema.params || schema.headers || schema.cookies)) {\n precompileSchemas(schema);\n }\n\n return async (req: Request): Promise<Response> => {\n try {\n const query = parseQuery(req);\n const headers = parseHeaders(req);\n const cookies = parseCookies(req);\n const params = ((req as unknown as Record<string, unknown>).params as Record<string, string>) || {};\n\n let body: unknown = undefined;\n if (req.method !== \"GET\" && req.method !== \"HEAD\") {\n try {\n body = await parseBody(req);\n } catch {\n // 忽略解析错误\n }\n }\n\n const data = { body, query, params, headers, cookies };\n if (schema && (schema.body || schema.query || schema.params || schema.headers || schema.cookies)) {\n validateAllSchemas(schema, data);\n }\n\n // 获取中间件注入的上下文\n const extraCtx = (req as unknown as { __locals?: unknown }).__locals || {};\n\n const result = await userHandler({\n req,\n body: body as HandlerContext<TSchema>[\"body\"],\n query: query as HandlerContext<TSchema>[\"query\"],\n params: params as HandlerContext<TSchema>[\"params\"],\n headers: headers as HandlerContext<TSchema>[\"headers\"],\n cookies: cookies as HandlerContext<TSchema>[\"cookies\"],\n ...extraCtx,\n } as HandlerContext<TSchema>);\n\n return autoResponse(result);\n } catch (error) {\n // 如果是 VafastError,重新抛出让错误处理中间件处理\n if (error instanceof VafastError) {\n throw error;\n }\n if (error instanceof Error && error.message.includes(\"验证失败\")) {\n return json({ code: 400, message: error.message }, 400);\n }\n return json({ code: 500, message: error instanceof Error ? error.message : \"未知错误\" }, 500);\n }\n };\n}\n\n// ============= 判断路由类型 =============\n\n/** 判断是否为叶子路由 */\nfunction isLeafRoute(route: RouteConfigResult): route is LeafRouteConfig {\n return \"method\" in route && \"handler\" in route;\n}\n\n/** 判断是否为嵌套路由 */\nfunction isNestedRoute(route: RouteConfigResult): route is NestedRouteConfig {\n return \"children\" in route;\n}\n\n// ============= defineRoute 函数(支持重载) =============\n\n/**\n * 定义叶子路由(有 method 和 handler),支持中间件类型推断和显式上下文类型\n *\n * @example\n * ```typescript\n * // 方式1:通过中间件自动推断上下文\n * defineRoute({\n * method: 'GET',\n * path: '/profile',\n * middleware: [authMiddleware],\n * handler: ({ user }) => { ... } // user 来自 authMiddleware\n * })\n *\n * // 方式2:显式声明上下文类型(用于父级中间件注入的场景)\n * defineRoute({\n * method: 'GET',\n * path: '/profile',\n * context: {} as { userInfo: UserInfo },\n * handler: ({ userInfo }) => { ... } // userInfo 有类型\n * })\n * ```\n */\nexport function defineRoute<\n const TSchema extends RouteSchema,\n TReturn,\n const TMiddleware extends readonly AnyMiddleware[],\n TContext extends object = object,\n TMethod extends HTTPMethod = HTTPMethod,\n TPath extends string = string\n>(config: {\n readonly method: TMethod;\n readonly path: TPath;\n readonly name?: string;\n readonly description?: string;\n readonly schema?: TSchema;\n /** 显式声明上下文类型(用于父级中间件注入的场景) */\n readonly context?: TContext;\n readonly handler: (\n ctx: HandlerContextWithExtra<TSchema, TContext & MergeMiddlewareContexts<TMiddleware>>\n ) => TReturn | Promise<TReturn>;\n readonly middleware?: TMiddleware;\n readonly docs?: {\n tags?: string[];\n security?: unknown[];\n responses?: Record<string, unknown>;\n };\n}): LeafRouteConfig<TMethod, TPath, TSchema, TReturn, TMiddleware>;\n\n/**\n * 定义嵌套路由(有 children),支持中间件类型推断\n */\nexport function defineRoute<\n const TMiddleware extends readonly AnyMiddleware[],\n TPath extends string = string\n>(config: {\n readonly path: TPath;\n readonly name?: string;\n readonly description?: string;\n readonly middleware?: TMiddleware;\n readonly children: ReadonlyArray<RouteConfigResult>;\n}): NestedRouteConfig<TPath, TMiddleware>;\n\n/**\n * defineRoute 实现\n */\nexport function defineRoute(config: {\n readonly method?: HTTPMethod;\n readonly path: string;\n readonly name?: string;\n readonly description?: string;\n readonly schema?: RouteSchema;\n readonly context?: object;\n readonly handler?: (ctx: HandlerContext<RouteSchema>) => unknown | Promise<unknown>;\n readonly middleware?: readonly AnyMiddleware[];\n readonly children?: ReadonlyArray<RouteConfigResult>;\n readonly docs?: {\n tags?: string[];\n security?: unknown[];\n responses?: Record<string, unknown>;\n };\n}): RouteConfigResult {\n return config as RouteConfigResult;\n}\n\n// ============= withContext 工厂函数 =============\n\n/**\n * 创建带预设上下文类型的路由定义器\n *\n * 用于父级中间件注入上下文的场景,定义一次,多处复用\n *\n * @example\n * ```typescript\n * // 1. 在 middleware/index.ts 中定义\n * export const defineAuthRoute = withContext<{ userInfo: UserInfo }>()\n *\n * // 2. 在路由文件中使用\n * defineAuthRoute({\n * method: 'GET',\n * path: '/profile',\n * handler: ({ userInfo }) => {\n * // userInfo 自动有类型!\n * return { id: userInfo.id }\n * }\n * })\n * ```\n */\n/**\n * 带扩展类型的路由定义器\n *\n * @typeParam TContext - Handler 上下文类型(如 { userInfo: UserInfo })\n * @typeParam TExtensions - 路由扩展字段类型(如 { webhook?: boolean })\n *\n * @example\n * ```typescript\n * // 定义扩展类型\n * interface WebhookExtension {\n * webhook?: boolean | { eventKey?: string }\n * }\n *\n * // 使用扩展\n * const defineRoute = withContext<MyContext, WebhookExtension>()\n * defineRoute({\n * method: 'POST',\n * path: '/create',\n * webhook: true, // TypeScript 严格检查\n * handler: ...\n * })\n * ```\n */\nexport function withContext<\n TContext extends object,\n TExtensions extends object = object\n>() {\n return <\n const TSchema extends RouteSchema,\n TReturn,\n const TMiddleware extends readonly AnyMiddleware[],\n TMethod extends HTTPMethod = HTTPMethod,\n TPath extends string = string\n >(config: {\n readonly method: TMethod;\n readonly path: TPath;\n readonly name?: string;\n readonly description?: string;\n readonly schema?: TSchema;\n readonly handler: (\n ctx: HandlerContextWithExtra<TSchema, TContext & MergeMiddlewareContexts<TMiddleware>>\n ) => TReturn | Promise<TReturn>;\n readonly middleware?: TMiddleware;\n readonly docs?: {\n tags?: string[];\n security?: unknown[];\n responses?: Record<string, unknown>;\n };\n } & TExtensions): LeafRouteConfig<TMethod, TPath, TSchema, TReturn, TMiddleware> => {\n return config as unknown as LeafRouteConfig<TMethod, TPath, TSchema, TReturn, TMiddleware>;\n };\n}\n\n// ============= 扁平化嵌套路由 =============\n\n/** 父级路由信息 */\ninterface ParentRouteInfo {\n /** 父级路由路径 */\n path: string;\n /** 父级路由名称 */\n name?: string;\n /** 父级路由描述 */\n description?: string;\n}\n\n/**\n * 递归扁平化路由,合并路径和中间件\n */\nfunction flattenRoutes(\n routes: ReadonlyArray<RouteConfigResult>,\n parentPath: string = \"\",\n parentMiddleware: readonly AnyMiddleware[] = [],\n parent?: ParentRouteInfo\n): ProcessedRoute[] {\n const result: ProcessedRoute[] = [];\n\n for (const route of routes) {\n const fullPath = parentPath + route.path;\n const mergedMiddleware = [...parentMiddleware, ...(route.middleware || [])];\n\n if (isLeafRoute(route)) {\n // 基础属性\n const processed: ProcessedRoute = {\n method: route.method,\n path: fullPath,\n name: route.name,\n description: route.description,\n schema: route.schema,\n handler: wrapHandler(route.schema, route.handler as (ctx: HandlerContext<RouteSchema>) => unknown),\n middleware: mergedMiddleware.length > 0 ? mergedMiddleware : undefined,\n docs: route.docs,\n };\n // 添加父级路由信息\n if (parent) {\n processed.parent = parent;\n }\n // 复制扩展属性(如 webhook, permission 等)\n const knownKeys = ['method', 'path', 'name', 'description', 'schema', 'handler', 'middleware', 'docs'];\n for (const key of Object.keys(route)) {\n if (!knownKeys.includes(key)) {\n processed[key] = (route as unknown as Record<string, unknown>)[key];\n }\n }\n result.push(processed);\n } else if (isNestedRoute(route)) {\n // 传递当前路由信息作为子路由的 parent\n const currentParent: ParentRouteInfo = {\n path: fullPath,\n name: route.name,\n description: route.description,\n };\n result.push(...flattenRoutes(route.children, fullPath, mergedMiddleware, currentParent));\n }\n }\n\n return result;\n}\n\n// ============= defineRoutes 函数 =============\n\n/** 带原始类型信息的路由数组 */\nexport type RoutesWithSource<T extends readonly RouteConfigResult[]> = ProcessedRoute[] & { __source: T };\n\n/**\n * 定义路由数组,支持嵌套路由\n *\n * 使用 `const T` 泛型自动保留字面量类型,无需手动添加 `as const`\n *\n * @example\n * ```typescript\n * const routes = defineRoutes([\n * defineRoute({ method: 'GET', path: '/users', handler: ... }),\n * defineRoute({ method: 'POST', path: '/users', handler: ... }),\n * ])\n *\n * // 类型推断自动工作,无需 as const\n * type Api = InferEden<typeof routes>\n * ```\n */\nexport function defineRoutes<const T extends readonly RouteConfigResult[]>(\n routes: T\n): RoutesWithSource<T> {\n const processed = flattenRoutes(routes);\n // 附加原始类型信息(仅用于类型推断,运行时不使用)\n return Object.assign(processed, { __source: routes }) as RoutesWithSource<T>;\n}\n\n// ============= 用于 API Client 的类型推断 =============\n\n/** 可推断的路由类型(供 vafast-api-client 使用) */\nexport type InferableRoute<\n TMethod extends string = string,\n TPath extends string = string,\n TReturn = unknown,\n TSchema extends RouteSchema = RouteSchema\n> = {\n readonly method: TMethod;\n readonly path: TPath;\n readonly name?: string;\n readonly description?: string;\n readonly schema?: TSchema;\n readonly handler: {\n __returnType: TReturn;\n __schema: TSchema;\n };\n readonly middleware?: ReadonlyArray<AnyMiddleware>;\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"defineRoute.mjs","names":[],"sources":["../src/defineRoute.ts"],"sourcesContent":["/**\n * 路由定义 - Schema 在路由级别定义,支持嵌套路由和中间件类型推断\n *\n * @example\n * ```typescript\n * // 定义带类型的中间件(函数式风格,通过 next 传递上下文)\n * const authMiddleware = defineMiddleware<{ user: User }>((req, next) => {\n * const user = getUser(req)\n * return next({ user }) // 通过 next 参数传递上下文\n * })\n *\n * // 路由自动推断中间件注入的类型\n * const routes = defineRoutes([\n * defineRoute({\n * path: '/api',\n * middleware: [authMiddleware],\n * children: [\n * defineRoute({\n * method: 'GET',\n * path: '/profile',\n * handler: ({ user }) => ({ name: user.name }) // ✅ user 有类型\n * })\n * ]\n * })\n * ])\n * ```\n */\n\nimport type { TSchema, Static } from \"@sinclair/typebox\";\nimport { parseBody, parseQuery, parseHeaders, parseCookies } from \"./utils/parsers\";\nimport { validateAllSchemas, precompileSchemas } from \"./utils/validators/validators\";\nimport { json } from \"./utils/response\";\nimport { VafastError } from \"./middleware\";\n\n// ============= Schema 类型 =============\n\n/** 路由 Schema 配置 */\nexport interface RouteSchema {\n body?: TSchema;\n query?: TSchema;\n params?: TSchema;\n headers?: TSchema;\n cookies?: TSchema;\n /** 响应类型 schema(用于类型同步,运行时不做校验) */\n response?: TSchema;\n}\n\n/** 从 Schema 推断类型 */\ntype InferSchemaType<T extends RouteSchema> = {\n body: T[\"body\"] extends TSchema ? Static<T[\"body\"]> : unknown;\n query: T[\"query\"] extends TSchema ? Static<T[\"query\"]> : Record<string, string>;\n params: T[\"params\"] extends TSchema ? Static<T[\"params\"]> : Record<string, string>;\n headers: T[\"headers\"] extends TSchema ? Static<T[\"headers\"]> : Record<string, string>;\n cookies: T[\"cookies\"] extends TSchema ? Static<T[\"cookies\"]> : Record<string, string>;\n};\n\n// ============= 中间件类型系统 =============\n\n/** 带类型标记的中间件 */\nexport interface TypedMiddleware<TContext extends object = object> {\n (req: Request, next: (ctx?: TContext) => Promise<Response>): Response | Promise<Response>;\n /** 类型标记(仅编译时使用) */\n __context?: TContext;\n}\n\n/** 普通中间件(无类型注入) */\ntype PlainMiddleware = (req: Request, next: () => Promise<Response>) => Response | Promise<Response>;\n\n/** 任意中间件类型 */\ntype AnyMiddleware = TypedMiddleware<object> | PlainMiddleware;\n\n/** 从中间件提取上下文类型 */\ntype ExtractMiddlewareContext<T> = T extends TypedMiddleware<infer C> ? C : object;\n\n/** 合并中间件数组的上下文类型 */\ntype MergeMiddlewareContexts<T extends readonly unknown[]> =\n T extends readonly [infer First, ...infer Rest]\n ? ExtractMiddlewareContext<First> & MergeMiddlewareContexts<Rest>\n : object;\n\n// ============= Handler 上下文 =============\n\n/** Handler 上下文(包含 schema 推断) */\nexport interface HandlerContext<TSchema extends RouteSchema = RouteSchema> {\n req: Request;\n body: InferSchemaType<TSchema>[\"body\"];\n query: InferSchemaType<TSchema>[\"query\"];\n params: InferSchemaType<TSchema>[\"params\"];\n headers: InferSchemaType<TSchema>[\"headers\"];\n cookies: InferSchemaType<TSchema>[\"cookies\"];\n}\n\n/** Handler 上下文(带中间件注入的额外类型) */\ntype HandlerContextWithExtra<TSchema extends RouteSchema, TExtra> =\n HandlerContext<TSchema> & TExtra;\n\n// ============= 路由配置类型 =============\n\n/** HTTP 方法 */\ntype HTTPMethod = \"GET\" | \"POST\" | \"PUT\" | \"DELETE\" | \"PATCH\" | \"OPTIONS\" | \"HEAD\";\n\n/** 叶子路由配置(有 method 和 handler) */\nexport interface LeafRouteConfig<\n TMethod extends HTTPMethod = HTTPMethod,\n TPath extends string = string,\n TSchema extends RouteSchema = RouteSchema,\n TReturn = unknown,\n TMiddleware extends readonly AnyMiddleware[] = readonly AnyMiddleware[]\n> {\n readonly method: TMethod;\n readonly path: TPath;\n readonly name?: string;\n readonly description?: string;\n readonly schema?: TSchema;\n readonly handler: (\n ctx: HandlerContextWithExtra<TSchema, MergeMiddlewareContexts<TMiddleware>>\n ) => TReturn | Promise<TReturn>;\n readonly middleware?: TMiddleware;\n readonly docs?: {\n tags?: string[];\n security?: unknown[];\n responses?: Record<string, unknown>;\n };\n}\n\n/** 嵌套路由配置(有 children,无 method 和 handler) */\nexport interface NestedRouteConfig<\n TPath extends string = string,\n TMiddleware extends readonly AnyMiddleware[] = readonly AnyMiddleware[]\n> {\n readonly path: TPath;\n readonly name?: string;\n readonly description?: string;\n readonly middleware?: TMiddleware;\n readonly children: ReadonlyArray<RouteConfigResult>;\n}\n\n/** defineRoute 返回的类型 */\ntype RouteConfigResult =\n | LeafRouteConfig<HTTPMethod, string, RouteSchema, unknown, readonly AnyMiddleware[]>\n | NestedRouteConfig<string, readonly AnyMiddleware[]>;\n\n/** 处理后的扁平路由 */\nexport interface ProcessedRoute {\n method: HTTPMethod;\n path: string;\n name?: string;\n description?: string;\n schema?: RouteSchema;\n handler: (req: Request) => Promise<Response>;\n middleware?: readonly AnyMiddleware[];\n docs?: {\n tags?: string[];\n security?: unknown[];\n responses?: Record<string, unknown>;\n };\n /** 允许任意扩展(兼容 Route 类型) */\n [key: string]: unknown;\n}\n\n/** Route 类型别名(便于用户使用) */\nexport type Route = ProcessedRoute;\n\n// ============= defineMiddleware =============\n\n/**\n * 定义带类型的中间件(函数式风格)\n *\n * 通过 next() 参数传递上下文,更符合函数式编程风格\n *\n * @example\n * ```typescript\n * type AuthContext = { user: { id: string; name: string } }\n *\n * const authMiddleware = defineMiddleware<AuthContext>((req, next) => {\n * const user = getUserFromToken(req)\n * return next({ user }) // 通过 next 传递上下文\n * })\n * ```\n */\nexport function defineMiddleware<TContext extends object = object>(\n handler: (\n req: Request,\n next: (ctx?: TContext) => Promise<Response>\n ) => Promise<Response>\n): TypedMiddleware<TContext> {\n // 包装成标准中间件签名\n const middleware = ((req: Request, originalNext: () => Promise<Response>) => {\n // 包装 next,接收上下文参数并存储到 req.__locals\n const nextWithContext = (ctx?: TContext): Promise<Response> => {\n if (ctx) {\n const target = req as unknown as { __locals?: object };\n target.__locals = { ...(target.__locals || {}), ...ctx };\n }\n return originalNext();\n };\n return handler(req, nextWithContext);\n }) as TypedMiddleware<TContext>;\n\n return middleware;\n}\n\n// ============= 响应处理 =============\n\n/** 自动转换返回值为 Response */\nfunction autoResponse(result: unknown): Response {\n if (result instanceof Response) return result;\n if (result === null || result === undefined) return new Response(null, { status: 204 });\n if (typeof result === \"string\") {\n return new Response(result, { headers: { \"Content-Type\": \"text/plain; charset=utf-8\" } });\n }\n if (typeof result === \"number\" || typeof result === \"boolean\") {\n return new Response(String(result), { headers: { \"Content-Type\": \"text/plain; charset=utf-8\" } });\n }\n if (typeof result === \"object\") {\n const obj = result as Record<string, unknown>;\n if (\"data\" in obj && (\"status\" in obj || \"headers\" in obj)) {\n const { data, status = 200, headers = {} } = obj;\n if (data === null || data === undefined) {\n return new Response(null, { status: status === 200 ? 204 : (status as number), headers: headers as HeadersInit });\n }\n return json(data, status as number, headers as Record<string, string>);\n }\n return json(result);\n }\n return new Response(null, { status: 204 });\n}\n\n/** 创建包装后的 handler */\nfunction wrapHandler<TSchema extends RouteSchema>(\n schema: TSchema | undefined,\n userHandler: (ctx: HandlerContext<TSchema>) => unknown | Promise<unknown>\n): (req: Request) => Promise<Response> {\n if (schema && (schema.body || schema.query || schema.params || schema.headers || schema.cookies)) {\n precompileSchemas(schema);\n }\n\n return async (req: Request): Promise<Response> => {\n try {\n const query = parseQuery(req);\n const headers = parseHeaders(req);\n const cookies = parseCookies(req);\n const params = ((req as unknown as Record<string, unknown>).params as Record<string, string>) || {};\n\n let body: unknown = undefined;\n if (req.method !== \"GET\" && req.method !== \"HEAD\") {\n try {\n body = await parseBody(req);\n } catch {\n // 忽略解析错误\n }\n }\n\n const data = { body, query, params, headers, cookies };\n if (schema && (schema.body || schema.query || schema.params || schema.headers || schema.cookies)) {\n validateAllSchemas(schema, data);\n }\n\n // 获取中间件注入的上下文\n const extraCtx = (req as unknown as { __locals?: unknown }).__locals || {};\n\n const result = await userHandler({\n req,\n body: body as HandlerContext<TSchema>[\"body\"],\n query: query as HandlerContext<TSchema>[\"query\"],\n params: params as HandlerContext<TSchema>[\"params\"],\n headers: headers as HandlerContext<TSchema>[\"headers\"],\n cookies: cookies as HandlerContext<TSchema>[\"cookies\"],\n ...extraCtx,\n } as HandlerContext<TSchema>);\n\n return autoResponse(result);\n } catch (error) {\n // 如果是 VafastError,重新抛出让错误处理中间件处理\n if (error instanceof VafastError) {\n throw error;\n }\n if (error instanceof Error && error.message.includes(\"验证失败\")) {\n return json({ code: 400, message: error.message }, 400);\n }\n return json({ code: 500, message: error instanceof Error ? error.message : \"未知错误\" }, 500);\n }\n };\n}\n\n// ============= 判断路由类型 =============\n\n/** 判断是否为叶子路由 */\nfunction isLeafRoute(route: RouteConfigResult): route is LeafRouteConfig {\n return \"method\" in route && \"handler\" in route;\n}\n\n/** 判断是否为嵌套路由 */\nfunction isNestedRoute(route: RouteConfigResult): route is NestedRouteConfig {\n return \"children\" in route;\n}\n\n// ============= defineRoute 函数(支持重载) =============\n\n/**\n * 定义叶子路由(有 method 和 handler),支持中间件类型推断和显式上下文类型\n *\n * @example\n * ```typescript\n * // 方式1:通过中间件自动推断上下文\n * defineRoute({\n * method: 'GET',\n * path: '/profile',\n * middleware: [authMiddleware],\n * handler: ({ user }) => { ... } // user 来自 authMiddleware\n * })\n *\n * // 方式2:显式声明上下文类型(用于父级中间件注入的场景)\n * defineRoute({\n * method: 'GET',\n * path: '/profile',\n * context: {} as { userInfo: UserInfo },\n * handler: ({ userInfo }) => { ... } // userInfo 有类型\n * })\n * ```\n */\nexport function defineRoute<\n const TSchema extends RouteSchema,\n TReturn,\n const TMiddleware extends readonly AnyMiddleware[],\n TContext extends object = object,\n TMethod extends HTTPMethod = HTTPMethod,\n TPath extends string = string\n>(config: {\n readonly method: TMethod;\n readonly path: TPath;\n readonly name?: string;\n readonly description?: string;\n readonly schema?: TSchema;\n /** 显式声明上下文类型(用于父级中间件注入的场景) */\n readonly context?: TContext;\n readonly handler: (\n ctx: HandlerContextWithExtra<TSchema, TContext & MergeMiddlewareContexts<TMiddleware>>\n ) => TReturn | Promise<TReturn>;\n readonly middleware?: TMiddleware;\n readonly docs?: {\n tags?: string[];\n security?: unknown[];\n responses?: Record<string, unknown>;\n };\n}): LeafRouteConfig<TMethod, TPath, TSchema, TReturn, TMiddleware>;\n\n/**\n * 定义嵌套路由(有 children),支持中间件类型推断\n */\nexport function defineRoute<\n const TMiddleware extends readonly AnyMiddleware[],\n TPath extends string = string\n>(config: {\n readonly path: TPath;\n readonly name?: string;\n readonly description?: string;\n readonly middleware?: TMiddleware;\n readonly children: ReadonlyArray<RouteConfigResult>;\n}): NestedRouteConfig<TPath, TMiddleware>;\n\n/**\n * defineRoute 实现\n */\nexport function defineRoute(config: {\n readonly method?: HTTPMethod;\n readonly path: string;\n readonly name?: string;\n readonly description?: string;\n readonly schema?: RouteSchema;\n readonly context?: object;\n readonly handler?: (ctx: HandlerContext<RouteSchema>) => unknown | Promise<unknown>;\n readonly middleware?: readonly AnyMiddleware[];\n readonly children?: ReadonlyArray<RouteConfigResult>;\n readonly docs?: {\n tags?: string[];\n security?: unknown[];\n responses?: Record<string, unknown>;\n };\n}): RouteConfigResult {\n return config as RouteConfigResult;\n}\n\n// ============= withContext 工厂函数 =============\n\n/**\n * 创建带预设上下文类型的路由定义器\n *\n * 用于父级中间件注入上下文的场景,定义一次,多处复用\n *\n * @example\n * ```typescript\n * // 1. 在 middleware/index.ts 中定义\n * export const defineAuthRoute = withContext<{ userInfo: UserInfo }>()\n *\n * // 2. 在路由文件中使用\n * defineAuthRoute({\n * method: 'GET',\n * path: '/profile',\n * handler: ({ userInfo }) => {\n * // userInfo 自动有类型!\n * return { id: userInfo.id }\n * }\n * })\n * ```\n */\n/**\n * 带扩展类型的路由定义器\n *\n * @typeParam TContext - Handler 上下文类型(如 { userInfo: UserInfo })\n * @typeParam TExtensions - 路由扩展字段类型(如 { webhook?: boolean })\n *\n * @example\n * ```typescript\n * // 定义扩展类型\n * interface WebhookExtension {\n * webhook?: boolean | { eventKey?: string }\n * }\n *\n * // 使用扩展\n * const defineRoute = withContext<MyContext, WebhookExtension>()\n * defineRoute({\n * method: 'POST',\n * path: '/create',\n * webhook: true, // TypeScript 严格检查\n * handler: ...\n * })\n * ```\n */\nexport function withContext<\n TContext extends object,\n TExtensions extends object = object\n>() {\n return <\n const TSchema extends RouteSchema,\n TReturn,\n const TMiddleware extends readonly AnyMiddleware[],\n TMethod extends HTTPMethod = HTTPMethod,\n TPath extends string = string\n >(config: {\n readonly method: TMethod;\n readonly path: TPath;\n readonly name?: string;\n readonly description?: string;\n readonly schema?: TSchema;\n readonly handler: (\n ctx: HandlerContextWithExtra<TSchema, TContext & MergeMiddlewareContexts<TMiddleware>>\n ) => TReturn | Promise<TReturn>;\n readonly middleware?: TMiddleware;\n readonly docs?: {\n tags?: string[];\n security?: unknown[];\n responses?: Record<string, unknown>;\n };\n } & TExtensions): LeafRouteConfig<TMethod, TPath, TSchema, TReturn, TMiddleware> => {\n return config as unknown as LeafRouteConfig<TMethod, TPath, TSchema, TReturn, TMiddleware>;\n };\n}\n\n// ============= 扁平化嵌套路由 =============\n\n/** 父级路由信息 */\ninterface ParentRouteInfo {\n /** 父级路由路径 */\n path: string;\n /** 父级路由名称 */\n name?: string;\n /** 父级路由描述 */\n description?: string;\n}\n\n/**\n * 递归扁平化路由,合并路径和中间件\n */\nfunction flattenRoutes(\n routes: ReadonlyArray<RouteConfigResult>,\n parentPath: string = \"\",\n parentMiddleware: readonly AnyMiddleware[] = [],\n parent?: ParentRouteInfo\n): ProcessedRoute[] {\n const result: ProcessedRoute[] = [];\n\n for (const route of routes) {\n const fullPath = parentPath + route.path;\n const mergedMiddleware = [...parentMiddleware, ...(route.middleware || [])];\n\n if (isLeafRoute(route)) {\n // 检测 SSE handler(handler 上有 __sse 标记)\n const originalHandler = route.handler as unknown as { __sse?: { readonly __brand: 'SSE' } };\n const isSSE = originalHandler?.__sse?.__brand === 'SSE';\n \n // 基础属性\n const processed: ProcessedRoute = {\n method: route.method,\n path: fullPath,\n name: route.name,\n description: route.description,\n schema: route.schema,\n // SSE handler 不需要包装,它自己处理请求和响应\n handler: isSSE \n ? (route.handler as unknown as (req: Request) => Promise<Response>)\n : wrapHandler(route.schema, route.handler as (ctx: HandlerContext<RouteSchema>) => unknown),\n middleware: mergedMiddleware.length > 0 ? mergedMiddleware : undefined,\n docs: route.docs,\n };\n \n // 保留 SSE 标记到 handler 上,供契约生成使用\n if (isSSE) {\n (processed.handler as unknown as { __sse: { readonly __brand: 'SSE' } }).__sse = { __brand: 'SSE' };\n }\n \n // 添加父级路由信息\n if (parent) {\n processed.parent = parent;\n }\n // 复制扩展属性(如 webhook, permission 等)\n const knownKeys = ['method', 'path', 'name', 'description', 'schema', 'handler', 'middleware', 'docs'];\n for (const key of Object.keys(route)) {\n if (!knownKeys.includes(key)) {\n processed[key] = (route as unknown as Record<string, unknown>)[key];\n }\n }\n result.push(processed);\n } else if (isNestedRoute(route)) {\n // 传递当前路由信息作为子路由的 parent\n const currentParent: ParentRouteInfo = {\n path: fullPath,\n name: route.name,\n description: route.description,\n };\n result.push(...flattenRoutes(route.children, fullPath, mergedMiddleware, currentParent));\n }\n }\n\n return result;\n}\n\n// ============= defineRoutes 函数 =============\n\n/** 带原始类型信息的路由数组 */\nexport type RoutesWithSource<T extends readonly RouteConfigResult[]> = ProcessedRoute[] & { __source: T };\n\n/**\n * 定义路由数组,支持嵌套路由\n *\n * 使用 `const T` 泛型自动保留字面量类型,无需手动添加 `as const`\n *\n * @example\n * ```typescript\n * const routes = defineRoutes([\n * defineRoute({ method: 'GET', path: '/users', handler: ... }),\n * defineRoute({ method: 'POST', path: '/users', handler: ... }),\n * ])\n *\n * // 类型推断自动工作,无需 as const\n * type Api = InferEden<typeof routes>\n * ```\n */\nexport function defineRoutes<const T extends readonly RouteConfigResult[]>(\n routes: T\n): RoutesWithSource<T> {\n const processed = flattenRoutes(routes);\n // 附加原始类型信息(仅用于类型推断,运行时不使用)\n return Object.assign(processed, { __source: routes }) as RoutesWithSource<T>;\n}\n\n// ============= 用于 API Client 的类型推断 =============\n\n/** 可推断的路由类型(供 vafast-api-client 使用) */\nexport type InferableRoute<\n TMethod extends string = string,\n TPath extends string = string,\n TReturn = unknown,\n TSchema extends RouteSchema = RouteSchema\n> = {\n readonly method: TMethod;\n readonly path: TPath;\n readonly name?: string;\n readonly description?: string;\n readonly schema?: TSchema;\n readonly handler: {\n __returnType: TReturn;\n __schema: TSchema;\n };\n readonly middleware?: ReadonlyArray<AnyMiddleware>;\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAoLA,SAAgB,iBACd,SAI2B;CAE3B,MAAM,eAAe,KAAc,iBAA0C;EAE3E,MAAM,mBAAmB,QAAsC;AAC7D,OAAI,KAAK;IACP,MAAM,SAAS;AACf,WAAO,WAAW;KAAE,GAAI,OAAO,YAAY,EAAE;KAAG,GAAG;KAAK;;AAE1D,UAAO,cAAc;;AAEvB,SAAO,QAAQ,KAAK,gBAAgB;;AAGtC,QAAO;;;AAMT,SAAS,aAAa,QAA2B;AAC/C,KAAI,kBAAkB,SAAU,QAAO;AACvC,KAAI,WAAW,QAAQ,WAAW,OAAW,QAAO,IAAI,SAAS,MAAM,EAAE,QAAQ,KAAK,CAAC;AACvF,KAAI,OAAO,WAAW,SACpB,QAAO,IAAI,SAAS,QAAQ,EAAE,SAAS,EAAE,gBAAgB,6BAA6B,EAAE,CAAC;AAE3F,KAAI,OAAO,WAAW,YAAY,OAAO,WAAW,UAClD,QAAO,IAAI,SAAS,OAAO,OAAO,EAAE,EAAE,SAAS,EAAE,gBAAgB,6BAA6B,EAAE,CAAC;AAEnG,KAAI,OAAO,WAAW,UAAU;EAC9B,MAAM,MAAM;AACZ,MAAI,UAAU,QAAQ,YAAY,OAAO,aAAa,MAAM;GAC1D,MAAM,EAAE,MAAM,SAAS,KAAK,UAAU,EAAE,KAAK;AAC7C,OAAI,SAAS,QAAQ,SAAS,OAC5B,QAAO,IAAI,SAAS,MAAM;IAAE,QAAQ,WAAW,MAAM,MAAO;IAA4B;IAAwB,CAAC;AAEnH,UAAO,KAAK,MAAM,QAAkB,QAAkC;;AAExE,SAAO,KAAK,OAAO;;AAErB,QAAO,IAAI,SAAS,MAAM,EAAE,QAAQ,KAAK,CAAC;;;AAI5C,SAAS,YACP,QACA,aACqC;AACrC,KAAI,WAAW,OAAO,QAAQ,OAAO,SAAS,OAAO,UAAU,OAAO,WAAW,OAAO,SACtF,mBAAkB,OAAO;AAG3B,QAAO,OAAO,QAAoC;AAChD,MAAI;GACF,MAAM,QAAQ,WAAW,IAAI;GAC7B,MAAM,UAAU,aAAa,IAAI;GACjC,MAAM,UAAU,aAAa,IAAI;GACjC,MAAM,SAAW,IAA2C,UAAqC,EAAE;GAEnG,IAAI,OAAgB;AACpB,OAAI,IAAI,WAAW,SAAS,IAAI,WAAW,OACzC,KAAI;AACF,WAAO,MAAM,UAAU,IAAI;WACrB;GAKV,MAAM,OAAO;IAAE;IAAM;IAAO;IAAQ;IAAS;IAAS;AACtD,OAAI,WAAW,OAAO,QAAQ,OAAO,SAAS,OAAO,UAAU,OAAO,WAAW,OAAO,SACtF,oBAAmB,QAAQ,KAAK;GAIlC,MAAM,WAAY,IAA0C,YAAY,EAAE;AAY1E,UAAO,aAVQ,MAAM,YAAY;IAC/B;IACM;IACC;IACC;IACC;IACA;IACT,GAAG;IACJ,CAA4B,CAEF;WACpB,OAAO;AAEd,OAAI,iBAAiB,YACnB,OAAM;AAER,OAAI,iBAAiB,SAAS,MAAM,QAAQ,SAAS,OAAO,CAC1D,QAAO,KAAK;IAAE,MAAM;IAAK,SAAS,MAAM;IAAS,EAAE,IAAI;AAEzD,UAAO,KAAK;IAAE,MAAM;IAAK,SAAS,iBAAiB,QAAQ,MAAM,UAAU;IAAQ,EAAE,IAAI;;;;;AAQ/F,SAAS,YAAY,OAAoD;AACvE,QAAO,YAAY,SAAS,aAAa;;;AAI3C,SAAS,cAAc,OAAsD;AAC3E,QAAO,cAAc;;;;;AAsEvB,SAAgB,YAAY,QAeN;AACpB,QAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAiDT,SAAgB,cAGZ;AACF,SAME,WAekF;AAClF,SAAO;;;;;;AAmBX,SAAS,cACP,QACA,aAAqB,IACrB,mBAA6C,EAAE,EAC/C,QACkB;CAClB,MAAM,SAA2B,EAAE;AAEnC,MAAK,MAAM,SAAS,QAAQ;EAC1B,MAAM,WAAW,aAAa,MAAM;EACpC,MAAM,mBAAmB,CAAC,GAAG,kBAAkB,GAAI,MAAM,cAAc,EAAE,CAAE;AAE3E,MAAI,YAAY,MAAM,EAAE;GAGtB,MAAM,QADkB,MAAM,SACC,OAAO,YAAY;GAGlD,MAAM,YAA4B;IAChC,QAAQ,MAAM;IACd,MAAM;IACN,MAAM,MAAM;IACZ,aAAa,MAAM;IACnB,QAAQ,MAAM;IAEd,SAAS,QACJ,MAAM,UACP,YAAY,MAAM,QAAQ,MAAM,QAAyD;IAC7F,YAAY,iBAAiB,SAAS,IAAI,mBAAmB;IAC7D,MAAM,MAAM;IACb;AAGD,OAAI,MACF,CAAC,UAAU,QAA8D,QAAQ,EAAE,SAAS,OAAO;AAIrG,OAAI,OACF,WAAU,SAAS;GAGrB,MAAM,YAAY;IAAC;IAAU;IAAQ;IAAQ;IAAe;IAAU;IAAW;IAAc;IAAO;AACtG,QAAK,MAAM,OAAO,OAAO,KAAK,MAAM,CAClC,KAAI,CAAC,UAAU,SAAS,IAAI,CAC1B,WAAU,OAAQ,MAA6C;AAGnE,UAAO,KAAK,UAAU;aACb,cAAc,MAAM,EAAE;GAE/B,MAAM,gBAAiC;IACrC,MAAM;IACN,MAAM,MAAM;IACZ,aAAa,MAAM;IACpB;AACD,UAAO,KAAK,GAAG,cAAc,MAAM,UAAU,UAAU,kBAAkB,cAAc,CAAC;;;AAI5F,QAAO;;;;;;;;;;;;;;;;;;AAwBT,SAAgB,aACd,QACqB;CACrB,MAAM,YAAY,cAAc,OAAO;AAEvC,QAAO,OAAO,OAAO,WAAW,EAAE,UAAU,QAAQ,CAAC"}
|
package/dist/index.d.mts
CHANGED
|
@@ -6,9 +6,9 @@ import { t as Server } from "./server-3uMr8Ujo.mjs";
|
|
|
6
6
|
import { t as DependencyManager } from "./dependency-manager-DIN9X0Gj.mjs";
|
|
7
7
|
import { t as ComponentServer } from "./component-server-DQ3nZWFc.mjs";
|
|
8
8
|
import { t as ServerFactory } from "./index-Cek4HyXL.mjs";
|
|
9
|
-
import { n as composeMiddleware, t as
|
|
9
|
+
import { a as isVafastError, i as errorHandler, n as VafastError, r as composeMiddleware, t as VAFAST_ERROR_SYMBOL } from "./middleware-DXssDt1F.mjs";
|
|
10
10
|
import { a as parseBody, c as parseCookiesFast, d as parseHeaders, f as parseQuery, i as getHeader, p as parseQueryFast, r as getCookie, s as parseCookies } from "./parsers-8hIAx0OV.mjs";
|
|
11
|
-
import { c as text, i as json, n as err, o as redirect, r as html, s as stream, t as empty } from "./response-
|
|
11
|
+
import { c as text, i as json, n as err, o as redirect, r as html, s as stream, t as empty } from "./response-F-VxN-cB.mjs";
|
|
12
12
|
import { t as goAwait } from "./go-await-DPtVBug4.mjs";
|
|
13
13
|
import { n as base64urlEncode, t as base64urlDecode } from "./base64url-DNUGwekK.mjs";
|
|
14
14
|
import { t as HtmlRenderer } from "./html-renderer-DhQxRuyi.mjs";
|
|
@@ -16,10 +16,10 @@ import { a as ValidationError, c as getValidatorCacheStats, d as validateFast, f
|
|
|
16
16
|
import { i as registerFormats, n as hasFormat, r as registerFormat, t as Patterns } from "./formats-DDDSFWP0.mjs";
|
|
17
17
|
import { a as createSSEHandler, i as SSEMarker, r as SSEHandler, t as SSEEvent } from "./sse-CjIH9WjQ.mjs";
|
|
18
18
|
import { a as getAllRoutes, c as getRoutesByMethod, i as filterRoutes, n as RouteRegistry, o as getRoute, r as createRouteRegistry, s as getRouteRegistry, t as RouteMeta } from "./route-registry-CAX54I8j.mjs";
|
|
19
|
-
import { n as generateAITools, r as getApiSpec, t as ApiSpec } from "./contract-
|
|
19
|
+
import { n as generateAITools, r as getApiSpec, t as ApiSpec } from "./contract-CuruqP6h.mjs";
|
|
20
20
|
import "./utils/index.mjs";
|
|
21
21
|
import { normalizePath } from "./router.mjs";
|
|
22
22
|
import { a as ServeResult, c as serve, i as ServeOptions, n as GracefulShutdownOptions, o as TrustProxyOption, r as RequestTimeoutOptions, t as FetchHandler } from "./serve-Ds8Px1wD.mjs";
|
|
23
23
|
import "./serve.mjs";
|
|
24
24
|
import { FormatRegistry, Type } from "@sinclair/typebox";
|
|
25
|
-
export { ApiSpec, BaseServer, ComponentRoute, ComponentServer, DependencyManager, type FetchHandler, FlattenedComponentRoute, FormatRegistry, type GracefulShutdownOptions, Handler, HandlerContext, HtmlRenderer, InferableRoute, LeafRouteConfig, Method, Middleware, NestedComponentRoute, NestedRouteConfig, Patterns, ProcessedRoute, type RequestTimeoutOptions, ResponseBody, Route, RouteMeta, RouteRegistry, RouteSchema, RoutesWithSource, SSEEvent, SSEHandler, SSEMarker, SchemaConfig, type ServeOptions, type ServeResult, Server, ServerFactory, type TrustProxyOption, Type, TypedMiddleware, VafastError, VafastRequest, ValidationError, ValidationResult, base64urlDecode, base64urlEncode, composeMiddleware, createRouteRegistry, createSSEHandler, createValidator, defineMiddleware, defineRoute, defineRoutes, empty, err, filterRoutes, generateAITools, getAllRoutes, getApiSpec, getCookie, getHeader, getRoute, getRouteRegistry, getRoutesByMethod, getValidatorCacheStats, goAwait, hasFormat, html, json, normalizePath, parseBody, parseCookies, parseCookiesFast, parseHeaders, parseQuery, parseQueryFast, precompileSchemas, redirect, registerFormat, registerFormats, serve, stream, text, validateAllSchemas, validateFast, validateSchema, validateSchemaOrThrow, withContext };
|
|
25
|
+
export { ApiSpec, BaseServer, ComponentRoute, ComponentServer, DependencyManager, type FetchHandler, FlattenedComponentRoute, FormatRegistry, type GracefulShutdownOptions, Handler, HandlerContext, HtmlRenderer, InferableRoute, LeafRouteConfig, Method, Middleware, NestedComponentRoute, NestedRouteConfig, Patterns, ProcessedRoute, type RequestTimeoutOptions, ResponseBody, Route, RouteMeta, RouteRegistry, RouteSchema, RoutesWithSource, SSEEvent, SSEHandler, SSEMarker, SchemaConfig, type ServeOptions, type ServeResult, Server, ServerFactory, type TrustProxyOption, Type, TypedMiddleware, VAFAST_ERROR_SYMBOL, VafastError, VafastRequest, ValidationError, ValidationResult, base64urlDecode, base64urlEncode, composeMiddleware, createRouteRegistry, createSSEHandler, createValidator, defineMiddleware, defineRoute, defineRoutes, empty, err, errorHandler, filterRoutes, generateAITools, getAllRoutes, getApiSpec, getCookie, getHeader, getRoute, getRouteRegistry, getRoutesByMethod, getValidatorCacheStats, goAwait, hasFormat, html, isVafastError, json, normalizePath, parseBody, parseCookies, parseCookiesFast, parseHeaders, parseQuery, parseQueryFast, precompileSchemas, redirect, registerFormat, registerFormats, serve, stream, text, validateAllSchemas, validateFast, validateSchema, validateSchemaOrThrow, withContext };
|
package/dist/index.mjs
CHANGED
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
import { a as parseCookies, d as parseQueryFast, l as parseHeaders, n as getHeader, o as parseCookiesFast, r as parseBody, t as getCookie, u as parseQuery } from "./parsers-BrG_mRLq.mjs";
|
|
2
2
|
import { a as validateAllSchemas, c as validateSchemaOrThrow, i as precompileSchemas, n as createValidator, o as validateFast, r as getValidatorCacheStats, s as validateSchema } from "./validators-CkfvNBbK.mjs";
|
|
3
|
-
import { c as text, i as json, l as
|
|
3
|
+
import { c as text, d as composeMiddleware, f as errorHandler, i as json, l as VAFAST_ERROR_SYMBOL, n as err, o as redirect, p as isVafastError, r as html, s as stream, t as empty, u as VafastError } from "./response-Bs9GhJz-.mjs";
|
|
4
4
|
import { defineMiddleware, defineRoute, defineRoutes, withContext } from "./defineRoute.mjs";
|
|
5
5
|
import { t as BaseServer } from "./base-server-CE0mfqPY.mjs";
|
|
6
|
-
import { a as getRoute, i as getAllRoutes, n as createRouteRegistry, o as getRouteRegistry, r as filterRoutes, s as getRoutesByMethod, t as RouteRegistry } from "./route-registry-
|
|
7
|
-
import { t as Server } from "./server-
|
|
6
|
+
import { a as getRoute, i as getAllRoutes, n as createRouteRegistry, o as getRouteRegistry, r as filterRoutes, s as getRoutesByMethod, t as RouteRegistry } from "./route-registry-DsPslV2b.mjs";
|
|
7
|
+
import { t as Server } from "./server-DWndB63Z.mjs";
|
|
8
8
|
import { t as HtmlRenderer } from "./html-renderer-D1zzDVQM.mjs";
|
|
9
9
|
import { t as DependencyManager } from "./dependency-manager-CGMZJTer.mjs";
|
|
10
10
|
import { t as ComponentServer } from "./component-server-Cya46YN3.mjs";
|
|
11
|
-
import { t as ServerFactory } from "./server-
|
|
11
|
+
import { t as ServerFactory } from "./server-CAhwnEPW.mjs";
|
|
12
12
|
import { t as goAwait } from "./go-await-B1U27PgB.mjs";
|
|
13
13
|
import { n as base64urlEncode, t as base64urlDecode } from "./base64url-Cc77f1ms.mjs";
|
|
14
14
|
import { Patterns, hasFormat, registerFormat, registerFormats } from "./utils/formats.mjs";
|
|
15
15
|
import { t as createSSEHandler } from "./sse-C0_ODr4_.mjs";
|
|
16
|
-
import { n as getApiSpec, t as generateAITools } from "./contract-
|
|
16
|
+
import { n as getApiSpec, t as generateAITools } from "./contract-Cwmmo-Nn.mjs";
|
|
17
17
|
import "./utils/index.mjs";
|
|
18
18
|
import { normalizePath } from "./router.mjs";
|
|
19
19
|
import "./types/index.mjs";
|
|
@@ -26,5 +26,5 @@ import { FormatRegistry, Type } from "@sinclair/typebox";
|
|
|
26
26
|
registerFormats();
|
|
27
27
|
|
|
28
28
|
//#endregion
|
|
29
|
-
export { BaseServer, ComponentServer, DependencyManager, FormatRegistry, HtmlRenderer, Patterns, RouteRegistry, Server, ServerFactory, Type, VafastError, base64urlDecode, base64urlEncode, composeMiddleware, createRouteRegistry, createSSEHandler, createValidator, defineMiddleware, defineRoute, defineRoutes, empty, err, filterRoutes, generateAITools, getAllRoutes, getApiSpec, getCookie, getHeader, getRoute, getRouteRegistry, getRoutesByMethod, getValidatorCacheStats, goAwait, hasFormat, html, json, normalizePath, parseBody, parseCookies, parseCookiesFast, parseHeaders, parseQuery, parseQueryFast, precompileSchemas, redirect, registerFormat, registerFormats, serve, stream, text, validateAllSchemas, validateFast, validateSchema, validateSchemaOrThrow, withContext };
|
|
29
|
+
export { BaseServer, ComponentServer, DependencyManager, FormatRegistry, HtmlRenderer, Patterns, RouteRegistry, Server, ServerFactory, Type, VAFAST_ERROR_SYMBOL, VafastError, base64urlDecode, base64urlEncode, composeMiddleware, createRouteRegistry, createSSEHandler, createValidator, defineMiddleware, defineRoute, defineRoutes, empty, err, errorHandler, filterRoutes, generateAITools, getAllRoutes, getApiSpec, getCookie, getHeader, getRoute, getRouteRegistry, getRoutesByMethod, getValidatorCacheStats, goAwait, hasFormat, html, isVafastError, json, normalizePath, parseBody, parseCookies, parseCookiesFast, parseHeaders, parseQuery, parseQueryFast, precompileSchemas, redirect, registerFormat, registerFormats, serve, stream, text, validateAllSchemas, validateFast, validateSchema, validateSchemaOrThrow, withContext };
|
|
30
30
|
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { r as Middleware, t as Handler } from "./types-D4pCpFZ_.mjs";
|
|
2
|
+
|
|
3
|
+
//#region src/middleware.d.ts
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* 全局 Symbol 标识 VafastError
|
|
7
|
+
*
|
|
8
|
+
* 使用 Symbol.for() 确保跨包共享同一个 Symbol 引用,
|
|
9
|
+
* 彻底解决多包实例 instanceof 失效的问题。
|
|
10
|
+
*/
|
|
11
|
+
declare const VAFAST_ERROR_SYMBOL: unique symbol;
|
|
12
|
+
/** Vafast 自定义错误类型 */
|
|
13
|
+
declare class VafastError extends Error {
|
|
14
|
+
/** 全局标识,用于跨包识别 */
|
|
15
|
+
readonly [VAFAST_ERROR_SYMBOL] = true;
|
|
16
|
+
status: number;
|
|
17
|
+
code: number;
|
|
18
|
+
expose: boolean;
|
|
19
|
+
constructor(message: string, options?: {
|
|
20
|
+
status?: number;
|
|
21
|
+
code?: number;
|
|
22
|
+
expose?: boolean;
|
|
23
|
+
cause?: unknown;
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
/** 检查是否是 VafastError(跨包安全) */
|
|
27
|
+
declare function isVafastError(err: unknown): err is VafastError;
|
|
28
|
+
/**
|
|
29
|
+
* 组合中间件链
|
|
30
|
+
*
|
|
31
|
+
* 纯粹的中间件组合,不自动注入任何中间件。
|
|
32
|
+
* 错误处理器由 Server 在适当位置注入,确保洋葱模型正确:
|
|
33
|
+
*
|
|
34
|
+
* ```
|
|
35
|
+
* CORS.before → errorHandler.try → auth → handler
|
|
36
|
+
* ↓ (error)
|
|
37
|
+
* errorHandler.catch → return errorResponse
|
|
38
|
+
* CORS.after (添加 CORS 头) ← errorResponse
|
|
39
|
+
* ```
|
|
40
|
+
*/
|
|
41
|
+
declare function composeMiddleware(middleware: Middleware[], finalHandler: Handler): (req: Request) => Promise<Response>;
|
|
42
|
+
/**
|
|
43
|
+
* 全局错误处理中间件
|
|
44
|
+
*
|
|
45
|
+
* 捕获后续中间件和 handler 抛出的所有错误,转换为标准 JSON 响应。
|
|
46
|
+
* 由 Server 自动注入到全局中间件之后、路由中间件之前。
|
|
47
|
+
*/
|
|
48
|
+
declare const errorHandler: Middleware;
|
|
49
|
+
//#endregion
|
|
50
|
+
export { isVafastError as a, errorHandler as i, VafastError as n, composeMiddleware as r, VAFAST_ERROR_SYMBOL as t };
|
|
51
|
+
//# sourceMappingURL=middleware-DXssDt1F.d.mts.map
|
package/dist/middleware.d.mts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { n as composeMiddleware, t as
|
|
2
|
-
export { VafastError, composeMiddleware };
|
|
1
|
+
import { a as isVafastError, i as errorHandler, n as VafastError, r as composeMiddleware, t as VAFAST_ERROR_SYMBOL } from "./middleware-DXssDt1F.mjs";
|
|
2
|
+
export { VAFAST_ERROR_SYMBOL, VafastError, composeMiddleware, errorHandler, isVafastError };
|
package/dist/middleware.mjs
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { l as
|
|
1
|
+
import { d as composeMiddleware, f as errorHandler, l as VAFAST_ERROR_SYMBOL, p as isVafastError, u as VafastError } from "./response-Bs9GhJz-.mjs";
|
|
2
2
|
|
|
3
|
-
export { VafastError, composeMiddleware };
|
|
3
|
+
export { VAFAST_ERROR_SYMBOL, VafastError, composeMiddleware, errorHandler, isVafastError };
|
|
@@ -1,7 +1,15 @@
|
|
|
1
1
|
//#region src/middleware.ts
|
|
2
|
-
/**
|
|
2
|
+
/**
|
|
3
|
+
* 全局 Symbol 标识 VafastError
|
|
4
|
+
*
|
|
5
|
+
* 使用 Symbol.for() 确保跨包共享同一个 Symbol 引用,
|
|
6
|
+
* 彻底解决多包实例 instanceof 失效的问题。
|
|
7
|
+
*/
|
|
8
|
+
const VAFAST_ERROR_SYMBOL = Symbol.for("vafast.error");
|
|
3
9
|
/** Vafast 自定义错误类型 */
|
|
4
10
|
var VafastError = class extends Error {
|
|
11
|
+
/** 全局标识,用于跨包识别 */
|
|
12
|
+
[VAFAST_ERROR_SYMBOL] = true;
|
|
5
13
|
status;
|
|
6
14
|
code;
|
|
7
15
|
expose;
|
|
@@ -14,19 +22,31 @@ var VafastError = class extends Error {
|
|
|
14
22
|
if (options.cause) this.cause = options.cause;
|
|
15
23
|
}
|
|
16
24
|
};
|
|
25
|
+
/** 检查是否是 VafastError(跨包安全) */
|
|
26
|
+
function isVafastError(err$1) {
|
|
27
|
+
return err$1 instanceof VafastError || typeof err$1 === "object" && err$1 !== null && err$1[VAFAST_ERROR_SYMBOL] === true;
|
|
28
|
+
}
|
|
17
29
|
/**
|
|
18
|
-
*
|
|
19
|
-
*
|
|
30
|
+
* 组合中间件链
|
|
31
|
+
*
|
|
32
|
+
* 纯粹的中间件组合,不自动注入任何中间件。
|
|
33
|
+
* 错误处理器由 Server 在适当位置注入,确保洋葱模型正确:
|
|
34
|
+
*
|
|
35
|
+
* ```
|
|
36
|
+
* CORS.before → errorHandler.try → auth → handler
|
|
37
|
+
* ↓ (error)
|
|
38
|
+
* errorHandler.catch → return errorResponse
|
|
39
|
+
* CORS.after (添加 CORS 头) ← errorResponse
|
|
40
|
+
* ```
|
|
20
41
|
*/
|
|
21
42
|
function composeMiddleware(middleware, finalHandler) {
|
|
22
|
-
const all = [...middleware, errorHandler];
|
|
23
43
|
return function composedHandler(req) {
|
|
24
44
|
let i = -1;
|
|
25
45
|
const dispatch = (index) => {
|
|
26
46
|
if (index <= i) return Promise.reject(/* @__PURE__ */ new Error("next() called multiple times"));
|
|
27
47
|
i = index;
|
|
28
|
-
if (index <
|
|
29
|
-
const mw =
|
|
48
|
+
if (index < middleware.length) {
|
|
49
|
+
const mw = middleware[index];
|
|
30
50
|
return Promise.resolve(mw(req, () => dispatch(index + 1)));
|
|
31
51
|
}
|
|
32
52
|
return Promise.resolve(finalHandler(req)).then(mapResponse);
|
|
@@ -34,13 +54,18 @@ function composeMiddleware(middleware, finalHandler) {
|
|
|
34
54
|
return dispatch(0);
|
|
35
55
|
};
|
|
36
56
|
}
|
|
37
|
-
/**
|
|
57
|
+
/**
|
|
58
|
+
* 全局错误处理中间件
|
|
59
|
+
*
|
|
60
|
+
* 捕获后续中间件和 handler 抛出的所有错误,转换为标准 JSON 响应。
|
|
61
|
+
* 由 Server 自动注入到全局中间件之后、路由中间件之前。
|
|
62
|
+
*/
|
|
38
63
|
const errorHandler = async (req, next) => {
|
|
39
64
|
try {
|
|
40
65
|
return await next();
|
|
41
66
|
} catch (err$1) {
|
|
42
67
|
console.error("未处理的错误:", err$1);
|
|
43
|
-
if (err$1
|
|
68
|
+
if (isVafastError(err$1)) return json({
|
|
44
69
|
code: err$1.code,
|
|
45
70
|
message: err$1.expose ? err$1.message : "发生了一个错误"
|
|
46
71
|
}, err$1.status);
|
|
@@ -189,5 +214,5 @@ err.tooMany = (message = "请求过于频繁", code) => err(message, 429, code);
|
|
|
189
214
|
err.internal = (message = "服务器内部错误", code) => err(message, 500, code);
|
|
190
215
|
|
|
191
216
|
//#endregion
|
|
192
|
-
export { mapResponse as a, text as c, json as i,
|
|
193
|
-
//# sourceMappingURL=response-
|
|
217
|
+
export { mapResponse as a, text as c, composeMiddleware as d, errorHandler as f, json as i, VAFAST_ERROR_SYMBOL as l, err as n, redirect as o, isVafastError as p, html as r, stream as s, empty as t, VafastError as u };
|
|
218
|
+
//# sourceMappingURL=response-Bs9GhJz-.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"response-Bs9GhJz-.mjs","names":["err","stream"],"sources":["../src/middleware.ts","../src/utils/response.ts"],"sourcesContent":["// src/middleware.ts\n\nimport { json, mapResponse } from \"./utils/response\";\n\nimport type { Handler, Middleware } from \"./types\";\n\n/**\n * 全局 Symbol 标识 VafastError\n *\n * 使用 Symbol.for() 确保跨包共享同一个 Symbol 引用,\n * 彻底解决多包实例 instanceof 失效的问题。\n */\nexport const VAFAST_ERROR_SYMBOL = Symbol.for(\"vafast.error\");\n\n/** Vafast 自定义错误类型 */\nexport class VafastError extends Error {\n /** 全局标识,用于跨包识别 */\n readonly [VAFAST_ERROR_SYMBOL] = true;\n\n status: number;\n code: number;\n expose: boolean;\n\n constructor(\n message: string,\n options: {\n status?: number;\n code?: number;\n expose?: boolean;\n cause?: unknown;\n } = {},\n ) {\n super(message);\n this.name = \"VafastError\";\n this.status = options.status ?? 500;\n this.code = options.code ?? options.status ?? 500;\n this.expose = options.expose ?? false;\n if (options.cause) (this as any).cause = options.cause;\n }\n}\n\n/** 检查是否是 VafastError(跨包安全) */\nexport function isVafastError(err: unknown): err is VafastError {\n return (\n err instanceof VafastError ||\n (typeof err === \"object\" &&\n err !== null &&\n (err as any)[VAFAST_ERROR_SYMBOL] === true)\n );\n}\n\n/**\n * 组合中间件链\n *\n * 纯粹的中间件组合,不自动注入任何中间件。\n * 错误处理器由 Server 在适当位置注入,确保洋葱模型正确:\n *\n * ```\n * CORS.before → errorHandler.try → auth → handler\n * ↓ (error)\n * errorHandler.catch → return errorResponse\n * CORS.after (添加 CORS 头) ← errorResponse\n * ```\n */\nexport function composeMiddleware(\n middleware: Middleware[],\n finalHandler: Handler,\n): (req: Request) => Promise<Response> {\n return function composedHandler(req: Request): Promise<Response> {\n let i = -1;\n\n const dispatch = (index: number): Promise<Response> => {\n if (index <= i)\n return Promise.reject(new Error(\"next() called multiple times\"));\n i = index;\n\n // 中间件阶段\n if (index < middleware.length) {\n const mw = middleware[index];\n return Promise.resolve(mw(req, () => dispatch(index + 1)));\n }\n\n // 最终 handler - 使用 mapResponse 转换返回值\n return Promise.resolve(finalHandler(req)).then(mapResponse);\n };\n\n return dispatch(0);\n };\n}\n\n/**\n * 全局错误处理中间件\n *\n * 捕获后续中间件和 handler 抛出的所有错误,转换为标准 JSON 响应。\n * 由 Server 自动注入到全局中间件之后、路由中间件之前。\n */\nexport const errorHandler: Middleware = async (req, next) => {\n try {\n return await next();\n } catch (err) {\n console.error(\"未处理的错误:\", err);\n\n // 使用 Symbol.for() 跨包安全识别 VafastError\n if (isVafastError(err)) {\n return json(\n {\n code: err.code,\n message: err.expose ? err.message : \"发生了一个错误\",\n },\n err.status,\n );\n }\n\n return json({ code: 500, message: \"出现了一些问题\" }, 500);\n }\n};\n","// src/response.ts\n\nimport { VafastError } from \"../middleware\";\n\n/** 生成 JSON 响应 */\nexport function json(\n data: unknown,\n status = 200,\n headers: HeadersInit = {},\n): Response {\n const body = JSON.stringify(data);\n\n // 优化:只在有自定义 headers 时才创建 Headers 对象\n if (Object.keys(headers).length === 0) {\n return new Response(body, {\n status,\n headers: { \"Content-Type\": \"application/json\" },\n });\n }\n\n // 有自定义 headers 时才创建 Headers 对象\n const h = new Headers({\n \"Content-Type\": \"application/json\",\n ...headers,\n });\n\n return new Response(body, {\n status,\n headers: h,\n });\n}\n\n// JSON 响应的预创建 headers(避免每次创建)\nconst JSON_HEADERS = { \"Content-Type\": \"application/json\" };\nconst TEXT_HEADERS = { \"Content-Type\": \"text/plain\" };\n\n/**\n * 类型特化的响应映射\n * 根据返回值类型直接生成 Response,避免不必要的检查\n */\nexport function mapResponse(response: unknown): Response {\n // 快速路径:已经是 Response\n if (response instanceof Response) return response;\n\n // 使用 constructor.name 进行类型判断(比 instanceof 更快)\n switch (response?.constructor?.name) {\n case \"String\":\n return new Response(response as string, { headers: TEXT_HEADERS });\n\n case \"Object\":\n case \"Array\":\n return new Response(JSON.stringify(response), { headers: JSON_HEADERS });\n\n case \"Number\":\n case \"Boolean\":\n return new Response(String(response), { headers: TEXT_HEADERS });\n\n case undefined:\n // null 或 undefined\n return new Response(null, { status: 204 });\n\n case \"ReadableStream\":\n return new Response(response as ReadableStream);\n\n case \"Blob\":\n return new Response(response as Blob);\n\n case \"ArrayBuffer\":\n return new Response(response as ArrayBuffer);\n\n case \"Uint8Array\":\n return new Response(response as unknown as BodyInit);\n\n default:\n // Promise 处理\n if (response instanceof Promise) {\n return response.then(mapResponse) as unknown as Response;\n }\n // 其他情况使用 JSON 序列化\n return new Response(JSON.stringify(response), { headers: JSON_HEADERS });\n }\n}\n\n/** 生成重定向响应 */\nexport function redirect(location: string, status: 301 | 302 = 302): Response {\n return new Response(null, {\n status,\n headers: {\n Location: location,\n },\n });\n}\n\n/** 生成纯文本响应 */\nexport function text(\n content: string,\n status = 200,\n headers: HeadersInit = {},\n): Response {\n const h = new Headers({\n \"Content-Type\": \"text/plain; charset=utf-8\",\n ...headers,\n });\n\n return new Response(content, {\n status,\n headers: h,\n });\n}\n\n/** 生成HTML响应 */\nexport function html(\n content: string,\n status = 200,\n headers: HeadersInit = {},\n): Response {\n const h = new Headers({\n \"Content-Type\": \"text/html; charset=utf-8\",\n ...headers,\n });\n\n return new Response(content, {\n status,\n headers: h,\n });\n}\n\n/** 生成空响应(204 No Content) */\nexport function empty(status = 204, headers: HeadersInit = {}): Response {\n return new Response(null, {\n status,\n headers,\n });\n}\n\n/** 生成流式响应 */\nexport function stream(\n stream: ReadableStream,\n status = 200,\n headers: HeadersInit = {},\n): Response {\n const h = new Headers({\n \"Content-Type\": \"application/octet-stream\",\n ...headers,\n });\n\n return new Response(stream, {\n status,\n headers: h,\n });\n}\n\n// ==================== 错误响应工具 ====================\n\n/**\n * 创建错误响应\n *\n * @param message - 错误信息\n * @param status - HTTP 状态码,默认 500\n * @param code - 业务错误码,默认等于 status\n *\n * @example\n * ```typescript\n * // 使用默认 code(等于 HTTP status)\n * throw err('用户不存在', 404)\n * // 响应: HTTP 404, { code: 404, message: \"用户不存在\" }\n *\n * // 使用自定义业务 code\n * throw err('用户不存在', 404, 10001)\n * // 响应: HTTP 404, { code: 10001, message: \"用户不存在\" }\n *\n * // 预定义错误\n * throw err.notFound('用户不存在')\n * throw err.notFound('用户不存在', 10001) // 带业务码\n * throw err.badRequest('参数错误')\n * throw err.unauthorized('请先登录')\n * ```\n */\nexport function err(message: string, status = 500, code?: number) {\n return new VafastError(message, { status, code: code ?? status, expose: true });\n}\n\n/** 400 Bad Request */\nerr.badRequest = (message = \"请求参数错误\", code?: number) =>\n err(message, 400, code);\n\n/** 401 Unauthorized */\nerr.unauthorized = (message = \"未授权\", code?: number) =>\n err(message, 401, code);\n\n/** 403 Forbidden */\nerr.forbidden = (message = \"禁止访问\", code?: number) =>\n err(message, 403, code);\n\n/** 404 Not Found */\nerr.notFound = (message = \"资源不存在\", code?: number) =>\n err(message, 404, code);\n\n/** 409 Conflict */\nerr.conflict = (message = \"资源冲突\", code?: number) =>\n err(message, 409, code);\n\n/** 422 Unprocessable Entity */\nerr.unprocessable = (message = \"无法处理的实体\", code?: number) =>\n err(message, 422, code);\n\n/** 429 Too Many Requests */\nerr.tooMany = (message = \"请求过于频繁\", code?: number) =>\n err(message, 429, code);\n\n/** 500 Internal Server Error */\nerr.internal = (message = \"服务器内部错误\", code?: number) =>\n err(message, 500, code);\n"],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { n as VafastError } from "./middleware-DXssDt1F.mjs";
|
|
2
2
|
|
|
3
3
|
//#region src/utils/response.d.ts
|
|
4
4
|
/** 生成 JSON 响应 */
|
|
@@ -55,4 +55,4 @@ declare namespace err {
|
|
|
55
55
|
}
|
|
56
56
|
//#endregion
|
|
57
57
|
export { mapResponse as a, text as c, json as i, err as n, redirect as o, html as r, stream as s, empty as t };
|
|
58
|
-
//# sourceMappingURL=response-
|
|
58
|
+
//# sourceMappingURL=response-F-VxN-cB.d.mts.map
|
|
@@ -26,6 +26,7 @@ var RouteRegistry = class {
|
|
|
26
26
|
description: route.description,
|
|
27
27
|
schema: route.schema
|
|
28
28
|
};
|
|
29
|
+
if (route.handler?.__sse?.__brand === "SSE") meta.sse = true;
|
|
29
30
|
for (const key of Object.keys(route)) if (![
|
|
30
31
|
"method",
|
|
31
32
|
"path",
|
|
@@ -237,4 +238,4 @@ function getRoutesByMethod(method) {
|
|
|
237
238
|
|
|
238
239
|
//#endregion
|
|
239
240
|
export { getRoute as a, setGlobalRegistry as c, getAllRoutes as i, createRouteRegistry as n, getRouteRegistry as o, filterRoutes as r, getRoutesByMethod as s, RouteRegistry as t };
|
|
240
|
-
//# sourceMappingURL=route-registry-
|
|
241
|
+
//# sourceMappingURL=route-registry-DsPslV2b.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"route-registry-DsPslV2b.mjs","names":[],"sources":["../src/utils/route-registry.ts"],"sourcesContent":["/**\n * 路由注册表\n *\n * 提供路由元信息的收集和查询能力\n * 可用于:API 文档生成、Webhook 事件注册、权限检查、审计日志等\n *\n * @example\n * ```typescript\n * // 创建注册表\n * const registry = createRouteRegistry(server.getRoutes())\n *\n * // 查询路由\n * const route = registry.get('POST', '/auth/signIn')\n *\n * // 筛选有 webhook 配置的路由\n * const webhookRoutes = registry.filter('webhook')\n *\n * // 按分类获取\n * const authRoutes = registry.getByCategory('auth')\n * ```\n */\n\nimport type { Method } from '../types'\nimport type { ProcessedRoute, RouteSchema } from '../defineRoute'\n\n/**\n * 路由元信息(不含 handler 和 middleware)\n */\nexport interface RouteMeta {\n method: Method\n path: string\n name?: string\n description?: string\n /** Schema 定义(用于契约生成) */\n schema?: RouteSchema\n /** 扩展字段 */\n [key: string]: unknown\n}\n\n/**\n * 路由注册表\n *\n * 泛型 T 用于定义扩展字段的类型\n */\nexport class RouteRegistry<T extends Record<string, unknown> = Record<string, unknown>> {\n /** 所有路由元信息 */\n private routes: RouteMeta[] = []\n\n /** 路由映射表:METHOD:path -> RouteMeta */\n private routeMap = new Map<string, RouteMeta>()\n\n /** 分类映射表:category -> RouteMeta[] */\n private categoryMap = new Map<string, RouteMeta[]>()\n\n constructor(routes: ProcessedRoute[]) {\n this.buildRegistry(routes)\n }\n\n /**\n * 构建注册表\n */\n private buildRegistry(routes: ProcessedRoute[]): void {\n for (const route of routes) {\n // 提取元信息(排除 handler 和 middleware)\n const meta: RouteMeta = {\n method: route.method as Method,\n path: route.path,\n name: route.name,\n description: route.description,\n schema: route.schema, // 保留 schema 用于契约生成\n }\n\n // 检测 SSE handler 并添加标记\n const handler = route.handler as unknown as { __sse?: { readonly __brand: 'SSE' } }\n if (handler?.__sse?.__brand === 'SSE') {\n meta.sse = true\n }\n\n // 复制扩展字段\n for (const key of Object.keys(route)) {\n if (!['method', 'path', 'name', 'description', 'handler', 'middleware', 'schema', 'docs'].includes(key)) {\n meta[key] = route[key]\n }\n }\n\n this.routes.push(meta)\n this.routeMap.set(`${route.method}:${route.path}`, meta)\n\n // 按分类索引\n const category = this.extractCategory(route.path)\n if (!this.categoryMap.has(category)) {\n this.categoryMap.set(category, [])\n }\n this.categoryMap.get(category)!.push(meta)\n }\n }\n\n /**\n * 提取分类(第一段路径)\n */\n private extractCategory(path: string): string {\n const segments = path.split('/').filter(Boolean)\n return segments[0] || 'root'\n }\n\n // ============================================\n // 查询接口\n // ============================================\n\n /**\n * 获取所有路由元信息\n */\n getAll(): RouteMeta[] {\n return [...this.routes]\n }\n\n /**\n * 按 method + path 查询路由\n */\n get(method: string, path: string): (RouteMeta & T) | undefined {\n return this.routeMap.get(`${method}:${path}`) as (RouteMeta & T) | undefined\n }\n\n /**\n * 检查路由是否存在\n */\n has(method: string, path: string): boolean {\n return this.routeMap.has(`${method}:${path}`)\n }\n\n /**\n * 按分类获取路由\n */\n getByCategory(category: string): RouteMeta[] {\n return this.categoryMap.get(category) || []\n }\n\n /**\n * 获取所有分类\n */\n getCategories(): string[] {\n return Array.from(this.categoryMap.keys()).sort()\n }\n\n /**\n * 筛选有特定字段的路由\n *\n * @example\n * ```typescript\n * // 获取所有配置了 webhook 的路由\n * const webhookRoutes = registry.filter('webhook')\n * ```\n */\n filter<K extends string>(field: K): (RouteMeta & Record<K, unknown>)[] {\n return this.routes.filter((r) => field in r && r[field] !== undefined) as (RouteMeta & Record<K, unknown>)[]\n }\n\n /**\n * 按条件筛选路由\n *\n * @example\n * ```typescript\n * // 获取所有 POST 请求\n * const postRoutes = registry.filterBy(r => r.method === 'POST')\n * ```\n */\n filterBy(predicate: (route: RouteMeta) => boolean): RouteMeta[] {\n return this.routes.filter(predicate)\n }\n\n /**\n * 获取路由数量\n */\n get size(): number {\n return this.routes.length\n }\n\n /**\n * 遍历所有路由\n */\n forEach(callback: (route: RouteMeta, index: number) => void): void {\n this.routes.forEach(callback)\n }\n\n /**\n * 映射所有路由\n */\n map<R>(callback: (route: RouteMeta, index: number) => R): R[] {\n return this.routes.map(callback)\n }\n}\n\n/**\n * 创建路由注册表\n *\n * @example\n * ```typescript\n * // 定义扩展字段类型\n * interface MyRouteMeta {\n * webhook?: { eventKey: string }\n * permission?: string\n * }\n *\n * // 创建带类型的注册表\n * const registry = createRouteRegistry<MyRouteMeta>(server.getRoutes())\n *\n * // 类型安全的查询\n * const route = registry.get('POST', '/auth/signIn')\n * if (route?.webhook) {\n * console.log(route.webhook.eventKey)\n * }\n * ```\n */\nexport function createRouteRegistry<T extends Record<string, unknown> = Record<string, unknown>>(\n routes: ProcessedRoute[]\n): RouteRegistry<T> {\n return new RouteRegistry<T>(routes)\n}\n\n// ============================================\n// 全局 Registry(单例模式)\n// ============================================\n\n/** 全局 registry 实例 */\nlet globalRegistry: RouteRegistry | null = null\n\n/**\n * 设置全局 registry(框架内部使用)\n * @internal\n */\nexport function setGlobalRegistry(registry: RouteRegistry): void {\n globalRegistry = registry\n}\n\n/**\n * 获取全局路由注册表\n *\n * @example\n * ```typescript\n * // 在任意文件中\n * import { getRouteRegistry } from 'vafast'\n *\n * const registry = getRouteRegistry()\n * const webhookRoutes = registry.filter('webhook')\n * ```\n *\n * @throws 如果 Server 尚未创建\n */\nexport function getRouteRegistry<T extends Record<string, unknown> = Record<string, unknown>>(): RouteRegistry<T> {\n if (!globalRegistry) {\n throw new Error('RouteRegistry not initialized. Make sure Server is created first.')\n }\n return globalRegistry as RouteRegistry<T>\n}\n\n/**\n * 按 method + path 获取单个路由\n *\n * 便捷函数,无需先获取 registry\n *\n * @example\n * ```typescript\n * // 在中间件或接口中\n * import { getRoute } from 'vafast'\n *\n * const route = getRoute('POST', '/auth/signIn')\n * if (route?.webhook) {\n * console.log('This route has webhook:', route.webhook)\n * }\n * ```\n */\nexport function getRoute<T extends Record<string, unknown> = Record<string, unknown>>(\n method: string,\n path: string\n): (RouteMeta & T) | undefined {\n return getRouteRegistry<T>().get(method, path)\n}\n\n/**\n * 获取所有路由\n *\n * @example\n * ```typescript\n * import { getAllRoutes } from 'vafast'\n *\n * const routes = getAllRoutes()\n * console.log(`Total ${routes.length} routes`)\n * ```\n */\nexport function getAllRoutes(): RouteMeta[] {\n return getRouteRegistry().getAll()\n}\n\n/**\n * 筛选有特定字段的路由\n *\n * @example\n * ```typescript\n * import { filterRoutes } from 'vafast'\n *\n * // 获取所有配置了 webhook 的路由\n * const webhookRoutes = filterRoutes('webhook')\n * ```\n */\nexport function filterRoutes<K extends string>(field: K): (RouteMeta & Record<K, unknown>)[] {\n return getRouteRegistry().filter(field)\n}\n\n/**\n * 按 HTTP 方法获取路由\n *\n * @example\n * ```typescript\n * import { getRoutesByMethod } from 'vafast'\n *\n * const getRoutes = getRoutesByMethod('GET')\n * const postRoutes = getRoutesByMethod('POST')\n * ```\n */\nexport function getRoutesByMethod(method: string): RouteMeta[] {\n return getRouteRegistry().filterBy(r => r.method === method)\n}\n\n"],"mappings":";;;;;;AA4CA,IAAa,gBAAb,MAAwF;;CAEtF,AAAQ,SAAsB,EAAE;;CAGhC,AAAQ,2BAAW,IAAI,KAAwB;;CAG/C,AAAQ,8BAAc,IAAI,KAA0B;CAEpD,YAAY,QAA0B;AACpC,OAAK,cAAc,OAAO;;;;;CAM5B,AAAQ,cAAc,QAAgC;AACpD,OAAK,MAAM,SAAS,QAAQ;GAE1B,MAAM,OAAkB;IACtB,QAAQ,MAAM;IACd,MAAM,MAAM;IACZ,MAAM,MAAM;IACZ,aAAa,MAAM;IACnB,QAAQ,MAAM;IACf;AAID,OADgB,MAAM,SACT,OAAO,YAAY,MAC9B,MAAK,MAAM;AAIb,QAAK,MAAM,OAAO,OAAO,KAAK,MAAM,CAClC,KAAI,CAAC;IAAC;IAAU;IAAQ;IAAQ;IAAe;IAAW;IAAc;IAAU;IAAO,CAAC,SAAS,IAAI,CACrG,MAAK,OAAO,MAAM;AAItB,QAAK,OAAO,KAAK,KAAK;AACtB,QAAK,SAAS,IAAI,GAAG,MAAM,OAAO,GAAG,MAAM,QAAQ,KAAK;GAGxD,MAAM,WAAW,KAAK,gBAAgB,MAAM,KAAK;AACjD,OAAI,CAAC,KAAK,YAAY,IAAI,SAAS,CACjC,MAAK,YAAY,IAAI,UAAU,EAAE,CAAC;AAEpC,QAAK,YAAY,IAAI,SAAS,CAAE,KAAK,KAAK;;;;;;CAO9C,AAAQ,gBAAgB,MAAsB;AAE5C,SADiB,KAAK,MAAM,IAAI,CAAC,OAAO,QAAQ,CAChC,MAAM;;;;;CAUxB,SAAsB;AACpB,SAAO,CAAC,GAAG,KAAK,OAAO;;;;;CAMzB,IAAI,QAAgB,MAA2C;AAC7D,SAAO,KAAK,SAAS,IAAI,GAAG,OAAO,GAAG,OAAO;;;;;CAM/C,IAAI,QAAgB,MAAuB;AACzC,SAAO,KAAK,SAAS,IAAI,GAAG,OAAO,GAAG,OAAO;;;;;CAM/C,cAAc,UAA+B;AAC3C,SAAO,KAAK,YAAY,IAAI,SAAS,IAAI,EAAE;;;;;CAM7C,gBAA0B;AACxB,SAAO,MAAM,KAAK,KAAK,YAAY,MAAM,CAAC,CAAC,MAAM;;;;;;;;;;;CAYnD,OAAyB,OAA8C;AACrE,SAAO,KAAK,OAAO,QAAQ,MAAM,SAAS,KAAK,EAAE,WAAW,OAAU;;;;;;;;;;;CAYxE,SAAS,WAAuD;AAC9D,SAAO,KAAK,OAAO,OAAO,UAAU;;;;;CAMtC,IAAI,OAAe;AACjB,SAAO,KAAK,OAAO;;;;;CAMrB,QAAQ,UAA2D;AACjE,OAAK,OAAO,QAAQ,SAAS;;;;;CAM/B,IAAO,UAAuD;AAC5D,SAAO,KAAK,OAAO,IAAI,SAAS;;;;;;;;;;;;;;;;;;;;;;;;AAyBpC,SAAgB,oBACd,QACkB;AAClB,QAAO,IAAI,cAAiB,OAAO;;;AAQrC,IAAI,iBAAuC;;;;;AAM3C,SAAgB,kBAAkB,UAA+B;AAC/D,kBAAiB;;;;;;;;;;;;;;;;AAiBnB,SAAgB,mBAAkG;AAChH,KAAI,CAAC,eACH,OAAM,IAAI,MAAM,oEAAoE;AAEtF,QAAO;;;;;;;;;;;;;;;;;;AAmBT,SAAgB,SACd,QACA,MAC6B;AAC7B,QAAO,kBAAqB,CAAC,IAAI,QAAQ,KAAK;;;;;;;;;;;;;AAchD,SAAgB,eAA4B;AAC1C,QAAO,kBAAkB,CAAC,QAAQ;;;;;;;;;;;;;AAcpC,SAAgB,aAA+B,OAA8C;AAC3F,QAAO,kBAAkB,CAAC,OAAO,MAAM;;;;;;;;;;;;;AAczC,SAAgB,kBAAkB,QAA6B;AAC7D,QAAO,kBAAkB,CAAC,UAAS,MAAK,EAAE,WAAW,OAAO"}
|
package/dist/server/index.mjs
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import "../response-
|
|
1
|
+
import "../response-Bs9GhJz-.mjs";
|
|
2
2
|
import { t as BaseServer } from "../base-server-CE0mfqPY.mjs";
|
|
3
|
-
import { t as Server } from "../server-
|
|
3
|
+
import { t as Server } from "../server-DWndB63Z.mjs";
|
|
4
4
|
import "../dependency-manager-CGMZJTer.mjs";
|
|
5
5
|
import { t as ComponentServer } from "../component-server-Cya46YN3.mjs";
|
|
6
|
-
import { t as ServerFactory } from "../server-
|
|
6
|
+
import { t as ServerFactory } from "../server-CAhwnEPW.mjs";
|
|
7
7
|
|
|
8
8
|
export { BaseServer, ComponentServer, Server, ServerFactory };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import "../response-
|
|
2
|
-
import "../server-
|
|
1
|
+
import "../response-Bs9GhJz-.mjs";
|
|
2
|
+
import "../server-DWndB63Z.mjs";
|
|
3
3
|
import "../dependency-manager-CGMZJTer.mjs";
|
|
4
4
|
import "../component-server-Cya46YN3.mjs";
|
|
5
|
-
import { t as ServerFactory } from "../server-
|
|
5
|
+
import { t as ServerFactory } from "../server-CAhwnEPW.mjs";
|
|
6
6
|
|
|
7
7
|
export { ServerFactory };
|
package/dist/server/server.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { t as Server } from "./server-
|
|
1
|
+
import { t as Server } from "./server-DWndB63Z.mjs";
|
|
2
2
|
import { t as ComponentServer } from "./component-server-Cya46YN3.mjs";
|
|
3
3
|
|
|
4
4
|
//#region src/server/server-factory.ts
|
|
@@ -67,4 +67,4 @@ var ServerFactory = class {
|
|
|
67
67
|
|
|
68
68
|
//#endregion
|
|
69
69
|
export { ServerFactory as t };
|
|
70
|
-
//# sourceMappingURL=server-
|
|
70
|
+
//# sourceMappingURL=server-CAhwnEPW.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server-
|
|
1
|
+
{"version":3,"file":"server-CAhwnEPW.mjs","names":[],"sources":["../src/server/server-factory.ts"],"sourcesContent":["import type { ProcessedRoute } from \"../defineRoute\";\nimport type {\n ComponentRoute,\n NestedComponentRoute,\n} from \"../types/component-route\";\nimport { Server } from \"../server\";\nimport { ComponentServer } from \"./component-server\";\n\n/**\n * 服务器工厂类\n * 用于创建和管理不同类型的服务器\n */\nexport class ServerFactory {\n private servers: Map<string, Server | ComponentServer> = new Map();\n\n /**\n * 创建标准REST API服务器\n */\n createRestServer(routes: ProcessedRoute[]): Server {\n const server = new Server(routes);\n this.servers.set(\"rest\", server);\n return server;\n }\n\n /**\n * 创建组件服务器\n */\n createComponentServer(\n routes: (ComponentRoute | NestedComponentRoute)[],\n ): ComponentServer {\n const server = new ComponentServer(routes);\n this.servers.set(\"component\", server);\n return server;\n }\n\n /**\n * 获取指定类型的服务器\n */\n getServer(type: \"rest\" | \"component\"): Server | ComponentServer | undefined {\n return this.servers.get(type);\n }\n\n /**\n * 获取所有服务器\n */\n getAllServers(): Map<string, Server | ComponentServer> {\n return this.servers;\n }\n\n /**\n * 移除指定类型的服务器\n */\n removeServer(type: string): boolean {\n return this.servers.delete(type);\n }\n\n /**\n * 清除所有服务器\n */\n clearServers(): void {\n this.servers.clear();\n }\n\n /**\n * 获取服务器状态信息\n */\n getServerStatus(): Record<string, { type: string; routes: number }> {\n const status: Record<string, { type: string; routes: number }> = {};\n\n for (const [name, server] of this.servers) {\n if (server instanceof Server) {\n status[name] = {\n type: \"REST API\",\n routes: (server as any).routes?.length || 0,\n };\n } else if (server instanceof ComponentServer) {\n status[name] = {\n type: \"Component\",\n routes: (server as any).routes?.length || 0,\n };\n }\n }\n\n return status;\n }\n}\n"],"mappings":";;;;;;;;AAYA,IAAa,gBAAb,MAA2B;CACzB,AAAQ,0BAAiD,IAAI,KAAK;;;;CAKlE,iBAAiB,QAAkC;EACjD,MAAM,SAAS,IAAI,OAAO,OAAO;AACjC,OAAK,QAAQ,IAAI,QAAQ,OAAO;AAChC,SAAO;;;;;CAMT,sBACE,QACiB;EACjB,MAAM,SAAS,IAAI,gBAAgB,OAAO;AAC1C,OAAK,QAAQ,IAAI,aAAa,OAAO;AACrC,SAAO;;;;;CAMT,UAAU,MAAkE;AAC1E,SAAO,KAAK,QAAQ,IAAI,KAAK;;;;;CAM/B,gBAAuD;AACrD,SAAO,KAAK;;;;;CAMd,aAAa,MAAuB;AAClC,SAAO,KAAK,QAAQ,OAAO,KAAK;;;;;CAMlC,eAAqB;AACnB,OAAK,QAAQ,OAAO;;;;;CAMtB,kBAAoE;EAClE,MAAM,SAA2D,EAAE;AAEnE,OAAK,MAAM,CAAC,MAAM,WAAW,KAAK,QAChC,KAAI,kBAAkB,OACpB,QAAO,QAAQ;GACb,MAAM;GACN,QAAS,OAAe,QAAQ,UAAU;GAC3C;WACQ,kBAAkB,gBAC3B,QAAO,QAAQ;GACb,MAAM;GACN,QAAS,OAAe,QAAQ,UAAU;GAC3C;AAIL,SAAO"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { d as composeMiddleware, f as errorHandler, i as json } from "./response-Bs9GhJz-.mjs";
|
|
2
2
|
import { t as BaseServer } from "./base-server-CE0mfqPY.mjs";
|
|
3
3
|
import { t as RadixRouter } from "./radix-tree-CccjvyTP.mjs";
|
|
4
|
-
import { c as setGlobalRegistry, t as RouteRegistry } from "./route-registry-
|
|
4
|
+
import { c as setGlobalRegistry, t as RouteRegistry } from "./route-registry-DsPslV2b.mjs";
|
|
5
5
|
|
|
6
6
|
//#region src/server/server.ts
|
|
7
7
|
/**
|
|
@@ -63,13 +63,21 @@ var Server = class extends BaseServer {
|
|
|
63
63
|
const match = this.router.match(method, pathname);
|
|
64
64
|
if (match) {
|
|
65
65
|
req.params = match.params;
|
|
66
|
-
return composeMiddleware([
|
|
66
|
+
return composeMiddleware([
|
|
67
|
+
...this.globalMiddleware,
|
|
68
|
+
errorHandler,
|
|
69
|
+
...match.middleware
|
|
70
|
+
], match.handler)(req);
|
|
67
71
|
}
|
|
68
72
|
if (method === "OPTIONS") {
|
|
69
73
|
const allowedMethods = this.router.getAllowedMethods(pathname);
|
|
70
74
|
if (allowedMethods.length > 0) {
|
|
71
75
|
const routeMiddleware = this.router.match(allowedMethods[0], pathname)?.middleware || [];
|
|
72
|
-
const allMiddleware = [
|
|
76
|
+
const allMiddleware = [
|
|
77
|
+
...this.globalMiddleware,
|
|
78
|
+
errorHandler,
|
|
79
|
+
...routeMiddleware
|
|
80
|
+
];
|
|
73
81
|
const optionsHandler = () => new Response(null, {
|
|
74
82
|
status: 204,
|
|
75
83
|
headers: { Allow: allowedMethods.join(", ") }
|
|
@@ -77,7 +85,7 @@ var Server = class extends BaseServer {
|
|
|
77
85
|
return composeMiddleware(allMiddleware, optionsHandler)(req);
|
|
78
86
|
}
|
|
79
87
|
}
|
|
80
|
-
if (this.globalMiddleware.length > 0) return composeMiddleware(this.globalMiddleware, () => this.createErrorResponse(method, pathname))(req);
|
|
88
|
+
if (this.globalMiddleware.length > 0) return composeMiddleware([...this.globalMiddleware, errorHandler], () => this.createErrorResponse(method, pathname))(req);
|
|
81
89
|
return this.createErrorResponse(method, pathname);
|
|
82
90
|
};
|
|
83
91
|
/** 动态添加单个路由 */
|
|
@@ -105,4 +113,4 @@ var Server = class extends BaseServer {
|
|
|
105
113
|
|
|
106
114
|
//#endregion
|
|
107
115
|
export { Server as t };
|
|
108
|
-
//# sourceMappingURL=server-
|
|
116
|
+
//# sourceMappingURL=server-DWndB63Z.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server-DWndB63Z.mjs","names":[],"sources":["../src/server/server.ts"],"sourcesContent":["/**\n * Vafast 核心服务器\n *\n * 基于 Radix Tree 的高性能路由匹配\n * 时间复杂度: O(k),k 为路径段数\n */\n\nimport type { Method, Middleware } from \"../types\";\nimport type { ProcessedRoute } from \"../defineRoute\";\nimport { composeMiddleware, errorHandler } from \"../middleware\";\nimport { json } from \"../utils/response\";\nimport { BaseServer } from \"./base-server\";\nimport { RadixRouter } from \"../router/radix-tree\";\nimport { RouteRegistry, setGlobalRegistry } from \"../utils/route-registry\";\n\n/**\n * Vafast 服务器\n *\n * @example\n * ```typescript\n * const routes = defineRoutes([\n * defineRoute({ method: \"GET\", path: \"/\", handler: () => \"Hello\" }),\n * ])\n * const server = new Server(routes)\n * export default { fetch: server.fetch }\n * ```\n */\nexport class Server extends BaseServer {\n private router: RadixRouter;\n private routes: ProcessedRoute[];\n\n constructor(routes: readonly ProcessedRoute[] = []) {\n super();\n this.router = new RadixRouter();\n this.routes = [];\n\n if (routes.length > 0) {\n this.registerRoutes([...routes]);\n }\n }\n\n private registerRoutes(routes: ProcessedRoute[]): void {\n this.routes.push(...routes);\n\n for (const route of routes) {\n this.router.register(\n route.method as Method,\n route.path,\n route.handler,\n (route.middleware || []) as Middleware[],\n );\n }\n\n this.detectRouteConflicts(routes);\n this.logRoutes(routes);\n\n // 自动设置全局 RouteRegistry\n setGlobalRegistry(new RouteRegistry(this.routes));\n }\n\n /** 快速提取 pathname */\n private extractPathname(url: string): string {\n let start = url.indexOf(\"://\");\n start = start === -1 ? 0 : start + 3;\n\n const pathStart = url.indexOf(\"/\", start);\n if (pathStart === -1) return \"/\";\n\n let end = url.indexOf(\"?\", pathStart);\n if (end === -1) end = url.indexOf(\"#\", pathStart);\n if (end === -1) end = url.length;\n\n return url.substring(pathStart, end) || \"/\";\n }\n\n /** 生成 404/405 响应 */\n private createErrorResponse(method: string, pathname: string): Response {\n const allowedMethods = this.router.getAllowedMethods(pathname);\n if (allowedMethods.length > 0) {\n return json(\n {\n code: 405,\n message: `Method ${method} not allowed for this endpoint`,\n allowedMethods,\n },\n 405,\n { Allow: allowedMethods.join(\", \") },\n );\n }\n return json({ code: 404, message: \"Not Found\" }, 404);\n }\n\n /** 处理请求 */\n fetch = async (req: Request): Promise<Response> => {\n const pathname = this.extractPathname(req.url);\n const method = req.method as Method;\n\n const match = this.router.match(method, pathname);\n\n if (match) {\n (req as unknown as Record<string, unknown>).params = match.params;\n\n // 运行时组合中间件\n // 洋葱模型:globalMiddleware → errorHandler → routeMiddleware → handler\n // 这样 errorHandler 返回的错误响应也会经过 globalMiddleware(如 CORS)的后处理\n const allMiddleware = [\n ...this.globalMiddleware,\n errorHandler,\n ...match.middleware,\n ];\n const handler = composeMiddleware(allMiddleware, match.handler);\n\n return handler(req);\n }\n\n // OPTIONS 预检请求特殊处理:查找同路径其他方法的路由,使用其中间件\n // 这允许路由级 CORS 中间件正常工作\n if (method === \"OPTIONS\") {\n const allowedMethods = this.router.getAllowedMethods(pathname);\n if (allowedMethods.length > 0) {\n // 尝试获取该路径任意方法的路由中间件\n const anyMatch = this.router.match(\n allowedMethods[0] as Method,\n pathname,\n );\n const routeMiddleware = anyMatch?.middleware || [];\n const allMiddleware = [\n ...this.globalMiddleware,\n errorHandler,\n ...routeMiddleware,\n ];\n\n // OPTIONS 请求默认返回 204(中间件如 CORS 可能会提前响应)\n const optionsHandler = () =>\n new Response(null, {\n status: 204,\n headers: { Allow: allowedMethods.join(\", \") },\n });\n\n const handler = composeMiddleware(allMiddleware, optionsHandler);\n return handler(req);\n }\n }\n\n // 未匹配路由时,仍执行全局中间件(如 CORS 处理 OPTIONS 预检)\n if (this.globalMiddleware.length > 0) {\n const allMiddleware = [...this.globalMiddleware, errorHandler];\n const handler = composeMiddleware(allMiddleware, () =>\n this.createErrorResponse(method, pathname),\n );\n return handler(req);\n }\n\n return this.createErrorResponse(method, pathname);\n };\n\n /** 动态添加单个路由 */\n addRoute(route: ProcessedRoute): void {\n this.routes.push(route);\n this.router.register(\n route.method as Method,\n route.path,\n route.handler,\n (route.middleware || []) as Middleware[],\n );\n }\n\n /** 动态添加多个路由 */\n addRoutes(routes: readonly ProcessedRoute[]): void {\n this.registerRoutes([...routes]);\n }\n\n /** 获取路由列表 */\n getRoutes(): Array<{ method: Method; path: string }> {\n return this.router.getRoutes();\n }\n\n /**\n * 获取完整的路由元信息\n *\n * 用于 API 文档生成、Webhook 事件注册、权限检查等场景\n */\n getRoutesWithMeta(): ProcessedRoute[] {\n return this.routes;\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AA2BA,IAAa,SAAb,cAA4B,WAAW;CACrC,AAAQ;CACR,AAAQ;CAER,YAAY,SAAoC,EAAE,EAAE;AAClD,SAAO;AACP,OAAK,SAAS,IAAI,aAAa;AAC/B,OAAK,SAAS,EAAE;AAEhB,MAAI,OAAO,SAAS,EAClB,MAAK,eAAe,CAAC,GAAG,OAAO,CAAC;;CAIpC,AAAQ,eAAe,QAAgC;AACrD,OAAK,OAAO,KAAK,GAAG,OAAO;AAE3B,OAAK,MAAM,SAAS,OAClB,MAAK,OAAO,SACV,MAAM,QACN,MAAM,MACN,MAAM,SACL,MAAM,cAAc,EAAE,CACxB;AAGH,OAAK,qBAAqB,OAAO;AACjC,OAAK,UAAU,OAAO;AAGtB,oBAAkB,IAAI,cAAc,KAAK,OAAO,CAAC;;;CAInD,AAAQ,gBAAgB,KAAqB;EAC3C,IAAI,QAAQ,IAAI,QAAQ,MAAM;AAC9B,UAAQ,UAAU,KAAK,IAAI,QAAQ;EAEnC,MAAM,YAAY,IAAI,QAAQ,KAAK,MAAM;AACzC,MAAI,cAAc,GAAI,QAAO;EAE7B,IAAI,MAAM,IAAI,QAAQ,KAAK,UAAU;AACrC,MAAI,QAAQ,GAAI,OAAM,IAAI,QAAQ,KAAK,UAAU;AACjD,MAAI,QAAQ,GAAI,OAAM,IAAI;AAE1B,SAAO,IAAI,UAAU,WAAW,IAAI,IAAI;;;CAI1C,AAAQ,oBAAoB,QAAgB,UAA4B;EACtE,MAAM,iBAAiB,KAAK,OAAO,kBAAkB,SAAS;AAC9D,MAAI,eAAe,SAAS,EAC1B,QAAO,KACL;GACE,MAAM;GACN,SAAS,UAAU,OAAO;GAC1B;GACD,EACD,KACA,EAAE,OAAO,eAAe,KAAK,KAAK,EAAE,CACrC;AAEH,SAAO,KAAK;GAAE,MAAM;GAAK,SAAS;GAAa,EAAE,IAAI;;;CAIvD,QAAQ,OAAO,QAAoC;EACjD,MAAM,WAAW,KAAK,gBAAgB,IAAI,IAAI;EAC9C,MAAM,SAAS,IAAI;EAEnB,MAAM,QAAQ,KAAK,OAAO,MAAM,QAAQ,SAAS;AAEjD,MAAI,OAAO;AACT,GAAC,IAA2C,SAAS,MAAM;AAY3D,UAFgB,kBALM;IACpB,GAAG,KAAK;IACR;IACA,GAAG,MAAM;IACV,EACgD,MAAM,QAAQ,CAEhD,IAAI;;AAKrB,MAAI,WAAW,WAAW;GACxB,MAAM,iBAAiB,KAAK,OAAO,kBAAkB,SAAS;AAC9D,OAAI,eAAe,SAAS,GAAG;IAM7B,MAAM,kBAJW,KAAK,OAAO,MAC3B,eAAe,IACf,SACD,EACiC,cAAc,EAAE;IAClD,MAAM,gBAAgB;KACpB,GAAG,KAAK;KACR;KACA,GAAG;KACJ;IAGD,MAAM,uBACJ,IAAI,SAAS,MAAM;KACjB,QAAQ;KACR,SAAS,EAAE,OAAO,eAAe,KAAK,KAAK,EAAE;KAC9C,CAAC;AAGJ,WADgB,kBAAkB,eAAe,eAAe,CACjD,IAAI;;;AAKvB,MAAI,KAAK,iBAAiB,SAAS,EAKjC,QAHgB,kBADM,CAAC,GAAG,KAAK,kBAAkB,aAAa,QAE5D,KAAK,oBAAoB,QAAQ,SAAS,CAC3C,CACc,IAAI;AAGrB,SAAO,KAAK,oBAAoB,QAAQ,SAAS;;;CAInD,SAAS,OAA6B;AACpC,OAAK,OAAO,KAAK,MAAM;AACvB,OAAK,OAAO,SACV,MAAM,QACN,MAAM,MACN,MAAM,SACL,MAAM,cAAc,EAAE,CACxB;;;CAIH,UAAU,QAAyC;AACjD,OAAK,eAAe,CAAC,GAAG,OAAO,CAAC;;;CAIlC,YAAqD;AACnD,SAAO,KAAK,OAAO,WAAW;;;;;;;CAQhC,oBAAsC;AACpC,SAAO,KAAK"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { n as generateAITools, r as getApiSpec, t as ApiSpec } from "../contract-
|
|
1
|
+
import { n as generateAITools, r as getApiSpec, t as ApiSpec } from "../contract-CuruqP6h.mjs";
|
|
2
2
|
export { ApiSpec, generateAITools, getApiSpec };
|
package/dist/utils/contract.mjs
CHANGED
package/dist/utils/index.d.mts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import "../defineRoute-DyPa9FHa.mjs";
|
|
2
2
|
import { t as DependencyManager } from "../dependency-manager-DIN9X0Gj.mjs";
|
|
3
3
|
import { a as parseBody, c as parseCookiesFast, d as parseHeaders, f as parseQuery, i as getHeader, p as parseQueryFast, r as getCookie, s as parseCookies } from "../parsers-8hIAx0OV.mjs";
|
|
4
|
-
import { c as text, i as json, n as err, o as redirect, r as html, s as stream, t as empty } from "../response-
|
|
4
|
+
import { c as text, i as json, n as err, o as redirect, r as html, s as stream, t as empty } from "../response-F-VxN-cB.mjs";
|
|
5
5
|
import { t as goAwait } from "../go-await-DPtVBug4.mjs";
|
|
6
6
|
import { n as base64urlEncode, t as base64urlDecode } from "../base64url-DNUGwekK.mjs";
|
|
7
7
|
import { t as HtmlRenderer } from "../html-renderer-DhQxRuyi.mjs";
|
|
@@ -9,5 +9,5 @@ import { a as ValidationError, c as getValidatorCacheStats, d as validateFast, f
|
|
|
9
9
|
import { i as registerFormats, n as hasFormat, r as registerFormat, t as Patterns } from "../formats-DDDSFWP0.mjs";
|
|
10
10
|
import { a as createSSEHandler, i as SSEMarker, r as SSEHandler, t as SSEEvent } from "../sse-CjIH9WjQ.mjs";
|
|
11
11
|
import { a as getAllRoutes, c as getRoutesByMethod, i as filterRoutes, n as RouteRegistry, o as getRoute, r as createRouteRegistry, s as getRouteRegistry, t as RouteMeta } from "../route-registry-CAX54I8j.mjs";
|
|
12
|
-
import { n as generateAITools, r as getApiSpec, t as ApiSpec } from "../contract-
|
|
12
|
+
import { n as generateAITools, r as getApiSpec, t as ApiSpec } from "../contract-CuruqP6h.mjs";
|
|
13
13
|
export { type ApiSpec, DependencyManager, HtmlRenderer, Patterns, type RouteMeta, RouteRegistry, type SSEEvent, type SSEHandler, type SSEMarker, type SchemaConfig, type ValidationError, type ValidationResult, base64urlDecode, base64urlEncode, createRouteRegistry, createSSEHandler, createValidator, empty, err, filterRoutes, generateAITools, getAllRoutes, getApiSpec, getCookie, getHeader, getRoute, getRouteRegistry, getRoutesByMethod, getValidatorCacheStats, goAwait, hasFormat, html, json, parseBody, parseCookies, parseCookiesFast, parseHeaders, parseQuery, parseQueryFast, precompileSchemas, redirect, registerFormat, registerFormats, stream, text, validateAllSchemas, validateFast, validateSchema, validateSchemaOrThrow };
|
package/dist/utils/index.mjs
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { a as parseCookies, d as parseQueryFast, l as parseHeaders, n as getHeader, o as parseCookiesFast, r as parseBody, t as getCookie, u as parseQuery } from "../parsers-BrG_mRLq.mjs";
|
|
2
2
|
import { a as validateAllSchemas, c as validateSchemaOrThrow, i as precompileSchemas, n as createValidator, o as validateFast, r as getValidatorCacheStats, s as validateSchema } from "../validators-CkfvNBbK.mjs";
|
|
3
|
-
import { c as text, i as json, n as err, o as redirect, r as html, s as stream, t as empty } from "../response-
|
|
4
|
-
import { a as getRoute, i as getAllRoutes, n as createRouteRegistry, o as getRouteRegistry, r as filterRoutes, s as getRoutesByMethod, t as RouteRegistry } from "../route-registry-
|
|
3
|
+
import { c as text, i as json, n as err, o as redirect, r as html, s as stream, t as empty } from "../response-Bs9GhJz-.mjs";
|
|
4
|
+
import { a as getRoute, i as getAllRoutes, n as createRouteRegistry, o as getRouteRegistry, r as filterRoutes, s as getRoutesByMethod, t as RouteRegistry } from "../route-registry-DsPslV2b.mjs";
|
|
5
5
|
import { t as HtmlRenderer } from "../html-renderer-D1zzDVQM.mjs";
|
|
6
6
|
import { t as DependencyManager } from "../dependency-manager-CGMZJTer.mjs";
|
|
7
7
|
import { t as goAwait } from "../go-await-B1U27PgB.mjs";
|
|
8
8
|
import { n as base64urlEncode, t as base64urlDecode } from "../base64url-Cc77f1ms.mjs";
|
|
9
9
|
import { Patterns, hasFormat, registerFormat, registerFormats } from "./formats.mjs";
|
|
10
10
|
import { t as createSSEHandler } from "../sse-C0_ODr4_.mjs";
|
|
11
|
-
import { n as getApiSpec, t as generateAITools } from "../contract-
|
|
11
|
+
import { n as getApiSpec, t as generateAITools } from "../contract-Cwmmo-Nn.mjs";
|
|
12
12
|
|
|
13
13
|
export { DependencyManager, HtmlRenderer, Patterns, RouteRegistry, base64urlDecode, base64urlEncode, createRouteRegistry, createSSEHandler, createValidator, empty, err, filterRoutes, generateAITools, getAllRoutes, getApiSpec, getCookie, getHeader, getRoute, getRouteRegistry, getRoutesByMethod, getValidatorCacheStats, goAwait, hasFormat, html, json, parseBody, parseCookies, parseCookiesFast, parseHeaders, parseQuery, parseQueryFast, precompileSchemas, redirect, registerFormat, registerFormats, stream, text, validateAllSchemas, validateFast, validateSchema, validateSchemaOrThrow };
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as mapResponse, c as text, i as json, n as err, o as redirect, r as html, s as stream, t as empty } from "../response-
|
|
1
|
+
import { a as mapResponse, c as text, i as json, n as err, o as redirect, r as html, s as stream, t as empty } from "../response-F-VxN-cB.mjs";
|
|
2
2
|
export { empty, err, html, json, mapResponse, redirect, stream, text };
|
package/dist/utils/response.mjs
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { a as mapResponse, c as text, i as json, n as err, o as redirect, r as html, s as stream, t as empty } from "../response-
|
|
1
|
+
import { a as mapResponse, c as text, i as json, n as err, o as redirect, r as html, s as stream, t as empty } from "../response-Bs9GhJz-.mjs";
|
|
2
2
|
|
|
3
3
|
export { empty, err, html, json, mapResponse, redirect, stream, text };
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { a as getRoute, c as setGlobalRegistry, i as getAllRoutes, n as createRouteRegistry, o as getRouteRegistry, r as filterRoutes, s as getRoutesByMethod, t as RouteRegistry } from "../route-registry-
|
|
1
|
+
import { a as getRoute, c as setGlobalRegistry, i as getAllRoutes, n as createRouteRegistry, o as getRouteRegistry, r as filterRoutes, s as getRoutesByMethod, t as RouteRegistry } from "../route-registry-DsPslV2b.mjs";
|
|
2
2
|
|
|
3
3
|
export { RouteRegistry, createRouteRegistry, filterRoutes, getAllRoutes, getRoute, getRouteRegistry, getRoutesByMethod, setGlobalRegistry };
|
package/package.json
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"contract-CVd4v1jb.mjs","names":[],"sources":["../src/utils/contract.ts"],"sourcesContent":["/**\n * API Spec 生成器\n *\n * 用于生成 API 规范,支持:\n * - 跨仓库类型同步\n * - AI 工具函数生成\n * - Swagger/OpenAPI 文档生成\n *\n * @example\n * ```typescript\n * import { getApiSpec } from 'vafast'\n *\n * // 方式 1:直接作为 handler(推荐,自动从全局 Registry 获取)\n * const allRoutes = [\n * ...routes,\n * { method: 'GET', path: '/api-spec', handler: getApiSpec }\n * ]\n *\n * // 方式 2:显式传参(灵活场景,如只暴露公开 API)\n * { handler: () => getApiSpec(publicRoutes) }\n * ```\n */\n\nimport type { TSchema } from '@sinclair/typebox'\nimport type { RouteSchema } from '../defineRoute'\nimport { getRouteRegistry } from './route-registry'\n\n/** 路由规范信息 */\ninterface RouteSpec {\n method: string\n path: string\n name?: string\n description?: string\n schema?: {\n body?: TSchema\n query?: TSchema\n params?: TSchema\n headers?: TSchema\n cookies?: TSchema\n response?: TSchema\n }\n}\n\n/** API 规范 */\nexport interface ApiSpec {\n version: string\n generatedAt: string\n routes: RouteSpec[]\n}\n\n/** AI 工具函数格式 */\ninterface AITool {\n name: string\n description?: string\n parameters?: TSchema\n}\n\n/**\n * 从路由数组生成 API 规范\n */\nfunction generateSpec(routes: readonly unknown[]): ApiSpec {\n const routeSpecs: RouteSpec[] = []\n\n for (const route of routes) {\n const r = route as {\n method?: string\n path?: string\n name?: string\n description?: string\n schema?: RouteSchema\n }\n\n if (!r.method || !r.path) continue\n\n // 跳过 spec 接口本身\n if (r.path === '/api-spec' || r.path === '/__spec__') continue\n\n const spec: RouteSpec = {\n method: r.method,\n path: r.path,\n }\n\n // 直接从路由获取 name 和 description\n if (r.name) spec.name = r.name\n if (r.description) spec.description = r.description\n\n // 直接从路由获取 schema\n if (r.schema) {\n const schema = r.schema\n if (schema.body || schema.query || schema.params || schema.headers || schema.cookies || schema.response) {\n spec.schema = {}\n if (schema.body) spec.schema.body = schema.body\n if (schema.query) spec.schema.query = schema.query\n if (schema.params) spec.schema.params = schema.params\n if (schema.headers) spec.schema.headers = schema.headers\n if (schema.cookies) spec.schema.cookies = schema.cookies\n if (schema.response) spec.schema.response = schema.response\n }\n }\n\n routeSpecs.push(spec)\n }\n\n return {\n version: '1.0.0',\n generatedAt: new Date().toISOString(),\n routes: routeSpecs,\n }\n}\n\n/**\n * 从路由数组生成 AI 工具函数\n *\n * 可直接用于 OpenAI Function Calling / Claude Tools\n *\n * @example\n * ```typescript\n * const tools = generateAITools(routes)\n * // [{ name: 'get_users', description: '获取用户列表', parameters: {...} }]\n * ```\n */\nexport function generateAITools(routes: readonly unknown[]): AITool[] {\n const tools: AITool[] = []\n\n for (const route of routes) {\n const r = route as {\n method?: string\n path?: string\n name?: string\n description?: string\n schema?: RouteSchema\n }\n\n if (!r.method || !r.path) continue\n if (r.path === '/api-spec' || r.path === '/__spec__') continue\n\n // 使用 name 或从 path 生成\n const name = r.name || pathToToolName(r.method, r.path)\n\n const tool: AITool = { name }\n if (r.description) tool.description = r.description\n\n // GET 用 query,其他用 body\n if (r.schema) {\n if (r.method === 'GET' && r.schema.query) {\n tool.parameters = r.schema.query\n } else if (r.schema.body) {\n tool.parameters = r.schema.body\n }\n }\n\n tools.push(tool)\n }\n\n return tools\n}\n\n/** 从路径生成工具名 */\nfunction pathToToolName(method: string, path: string): string {\n const segments = path.split('/').filter(Boolean)\n const cleanSegments = segments\n .map(s => s.startsWith(':') ? '' : s)\n .filter(Boolean)\n\n const prefix = method.toLowerCase()\n const suffix = cleanSegments.join('_')\n\n return suffix ? `${prefix}_${suffix}` : prefix\n}\n\n/**\n * 获取 API 规范\n *\n * 支持多种调用方式:\n * 1. 直接作为 handler:自动从全局 Registry 获取(推荐)\n * 2. 无参调用:同上,用于 CLI/测试\n * 3. 有参调用:显式传递路由数组(灵活场景)\n *\n * @param routesOrContext - 可选,路由数组或 handler context。不传则从全局 Registry 获取\n * @returns ApiSpec 对象\n *\n * @example\n * ```typescript\n * import { getApiSpec } from 'vafast'\n *\n * // 方式 1:直接作为 handler(推荐,最简洁)\n * { method: 'GET', path: '/api-spec', handler: getApiSpec }\n *\n * // 方式 2:显式传参(只暴露公开 API)\n * { handler: () => getApiSpec(publicRoutes) }\n *\n * // 方式 3:本地使用(CLI、测试)\n * const spec = getApiSpec()\n * ```\n */\nexport function getApiSpec(routesOrContext?: readonly unknown[] | Record<string, unknown>): ApiSpec {\n // 智能检测:是路由数组还是 handler context\n // 路由数组:Array && 第一个元素有 method 属性\n const isRoutesArray = Array.isArray(routesOrContext) &&\n routesOrContext.length > 0 &&\n typeof (routesOrContext[0] as Record<string, unknown>)?.method === 'string'\n\n const routeList = isRoutesArray\n ? routesOrContext\n : getRoutesFromRegistry()\n\n return generateSpec(routeList)\n}\n\n/**\n * 从全局 Registry 获取路由列表\n * 包含 schema 信息(Registry 存储完整路由)\n */\nfunction getRoutesFromRegistry(): readonly unknown[] {\n try {\n const registry = getRouteRegistry()\n return registry.getAll()\n } catch {\n // Registry 未初始化时返回空数组\n return []\n }\n}\n"],"mappings":";;;;;;AA4DA,SAAS,aAAa,QAAqC;CACzD,MAAM,aAA0B,EAAE;AAElC,MAAK,MAAM,SAAS,QAAQ;EAC1B,MAAM,IAAI;AAQV,MAAI,CAAC,EAAE,UAAU,CAAC,EAAE,KAAM;AAG1B,MAAI,EAAE,SAAS,eAAe,EAAE,SAAS,YAAa;EAEtD,MAAM,OAAkB;GACtB,QAAQ,EAAE;GACV,MAAM,EAAE;GACT;AAGD,MAAI,EAAE,KAAM,MAAK,OAAO,EAAE;AAC1B,MAAI,EAAE,YAAa,MAAK,cAAc,EAAE;AAGxC,MAAI,EAAE,QAAQ;GACZ,MAAM,SAAS,EAAE;AACjB,OAAI,OAAO,QAAQ,OAAO,SAAS,OAAO,UAAU,OAAO,WAAW,OAAO,WAAW,OAAO,UAAU;AACvG,SAAK,SAAS,EAAE;AAChB,QAAI,OAAO,KAAM,MAAK,OAAO,OAAO,OAAO;AAC3C,QAAI,OAAO,MAAO,MAAK,OAAO,QAAQ,OAAO;AAC7C,QAAI,OAAO,OAAQ,MAAK,OAAO,SAAS,OAAO;AAC/C,QAAI,OAAO,QAAS,MAAK,OAAO,UAAU,OAAO;AACjD,QAAI,OAAO,QAAS,MAAK,OAAO,UAAU,OAAO;AACjD,QAAI,OAAO,SAAU,MAAK,OAAO,WAAW,OAAO;;;AAIvD,aAAW,KAAK,KAAK;;AAGvB,QAAO;EACL,SAAS;EACT,8BAAa,IAAI,MAAM,EAAC,aAAa;EACrC,QAAQ;EACT;;;;;;;;;;;;;AAcH,SAAgB,gBAAgB,QAAsC;CACpE,MAAM,QAAkB,EAAE;AAE1B,MAAK,MAAM,SAAS,QAAQ;EAC1B,MAAM,IAAI;AAQV,MAAI,CAAC,EAAE,UAAU,CAAC,EAAE,KAAM;AAC1B,MAAI,EAAE,SAAS,eAAe,EAAE,SAAS,YAAa;EAKtD,MAAM,OAAe,EAAE,MAFV,EAAE,QAAQ,eAAe,EAAE,QAAQ,EAAE,KAAK,EAE1B;AAC7B,MAAI,EAAE,YAAa,MAAK,cAAc,EAAE;AAGxC,MAAI,EAAE,QACJ;OAAI,EAAE,WAAW,SAAS,EAAE,OAAO,MACjC,MAAK,aAAa,EAAE,OAAO;YAClB,EAAE,OAAO,KAClB,MAAK,aAAa,EAAE,OAAO;;AAI/B,QAAM,KAAK,KAAK;;AAGlB,QAAO;;;AAIT,SAAS,eAAe,QAAgB,MAAsB;CAE5D,MAAM,gBADW,KAAK,MAAM,IAAI,CAAC,OAAO,QAAQ,CAE7C,KAAI,MAAK,EAAE,WAAW,IAAI,GAAG,KAAK,EAAE,CACpC,OAAO,QAAQ;CAElB,MAAM,SAAS,OAAO,aAAa;CACnC,MAAM,SAAS,cAAc,KAAK,IAAI;AAEtC,QAAO,SAAS,GAAG,OAAO,GAAG,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;AA4B1C,SAAgB,WAAW,iBAAyE;AAWlG,QAAO,aARe,MAAM,QAAQ,gBAAgB,IAClD,gBAAgB,SAAS,KACzB,OAAQ,gBAAgB,IAAgC,WAAW,WAGjE,kBACA,uBAAuB,CAEG;;;;;;AAOhC,SAAS,wBAA4C;AACnD,KAAI;AAEF,SADiB,kBAAkB,CACnB,QAAQ;SAClB;AAEN,SAAO,EAAE"}
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import { r as Middleware, t as Handler } from "./types-D4pCpFZ_.mjs";
|
|
2
|
-
|
|
3
|
-
//#region src/middleware.d.ts
|
|
4
|
-
/** 中间件类型:使用 next() 传递给下一个处理 */
|
|
5
|
-
/** Vafast 自定义错误类型 */
|
|
6
|
-
declare class VafastError extends Error {
|
|
7
|
-
status: number;
|
|
8
|
-
code: number;
|
|
9
|
-
expose: boolean;
|
|
10
|
-
constructor(message: string, options?: {
|
|
11
|
-
status?: number;
|
|
12
|
-
code?: number;
|
|
13
|
-
expose?: boolean;
|
|
14
|
-
cause?: unknown;
|
|
15
|
-
});
|
|
16
|
-
}
|
|
17
|
-
/**
|
|
18
|
-
* 组合类型: 自动注入错误处理器进行中间件组合
|
|
19
|
-
* errorHandler 放在中间件之后,确保 CORS 等中间件能处理错误响应
|
|
20
|
-
*/
|
|
21
|
-
declare function composeMiddleware(middleware: Middleware[], finalHandler: Handler): (req: Request) => Promise<Response>;
|
|
22
|
-
//#endregion
|
|
23
|
-
export { composeMiddleware as n, VafastError as t };
|
|
24
|
-
//# sourceMappingURL=middleware-CD1F3BEw.d.mts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"response-kuzDQDr9.mjs","names":["err","stream"],"sources":["../src/middleware.ts","../src/utils/response.ts"],"sourcesContent":["// src/middleware.ts\n\nimport { json, mapResponse } from \"./utils/response\";\n\nimport type { Handler, Middleware } from \"./types\";\n/** 中间件类型:使用 next() 传递给下一个处理 */\n\n/** Vafast 自定义错误类型 */\nexport class VafastError extends Error {\n status: number;\n code: number;\n expose: boolean;\n\n constructor(\n message: string,\n options: {\n status?: number;\n code?: number;\n expose?: boolean;\n cause?: unknown;\n } = {},\n ) {\n super(message);\n this.name = \"VafastError\";\n this.status = options.status ?? 500;\n this.code = options.code ?? options.status ?? 500;\n this.expose = options.expose ?? false;\n if (options.cause) (this as any).cause = options.cause;\n }\n}\n\n/**\n * 组合类型: 自动注入错误处理器进行中间件组合\n * errorHandler 放在中间件之后,确保 CORS 等中间件能处理错误响应\n */\nexport function composeMiddleware(\n middleware: Middleware[],\n finalHandler: Handler,\n): (req: Request) => Promise<Response> {\n const all = [...middleware, errorHandler];\n\n return function composedHandler(req: Request): Promise<Response> {\n let i = -1;\n\n const dispatch = (index: number): Promise<Response> => {\n if (index <= i)\n return Promise.reject(new Error(\"next() called multiple times\"));\n i = index;\n\n // 中间件阶段\n if (index < all.length) {\n const mw = all[index];\n return Promise.resolve(mw(req, () => dispatch(index + 1)));\n }\n\n // 最终 handler - 使用 mapResponse 转换返回值\n return Promise.resolve(finalHandler(req)).then(mapResponse);\n };\n\n return dispatch(0);\n };\n}\n\n/** 默认包含的全局错误处理器 */\nconst errorHandler: Middleware = async (req, next) => {\n try {\n return await next();\n } catch (err) {\n console.error(\"未处理的错误:\", err);\n\n if (err instanceof VafastError) {\n return json(\n {\n code: err.code,\n message: err.expose ? err.message : \"发生了一个错误\",\n },\n err.status,\n );\n }\n\n return json({ code: 500, message: \"出现了一些问题\" }, 500);\n }\n};\n","// src/response.ts\n\nimport { VafastError } from \"../middleware\";\n\n/** 生成 JSON 响应 */\nexport function json(\n data: unknown,\n status = 200,\n headers: HeadersInit = {},\n): Response {\n const body = JSON.stringify(data);\n\n // 优化:只在有自定义 headers 时才创建 Headers 对象\n if (Object.keys(headers).length === 0) {\n return new Response(body, {\n status,\n headers: { \"Content-Type\": \"application/json\" },\n });\n }\n\n // 有自定义 headers 时才创建 Headers 对象\n const h = new Headers({\n \"Content-Type\": \"application/json\",\n ...headers,\n });\n\n return new Response(body, {\n status,\n headers: h,\n });\n}\n\n// JSON 响应的预创建 headers(避免每次创建)\nconst JSON_HEADERS = { \"Content-Type\": \"application/json\" };\nconst TEXT_HEADERS = { \"Content-Type\": \"text/plain\" };\n\n/**\n * 类型特化的响应映射\n * 根据返回值类型直接生成 Response,避免不必要的检查\n */\nexport function mapResponse(response: unknown): Response {\n // 快速路径:已经是 Response\n if (response instanceof Response) return response;\n\n // 使用 constructor.name 进行类型判断(比 instanceof 更快)\n switch (response?.constructor?.name) {\n case \"String\":\n return new Response(response as string, { headers: TEXT_HEADERS });\n\n case \"Object\":\n case \"Array\":\n return new Response(JSON.stringify(response), { headers: JSON_HEADERS });\n\n case \"Number\":\n case \"Boolean\":\n return new Response(String(response), { headers: TEXT_HEADERS });\n\n case undefined:\n // null 或 undefined\n return new Response(null, { status: 204 });\n\n case \"ReadableStream\":\n return new Response(response as ReadableStream);\n\n case \"Blob\":\n return new Response(response as Blob);\n\n case \"ArrayBuffer\":\n return new Response(response as ArrayBuffer);\n\n case \"Uint8Array\":\n return new Response(response as unknown as BodyInit);\n\n default:\n // Promise 处理\n if (response instanceof Promise) {\n return response.then(mapResponse) as unknown as Response;\n }\n // 其他情况使用 JSON 序列化\n return new Response(JSON.stringify(response), { headers: JSON_HEADERS });\n }\n}\n\n/** 生成重定向响应 */\nexport function redirect(location: string, status: 301 | 302 = 302): Response {\n return new Response(null, {\n status,\n headers: {\n Location: location,\n },\n });\n}\n\n/** 生成纯文本响应 */\nexport function text(\n content: string,\n status = 200,\n headers: HeadersInit = {},\n): Response {\n const h = new Headers({\n \"Content-Type\": \"text/plain; charset=utf-8\",\n ...headers,\n });\n\n return new Response(content, {\n status,\n headers: h,\n });\n}\n\n/** 生成HTML响应 */\nexport function html(\n content: string,\n status = 200,\n headers: HeadersInit = {},\n): Response {\n const h = new Headers({\n \"Content-Type\": \"text/html; charset=utf-8\",\n ...headers,\n });\n\n return new Response(content, {\n status,\n headers: h,\n });\n}\n\n/** 生成空响应(204 No Content) */\nexport function empty(status = 204, headers: HeadersInit = {}): Response {\n return new Response(null, {\n status,\n headers,\n });\n}\n\n/** 生成流式响应 */\nexport function stream(\n stream: ReadableStream,\n status = 200,\n headers: HeadersInit = {},\n): Response {\n const h = new Headers({\n \"Content-Type\": \"application/octet-stream\",\n ...headers,\n });\n\n return new Response(stream, {\n status,\n headers: h,\n });\n}\n\n// ==================== 错误响应工具 ====================\n\n/**\n * 创建错误响应\n *\n * @param message - 错误信息\n * @param status - HTTP 状态码,默认 500\n * @param code - 业务错误码,默认等于 status\n *\n * @example\n * ```typescript\n * // 使用默认 code(等于 HTTP status)\n * throw err('用户不存在', 404)\n * // 响应: HTTP 404, { code: 404, message: \"用户不存在\" }\n *\n * // 使用自定义业务 code\n * throw err('用户不存在', 404, 10001)\n * // 响应: HTTP 404, { code: 10001, message: \"用户不存在\" }\n *\n * // 预定义错误\n * throw err.notFound('用户不存在')\n * throw err.notFound('用户不存在', 10001) // 带业务码\n * throw err.badRequest('参数错误')\n * throw err.unauthorized('请先登录')\n * ```\n */\nexport function err(message: string, status = 500, code?: number) {\n return new VafastError(message, { status, code: code ?? status, expose: true });\n}\n\n/** 400 Bad Request */\nerr.badRequest = (message = \"请求参数错误\", code?: number) =>\n err(message, 400, code);\n\n/** 401 Unauthorized */\nerr.unauthorized = (message = \"未授权\", code?: number) =>\n err(message, 401, code);\n\n/** 403 Forbidden */\nerr.forbidden = (message = \"禁止访问\", code?: number) =>\n err(message, 403, code);\n\n/** 404 Not Found */\nerr.notFound = (message = \"资源不存在\", code?: number) =>\n err(message, 404, code);\n\n/** 409 Conflict */\nerr.conflict = (message = \"资源冲突\", code?: number) =>\n err(message, 409, code);\n\n/** 422 Unprocessable Entity */\nerr.unprocessable = (message = \"无法处理的实体\", code?: number) =>\n err(message, 422, code);\n\n/** 429 Too Many Requests */\nerr.tooMany = (message = \"请求过于频繁\", code?: number) =>\n err(message, 429, code);\n\n/** 500 Internal Server Error */\nerr.internal = (message = \"服务器内部错误\", code?: number) =>\n err(message, 500, code);\n"],"mappings":";;;AAQA,IAAa,cAAb,cAAiC,MAAM;CACrC;CACA;CACA;CAEA,YACE,SACA,UAKI,EAAE,EACN;AACA,QAAM,QAAQ;AACd,OAAK,OAAO;AACZ,OAAK,SAAS,QAAQ,UAAU;AAChC,OAAK,OAAO,QAAQ,QAAQ,QAAQ,UAAU;AAC9C,OAAK,SAAS,QAAQ,UAAU;AAChC,MAAI,QAAQ,MAAO,CAAC,KAAa,QAAQ,QAAQ;;;;;;;AAQrD,SAAgB,kBACd,YACA,cACqC;CACrC,MAAM,MAAM,CAAC,GAAG,YAAY,aAAa;AAEzC,QAAO,SAAS,gBAAgB,KAAiC;EAC/D,IAAI,IAAI;EAER,MAAM,YAAY,UAAqC;AACrD,OAAI,SAAS,EACX,QAAO,QAAQ,uBAAO,IAAI,MAAM,+BAA+B,CAAC;AAClE,OAAI;AAGJ,OAAI,QAAQ,IAAI,QAAQ;IACtB,MAAM,KAAK,IAAI;AACf,WAAO,QAAQ,QAAQ,GAAG,WAAW,SAAS,QAAQ,EAAE,CAAC,CAAC;;AAI5D,UAAO,QAAQ,QAAQ,aAAa,IAAI,CAAC,CAAC,KAAK,YAAY;;AAG7D,SAAO,SAAS,EAAE;;;;AAKtB,MAAM,eAA2B,OAAO,KAAK,SAAS;AACpD,KAAI;AACF,SAAO,MAAM,MAAM;UACZA,OAAK;AACZ,UAAQ,MAAM,WAAWA,MAAI;AAE7B,MAAIA,iBAAe,YACjB,QAAO,KACL;GACE,MAAMA,MAAI;GACV,SAASA,MAAI,SAASA,MAAI,UAAU;GACrC,EACDA,MAAI,OACL;AAGH,SAAO,KAAK;GAAE,MAAM;GAAK,SAAS;GAAW,EAAE,IAAI;;;;;;;AC3EvD,SAAgB,KACd,MACA,SAAS,KACT,UAAuB,EAAE,EACf;CACV,MAAM,OAAO,KAAK,UAAU,KAAK;AAGjC,KAAI,OAAO,KAAK,QAAQ,CAAC,WAAW,EAClC,QAAO,IAAI,SAAS,MAAM;EACxB;EACA,SAAS,EAAE,gBAAgB,oBAAoB;EAChD,CAAC;CAIJ,MAAM,IAAI,IAAI,QAAQ;EACpB,gBAAgB;EAChB,GAAG;EACJ,CAAC;AAEF,QAAO,IAAI,SAAS,MAAM;EACxB;EACA,SAAS;EACV,CAAC;;AAIJ,MAAM,eAAe,EAAE,gBAAgB,oBAAoB;AAC3D,MAAM,eAAe,EAAE,gBAAgB,cAAc;;;;;AAMrD,SAAgB,YAAY,UAA6B;AAEvD,KAAI,oBAAoB,SAAU,QAAO;AAGzC,SAAQ,UAAU,aAAa,MAA/B;EACE,KAAK,SACH,QAAO,IAAI,SAAS,UAAoB,EAAE,SAAS,cAAc,CAAC;EAEpE,KAAK;EACL,KAAK,QACH,QAAO,IAAI,SAAS,KAAK,UAAU,SAAS,EAAE,EAAE,SAAS,cAAc,CAAC;EAE1E,KAAK;EACL,KAAK,UACH,QAAO,IAAI,SAAS,OAAO,SAAS,EAAE,EAAE,SAAS,cAAc,CAAC;EAElE,KAAK,OAEH,QAAO,IAAI,SAAS,MAAM,EAAE,QAAQ,KAAK,CAAC;EAE5C,KAAK,iBACH,QAAO,IAAI,SAAS,SAA2B;EAEjD,KAAK,OACH,QAAO,IAAI,SAAS,SAAiB;EAEvC,KAAK,cACH,QAAO,IAAI,SAAS,SAAwB;EAE9C,KAAK,aACH,QAAO,IAAI,SAAS,SAAgC;EAEtD;AAEE,OAAI,oBAAoB,QACtB,QAAO,SAAS,KAAK,YAAY;AAGnC,UAAO,IAAI,SAAS,KAAK,UAAU,SAAS,EAAE,EAAE,SAAS,cAAc,CAAC;;;;AAK9E,SAAgB,SAAS,UAAkB,SAAoB,KAAe;AAC5E,QAAO,IAAI,SAAS,MAAM;EACxB;EACA,SAAS,EACP,UAAU,UACX;EACF,CAAC;;;AAIJ,SAAgB,KACd,SACA,SAAS,KACT,UAAuB,EAAE,EACf;CACV,MAAM,IAAI,IAAI,QAAQ;EACpB,gBAAgB;EAChB,GAAG;EACJ,CAAC;AAEF,QAAO,IAAI,SAAS,SAAS;EAC3B;EACA,SAAS;EACV,CAAC;;;AAIJ,SAAgB,KACd,SACA,SAAS,KACT,UAAuB,EAAE,EACf;CACV,MAAM,IAAI,IAAI,QAAQ;EACpB,gBAAgB;EAChB,GAAG;EACJ,CAAC;AAEF,QAAO,IAAI,SAAS,SAAS;EAC3B;EACA,SAAS;EACV,CAAC;;;AAIJ,SAAgB,MAAM,SAAS,KAAK,UAAuB,EAAE,EAAY;AACvE,QAAO,IAAI,SAAS,MAAM;EACxB;EACA;EACD,CAAC;;;AAIJ,SAAgB,OACd,UACA,SAAS,KACT,UAAuB,EAAE,EACf;CACV,MAAM,IAAI,IAAI,QAAQ;EACpB,gBAAgB;EAChB,GAAG;EACJ,CAAC;AAEF,QAAO,IAAI,SAASC,UAAQ;EAC1B;EACA,SAAS;EACV,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;AA6BJ,SAAgB,IAAI,SAAiB,SAAS,KAAK,MAAe;AAChE,QAAO,IAAI,YAAY,SAAS;EAAE;EAAQ,MAAM,QAAQ;EAAQ,QAAQ;EAAM,CAAC;;;AAIjF,IAAI,cAAc,UAAU,UAAU,SACpC,IAAI,SAAS,KAAK,KAAK;;AAGzB,IAAI,gBAAgB,UAAU,OAAO,SACnC,IAAI,SAAS,KAAK,KAAK;;AAGzB,IAAI,aAAa,UAAU,QAAQ,SACjC,IAAI,SAAS,KAAK,KAAK;;AAGzB,IAAI,YAAY,UAAU,SAAS,SACjC,IAAI,SAAS,KAAK,KAAK;;AAGzB,IAAI,YAAY,UAAU,QAAQ,SAChC,IAAI,SAAS,KAAK,KAAK;;AAGzB,IAAI,iBAAiB,UAAU,WAAW,SACxC,IAAI,SAAS,KAAK,KAAK;;AAGzB,IAAI,WAAW,UAAU,UAAU,SACjC,IAAI,SAAS,KAAK,KAAK;;AAGzB,IAAI,YAAY,UAAU,WAAW,SACnC,IAAI,SAAS,KAAK,KAAK"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"route-registry-BzExlM2t.mjs","names":[],"sources":["../src/utils/route-registry.ts"],"sourcesContent":["/**\n * 路由注册表\n *\n * 提供路由元信息的收集和查询能力\n * 可用于:API 文档生成、Webhook 事件注册、权限检查、审计日志等\n *\n * @example\n * ```typescript\n * // 创建注册表\n * const registry = createRouteRegistry(server.getRoutes())\n *\n * // 查询路由\n * const route = registry.get('POST', '/auth/signIn')\n *\n * // 筛选有 webhook 配置的路由\n * const webhookRoutes = registry.filter('webhook')\n *\n * // 按分类获取\n * const authRoutes = registry.getByCategory('auth')\n * ```\n */\n\nimport type { Method } from '../types'\nimport type { ProcessedRoute, RouteSchema } from '../defineRoute'\n\n/**\n * 路由元信息(不含 handler 和 middleware)\n */\nexport interface RouteMeta {\n method: Method\n path: string\n name?: string\n description?: string\n /** Schema 定义(用于契约生成) */\n schema?: RouteSchema\n /** 扩展字段 */\n [key: string]: unknown\n}\n\n/**\n * 路由注册表\n *\n * 泛型 T 用于定义扩展字段的类型\n */\nexport class RouteRegistry<T extends Record<string, unknown> = Record<string, unknown>> {\n /** 所有路由元信息 */\n private routes: RouteMeta[] = []\n\n /** 路由映射表:METHOD:path -> RouteMeta */\n private routeMap = new Map<string, RouteMeta>()\n\n /** 分类映射表:category -> RouteMeta[] */\n private categoryMap = new Map<string, RouteMeta[]>()\n\n constructor(routes: ProcessedRoute[]) {\n this.buildRegistry(routes)\n }\n\n /**\n * 构建注册表\n */\n private buildRegistry(routes: ProcessedRoute[]): void {\n for (const route of routes) {\n // 提取元信息(排除 handler 和 middleware)\n const meta: RouteMeta = {\n method: route.method as Method,\n path: route.path,\n name: route.name,\n description: route.description,\n schema: route.schema, // 保留 schema 用于契约生成\n }\n\n // 复制扩展字段\n for (const key of Object.keys(route)) {\n if (!['method', 'path', 'name', 'description', 'handler', 'middleware', 'schema', 'docs'].includes(key)) {\n meta[key] = route[key]\n }\n }\n\n this.routes.push(meta)\n this.routeMap.set(`${route.method}:${route.path}`, meta)\n\n // 按分类索引\n const category = this.extractCategory(route.path)\n if (!this.categoryMap.has(category)) {\n this.categoryMap.set(category, [])\n }\n this.categoryMap.get(category)!.push(meta)\n }\n }\n\n /**\n * 提取分类(第一段路径)\n */\n private extractCategory(path: string): string {\n const segments = path.split('/').filter(Boolean)\n return segments[0] || 'root'\n }\n\n // ============================================\n // 查询接口\n // ============================================\n\n /**\n * 获取所有路由元信息\n */\n getAll(): RouteMeta[] {\n return [...this.routes]\n }\n\n /**\n * 按 method + path 查询路由\n */\n get(method: string, path: string): (RouteMeta & T) | undefined {\n return this.routeMap.get(`${method}:${path}`) as (RouteMeta & T) | undefined\n }\n\n /**\n * 检查路由是否存在\n */\n has(method: string, path: string): boolean {\n return this.routeMap.has(`${method}:${path}`)\n }\n\n /**\n * 按分类获取路由\n */\n getByCategory(category: string): RouteMeta[] {\n return this.categoryMap.get(category) || []\n }\n\n /**\n * 获取所有分类\n */\n getCategories(): string[] {\n return Array.from(this.categoryMap.keys()).sort()\n }\n\n /**\n * 筛选有特定字段的路由\n *\n * @example\n * ```typescript\n * // 获取所有配置了 webhook 的路由\n * const webhookRoutes = registry.filter('webhook')\n * ```\n */\n filter<K extends string>(field: K): (RouteMeta & Record<K, unknown>)[] {\n return this.routes.filter((r) => field in r && r[field] !== undefined) as (RouteMeta & Record<K, unknown>)[]\n }\n\n /**\n * 按条件筛选路由\n *\n * @example\n * ```typescript\n * // 获取所有 POST 请求\n * const postRoutes = registry.filterBy(r => r.method === 'POST')\n * ```\n */\n filterBy(predicate: (route: RouteMeta) => boolean): RouteMeta[] {\n return this.routes.filter(predicate)\n }\n\n /**\n * 获取路由数量\n */\n get size(): number {\n return this.routes.length\n }\n\n /**\n * 遍历所有路由\n */\n forEach(callback: (route: RouteMeta, index: number) => void): void {\n this.routes.forEach(callback)\n }\n\n /**\n * 映射所有路由\n */\n map<R>(callback: (route: RouteMeta, index: number) => R): R[] {\n return this.routes.map(callback)\n }\n}\n\n/**\n * 创建路由注册表\n *\n * @example\n * ```typescript\n * // 定义扩展字段类型\n * interface MyRouteMeta {\n * webhook?: { eventKey: string }\n * permission?: string\n * }\n *\n * // 创建带类型的注册表\n * const registry = createRouteRegistry<MyRouteMeta>(server.getRoutes())\n *\n * // 类型安全的查询\n * const route = registry.get('POST', '/auth/signIn')\n * if (route?.webhook) {\n * console.log(route.webhook.eventKey)\n * }\n * ```\n */\nexport function createRouteRegistry<T extends Record<string, unknown> = Record<string, unknown>>(\n routes: ProcessedRoute[]\n): RouteRegistry<T> {\n return new RouteRegistry<T>(routes)\n}\n\n// ============================================\n// 全局 Registry(单例模式)\n// ============================================\n\n/** 全局 registry 实例 */\nlet globalRegistry: RouteRegistry | null = null\n\n/**\n * 设置全局 registry(框架内部使用)\n * @internal\n */\nexport function setGlobalRegistry(registry: RouteRegistry): void {\n globalRegistry = registry\n}\n\n/**\n * 获取全局路由注册表\n *\n * @example\n * ```typescript\n * // 在任意文件中\n * import { getRouteRegistry } from 'vafast'\n *\n * const registry = getRouteRegistry()\n * const webhookRoutes = registry.filter('webhook')\n * ```\n *\n * @throws 如果 Server 尚未创建\n */\nexport function getRouteRegistry<T extends Record<string, unknown> = Record<string, unknown>>(): RouteRegistry<T> {\n if (!globalRegistry) {\n throw new Error('RouteRegistry not initialized. Make sure Server is created first.')\n }\n return globalRegistry as RouteRegistry<T>\n}\n\n/**\n * 按 method + path 获取单个路由\n *\n * 便捷函数,无需先获取 registry\n *\n * @example\n * ```typescript\n * // 在中间件或接口中\n * import { getRoute } from 'vafast'\n *\n * const route = getRoute('POST', '/auth/signIn')\n * if (route?.webhook) {\n * console.log('This route has webhook:', route.webhook)\n * }\n * ```\n */\nexport function getRoute<T extends Record<string, unknown> = Record<string, unknown>>(\n method: string,\n path: string\n): (RouteMeta & T) | undefined {\n return getRouteRegistry<T>().get(method, path)\n}\n\n/**\n * 获取所有路由\n *\n * @example\n * ```typescript\n * import { getAllRoutes } from 'vafast'\n *\n * const routes = getAllRoutes()\n * console.log(`Total ${routes.length} routes`)\n * ```\n */\nexport function getAllRoutes(): RouteMeta[] {\n return getRouteRegistry().getAll()\n}\n\n/**\n * 筛选有特定字段的路由\n *\n * @example\n * ```typescript\n * import { filterRoutes } from 'vafast'\n *\n * // 获取所有配置了 webhook 的路由\n * const webhookRoutes = filterRoutes('webhook')\n * ```\n */\nexport function filterRoutes<K extends string>(field: K): (RouteMeta & Record<K, unknown>)[] {\n return getRouteRegistry().filter(field)\n}\n\n/**\n * 按 HTTP 方法获取路由\n *\n * @example\n * ```typescript\n * import { getRoutesByMethod } from 'vafast'\n *\n * const getRoutes = getRoutesByMethod('GET')\n * const postRoutes = getRoutesByMethod('POST')\n * ```\n */\nexport function getRoutesByMethod(method: string): RouteMeta[] {\n return getRouteRegistry().filterBy(r => r.method === method)\n}\n\n"],"mappings":";;;;;;AA4CA,IAAa,gBAAb,MAAwF;;CAEtF,AAAQ,SAAsB,EAAE;;CAGhC,AAAQ,2BAAW,IAAI,KAAwB;;CAG/C,AAAQ,8BAAc,IAAI,KAA0B;CAEpD,YAAY,QAA0B;AACpC,OAAK,cAAc,OAAO;;;;;CAM5B,AAAQ,cAAc,QAAgC;AACpD,OAAK,MAAM,SAAS,QAAQ;GAE1B,MAAM,OAAkB;IACtB,QAAQ,MAAM;IACd,MAAM,MAAM;IACZ,MAAM,MAAM;IACZ,aAAa,MAAM;IACnB,QAAQ,MAAM;IACf;AAGD,QAAK,MAAM,OAAO,OAAO,KAAK,MAAM,CAClC,KAAI,CAAC;IAAC;IAAU;IAAQ;IAAQ;IAAe;IAAW;IAAc;IAAU;IAAO,CAAC,SAAS,IAAI,CACrG,MAAK,OAAO,MAAM;AAItB,QAAK,OAAO,KAAK,KAAK;AACtB,QAAK,SAAS,IAAI,GAAG,MAAM,OAAO,GAAG,MAAM,QAAQ,KAAK;GAGxD,MAAM,WAAW,KAAK,gBAAgB,MAAM,KAAK;AACjD,OAAI,CAAC,KAAK,YAAY,IAAI,SAAS,CACjC,MAAK,YAAY,IAAI,UAAU,EAAE,CAAC;AAEpC,QAAK,YAAY,IAAI,SAAS,CAAE,KAAK,KAAK;;;;;;CAO9C,AAAQ,gBAAgB,MAAsB;AAE5C,SADiB,KAAK,MAAM,IAAI,CAAC,OAAO,QAAQ,CAChC,MAAM;;;;;CAUxB,SAAsB;AACpB,SAAO,CAAC,GAAG,KAAK,OAAO;;;;;CAMzB,IAAI,QAAgB,MAA2C;AAC7D,SAAO,KAAK,SAAS,IAAI,GAAG,OAAO,GAAG,OAAO;;;;;CAM/C,IAAI,QAAgB,MAAuB;AACzC,SAAO,KAAK,SAAS,IAAI,GAAG,OAAO,GAAG,OAAO;;;;;CAM/C,cAAc,UAA+B;AAC3C,SAAO,KAAK,YAAY,IAAI,SAAS,IAAI,EAAE;;;;;CAM7C,gBAA0B;AACxB,SAAO,MAAM,KAAK,KAAK,YAAY,MAAM,CAAC,CAAC,MAAM;;;;;;;;;;;CAYnD,OAAyB,OAA8C;AACrE,SAAO,KAAK,OAAO,QAAQ,MAAM,SAAS,KAAK,EAAE,WAAW,OAAU;;;;;;;;;;;CAYxE,SAAS,WAAuD;AAC9D,SAAO,KAAK,OAAO,OAAO,UAAU;;;;;CAMtC,IAAI,OAAe;AACjB,SAAO,KAAK,OAAO;;;;;CAMrB,QAAQ,UAA2D;AACjE,OAAK,OAAO,QAAQ,SAAS;;;;;CAM/B,IAAO,UAAuD;AAC5D,SAAO,KAAK,OAAO,IAAI,SAAS;;;;;;;;;;;;;;;;;;;;;;;;AAyBpC,SAAgB,oBACd,QACkB;AAClB,QAAO,IAAI,cAAiB,OAAO;;;AAQrC,IAAI,iBAAuC;;;;;AAM3C,SAAgB,kBAAkB,UAA+B;AAC/D,kBAAiB;;;;;;;;;;;;;;;;AAiBnB,SAAgB,mBAAkG;AAChH,KAAI,CAAC,eACH,OAAM,IAAI,MAAM,oEAAoE;AAEtF,QAAO;;;;;;;;;;;;;;;;;;AAmBT,SAAgB,SACd,QACA,MAC6B;AAC7B,QAAO,kBAAqB,CAAC,IAAI,QAAQ,KAAK;;;;;;;;;;;;;AAchD,SAAgB,eAA4B;AAC1C,QAAO,kBAAkB,CAAC,QAAQ;;;;;;;;;;;;;AAcpC,SAAgB,aAA+B,OAA8C;AAC3F,QAAO,kBAAkB,CAAC,OAAO,MAAM;;;;;;;;;;;;;AAczC,SAAgB,kBAAkB,QAA6B;AAC7D,QAAO,kBAAkB,CAAC,UAAS,MAAK,EAAE,WAAW,OAAO"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"server-CzfQQrjf.mjs","names":[],"sources":["../src/server/server.ts"],"sourcesContent":["/**\n * Vafast 核心服务器\n *\n * 基于 Radix Tree 的高性能路由匹配\n * 时间复杂度: O(k),k 为路径段数\n */\n\nimport type { Method, Middleware } from \"../types\";\nimport type { ProcessedRoute } from \"../defineRoute\";\nimport { composeMiddleware } from \"../middleware\";\nimport { json } from \"../utils/response\";\nimport { BaseServer } from \"./base-server\";\nimport { RadixRouter } from \"../router/radix-tree\";\nimport { RouteRegistry, setGlobalRegistry } from \"../utils/route-registry\";\n\n/**\n * Vafast 服务器\n *\n * @example\n * ```typescript\n * const routes = defineRoutes([\n * defineRoute({ method: \"GET\", path: \"/\", handler: () => \"Hello\" }),\n * ])\n * const server = new Server(routes)\n * export default { fetch: server.fetch }\n * ```\n */\nexport class Server extends BaseServer {\n private router: RadixRouter;\n private routes: ProcessedRoute[];\n\n constructor(routes: readonly ProcessedRoute[] = []) {\n super();\n this.router = new RadixRouter();\n this.routes = [];\n\n if (routes.length > 0) {\n this.registerRoutes([...routes]);\n }\n }\n\n private registerRoutes(routes: ProcessedRoute[]): void {\n this.routes.push(...routes);\n\n for (const route of routes) {\n this.router.register(\n route.method as Method,\n route.path,\n route.handler,\n (route.middleware || []) as Middleware[],\n );\n }\n\n this.detectRouteConflicts(routes);\n this.logRoutes(routes);\n\n // 自动设置全局 RouteRegistry\n setGlobalRegistry(new RouteRegistry(this.routes));\n }\n\n /** 快速提取 pathname */\n private extractPathname(url: string): string {\n let start = url.indexOf(\"://\");\n start = start === -1 ? 0 : start + 3;\n\n const pathStart = url.indexOf(\"/\", start);\n if (pathStart === -1) return \"/\";\n\n let end = url.indexOf(\"?\", pathStart);\n if (end === -1) end = url.indexOf(\"#\", pathStart);\n if (end === -1) end = url.length;\n\n return url.substring(pathStart, end) || \"/\";\n }\n\n /** 生成 404/405 响应 */\n private createErrorResponse(method: string, pathname: string): Response {\n const allowedMethods = this.router.getAllowedMethods(pathname);\n if (allowedMethods.length > 0) {\n return json(\n {\n code: 405,\n message: `Method ${method} not allowed for this endpoint`,\n allowedMethods,\n },\n 405,\n { Allow: allowedMethods.join(\", \") },\n );\n }\n return json({ code: 404, message: \"Not Found\" }, 404);\n }\n\n /** 处理请求 */\n fetch = async (req: Request): Promise<Response> => {\n const pathname = this.extractPathname(req.url);\n const method = req.method as Method;\n\n const match = this.router.match(method, pathname);\n\n if (match) {\n (req as unknown as Record<string, unknown>).params = match.params;\n\n // 运行时组合中间件\n const allMiddleware = [...this.globalMiddleware, ...match.middleware];\n const handler = composeMiddleware(allMiddleware, match.handler);\n\n return handler(req);\n }\n\n // OPTIONS 预检请求特殊处理:查找同路径其他方法的路由,使用其中间件\n // 这允许路由级 CORS 中间件正常工作\n if (method === \"OPTIONS\") {\n const allowedMethods = this.router.getAllowedMethods(pathname);\n if (allowedMethods.length > 0) {\n // 尝试获取该路径任意方法的路由中间件\n const anyMatch = this.router.match(\n allowedMethods[0] as Method,\n pathname,\n );\n const routeMiddleware = anyMatch?.middleware || [];\n const allMiddleware = [...this.globalMiddleware, ...routeMiddleware];\n\n // OPTIONS 请求默认返回 204(中间件如 CORS 可能会提前响应)\n const optionsHandler = () =>\n new Response(null, {\n status: 204,\n headers: { Allow: allowedMethods.join(\", \") },\n });\n\n const handler = composeMiddleware(allMiddleware, optionsHandler);\n return handler(req);\n }\n }\n\n // 未匹配路由时,仍执行全局中间件(如 CORS 处理 OPTIONS 预检)\n if (this.globalMiddleware.length > 0) {\n const handler = composeMiddleware(this.globalMiddleware, () =>\n this.createErrorResponse(method, pathname),\n );\n return handler(req);\n }\n\n return this.createErrorResponse(method, pathname);\n };\n\n /** 动态添加单个路由 */\n addRoute(route: ProcessedRoute): void {\n this.routes.push(route);\n this.router.register(\n route.method as Method,\n route.path,\n route.handler,\n (route.middleware || []) as Middleware[],\n );\n }\n\n /** 动态添加多个路由 */\n addRoutes(routes: readonly ProcessedRoute[]): void {\n this.registerRoutes([...routes]);\n }\n\n /** 获取路由列表 */\n getRoutes(): Array<{ method: Method; path: string }> {\n return this.router.getRoutes();\n }\n\n /**\n * 获取完整的路由元信息\n *\n * 用于 API 文档生成、Webhook 事件注册、权限检查等场景\n */\n getRoutesWithMeta(): ProcessedRoute[] {\n return this.routes;\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AA2BA,IAAa,SAAb,cAA4B,WAAW;CACrC,AAAQ;CACR,AAAQ;CAER,YAAY,SAAoC,EAAE,EAAE;AAClD,SAAO;AACP,OAAK,SAAS,IAAI,aAAa;AAC/B,OAAK,SAAS,EAAE;AAEhB,MAAI,OAAO,SAAS,EAClB,MAAK,eAAe,CAAC,GAAG,OAAO,CAAC;;CAIpC,AAAQ,eAAe,QAAgC;AACrD,OAAK,OAAO,KAAK,GAAG,OAAO;AAE3B,OAAK,MAAM,SAAS,OAClB,MAAK,OAAO,SACV,MAAM,QACN,MAAM,MACN,MAAM,SACL,MAAM,cAAc,EAAE,CACxB;AAGH,OAAK,qBAAqB,OAAO;AACjC,OAAK,UAAU,OAAO;AAGtB,oBAAkB,IAAI,cAAc,KAAK,OAAO,CAAC;;;CAInD,AAAQ,gBAAgB,KAAqB;EAC3C,IAAI,QAAQ,IAAI,QAAQ,MAAM;AAC9B,UAAQ,UAAU,KAAK,IAAI,QAAQ;EAEnC,MAAM,YAAY,IAAI,QAAQ,KAAK,MAAM;AACzC,MAAI,cAAc,GAAI,QAAO;EAE7B,IAAI,MAAM,IAAI,QAAQ,KAAK,UAAU;AACrC,MAAI,QAAQ,GAAI,OAAM,IAAI,QAAQ,KAAK,UAAU;AACjD,MAAI,QAAQ,GAAI,OAAM,IAAI;AAE1B,SAAO,IAAI,UAAU,WAAW,IAAI,IAAI;;;CAI1C,AAAQ,oBAAoB,QAAgB,UAA4B;EACtE,MAAM,iBAAiB,KAAK,OAAO,kBAAkB,SAAS;AAC9D,MAAI,eAAe,SAAS,EAC1B,QAAO,KACL;GACE,MAAM;GACN,SAAS,UAAU,OAAO;GAC1B;GACD,EACD,KACA,EAAE,OAAO,eAAe,KAAK,KAAK,EAAE,CACrC;AAEH,SAAO,KAAK;GAAE,MAAM;GAAK,SAAS;GAAa,EAAE,IAAI;;;CAIvD,QAAQ,OAAO,QAAoC;EACjD,MAAM,WAAW,KAAK,gBAAgB,IAAI,IAAI;EAC9C,MAAM,SAAS,IAAI;EAEnB,MAAM,QAAQ,KAAK,OAAO,MAAM,QAAQ,SAAS;AAEjD,MAAI,OAAO;AACT,GAAC,IAA2C,SAAS,MAAM;AAM3D,UAFgB,kBADM,CAAC,GAAG,KAAK,kBAAkB,GAAG,MAAM,WAAW,EACpB,MAAM,QAAQ,CAEhD,IAAI;;AAKrB,MAAI,WAAW,WAAW;GACxB,MAAM,iBAAiB,KAAK,OAAO,kBAAkB,SAAS;AAC9D,OAAI,eAAe,SAAS,GAAG;IAM7B,MAAM,kBAJW,KAAK,OAAO,MAC3B,eAAe,IACf,SACD,EACiC,cAAc,EAAE;IAClD,MAAM,gBAAgB,CAAC,GAAG,KAAK,kBAAkB,GAAG,gBAAgB;IAGpE,MAAM,uBACJ,IAAI,SAAS,MAAM;KACjB,QAAQ;KACR,SAAS,EAAE,OAAO,eAAe,KAAK,KAAK,EAAE;KAC9C,CAAC;AAGJ,WADgB,kBAAkB,eAAe,eAAe,CACjD,IAAI;;;AAKvB,MAAI,KAAK,iBAAiB,SAAS,EAIjC,QAHgB,kBAAkB,KAAK,wBACrC,KAAK,oBAAoB,QAAQ,SAAS,CAC3C,CACc,IAAI;AAGrB,SAAO,KAAK,oBAAoB,QAAQ,SAAS;;;CAInD,SAAS,OAA6B;AACpC,OAAK,OAAO,KAAK,MAAM;AACvB,OAAK,OAAO,SACV,MAAM,QACN,MAAM,MACN,MAAM,SACL,MAAM,cAAc,EAAE,CACxB;;;CAIH,UAAU,QAAyC;AACjD,OAAK,eAAe,CAAC,GAAG,OAAO,CAAC;;;CAIlC,YAAqD;AACnD,SAAO,KAAK,OAAO,WAAW;;;;;;;CAQhC,oBAAsC;AACpC,SAAO,KAAK"}
|