vafast 0.8.1 → 0.8.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +54 -2
- package/dist/{base-server-CE0mfqPY.mjs → base-server-CbnxNoNf.mjs} +1 -1
- package/dist/{base-server-CE0mfqPY.mjs.map → base-server-CbnxNoNf.mjs.map} +1 -1
- package/dist/{base-server-CeJ8LTBk.d.mts → base-server-DDNu97SA.d.mts} +2 -2
- package/dist/{base64url-Cc77f1ms.mjs → base64url-Cc25EVjw.mjs} +1 -1
- package/dist/{base64url-Cc77f1ms.mjs.map → base64url-Cc25EVjw.mjs.map} +1 -1
- package/dist/{base64url-CAmasWF0.d.mts → base64url-wYuxnrb0.d.mts} +1 -1
- package/dist/{component-route-CGbmQm5P.d.mts → component-route-BAaJKp8X.d.mts} +2 -2
- package/dist/{component-router-B6M5XDnp.mjs → component-router-wQc8auz9.mjs} +1 -1
- package/dist/{component-router-B6M5XDnp.mjs.map → component-router-wQc8auz9.mjs.map} +1 -1
- package/dist/{component-server-CKcXIvMg.d.mts → component-server-CVhgbxOe.d.mts} +4 -4
- package/dist/{component-server-O6IQwKam.mjs → component-server-llGj--XB.mjs} +6 -6
- package/dist/{component-server-O6IQwKam.mjs.map → component-server-llGj--XB.mjs.map} +1 -1
- package/dist/{contract-vSyKiRwz.mjs → contract-BIqqOwPd.mjs} +2 -2
- package/dist/{contract-vSyKiRwz.mjs.map → contract-BIqqOwPd.mjs.map} +1 -1
- package/dist/{contract-C-xQoZ6r.d.mts → contract-ChZaybx9.d.mts} +1 -1
- package/dist/{defineRoute-Rod-Vi6-.d.mts → defineRoute-DUYeO8TR.d.mts} +1 -1
- package/dist/defineRoute.d.mts +1 -1
- package/dist/defineRoute.mjs +6 -13
- package/dist/defineRoute.mjs.map +1 -1
- package/dist/{dependency-manager-mqzLAocb.d.mts → dependency-manager-BIjSc7j3.d.mts} +1 -1
- package/dist/{dependency-manager-ChW24ep8.mjs → dependency-manager-DaDThGEc.mjs} +1 -1
- package/dist/{dependency-manager-ChW24ep8.mjs.map → dependency-manager-DaDThGEc.mjs.map} +1 -1
- package/dist/{formats-Ca7ASaYH.d.mts → formats-B1vlpb1f.d.mts} +1 -1
- package/dist/{go-await-B1U27PgB.mjs → go-await-C4ZdEUwY.mjs} +1 -1
- package/dist/{go-await-B1U27PgB.mjs.map → go-await-C4ZdEUwY.mjs.map} +1 -1
- package/dist/{go-await-Dz1CRSTT.d.mts → go-await-KQIMkGZy.d.mts} +1 -1
- package/dist/{html-renderer-D1zzDVQM.mjs → html-renderer-DgYwJP_a.mjs} +1 -1
- package/dist/{html-renderer-D1zzDVQM.mjs.map → html-renderer-DgYwJP_a.mjs.map} +1 -1
- package/dist/{html-renderer-Up52eIS6.d.mts → html-renderer-ZY_sfcpi.d.mts} +1 -1
- package/dist/{index-CqwSjhXb.d.mts → index-DgIsC7ua.d.mts} +5 -5
- package/dist/index.d.mts +22 -21
- package/dist/index.mjs +17 -16
- package/dist/index.mjs.map +1 -1
- package/dist/middleware/component-router.d.mts +1 -1
- package/dist/middleware/component-router.mjs +1 -1
- package/dist/{middleware-BTg4GbjC.d.mts → middleware-B_fMMYr3.d.mts} +2 -2
- package/dist/middleware.d.mts +1 -1
- package/dist/middleware.mjs +2 -1
- package/dist/monitoring/index.d.mts +3 -3
- package/dist/monitoring/native-monitor.d.mts +3 -3
- package/dist/node-server/index.d.mts +1 -1
- package/dist/node-server/index.mjs +3 -3
- package/dist/node-server/request.mjs +1 -1
- package/dist/node-server/response.mjs +1 -1
- package/dist/node-server/serve.d.mts +1 -1
- package/dist/node-server/serve.mjs +2 -2
- package/dist/{parsers-CEkO18PZ.d.mts → parsers-B1dewjiN.d.mts} +1 -1
- package/dist/{parsers-BPBVKby-.mjs → parsers-DPKyyUv3.mjs} +1 -1
- package/dist/{parsers-BPBVKby-.mjs.map → parsers-DPKyyUv3.mjs.map} +1 -1
- package/dist/{path-matcher-Dp-DRUV9.mjs → path-matcher-BwpVrcVG.mjs} +1 -1
- package/dist/{path-matcher-Dp-DRUV9.mjs.map → path-matcher-BwpVrcVG.mjs.map} +1 -1
- package/dist/{radix-tree-dyn3qDFX.mjs → radix-tree-DPcGUi6x.mjs} +1 -1
- package/dist/{radix-tree-dyn3qDFX.mjs.map → radix-tree-DPcGUi6x.mjs.map} +1 -1
- package/dist/{request-D5oMj6sH.mjs → request-COqaDa-c.mjs} +1 -1
- package/dist/{request-D5oMj6sH.mjs.map → request-COqaDa-c.mjs.map} +1 -1
- package/dist/{response-BlHLmKys.mjs → response-BK2pYq43.mjs} +1 -1
- package/dist/{response-BlHLmKys.mjs.map → response-BK2pYq43.mjs.map} +1 -1
- package/dist/{response-Bs9GhJz-.mjs → response-BlkS--Fb.mjs} +9 -2
- package/dist/response-BlkS--Fb.mjs.map +1 -0
- package/dist/{response-lI0YZoia.d.mts → response-DsH8MnBO.d.mts} +2 -2
- package/dist/{route-registry-DmbGzvgn.d.mts → route-registry-B0q_pJDC.d.mts} +3 -3
- package/dist/{route-registry-DsPslV2b.mjs → route-registry-ClmI5VwE.mjs} +1 -1
- package/dist/{route-registry-DsPslV2b.mjs.map → route-registry-ClmI5VwE.mjs.map} +1 -1
- package/dist/router/index.mjs +1 -1
- package/dist/router/radix-tree.d.mts +1 -1
- package/dist/router/radix-tree.mjs +1 -1
- package/dist/{serve-Cj0bYhav.mjs → serve-CozN9Tlz.mjs} +3 -3
- package/dist/{serve-Cj0bYhav.mjs.map → serve-CozN9Tlz.mjs.map} +1 -1
- package/dist/{serve-Dw-GHkc3.d.mts → serve-Dh6Ux5Pg.d.mts} +1 -1
- package/dist/serve.d.mts +1 -1
- package/dist/serve.mjs +2 -2
- package/dist/server/base-server.d.mts +1 -1
- package/dist/server/base-server.mjs +1 -1
- package/dist/server/component-server.d.mts +1 -1
- package/dist/server/component-server.mjs +2 -2
- package/dist/server/index.d.mts +5 -5
- package/dist/server/index.mjs +7 -6
- package/dist/server/server-factory.d.mts +3 -3
- package/dist/server/server-factory.mjs +6 -5
- package/dist/server/server.d.mts +2 -2
- package/dist/server/server.mjs +3 -2
- package/dist/{server-u4TNtYHx.mjs → server-D-jaEU-V.mjs} +3 -3
- package/dist/{server-u4TNtYHx.mjs.map → server-D-jaEU-V.mjs.map} +1 -1
- package/dist/{server-B-X9ANNh.d.mts → server-Dr_qEIqu.d.mts} +4 -4
- package/dist/{server-B4YDcNJy.mjs → server-Dtk8M7eI.mjs} +5 -5
- package/dist/{server-B4YDcNJy.mjs.map → server-Dtk8M7eI.mjs.map} +1 -1
- package/dist/{sse-BqvrwmK6.d.mts → sse-CZnFOi2K.d.mts} +1 -1
- package/dist/types/component-route.d.mts +1 -1
- package/dist/types/index.d.mts +2 -2
- package/dist/types/types.d.mts +1 -1
- package/dist/{types-aczawQFE.d.mts → types-BaIZETLg.d.mts} +1 -1
- package/dist/utils/base64url.d.mts +1 -1
- package/dist/utils/base64url.mjs +1 -1
- package/dist/utils/contract.d.mts +1 -1
- package/dist/utils/contract.mjs +1 -1
- package/dist/utils/dependency-manager.d.mts +1 -1
- package/dist/utils/dependency-manager.mjs +1 -1
- package/dist/utils/formats.d.mts +1 -1
- package/dist/utils/go-await.d.mts +1 -1
- package/dist/utils/go-await.mjs +1 -1
- package/dist/utils/handle.mjs +1 -1
- package/dist/utils/html-renderer.d.mts +1 -1
- package/dist/utils/html-renderer.mjs +1 -1
- package/dist/utils/index.d.mts +14 -13
- package/dist/utils/index.mjs +11 -10
- package/dist/utils/parsers.d.mts +1 -1
- package/dist/utils/parsers.mjs +1 -1
- package/dist/utils/path-matcher.mjs +1 -1
- package/dist/utils/response.d.mts +1 -1
- package/dist/utils/response.mjs +2 -1
- package/dist/utils/route-registry.d.mts +2 -2
- package/dist/utils/route-registry.mjs +1 -1
- package/dist/utils/sse.d.mts +1 -1
- package/dist/utils/validators/validation-errors.d.mts +2 -0
- package/dist/utils/validators/validation-errors.mjs +3 -0
- package/dist/utils/validators/validators.d.mts +1 -1
- package/dist/utils/validators/validators.mjs +2 -1
- package/dist/validation-errors-BXOr6Rf2.d.mts +36 -0
- package/dist/validation-errors-j7DAULlG.mjs +38 -0
- package/dist/validation-errors-j7DAULlG.mjs.map +1 -0
- package/dist/{validators-CkfvNBbK.mjs → validators-DdEfUzCC.mjs} +60 -21
- package/dist/validators-DdEfUzCC.mjs.map +1 -0
- package/dist/{validators-D5KJCSZr.d.mts → validators-jkeNFN8V.d.mts} +7 -5
- package/package.json +1 -1
- package/dist/response-Bs9GhJz-.mjs.map +0 -1
- package/dist/validators-CkfvNBbK.mjs.map +0 -1
- /package/dist/{component-route-DdKFowzp.mjs → component-route-B6jaTxLT.mjs} +0 -0
- /package/dist/{index-D4DUvwPf.d.mts → index-1-AkepUA.d.mts} +0 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"go-await-
|
|
1
|
+
{"version":3,"file":"go-await-C4ZdEUwY.mjs","names":[],"sources":["../src/utils/go-await.ts"],"sourcesContent":["/**\n * Go 风格的错误处理工具\n * 将 Promise 转换为 [Error | null, T | undefined] 格式\n *\n * @author Framework Team\n * @version 1.0.0\n * @license MIT\n */\n\n/**\n * Go 风格的错误处理工具\n * 将 Promise 转换为 [Error | null, T | undefined] 格式\n *\n * @param promise 要处理的 Promise\n * @returns [Error | null, T | undefined] 元组,第一个元素是错误,第二个是结果\n *\n * @example\n * ```typescript\n * const [error, result] = await goAwait(someAsyncFunction());\n * if (error) {\n * console.error(\"操作失败:\", error);\n * } else {\n * console.log(\"操作成功:\", result);\n * }\n * ```\n */\nexport function goAwait<T>(\n promise: Promise<T>,\n): Promise<[Error | null, T | undefined]> {\n return promise\n .then<[null, T]>((data) => [null, data])\n .catch<\n [Error, undefined]\n >((err) => [err instanceof Error ? err : new Error(String(err)), undefined]);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AA0BA,SAAgB,QACd,SACwC;AACxC,QAAO,QACJ,MAAiB,SAAS,CAAC,MAAM,KAAK,CAAC,CACvC,OAEE,QAAQ,CAAC,eAAe,QAAQ,MAAM,IAAI,MAAM,OAAO,IAAI,CAAC,EAAE,OAAU,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"html-renderer-
|
|
1
|
+
{"version":3,"file":"html-renderer-DgYwJP_a.mjs","names":[],"sources":["../src/utils/html-renderer.ts"],"sourcesContent":["/**\n * HTML渲染工具类\n * 提供统一的HTML模板生成功能\n */\nexport class HtmlRenderer {\n /**\n * 生成基础HTML模板\n */\n static generateBaseHtml(\n content: string,\n context: any,\n clientScriptPath: string = \"/client.js\",\n ): string {\n return `\n <!doctype html>\n <html>\n <head>\n <meta charset=\"utf-8\">\n <title>Vafast SSR App</title>\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n </head>\n <body>\n <div id=\"app\">${content}</div>\n <script>\n window.__ROUTE_INFO__ = {\n params: ${JSON.stringify(context.params || {})},\n query: ${JSON.stringify(context.query || {})},\n pathname: '${context.pathname}'\n };\n </script>\n <script type=\"module\" src=\"${clientScriptPath}\"></script>\n </body>\n </html>\n `;\n }\n\n /**\n * 生成Vue组件HTML\n */\n static generateVueHtml(\n content: string,\n context: any,\n clientScriptPath: string = \"/client.js\",\n ): string {\n return this.generateBaseHtml(content, context, clientScriptPath);\n }\n\n /**\n * 生成React组件HTML\n */\n static generateReactHtml(\n content: string,\n context: any,\n clientScriptPath: string = \"/client.js\",\n ): string {\n return `\n <!doctype html>\n <html>\n <head>\n <meta charset=\"utf-8\">\n <title>Vafast SSR App</title>\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n </head>\n <body>\n <div id=\"root\">${content}</div>\n <script>\n window.__ROUTE_INFO__ = {\n params: ${JSON.stringify(context.params || {})},\n query: ${JSON.stringify(context.query || {})},\n pathname: '${context.pathname}'\n };\n </script>\n <script type=\"module\" src=\"${clientScriptPath}\"></script>\n </body>\n </html>\n `;\n }\n}\n"],"mappings":";;;;;AAIA,IAAa,eAAb,MAA0B;;;;CAIxB,OAAO,iBACL,SACA,SACA,mBAA2B,cACnB;AACR,SAAO;;;;;;;;;0BASe,QAAQ;;;wBAGV,KAAK,UAAU,QAAQ,UAAU,EAAE,CAAC,CAAC;uBACtC,KAAK,UAAU,QAAQ,SAAS,EAAE,CAAC,CAAC;2BAChC,QAAQ,SAAS;;;uCAGL,iBAAiB;;;;;;;;CAStD,OAAO,gBACL,SACA,SACA,mBAA2B,cACnB;AACR,SAAO,KAAK,iBAAiB,SAAS,SAAS,iBAAiB;;;;;CAMlE,OAAO,kBACL,SACA,SACA,mBAA2B,cACnB;AACR,SAAO;;;;;;;;;2BASgB,QAAQ;;;wBAGX,KAAK,UAAU,QAAQ,UAAU,EAAE,CAAC,CAAC;uBACtC,KAAK,UAAU,QAAQ,SAAS,EAAE,CAAC,CAAC;2BAChC,QAAQ,SAAS;;;uCAGL,iBAAiB"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { a as ProcessedRoute } from "./defineRoute-
|
|
2
|
-
import { r as NestedComponentRoute, t as ComponentRoute } from "./component-route-
|
|
3
|
-
import { t as Server } from "./server-
|
|
4
|
-
import { t as ComponentServer } from "./component-server-
|
|
1
|
+
import { a as ProcessedRoute } from "./defineRoute-DUYeO8TR.mjs";
|
|
2
|
+
import { r as NestedComponentRoute, t as ComponentRoute } from "./component-route-BAaJKp8X.mjs";
|
|
3
|
+
import { t as Server } from "./server-Dr_qEIqu.mjs";
|
|
4
|
+
import { t as ComponentServer } from "./component-server-CVhgbxOe.mjs";
|
|
5
5
|
|
|
6
6
|
//#region src/server/server-factory.d.ts
|
|
7
7
|
|
|
@@ -45,4 +45,4 @@ declare class ServerFactory {
|
|
|
45
45
|
}
|
|
46
46
|
//#endregion
|
|
47
47
|
export { ServerFactory as t };
|
|
48
|
-
//# sourceMappingURL=index-
|
|
48
|
+
//# sourceMappingURL=index-DgIsC7ua.d.mts.map
|
package/dist/index.d.mts
CHANGED
|
@@ -1,25 +1,26 @@
|
|
|
1
|
-
import { _ as sse, a as ProcessedRoute, c as RoutesWithSource, d as SSEGeneratorType, f as SSEMeta, g as defineRoutes, h as defineRoute, i as NestedRouteConfig, l as SSEEventType, m as defineMiddleware, n as InferableRoute, o as Route, p as TypedMiddleware, r as LeafRouteConfig, s as RouteSchema, t as HandlerContext, u as SSEEventWithMeta, v as withContext } from "./defineRoute-
|
|
2
|
-
import { a as VafastRequest, i as ResponseBody, n as Method, r as Middleware, t as Handler } from "./types-
|
|
3
|
-
import { n as FlattenedComponentRoute, r as NestedComponentRoute, t as ComponentRoute } from "./component-route-
|
|
4
|
-
import { t as BaseServer } from "./base-server-
|
|
5
|
-
import { t as Server } from "./server-
|
|
6
|
-
import { t as DependencyManager } from "./dependency-manager-
|
|
7
|
-
import { t as ComponentServer } from "./component-server-
|
|
8
|
-
import { t as ServerFactory } from "./index-
|
|
9
|
-
import { a as isVafastError, i as errorHandler, n as VafastError, r as composeMiddleware, t as VAFAST_ERROR_SYMBOL } from "./middleware-
|
|
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-
|
|
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-
|
|
12
|
-
import { t as goAwait } from "./go-await-
|
|
13
|
-
import { n as base64urlEncode, t as base64urlDecode } from "./base64url-
|
|
14
|
-
import { t as HtmlRenderer } from "./html-renderer-
|
|
15
|
-
import { a as
|
|
16
|
-
import {
|
|
17
|
-
import { t as
|
|
18
|
-
import {
|
|
19
|
-
import { n as
|
|
1
|
+
import { _ as sse, a as ProcessedRoute, c as RoutesWithSource, d as SSEGeneratorType, f as SSEMeta, g as defineRoutes, h as defineRoute, i as NestedRouteConfig, l as SSEEventType, m as defineMiddleware, n as InferableRoute, o as Route, p as TypedMiddleware, r as LeafRouteConfig, s as RouteSchema, t as HandlerContext, u as SSEEventWithMeta, v as withContext } from "./defineRoute-DUYeO8TR.mjs";
|
|
2
|
+
import { a as VafastRequest, i as ResponseBody, n as Method, r as Middleware, t as Handler } from "./types-BaIZETLg.mjs";
|
|
3
|
+
import { n as FlattenedComponentRoute, r as NestedComponentRoute, t as ComponentRoute } from "./component-route-BAaJKp8X.mjs";
|
|
4
|
+
import { t as BaseServer } from "./base-server-DDNu97SA.mjs";
|
|
5
|
+
import { t as Server } from "./server-Dr_qEIqu.mjs";
|
|
6
|
+
import { t as DependencyManager } from "./dependency-manager-BIjSc7j3.mjs";
|
|
7
|
+
import { t as ComponentServer } from "./component-server-CVhgbxOe.mjs";
|
|
8
|
+
import { t as ServerFactory } from "./index-DgIsC7ua.mjs";
|
|
9
|
+
import { a as isVafastError, i as errorHandler, n as VafastError, r as composeMiddleware, t as VAFAST_ERROR_SYMBOL } from "./middleware-B_fMMYr3.mjs";
|
|
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-B1dewjiN.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-DsH8MnBO.mjs";
|
|
12
|
+
import { t as goAwait } from "./go-await-KQIMkGZy.mjs";
|
|
13
|
+
import { n as base64urlEncode, t as base64urlDecode } from "./base64url-wYuxnrb0.mjs";
|
|
14
|
+
import { t as HtmlRenderer } from "./html-renderer-ZY_sfcpi.mjs";
|
|
15
|
+
import { a as createErrorDetail, i as ValidationFailedError, n as SchemaLocation, o as isValidationFailedError, r as VALIDATION_FAILED_SYMBOL, s as pathToField, t as ErrorDetail } from "./validation-errors-BXOr6Rf2.mjs";
|
|
16
|
+
import { a as ValidationError, c as getValidatorCacheStats, d as validateFast, f as validateSchema, l as precompileSchemas, o as ValidationResult, p as validateSchemaOrThrow, s as createValidator, t as SchemaConfig, u as validateAllSchemas } from "./validators-jkeNFN8V.mjs";
|
|
17
|
+
import { i as registerFormats, n as hasFormat, r as registerFormat, t as Patterns } from "./formats-B1vlpb1f.mjs";
|
|
18
|
+
import { t as SSEEvent } from "./sse-CZnFOi2K.mjs";
|
|
19
|
+
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-B0q_pJDC.mjs";
|
|
20
|
+
import { n as generateAITools, r as getApiSpec, t as ApiSpec } from "./contract-ChZaybx9.mjs";
|
|
20
21
|
import "./utils/index.mjs";
|
|
21
22
|
import { normalizePath } from "./router.mjs";
|
|
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-
|
|
23
|
+
import { a as ServeResult, c as serve, i as ServeOptions, n as GracefulShutdownOptions, o as TrustProxyOption, r as RequestTimeoutOptions, t as FetchHandler } from "./serve-Dh6Ux5Pg.mjs";
|
|
23
24
|
import "./serve.mjs";
|
|
24
25
|
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, SSEEventType, SSEEventWithMeta, SSEGeneratorType, SSEMeta, SchemaConfig, type ServeOptions, type ServeResult, Server, ServerFactory, type TrustProxyOption, Type, TypedMiddleware, VAFAST_ERROR_SYMBOL, VafastError, VafastRequest, ValidationError, ValidationResult, base64urlDecode, base64urlEncode, composeMiddleware, createRouteRegistry, 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, sse, stream, text, validateAllSchemas, validateFast, validateSchema, validateSchemaOrThrow, withContext };
|
|
26
|
+
export { ApiSpec, BaseServer, ComponentRoute, ComponentServer, DependencyManager, ErrorDetail, 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, SSEEventType, SSEEventWithMeta, SSEGeneratorType, SSEMeta, SchemaConfig, SchemaLocation, type ServeOptions, type ServeResult, Server, ServerFactory, type TrustProxyOption, Type, TypedMiddleware, VAFAST_ERROR_SYMBOL, VALIDATION_FAILED_SYMBOL, VafastError, VafastRequest, ValidationError, ValidationFailedError, ValidationResult, base64urlDecode, base64urlEncode, composeMiddleware, createErrorDetail, createRouteRegistry, createValidator, defineMiddleware, defineRoute, defineRoutes, empty, err, errorHandler, filterRoutes, generateAITools, getAllRoutes, getApiSpec, getCookie, getHeader, getRoute, getRouteRegistry, getRoutesByMethod, getValidatorCacheStats, goAwait, hasFormat, html, isVafastError, isValidationFailedError, json, normalizePath, parseBody, parseCookies, parseCookiesFast, parseHeaders, parseQuery, parseQueryFast, pathToField, precompileSchemas, redirect, registerFormat, registerFormats, serve, sse, stream, text, validateAllSchemas, validateFast, validateSchema, validateSchemaOrThrow, withContext };
|
package/dist/index.mjs
CHANGED
|
@@ -1,23 +1,24 @@
|
|
|
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-
|
|
2
|
-
import { a as
|
|
3
|
-
import {
|
|
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-DPKyyUv3.mjs";
|
|
2
|
+
import { a as pathToField, i as isValidationFailedError, n as ValidationFailedError, r as createErrorDetail, t as VALIDATION_FAILED_SYMBOL } from "./validation-errors-j7DAULlG.mjs";
|
|
3
|
+
import { a as validateAllSchemas, c as validateSchemaOrThrow, i as precompileSchemas, n as createValidator, o as validateFast, r as getValidatorCacheStats, s as validateSchema } from "./validators-DdEfUzCC.mjs";
|
|
4
|
+
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-BlkS--Fb.mjs";
|
|
4
5
|
import { defineMiddleware, defineRoute, defineRoutes, sse, withContext } from "./defineRoute.mjs";
|
|
5
|
-
import { t as BaseServer } from "./base-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-
|
|
7
|
-
import { t as Server } from "./server-
|
|
8
|
-
import { t as HtmlRenderer } from "./html-renderer-
|
|
9
|
-
import { t as DependencyManager } from "./dependency-manager-
|
|
10
|
-
import { t as ComponentServer } from "./component-server-
|
|
11
|
-
import { t as ServerFactory } from "./server-
|
|
12
|
-
import { t as goAwait } from "./go-await-
|
|
13
|
-
import { n as base64urlEncode, t as base64urlDecode } from "./base64url-
|
|
6
|
+
import { t as BaseServer } from "./base-server-CbnxNoNf.mjs";
|
|
7
|
+
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-ClmI5VwE.mjs";
|
|
8
|
+
import { t as Server } from "./server-Dtk8M7eI.mjs";
|
|
9
|
+
import { t as HtmlRenderer } from "./html-renderer-DgYwJP_a.mjs";
|
|
10
|
+
import { t as DependencyManager } from "./dependency-manager-DaDThGEc.mjs";
|
|
11
|
+
import { t as ComponentServer } from "./component-server-llGj--XB.mjs";
|
|
12
|
+
import { t as ServerFactory } from "./server-D-jaEU-V.mjs";
|
|
13
|
+
import { t as goAwait } from "./go-await-C4ZdEUwY.mjs";
|
|
14
|
+
import { n as base64urlEncode, t as base64urlDecode } from "./base64url-Cc25EVjw.mjs";
|
|
14
15
|
import { Patterns, hasFormat, registerFormat, registerFormats } from "./utils/formats.mjs";
|
|
15
|
-
import { n as getApiSpec, t as generateAITools } from "./contract-
|
|
16
|
+
import { n as getApiSpec, t as generateAITools } from "./contract-BIqqOwPd.mjs";
|
|
16
17
|
import "./utils/index.mjs";
|
|
17
18
|
import { normalizePath } from "./router.mjs";
|
|
18
19
|
import "./types/index.mjs";
|
|
19
|
-
import "./request-
|
|
20
|
-
import { n as serve } from "./serve-
|
|
20
|
+
import "./request-COqaDa-c.mjs";
|
|
21
|
+
import { n as serve } from "./serve-CozN9Tlz.mjs";
|
|
21
22
|
import "./serve.mjs";
|
|
22
23
|
import { FormatRegistry, Type } from "@sinclair/typebox";
|
|
23
24
|
|
|
@@ -25,5 +26,5 @@ import { FormatRegistry, Type } from "@sinclair/typebox";
|
|
|
25
26
|
registerFormats();
|
|
26
27
|
|
|
27
28
|
//#endregion
|
|
28
|
-
export { BaseServer, ComponentServer, DependencyManager, FormatRegistry, HtmlRenderer, Patterns, RouteRegistry, Server, ServerFactory, Type, VAFAST_ERROR_SYMBOL, VafastError, base64urlDecode, base64urlEncode, composeMiddleware, createRouteRegistry, 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, sse, stream, text, validateAllSchemas, validateFast, validateSchema, validateSchemaOrThrow, withContext };
|
|
29
|
+
export { BaseServer, ComponentServer, DependencyManager, FormatRegistry, HtmlRenderer, Patterns, RouteRegistry, Server, ServerFactory, Type, VAFAST_ERROR_SYMBOL, VALIDATION_FAILED_SYMBOL, VafastError, ValidationFailedError, base64urlDecode, base64urlEncode, composeMiddleware, createErrorDetail, createRouteRegistry, createValidator, defineMiddleware, defineRoute, defineRoutes, empty, err, errorHandler, filterRoutes, generateAITools, getAllRoutes, getApiSpec, getCookie, getHeader, getRoute, getRouteRegistry, getRoutesByMethod, getValidatorCacheStats, goAwait, hasFormat, html, isVafastError, isValidationFailedError, json, normalizePath, parseBody, parseCookies, parseCookiesFast, parseHeaders, parseQuery, parseQueryFast, pathToField, precompileSchemas, redirect, registerFormat, registerFormats, serve, sse, stream, text, validateAllSchemas, validateFast, validateSchema, validateSchemaOrThrow, withContext };
|
|
29
30
|
//# sourceMappingURL=index.mjs.map
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../src/index.ts"],"sourcesContent":["export * from \"./server\";\nexport * from \"./middleware\";\nexport * from \"./utils\";\nexport * from \"./router\";\nexport * from \"./defineRoute\";\nexport * from \"./types\";\n\n// 统一的 serve 函数\nexport { serve } from \"./serve\";\nexport type {\n ServeOptions,\n ServeResult,\n FetchHandler,\n GracefulShutdownOptions,\n RequestTimeoutOptions,\n TrustProxyOption,\n} from \"./serve\";\n\n// 重新导出 TypeBox 类型\nexport { Type, FormatRegistry } from \"@sinclair/typebox\";\n\n// 自动注册内置 format 验证器\nimport { registerFormats } from \"./utils/formats\";\nregisterFormats();\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../src/index.ts"],"sourcesContent":["export * from \"./server\";\nexport * from \"./middleware\";\nexport * from \"./utils\";\nexport * from \"./router\";\nexport * from \"./defineRoute\";\nexport * from \"./types\";\n\n// 统一的 serve 函数\nexport { serve } from \"./serve\";\nexport type {\n ServeOptions,\n ServeResult,\n FetchHandler,\n GracefulShutdownOptions,\n RequestTimeoutOptions,\n TrustProxyOption,\n} from \"./serve\";\n\n// 重新导出 TypeBox 类型\nexport { Type, FormatRegistry } from \"@sinclair/typebox\";\n\n// 自动注册内置 format 验证器\nimport { registerFormats } from \"./utils/formats\";\nregisterFormats();\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;AAuBA,iBAAiB"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { n as FlattenedComponentRoute, r as NestedComponentRoute, t as ComponentRoute } from "../component-route-
|
|
1
|
+
import { n as FlattenedComponentRoute, r as NestedComponentRoute, t as ComponentRoute } from "../component-route-BAaJKp8X.mjs";
|
|
2
2
|
|
|
3
3
|
//#region src/middleware/component-router.d.ts
|
|
4
4
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { r as Middleware, t as Handler } from "./types-
|
|
1
|
+
import { r as Middleware, t as Handler } from "./types-BaIZETLg.mjs";
|
|
2
2
|
|
|
3
3
|
//#region src/middleware.d.ts
|
|
4
4
|
|
|
@@ -48,4 +48,4 @@ declare function composeMiddleware(middleware: Middleware[], finalHandler: Handl
|
|
|
48
48
|
declare const errorHandler: Middleware;
|
|
49
49
|
//#endregion
|
|
50
50
|
export { isVafastError as a, errorHandler as i, VafastError as n, composeMiddleware as r, VAFAST_ERROR_SYMBOL as t };
|
|
51
|
-
//# sourceMappingURL=middleware-
|
|
51
|
+
//# sourceMappingURL=middleware-B_fMMYr3.d.mts.map
|
package/dist/middleware.d.mts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as isVafastError, i as errorHandler, n as VafastError, r as composeMiddleware, t as VAFAST_ERROR_SYMBOL } from "./middleware-
|
|
1
|
+
import { a as isVafastError, i as errorHandler, n as VafastError, r as composeMiddleware, t as VAFAST_ERROR_SYMBOL } from "./middleware-B_fMMYr3.mjs";
|
|
2
2
|
export { VAFAST_ERROR_SYMBOL, VafastError, composeMiddleware, errorHandler, isVafastError };
|
package/dist/middleware.mjs
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import "./validation-errors-j7DAULlG.mjs";
|
|
2
|
+
import { d as composeMiddleware, f as errorHandler, l as VAFAST_ERROR_SYMBOL, p as isVafastError, u as VafastError } from "./response-BlkS--Fb.mjs";
|
|
2
3
|
|
|
3
4
|
export { VAFAST_ERROR_SYMBOL, VafastError, composeMiddleware, errorHandler, isVafastError };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import "../defineRoute-
|
|
2
|
-
import "../server-
|
|
3
|
-
import "../index-
|
|
1
|
+
import "../defineRoute-DUYeO8TR.mjs";
|
|
2
|
+
import "../server-Dr_qEIqu.mjs";
|
|
3
|
+
import "../index-DgIsC7ua.mjs";
|
|
4
4
|
import { MemoryInfo, MonitoredServer, MonitoringConfig, MonitoringMetrics, MonitoringStatus, PathStats, StatusCodeDistribution, TimeWindowStats, createMonitoredServer, withMonitoring } from "./native-monitor.mjs";
|
|
5
5
|
export { type MemoryInfo, type MonitoredServer, type MonitoringConfig, type MonitoringMetrics, type MonitoringStatus, type PathStats, type StatusCodeDistribution, type TimeWindowStats, createMonitoredServer, withMonitoring };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import "../defineRoute-
|
|
2
|
-
import { t as Server } from "../server-
|
|
3
|
-
import "../index-
|
|
1
|
+
import "../defineRoute-DUYeO8TR.mjs";
|
|
2
|
+
import { t as Server } from "../server-Dr_qEIqu.mjs";
|
|
3
|
+
import "../index-DgIsC7ua.mjs";
|
|
4
4
|
|
|
5
5
|
//#region src/monitoring/native-monitor.d.ts
|
|
6
6
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as ServeResult, c as serve, i as ServeOptions, n as GracefulShutdownOptions, o as TrustProxyOption, r as RequestTimeoutOptions, s as createAdaptorServer, t as FetchHandler } from "../serve-
|
|
1
|
+
import { a as ServeResult, c as serve, i as ServeOptions, n as GracefulShutdownOptions, o as TrustProxyOption, r as RequestTimeoutOptions, s as createAdaptorServer, t as FetchHandler } from "../serve-Dh6Ux5Pg.mjs";
|
|
2
2
|
import { createProxyRequest } from "./request.mjs";
|
|
3
3
|
import { writeResponse, writeResponseSimple } from "./response.mjs";
|
|
4
4
|
export { type FetchHandler, type GracefulShutdownOptions, type RequestTimeoutOptions, type ServeOptions, type ServeResult, type TrustProxyOption, createAdaptorServer, createProxyRequest, serve, writeResponse, writeResponseSimple };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { t as createProxyRequest } from "../request-
|
|
2
|
-
import { n as writeResponseSimple, t as writeResponse } from "../response-
|
|
3
|
-
import { n as serve, t as createAdaptorServer } from "../serve-
|
|
1
|
+
import { t as createProxyRequest } from "../request-COqaDa-c.mjs";
|
|
2
|
+
import { n as writeResponseSimple, t as writeResponse } from "../response-BK2pYq43.mjs";
|
|
3
|
+
import { n as serve, t as createAdaptorServer } from "../serve-CozN9Tlz.mjs";
|
|
4
4
|
|
|
5
5
|
export { createAdaptorServer, createProxyRequest, serve, writeResponse, writeResponseSimple };
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as ServeResult, c as serve, i as ServeOptions, n as GracefulShutdownOptions, o as TrustProxyOption, r as RequestTimeoutOptions, s as createAdaptorServer, t as FetchHandler } from "../serve-
|
|
1
|
+
import { a as ServeResult, c as serve, i as ServeOptions, n as GracefulShutdownOptions, o as TrustProxyOption, r as RequestTimeoutOptions, s as createAdaptorServer, t as FetchHandler } from "../serve-Dh6Ux5Pg.mjs";
|
|
2
2
|
export { FetchHandler, GracefulShutdownOptions, RequestTimeoutOptions, ServeOptions, ServeResult, TrustProxyOption, createAdaptorServer, serve };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import "../request-
|
|
2
|
-
import { n as serve, t as createAdaptorServer } from "../serve-
|
|
1
|
+
import "../request-COqaDa-c.mjs";
|
|
2
|
+
import { n as serve, t as createAdaptorServer } from "../serve-CozN9Tlz.mjs";
|
|
3
3
|
|
|
4
4
|
export { createAdaptorServer, serve };
|
|
@@ -79,4 +79,4 @@ declare function parseCookiesFast(req: Request): Record<string, string>;
|
|
|
79
79
|
declare function getCookie(req: Request, name: string): string | null;
|
|
80
80
|
//#endregion
|
|
81
81
|
export { parseBody as a, parseCookiesFast as c, parseHeaders as d, parseQuery as f, getHeader as i, parseFile as l, FormData as n, parseBodyAs as o, parseQueryFast as p, getCookie as r, parseCookies as s, FileInfo as t, parseFormData as u };
|
|
82
|
-
//# sourceMappingURL=parsers-
|
|
82
|
+
//# sourceMappingURL=parsers-B1dewjiN.d.mts.map
|
|
@@ -196,4 +196,4 @@ function getCookie(req, name) {
|
|
|
196
196
|
|
|
197
197
|
//#endregion
|
|
198
198
|
export { parseCookies as a, parseFormData as c, parseQueryFast as d, parseBodyAs as i, parseHeaders as l, getHeader as n, parseCookiesFast as o, parseBody as r, parseFile as s, getCookie as t, parseQuery as u };
|
|
199
|
-
//# sourceMappingURL=parsers-
|
|
199
|
+
//# sourceMappingURL=parsers-DPKyyUv3.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"parsers-BPBVKby-.mjs","names":[],"sources":["../src/utils/parsers.ts"],"sourcesContent":["// src/parsers.ts\nimport qs from \"qs\";\nimport cookie from \"cookie\";\n\n// 文件信息接口\nexport interface FileInfo {\n name: string;\n type: string;\n size: number;\n data: ArrayBuffer;\n}\n\n// 表单数据接口\nexport interface FormData {\n fields: Record<string, string>;\n files: Record<string, FileInfo>;\n}\n\n/**\n * 简化的请求体解析函数\n * 优先简洁性,处理最常见的场景\n * \n * 注意:此函数假设只用于有 body 的请求(POST/PUT/PATCH 等)\n * 对于 GET/HEAD 请求,调用方应在调用前检查请求方法\n * 参考:defineRoute.ts 中已有正确的检查逻辑\n * \n * 如果传入 GET/HEAD 请求,会返回 null(防御性编程)\n * 这样即使调用方忘记检查,也不会导致运行时错误\n */\nexport async function parseBody(req: Request): Promise<unknown> {\n // 防御性检查:GET/HEAD 请求没有 body\n // HTTP 规范:GET/HEAD 请求通常不带 body,即使带了 Content-Type 也不应解析\n // 参考 Fastify: \"for GET and HEAD requests, the payload is never parsed\"\n const method = req.method.toUpperCase();\n if (method === \"GET\" || method === \"HEAD\") {\n return null;\n }\n\n const contentType = req.headers.get(\"content-type\") || \"\";\n if (contentType.includes(\"application/json\")) {\n return await req.json();\n }\n if (contentType.includes(\"application/x-www-form-urlencoded\")) {\n const text = await req.text();\n return Object.fromEntries(new URLSearchParams(text));\n }\n if (contentType.includes(\"multipart/form-data\")) {\n return await parseMultipartFormData(req);\n }\n return await req.text(); // fallback\n}\n\n/**\n * 解析 multipart/form-data 格式\n * 支持文件上传和普通表单字段\n */\nasync function parseMultipartFormData(req: Request): Promise<FormData> {\n const formData = await req.formData();\n const result: FormData = {\n fields: {},\n files: {},\n };\n\n for (const [key, value] of formData.entries()) {\n if (\n typeof value === \"object\" &&\n value !== null &&\n \"name\" in value &&\n \"type\" in value &&\n \"size\" in value\n ) {\n // 处理文件\n const file = value as any;\n const arrayBuffer = await file.arrayBuffer();\n result.files[key] = {\n name: file.name,\n type: file.type,\n size: file.size,\n data: arrayBuffer,\n };\n } else {\n // 处理普通字段\n result.fields[key] = value as string;\n }\n }\n\n return result;\n}\n\n/**\n * 解析请求体为特定类型\n * 提供类型安全的解析方法\n */\nexport async function parseBodyAs<T>(req: Request): Promise<T> {\n const body = await parseBody(req);\n return body as T;\n}\n\n/**\n * 解析请求体为表单数据\n * 专门用于处理 multipart/form-data\n * \n * 支持的 HTTP 方法:\n * - POST:创建新资源(最常用)\n * - PUT:替换指定资源\n * - PATCH:部分更新(较少用于文件上传)\n * \n * 注意:GET/HEAD 请求没有 body,调用此函数会抛出错误\n */\nexport async function parseFormData(req: Request): Promise<FormData> {\n // 防御性检查:GET/HEAD 请求没有 body\n // HTTP 规范:GET/HEAD 请求不应有 body\n // 文件上传通常使用 POST(新建)或 PUT(替换)\n const method = req.method.toUpperCase();\n if (method === \"GET\" || method === \"HEAD\") {\n throw new Error(\"GET/HEAD 请求不能包含表单数据\");\n }\n\n const contentType = req.headers.get(\"content-type\") || \"\";\n\n if (!contentType.includes(\"multipart/form-data\")) {\n throw new Error(\"请求不是 multipart/form-data 格式\");\n }\n\n return await parseMultipartFormData(req);\n}\n\n/**\n * 解析请求体为文件\n * 专门用于处理文件上传\n * \n * 支持的 HTTP 方法:\n * - POST:上传新文件,服务器决定存储位置(最常用)\n * - PUT:上传到指定位置,或替换已有文件\n * \n * 业界实践参考:\n * - AWS S3、阿里云 OSS 等主流云存储都支持 POST 和 PUT\n * - POST 用于表单上传(multipart),PUT 用于直接上传(binary)\n * \n * 注意:GET/HEAD 请求没有 body,调用此函数会抛出错误\n */\nexport async function parseFile(req: Request): Promise<FileInfo> {\n // 防御性检查:GET/HEAD 请求没有 body\n // HTTP 规范:GET/HEAD 请求不应有 body\n const method = req.method.toUpperCase();\n if (method === \"GET\" || method === \"HEAD\") {\n throw new Error(\"GET/HEAD 请求不能包含文件\");\n }\n\n const contentType = req.headers.get(\"content-type\") || \"\";\n\n if (!contentType.includes(\"multipart/form-data\")) {\n throw new Error(\"请求不是 multipart/form-data 格式\");\n }\n\n const formData = await parseMultipartFormData(req);\n const fileKeys = Object.keys(formData.files);\n\n if (fileKeys.length === 0) {\n throw new Error(\"请求中没有文件\");\n }\n\n if (fileKeys.length > 1) {\n throw new Error(\"请求中包含多个文件,请使用 parseFormData\");\n }\n\n return formData.files[fileKeys[0]];\n}\n\n/**\n * 快速提取 query string(避免创建 URL 对象)\n */\nfunction extractQueryString(url: string): string {\n const qIndex = url.indexOf(\"?\");\n if (qIndex === -1) return \"\";\n\n const hashIndex = url.indexOf(\"#\", qIndex);\n return hashIndex === -1\n ? url.substring(qIndex + 1)\n : url.substring(qIndex + 1, hashIndex);\n}\n\n/** 获取查询字符串,直接返回对象 */\nexport function parseQuery(req: Request): Record<string, unknown> {\n const queryString = extractQueryString(req.url);\n if (!queryString) return {};\n return qs.parse(queryString);\n}\n\n/**\n * 快速解析简单查询字符串(不支持嵌套,但更快)\n * 适用于简单的 key=value&key2=value2 场景\n */\nexport function parseQueryFast(req: Request): Record<string, string> {\n const queryString = extractQueryString(req.url);\n if (!queryString) return {};\n\n const result: Record<string, string> = Object.create(null);\n const pairs = queryString.split(\"&\");\n\n for (const pair of pairs) {\n const eqIndex = pair.indexOf(\"=\");\n if (eqIndex === -1) {\n result[decodeURIComponent(pair)] = \"\";\n } else {\n const key = decodeURIComponent(pair.substring(0, eqIndex));\n const value = decodeURIComponent(pair.substring(eqIndex + 1));\n result[key] = value;\n }\n }\n\n return result;\n}\n\n/** 解析请求头,返回对象 */\nexport function parseHeaders(req: Request): Record<string, string> {\n const headers: Record<string, string> = Object.create(null);\n req.headers.forEach((value, key) => {\n headers[key] = value;\n });\n return headers;\n}\n\n/**\n * 获取单个请求头(避免解析全部)\n */\nexport function getHeader(req: Request, name: string): string | null {\n return req.headers.get(name);\n}\n\n/** 使用cookie库解析Cookie,保证可靠性 */\nexport function parseCookies(req: Request): Record<string, string> {\n const cookieHeader = req.headers.get(\"cookie\");\n if (!cookieHeader) return {};\n\n try {\n const parsed = cookie.parse(cookieHeader);\n // 过滤掉undefined和null值\n const result: Record<string, string> = {};\n for (const [key, value] of Object.entries(parsed)) {\n if (value !== undefined && value !== null) {\n result[key] = value;\n }\n }\n return result;\n } catch {\n return {};\n }\n}\n\n/**\n * 快速解析 Cookie(简化版,不使用外部库)\n * 适用于简单的 cookie 场景\n */\nexport function parseCookiesFast(req: Request): Record<string, string> {\n const cookieHeader = req.headers.get(\"cookie\");\n if (!cookieHeader) return {};\n\n const result: Record<string, string> = Object.create(null);\n const pairs = cookieHeader.split(\";\");\n\n for (const pair of pairs) {\n const trimmed = pair.trim();\n const eqIndex = trimmed.indexOf(\"=\");\n if (eqIndex > 0) {\n const key = trimmed.substring(0, eqIndex).trim();\n const value = trimmed.substring(eqIndex + 1).trim();\n // 移除引号\n result[key] =\n value.startsWith('\"') && value.endsWith('\"')\n ? value.slice(1, -1)\n : value;\n }\n }\n\n return result;\n}\n\n/**\n * 获取单个 Cookie 值(避免解析全部)\n */\nexport function getCookie(req: Request, name: string): string | null {\n const cookieHeader = req.headers.get(\"cookie\");\n if (!cookieHeader) return null;\n\n const prefix = `${name}=`;\n const pairs = cookieHeader.split(\";\");\n\n for (const pair of pairs) {\n const trimmed = pair.trim();\n if (trimmed.startsWith(prefix)) {\n const value = trimmed.substring(prefix.length).trim();\n return value.startsWith('\"') && value.endsWith('\"')\n ? value.slice(1, -1)\n : value;\n }\n }\n\n return null;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"parsers-DPKyyUv3.mjs","names":[],"sources":["../src/utils/parsers.ts"],"sourcesContent":["// src/parsers.ts\nimport qs from \"qs\";\nimport cookie from \"cookie\";\n\n// 文件信息接口\nexport interface FileInfo {\n name: string;\n type: string;\n size: number;\n data: ArrayBuffer;\n}\n\n// 表单数据接口\nexport interface FormData {\n fields: Record<string, string>;\n files: Record<string, FileInfo>;\n}\n\n/**\n * 简化的请求体解析函数\n * 优先简洁性,处理最常见的场景\n * \n * 注意:此函数假设只用于有 body 的请求(POST/PUT/PATCH 等)\n * 对于 GET/HEAD 请求,调用方应在调用前检查请求方法\n * 参考:defineRoute.ts 中已有正确的检查逻辑\n * \n * 如果传入 GET/HEAD 请求,会返回 null(防御性编程)\n * 这样即使调用方忘记检查,也不会导致运行时错误\n */\nexport async function parseBody(req: Request): Promise<unknown> {\n // 防御性检查:GET/HEAD 请求没有 body\n // HTTP 规范:GET/HEAD 请求通常不带 body,即使带了 Content-Type 也不应解析\n // 参考 Fastify: \"for GET and HEAD requests, the payload is never parsed\"\n const method = req.method.toUpperCase();\n if (method === \"GET\" || method === \"HEAD\") {\n return null;\n }\n\n const contentType = req.headers.get(\"content-type\") || \"\";\n if (contentType.includes(\"application/json\")) {\n return await req.json();\n }\n if (contentType.includes(\"application/x-www-form-urlencoded\")) {\n const text = await req.text();\n return Object.fromEntries(new URLSearchParams(text));\n }\n if (contentType.includes(\"multipart/form-data\")) {\n return await parseMultipartFormData(req);\n }\n return await req.text(); // fallback\n}\n\n/**\n * 解析 multipart/form-data 格式\n * 支持文件上传和普通表单字段\n */\nasync function parseMultipartFormData(req: Request): Promise<FormData> {\n const formData = await req.formData();\n const result: FormData = {\n fields: {},\n files: {},\n };\n\n for (const [key, value] of formData.entries()) {\n if (\n typeof value === \"object\" &&\n value !== null &&\n \"name\" in value &&\n \"type\" in value &&\n \"size\" in value\n ) {\n // 处理文件\n const file = value as any;\n const arrayBuffer = await file.arrayBuffer();\n result.files[key] = {\n name: file.name,\n type: file.type,\n size: file.size,\n data: arrayBuffer,\n };\n } else {\n // 处理普通字段\n result.fields[key] = value as string;\n }\n }\n\n return result;\n}\n\n/**\n * 解析请求体为特定类型\n * 提供类型安全的解析方法\n */\nexport async function parseBodyAs<T>(req: Request): Promise<T> {\n const body = await parseBody(req);\n return body as T;\n}\n\n/**\n * 解析请求体为表单数据\n * 专门用于处理 multipart/form-data\n * \n * 支持的 HTTP 方法:\n * - POST:创建新资源(最常用)\n * - PUT:替换指定资源\n * - PATCH:部分更新(较少用于文件上传)\n * \n * 注意:GET/HEAD 请求没有 body,调用此函数会抛出错误\n */\nexport async function parseFormData(req: Request): Promise<FormData> {\n // 防御性检查:GET/HEAD 请求没有 body\n // HTTP 规范:GET/HEAD 请求不应有 body\n // 文件上传通常使用 POST(新建)或 PUT(替换)\n const method = req.method.toUpperCase();\n if (method === \"GET\" || method === \"HEAD\") {\n throw new Error(\"GET/HEAD 请求不能包含表单数据\");\n }\n\n const contentType = req.headers.get(\"content-type\") || \"\";\n\n if (!contentType.includes(\"multipart/form-data\")) {\n throw new Error(\"请求不是 multipart/form-data 格式\");\n }\n\n return await parseMultipartFormData(req);\n}\n\n/**\n * 解析请求体为文件\n * 专门用于处理文件上传\n * \n * 支持的 HTTP 方法:\n * - POST:上传新文件,服务器决定存储位置(最常用)\n * - PUT:上传到指定位置,或替换已有文件\n * \n * 业界实践参考:\n * - AWS S3、阿里云 OSS 等主流云存储都支持 POST 和 PUT\n * - POST 用于表单上传(multipart),PUT 用于直接上传(binary)\n * \n * 注意:GET/HEAD 请求没有 body,调用此函数会抛出错误\n */\nexport async function parseFile(req: Request): Promise<FileInfo> {\n // 防御性检查:GET/HEAD 请求没有 body\n // HTTP 规范:GET/HEAD 请求不应有 body\n const method = req.method.toUpperCase();\n if (method === \"GET\" || method === \"HEAD\") {\n throw new Error(\"GET/HEAD 请求不能包含文件\");\n }\n\n const contentType = req.headers.get(\"content-type\") || \"\";\n\n if (!contentType.includes(\"multipart/form-data\")) {\n throw new Error(\"请求不是 multipart/form-data 格式\");\n }\n\n const formData = await parseMultipartFormData(req);\n const fileKeys = Object.keys(formData.files);\n\n if (fileKeys.length === 0) {\n throw new Error(\"请求中没有文件\");\n }\n\n if (fileKeys.length > 1) {\n throw new Error(\"请求中包含多个文件,请使用 parseFormData\");\n }\n\n return formData.files[fileKeys[0]];\n}\n\n/**\n * 快速提取 query string(避免创建 URL 对象)\n */\nfunction extractQueryString(url: string): string {\n const qIndex = url.indexOf(\"?\");\n if (qIndex === -1) return \"\";\n\n const hashIndex = url.indexOf(\"#\", qIndex);\n return hashIndex === -1\n ? url.substring(qIndex + 1)\n : url.substring(qIndex + 1, hashIndex);\n}\n\n/** 获取查询字符串,直接返回对象 */\nexport function parseQuery(req: Request): Record<string, unknown> {\n const queryString = extractQueryString(req.url);\n if (!queryString) return {};\n return qs.parse(queryString);\n}\n\n/**\n * 快速解析简单查询字符串(不支持嵌套,但更快)\n * 适用于简单的 key=value&key2=value2 场景\n */\nexport function parseQueryFast(req: Request): Record<string, string> {\n const queryString = extractQueryString(req.url);\n if (!queryString) return {};\n\n const result: Record<string, string> = Object.create(null);\n const pairs = queryString.split(\"&\");\n\n for (const pair of pairs) {\n const eqIndex = pair.indexOf(\"=\");\n if (eqIndex === -1) {\n result[decodeURIComponent(pair)] = \"\";\n } else {\n const key = decodeURIComponent(pair.substring(0, eqIndex));\n const value = decodeURIComponent(pair.substring(eqIndex + 1));\n result[key] = value;\n }\n }\n\n return result;\n}\n\n/** 解析请求头,返回对象 */\nexport function parseHeaders(req: Request): Record<string, string> {\n const headers: Record<string, string> = Object.create(null);\n req.headers.forEach((value, key) => {\n headers[key] = value;\n });\n return headers;\n}\n\n/**\n * 获取单个请求头(避免解析全部)\n */\nexport function getHeader(req: Request, name: string): string | null {\n return req.headers.get(name);\n}\n\n/** 使用cookie库解析Cookie,保证可靠性 */\nexport function parseCookies(req: Request): Record<string, string> {\n const cookieHeader = req.headers.get(\"cookie\");\n if (!cookieHeader) return {};\n\n try {\n const parsed = cookie.parse(cookieHeader);\n // 过滤掉undefined和null值\n const result: Record<string, string> = {};\n for (const [key, value] of Object.entries(parsed)) {\n if (value !== undefined && value !== null) {\n result[key] = value;\n }\n }\n return result;\n } catch {\n return {};\n }\n}\n\n/**\n * 快速解析 Cookie(简化版,不使用外部库)\n * 适用于简单的 cookie 场景\n */\nexport function parseCookiesFast(req: Request): Record<string, string> {\n const cookieHeader = req.headers.get(\"cookie\");\n if (!cookieHeader) return {};\n\n const result: Record<string, string> = Object.create(null);\n const pairs = cookieHeader.split(\";\");\n\n for (const pair of pairs) {\n const trimmed = pair.trim();\n const eqIndex = trimmed.indexOf(\"=\");\n if (eqIndex > 0) {\n const key = trimmed.substring(0, eqIndex).trim();\n const value = trimmed.substring(eqIndex + 1).trim();\n // 移除引号\n result[key] =\n value.startsWith('\"') && value.endsWith('\"')\n ? value.slice(1, -1)\n : value;\n }\n }\n\n return result;\n}\n\n/**\n * 获取单个 Cookie 值(避免解析全部)\n */\nexport function getCookie(req: Request, name: string): string | null {\n const cookieHeader = req.headers.get(\"cookie\");\n if (!cookieHeader) return null;\n\n const prefix = `${name}=`;\n const pairs = cookieHeader.split(\";\");\n\n for (const pair of pairs) {\n const trimmed = pair.trim();\n if (trimmed.startsWith(prefix)) {\n const value = trimmed.substring(prefix.length).trim();\n return value.startsWith('\"') && value.endsWith('\"')\n ? value.slice(1, -1)\n : value;\n }\n }\n\n return null;\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"path-matcher-
|
|
1
|
+
{"version":3,"file":"path-matcher-BwpVrcVG.mjs","names":[],"sources":["../src/utils/path-matcher.ts"],"sourcesContent":["/**\n * 路径匹配工具类\n * 提供统一的路径匹配和参数提取功能\n */\nexport class PathMatcher {\n /**\n * 路径匹配\n */\n static matchPath(pattern: string, path: string): boolean {\n const patternParts = pattern.split(\"/\").filter(Boolean);\n const pathParts = path.split(\"/\").filter(Boolean);\n\n if (patternParts.length !== pathParts.length) {\n return false;\n }\n\n for (let i = 0; i < patternParts.length; i++) {\n if (\n patternParts[i] !== pathParts[i] &&\n !patternParts[i].startsWith(\":\")\n ) {\n return false;\n }\n }\n\n return true;\n }\n\n /**\n * 提取路径参数\n */\n static extractParams(pattern: string, path: string): Record<string, string> {\n const params: Record<string, string> = {};\n const patternParts = pattern.split(\"/\").filter(Boolean);\n const pathParts = path.split(\"/\").filter(Boolean);\n\n for (let i = 0; i < patternParts.length; i++) {\n if (patternParts[i].startsWith(\":\")) {\n const paramName = patternParts[i].slice(1);\n params[paramName] = pathParts[i];\n }\n }\n\n return params;\n }\n\n /**\n * 计算路径特异性分数\n * 用于路由排序:静态 > 动态(:param) > 通配符(*)\n */\n static calculatePathScore(path: string): number {\n const parts = path.split(\"/\").filter(Boolean);\n let score = 0;\n for (const p of parts) {\n if (p === \"*\")\n score += 1; // 最弱\n else if (p.startsWith(\":\"))\n score += 2; // 中等\n else score += 3; // 静态最强\n }\n // 更长的路径更具体,略微提升\n return score * 10 + parts.length;\n }\n\n /**\n * 判断两个路径是否可能冲突\n */\n static pathsMayConflict(path1: string, path2: string): boolean {\n const parts1 = path1.split(\"/\").filter(Boolean);\n const parts2 = path2.split(\"/\").filter(Boolean);\n\n if (parts1.length !== parts2.length) return false;\n\n for (let i = 0; i < parts1.length; i++) {\n const p1 = parts1[i];\n const p2 = parts2[i];\n\n // 如果两个部分都是静态的且不同,则不会冲突\n if (\n !p1.startsWith(\":\") &&\n !p1.startsWith(\"*\") &&\n !p2.startsWith(\":\") &&\n !p2.startsWith(\"*\") &&\n p1 !== p2\n ) {\n return false;\n }\n\n // 如果一个是通配符,另一个是动态参数,可能冲突\n if (\n (p1 === \"*\" && p2.startsWith(\":\")) ||\n (p2 === \"*\" && p1.startsWith(\":\"))\n ) {\n return true;\n }\n }\n\n return false;\n }\n}\n"],"mappings":";;;;;AAIA,IAAa,cAAb,MAAyB;;;;CAIvB,OAAO,UAAU,SAAiB,MAAuB;EACvD,MAAM,eAAe,QAAQ,MAAM,IAAI,CAAC,OAAO,QAAQ;EACvD,MAAM,YAAY,KAAK,MAAM,IAAI,CAAC,OAAO,QAAQ;AAEjD,MAAI,aAAa,WAAW,UAAU,OACpC,QAAO;AAGT,OAAK,IAAI,IAAI,GAAG,IAAI,aAAa,QAAQ,IACvC,KACE,aAAa,OAAO,UAAU,MAC9B,CAAC,aAAa,GAAG,WAAW,IAAI,CAEhC,QAAO;AAIX,SAAO;;;;;CAMT,OAAO,cAAc,SAAiB,MAAsC;EAC1E,MAAM,SAAiC,EAAE;EACzC,MAAM,eAAe,QAAQ,MAAM,IAAI,CAAC,OAAO,QAAQ;EACvD,MAAM,YAAY,KAAK,MAAM,IAAI,CAAC,OAAO,QAAQ;AAEjD,OAAK,IAAI,IAAI,GAAG,IAAI,aAAa,QAAQ,IACvC,KAAI,aAAa,GAAG,WAAW,IAAI,EAAE;GACnC,MAAM,YAAY,aAAa,GAAG,MAAM,EAAE;AAC1C,UAAO,aAAa,UAAU;;AAIlC,SAAO;;;;;;CAOT,OAAO,mBAAmB,MAAsB;EAC9C,MAAM,QAAQ,KAAK,MAAM,IAAI,CAAC,OAAO,QAAQ;EAC7C,IAAI,QAAQ;AACZ,OAAK,MAAM,KAAK,MACd,KAAI,MAAM,IACR,UAAS;WACF,EAAE,WAAW,IAAI,CACxB,UAAS;MACN,UAAS;AAGhB,SAAO,QAAQ,KAAK,MAAM;;;;;CAM5B,OAAO,iBAAiB,OAAe,OAAwB;EAC7D,MAAM,SAAS,MAAM,MAAM,IAAI,CAAC,OAAO,QAAQ;EAC/C,MAAM,SAAS,MAAM,MAAM,IAAI,CAAC,OAAO,QAAQ;AAE/C,MAAI,OAAO,WAAW,OAAO,OAAQ,QAAO;AAE5C,OAAK,IAAI,IAAI,GAAG,IAAI,OAAO,QAAQ,KAAK;GACtC,MAAM,KAAK,OAAO;GAClB,MAAM,KAAK,OAAO;AAGlB,OACE,CAAC,GAAG,WAAW,IAAI,IACnB,CAAC,GAAG,WAAW,IAAI,IACnB,CAAC,GAAG,WAAW,IAAI,IACnB,CAAC,GAAG,WAAW,IAAI,IACnB,OAAO,GAEP,QAAO;AAIT,OACG,OAAO,OAAO,GAAG,WAAW,IAAI,IAChC,OAAO,OAAO,GAAG,WAAW,IAAI,CAEjC,QAAO;;AAIX,SAAO"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"radix-tree-dyn3qDFX.mjs","names":[],"sources":["../src/router/radix-tree.ts"],"sourcesContent":["/**\n * Radix Tree 路由匹配器\n *\n * 高性能路由匹配实现,时间复杂度 O(k),k 为路径段数\n *\n * 支持的路由模式:\n * - 静态路径: /users, /api/v1/health\n * - 动态参数: /users/:id, /posts/:postId/comments/:commentId\n * - 通配符: /files/*, /static/*filepath\n * \n * 特性:\n * - 同一位置支持不同参数名(如 /sessions/:id 和 /sessions/:sessionId/messages)\n * - 参数名冲突时会发出警告,但不影响功能\n */\n\nimport type { Handler, Middleware, Method } from \"../types\";\n\n/** 预编译的处理器类型 */\ntype CompiledHandler = (req: Request) => Promise<Response>;\n\n/** 路由处理信息 */\ninterface RouteHandler {\n handler: Handler;\n middleware: Middleware[];\n /** 预编译后的完整处理链(包含中间件) */\n compiled?: CompiledHandler;\n /** 该路由的参数名列表(按顺序) */\n paramNames: string[];\n}\n\n/** Radix Tree 节点 */\ninterface RadixNode {\n path: string;\n children: Record<string, RadixNode>;\n paramChild?: RadixNode;\n wildcardChild?: RadixNode;\n /** 该位置的参数名(仅用于调试和警告) */\n paramName?: string;\n handlers: Record<Method, RouteHandler | undefined>;\n}\n\n/** 路由匹配结果 */\nexport interface MatchResult {\n handler: Handler;\n middleware: Middleware[];\n params: Record<string, string>;\n /** 预编译后的完整处理链 */\n compiled?: CompiledHandler;\n}\n\n/**\n * Radix Tree 路由器\n *\n * @example\n * ```typescript\n * const router = new RadixRouter();\n * router.register(\"GET\", \"/users/:id\", handler);\n * const result = router.match(\"GET\", \"/users/123\");\n * // result.params = { id: \"123\" }\n * \n * // 支持同一位置使用不同参数名\n * router.register(\"PUT\", \"/sessions/:id\", updateHandler);\n * router.register(\"GET\", \"/sessions/:sessionId/messages\", messagesHandler);\n * // PUT /sessions/123 → params = { id: \"123\" }\n * // GET /sessions/123/messages → params = { sessionId: \"123\" }\n * ```\n */\nexport class RadixRouter {\n private root: RadixNode;\n\n constructor() {\n this.root = this.createNode(\"\");\n }\n\n private createNode(path: string): RadixNode {\n return {\n path,\n children: Object.create(null),\n handlers: Object.create(null),\n };\n }\n\n /** 分割路径 */\n private splitPath(path: string): string[] {\n return path.split(\"/\").filter(Boolean);\n }\n\n /** 编译器函数 - 用于预编译中间件链 */\n private compiler?: (\n middleware: Middleware[],\n handler: Handler,\n ) => CompiledHandler;\n\n /** 设置中间件编译器 */\n setCompiler(\n compiler: (middleware: Middleware[], handler: Handler) => CompiledHandler,\n ): void {\n this.compiler = compiler;\n }\n\n /**\n * 从路由模式中提取参数名列表\n * @example \"/users/:id/posts/:postId\" → [\"id\", \"postId\"]\n */\n private extractParamNames(pattern: string): string[] {\n const paramNames: string[] = [];\n const segments = this.splitPath(pattern);\n \n for (const segment of segments) {\n if (segment[0] === \":\") {\n paramNames.push(segment.substring(1));\n } else if (segment[0] === \"*\") {\n paramNames.push(segment.length > 1 ? segment.substring(1) : \"*\");\n }\n }\n \n return paramNames;\n }\n\n /** 注册路由 */\n register(\n method: Method,\n pattern: string,\n handler: Handler,\n middleware: Middleware[] = [],\n ): void {\n const segments = this.splitPath(pattern);\n const paramNames = this.extractParamNames(pattern);\n let node = this.root;\n\n for (const segment of segments) {\n const firstChar = segment[0];\n\n if (firstChar === \":\") {\n // 动态参数节点\n const paramName = segment.substring(1);\n if (!node.paramChild) {\n node.paramChild = this.createNode(segment);\n node.paramChild.paramName = paramName;\n } else if (node.paramChild.paramName !== paramName) {\n // 参数名冲突警告(但不影响功能)\n console.warn(\n `⚠️ 路由参数名冲突: 位置 \"${node.path || '/'}\" 下已有参数 \":${node.paramChild.paramName}\",` +\n `新路由使用 \":${paramName}\"。建议使用一致的参数名。`\n );\n }\n node = node.paramChild;\n } else if (firstChar === \"*\") {\n // 通配符节点\n if (!node.wildcardChild) {\n node.wildcardChild = this.createNode(segment);\n node.wildcardChild.paramName =\n segment.length > 1 ? segment.substring(1) : \"*\";\n }\n node = node.wildcardChild;\n break;\n } else {\n // 静态路径节点\n if (!node.children[segment]) {\n node.children[segment] = this.createNode(segment);\n }\n node = node.children[segment];\n }\n }\n\n const routeHandler: RouteHandler = { handler, middleware, paramNames };\n\n // 如果没有全局中间件且设置了编译器,预编译处理链\n if (this.compiler && middleware.length === 0) {\n routeHandler.compiled = this.compiler([], handler);\n }\n\n node.handlers[method] = routeHandler;\n }\n\n /** 预编译所有路由(在添加全局中间件后调用) */\n precompileAll(globalMiddleware: Middleware[]): void {\n if (!this.compiler) return;\n this.precompileNode(this.root, globalMiddleware);\n }\n\n private precompileNode(\n node: RadixNode,\n globalMiddleware: Middleware[],\n ): void {\n for (const method in node.handlers) {\n const routeHandler = node.handlers[method as Method];\n if (routeHandler) {\n const allMiddleware = [...globalMiddleware, ...routeHandler.middleware];\n routeHandler.compiled = this.compiler!(\n allMiddleware,\n routeHandler.handler,\n );\n }\n }\n\n for (const key in node.children) {\n this.precompileNode(node.children[key], globalMiddleware);\n }\n\n if (node.paramChild) {\n this.precompileNode(node.paramChild, globalMiddleware);\n }\n\n if (node.wildcardChild) {\n this.precompileNode(node.wildcardChild, globalMiddleware);\n }\n }\n\n /** 匹配路由 */\n match(method: Method, path: string): MatchResult | null {\n const segments = this.splitPath(path);\n // 使用数组按位置收集参数值\n const paramValues: string[] = [];\n\n const node = this.matchNode(this.root, segments, 0, paramValues);\n if (!node) return null;\n\n const routeHandler = node.handlers[method];\n if (!routeHandler) return null;\n\n // 根据路由自己的 paramNames 构建 params 对象\n const params: Record<string, string> = Object.create(null);\n for (let i = 0; i < routeHandler.paramNames.length; i++) {\n params[routeHandler.paramNames[i]] = paramValues[i];\n }\n\n return {\n handler: routeHandler.handler,\n middleware: routeHandler.middleware,\n params,\n compiled: routeHandler.compiled,\n };\n }\n\n /** 递归匹配节点 (优先级: 静态 > 动态参数 > 通配符) */\n private matchNode(\n node: RadixNode,\n segments: string[],\n index: number,\n paramValues: string[],\n ): RadixNode | null {\n if (index === segments.length) {\n for (const method in node.handlers) {\n if (node.handlers[method as Method]) return node;\n }\n return null;\n }\n\n const segment = segments[index];\n\n // 1. 静态路径\n const staticChild = node.children[segment];\n if (staticChild) {\n const result = this.matchNode(staticChild, segments, index + 1, paramValues);\n if (result) return result;\n }\n\n // 2. 动态参数\n if (node.paramChild) {\n const valueIndex = paramValues.length;\n paramValues.push(segment);\n \n const result = this.matchNode(\n node.paramChild,\n segments,\n index + 1,\n paramValues,\n );\n\n if (result) return result;\n\n // 回溯:移除添加的参数值\n paramValues.length = valueIndex;\n }\n\n // 3. 通配符\n if (node.wildcardChild) {\n paramValues.push(segments.slice(index).join(\"/\"));\n return node.wildcardChild;\n }\n\n return null;\n }\n\n /** 获取路径允许的 HTTP 方法 */\n getAllowedMethods(path: string): Method[] {\n const segments = this.splitPath(path);\n const node = this.findNode(segments);\n if (!node) return [];\n\n const methods: Method[] = [];\n for (const method in node.handlers) {\n if (node.handlers[method as Method]) {\n methods.push(method as Method);\n }\n }\n return methods;\n }\n\n /** 查找节点(不提取参数) */\n private findNode(segments: string[]): RadixNode | null {\n let node = this.root;\n\n for (const segment of segments) {\n if (node.children[segment]) {\n node = node.children[segment];\n } else if (node.paramChild) {\n node = node.paramChild;\n } else if (node.wildcardChild) {\n return node.wildcardChild;\n } else {\n return null;\n }\n }\n\n return node;\n }\n\n /** 获取所有已注册的路由 */\n getRoutes(): Array<{ method: Method; path: string }> {\n const routes: Array<{ method: Method; path: string }> = [];\n this.collectRoutes(this.root, \"\", routes);\n return routes;\n }\n\n private collectRoutes(\n node: RadixNode,\n prefix: string,\n routes: Array<{ method: Method; path: string }>,\n ): void {\n const currentPath = prefix + (node.path ? \"/\" + node.path : \"\");\n\n for (const method in node.handlers) {\n if (node.handlers[method as Method]) {\n routes.push({ method: method as Method, path: currentPath || \"/\" });\n }\n }\n\n for (const key in node.children) {\n this.collectRoutes(node.children[key], currentPath, routes);\n }\n\n if (node.paramChild) {\n this.collectRoutes(node.paramChild, currentPath, routes);\n }\n\n if (node.wildcardChild) {\n this.collectRoutes(node.wildcardChild, currentPath, routes);\n }\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAmEA,IAAa,cAAb,MAAyB;CACvB,AAAQ;CAER,cAAc;AACZ,OAAK,OAAO,KAAK,WAAW,GAAG;;CAGjC,AAAQ,WAAW,MAAyB;AAC1C,SAAO;GACL;GACA,UAAU,OAAO,OAAO,KAAK;GAC7B,UAAU,OAAO,OAAO,KAAK;GAC9B;;;CAIH,AAAQ,UAAU,MAAwB;AACxC,SAAO,KAAK,MAAM,IAAI,CAAC,OAAO,QAAQ;;;CAIxC,AAAQ;;CAMR,YACE,UACM;AACN,OAAK,WAAW;;;;;;CAOlB,AAAQ,kBAAkB,SAA2B;EACnD,MAAM,aAAuB,EAAE;EAC/B,MAAM,WAAW,KAAK,UAAU,QAAQ;AAExC,OAAK,MAAM,WAAW,SACpB,KAAI,QAAQ,OAAO,IACjB,YAAW,KAAK,QAAQ,UAAU,EAAE,CAAC;WAC5B,QAAQ,OAAO,IACxB,YAAW,KAAK,QAAQ,SAAS,IAAI,QAAQ,UAAU,EAAE,GAAG,IAAI;AAIpE,SAAO;;;CAIT,SACE,QACA,SACA,SACA,aAA2B,EAAE,EACvB;EACN,MAAM,WAAW,KAAK,UAAU,QAAQ;EACxC,MAAM,aAAa,KAAK,kBAAkB,QAAQ;EAClD,IAAI,OAAO,KAAK;AAEhB,OAAK,MAAM,WAAW,UAAU;GAC9B,MAAM,YAAY,QAAQ;AAE1B,OAAI,cAAc,KAAK;IAErB,MAAM,YAAY,QAAQ,UAAU,EAAE;AACtC,QAAI,CAAC,KAAK,YAAY;AACpB,UAAK,aAAa,KAAK,WAAW,QAAQ;AAC1C,UAAK,WAAW,YAAY;eACnB,KAAK,WAAW,cAAc,UAEvC,SAAQ,KACN,oBAAoB,KAAK,QAAQ,IAAI,YAAY,KAAK,WAAW,UAAU,YAChE,UAAU,eACtB;AAEH,WAAO,KAAK;cACH,cAAc,KAAK;AAE5B,QAAI,CAAC,KAAK,eAAe;AACvB,UAAK,gBAAgB,KAAK,WAAW,QAAQ;AAC7C,UAAK,cAAc,YACjB,QAAQ,SAAS,IAAI,QAAQ,UAAU,EAAE,GAAG;;AAEhD,WAAO,KAAK;AACZ;UACK;AAEL,QAAI,CAAC,KAAK,SAAS,SACjB,MAAK,SAAS,WAAW,KAAK,WAAW,QAAQ;AAEnD,WAAO,KAAK,SAAS;;;EAIzB,MAAM,eAA6B;GAAE;GAAS;GAAY;GAAY;AAGtE,MAAI,KAAK,YAAY,WAAW,WAAW,EACzC,cAAa,WAAW,KAAK,SAAS,EAAE,EAAE,QAAQ;AAGpD,OAAK,SAAS,UAAU;;;CAI1B,cAAc,kBAAsC;AAClD,MAAI,CAAC,KAAK,SAAU;AACpB,OAAK,eAAe,KAAK,MAAM,iBAAiB;;CAGlD,AAAQ,eACN,MACA,kBACM;AACN,OAAK,MAAM,UAAU,KAAK,UAAU;GAClC,MAAM,eAAe,KAAK,SAAS;AACnC,OAAI,cAAc;IAChB,MAAM,gBAAgB,CAAC,GAAG,kBAAkB,GAAG,aAAa,WAAW;AACvE,iBAAa,WAAW,KAAK,SAC3B,eACA,aAAa,QACd;;;AAIL,OAAK,MAAM,OAAO,KAAK,SACrB,MAAK,eAAe,KAAK,SAAS,MAAM,iBAAiB;AAG3D,MAAI,KAAK,WACP,MAAK,eAAe,KAAK,YAAY,iBAAiB;AAGxD,MAAI,KAAK,cACP,MAAK,eAAe,KAAK,eAAe,iBAAiB;;;CAK7D,MAAM,QAAgB,MAAkC;EACtD,MAAM,WAAW,KAAK,UAAU,KAAK;EAErC,MAAM,cAAwB,EAAE;EAEhC,MAAM,OAAO,KAAK,UAAU,KAAK,MAAM,UAAU,GAAG,YAAY;AAChE,MAAI,CAAC,KAAM,QAAO;EAElB,MAAM,eAAe,KAAK,SAAS;AACnC,MAAI,CAAC,aAAc,QAAO;EAG1B,MAAM,SAAiC,OAAO,OAAO,KAAK;AAC1D,OAAK,IAAI,IAAI,GAAG,IAAI,aAAa,WAAW,QAAQ,IAClD,QAAO,aAAa,WAAW,MAAM,YAAY;AAGnD,SAAO;GACL,SAAS,aAAa;GACtB,YAAY,aAAa;GACzB;GACA,UAAU,aAAa;GACxB;;;CAIH,AAAQ,UACN,MACA,UACA,OACA,aACkB;AAClB,MAAI,UAAU,SAAS,QAAQ;AAC7B,QAAK,MAAM,UAAU,KAAK,SACxB,KAAI,KAAK,SAAS,QAAmB,QAAO;AAE9C,UAAO;;EAGT,MAAM,UAAU,SAAS;EAGzB,MAAM,cAAc,KAAK,SAAS;AAClC,MAAI,aAAa;GACf,MAAM,SAAS,KAAK,UAAU,aAAa,UAAU,QAAQ,GAAG,YAAY;AAC5E,OAAI,OAAQ,QAAO;;AAIrB,MAAI,KAAK,YAAY;GACnB,MAAM,aAAa,YAAY;AAC/B,eAAY,KAAK,QAAQ;GAEzB,MAAM,SAAS,KAAK,UAClB,KAAK,YACL,UACA,QAAQ,GACR,YACD;AAED,OAAI,OAAQ,QAAO;AAGnB,eAAY,SAAS;;AAIvB,MAAI,KAAK,eAAe;AACtB,eAAY,KAAK,SAAS,MAAM,MAAM,CAAC,KAAK,IAAI,CAAC;AACjD,UAAO,KAAK;;AAGd,SAAO;;;CAIT,kBAAkB,MAAwB;EACxC,MAAM,WAAW,KAAK,UAAU,KAAK;EACrC,MAAM,OAAO,KAAK,SAAS,SAAS;AACpC,MAAI,CAAC,KAAM,QAAO,EAAE;EAEpB,MAAM,UAAoB,EAAE;AAC5B,OAAK,MAAM,UAAU,KAAK,SACxB,KAAI,KAAK,SAAS,QAChB,SAAQ,KAAK,OAAiB;AAGlC,SAAO;;;CAIT,AAAQ,SAAS,UAAsC;EACrD,IAAI,OAAO,KAAK;AAEhB,OAAK,MAAM,WAAW,SACpB,KAAI,KAAK,SAAS,SAChB,QAAO,KAAK,SAAS;WACZ,KAAK,WACd,QAAO,KAAK;WACH,KAAK,cACd,QAAO,KAAK;MAEZ,QAAO;AAIX,SAAO;;;CAIT,YAAqD;EACnD,MAAM,SAAkD,EAAE;AAC1D,OAAK,cAAc,KAAK,MAAM,IAAI,OAAO;AACzC,SAAO;;CAGT,AAAQ,cACN,MACA,QACA,QACM;EACN,MAAM,cAAc,UAAU,KAAK,OAAO,MAAM,KAAK,OAAO;AAE5D,OAAK,MAAM,UAAU,KAAK,SACxB,KAAI,KAAK,SAAS,QAChB,QAAO,KAAK;GAAU;GAAkB,MAAM,eAAe;GAAK,CAAC;AAIvE,OAAK,MAAM,OAAO,KAAK,SACrB,MAAK,cAAc,KAAK,SAAS,MAAM,aAAa,OAAO;AAG7D,MAAI,KAAK,WACP,MAAK,cAAc,KAAK,YAAY,aAAa,OAAO;AAG1D,MAAI,KAAK,cACP,MAAK,cAAc,KAAK,eAAe,aAAa,OAAO"}
|
|
1
|
+
{"version":3,"file":"radix-tree-DPcGUi6x.mjs","names":[],"sources":["../src/router/radix-tree.ts"],"sourcesContent":["/**\n * Radix Tree 路由匹配器\n *\n * 高性能路由匹配实现,时间复杂度 O(k),k 为路径段数\n *\n * 支持的路由模式:\n * - 静态路径: /users, /api/v1/health\n * - 动态参数: /users/:id, /posts/:postId/comments/:commentId\n * - 通配符: /files/*, /static/*filepath\n * \n * 特性:\n * - 同一位置支持不同参数名(如 /sessions/:id 和 /sessions/:sessionId/messages)\n * - 参数名冲突时会发出警告,但不影响功能\n */\n\nimport type { Handler, Middleware, Method } from \"../types\";\n\n/** 预编译的处理器类型 */\ntype CompiledHandler = (req: Request) => Promise<Response>;\n\n/** 路由处理信息 */\ninterface RouteHandler {\n handler: Handler;\n middleware: Middleware[];\n /** 预编译后的完整处理链(包含中间件) */\n compiled?: CompiledHandler;\n /** 该路由的参数名列表(按顺序) */\n paramNames: string[];\n}\n\n/** Radix Tree 节点 */\ninterface RadixNode {\n path: string;\n children: Record<string, RadixNode>;\n paramChild?: RadixNode;\n wildcardChild?: RadixNode;\n /** 该位置的参数名(仅用于调试和警告) */\n paramName?: string;\n handlers: Record<Method, RouteHandler | undefined>;\n}\n\n/** 路由匹配结果 */\nexport interface MatchResult {\n handler: Handler;\n middleware: Middleware[];\n params: Record<string, string>;\n /** 预编译后的完整处理链 */\n compiled?: CompiledHandler;\n}\n\n/**\n * Radix Tree 路由器\n *\n * @example\n * ```typescript\n * const router = new RadixRouter();\n * router.register(\"GET\", \"/users/:id\", handler);\n * const result = router.match(\"GET\", \"/users/123\");\n * // result.params = { id: \"123\" }\n * \n * // 支持同一位置使用不同参数名\n * router.register(\"PUT\", \"/sessions/:id\", updateHandler);\n * router.register(\"GET\", \"/sessions/:sessionId/messages\", messagesHandler);\n * // PUT /sessions/123 → params = { id: \"123\" }\n * // GET /sessions/123/messages → params = { sessionId: \"123\" }\n * ```\n */\nexport class RadixRouter {\n private root: RadixNode;\n\n constructor() {\n this.root = this.createNode(\"\");\n }\n\n private createNode(path: string): RadixNode {\n return {\n path,\n children: Object.create(null),\n handlers: Object.create(null),\n };\n }\n\n /** 分割路径 */\n private splitPath(path: string): string[] {\n return path.split(\"/\").filter(Boolean);\n }\n\n /** 编译器函数 - 用于预编译中间件链 */\n private compiler?: (\n middleware: Middleware[],\n handler: Handler,\n ) => CompiledHandler;\n\n /** 设置中间件编译器 */\n setCompiler(\n compiler: (middleware: Middleware[], handler: Handler) => CompiledHandler,\n ): void {\n this.compiler = compiler;\n }\n\n /**\n * 从路由模式中提取参数名列表\n * @example \"/users/:id/posts/:postId\" → [\"id\", \"postId\"]\n */\n private extractParamNames(pattern: string): string[] {\n const paramNames: string[] = [];\n const segments = this.splitPath(pattern);\n \n for (const segment of segments) {\n if (segment[0] === \":\") {\n paramNames.push(segment.substring(1));\n } else if (segment[0] === \"*\") {\n paramNames.push(segment.length > 1 ? segment.substring(1) : \"*\");\n }\n }\n \n return paramNames;\n }\n\n /** 注册路由 */\n register(\n method: Method,\n pattern: string,\n handler: Handler,\n middleware: Middleware[] = [],\n ): void {\n const segments = this.splitPath(pattern);\n const paramNames = this.extractParamNames(pattern);\n let node = this.root;\n\n for (const segment of segments) {\n const firstChar = segment[0];\n\n if (firstChar === \":\") {\n // 动态参数节点\n const paramName = segment.substring(1);\n if (!node.paramChild) {\n node.paramChild = this.createNode(segment);\n node.paramChild.paramName = paramName;\n } else if (node.paramChild.paramName !== paramName) {\n // 参数名冲突警告(但不影响功能)\n console.warn(\n `⚠️ 路由参数名冲突: 位置 \"${node.path || '/'}\" 下已有参数 \":${node.paramChild.paramName}\",` +\n `新路由使用 \":${paramName}\"。建议使用一致的参数名。`\n );\n }\n node = node.paramChild;\n } else if (firstChar === \"*\") {\n // 通配符节点\n if (!node.wildcardChild) {\n node.wildcardChild = this.createNode(segment);\n node.wildcardChild.paramName =\n segment.length > 1 ? segment.substring(1) : \"*\";\n }\n node = node.wildcardChild;\n break;\n } else {\n // 静态路径节点\n if (!node.children[segment]) {\n node.children[segment] = this.createNode(segment);\n }\n node = node.children[segment];\n }\n }\n\n const routeHandler: RouteHandler = { handler, middleware, paramNames };\n\n // 如果没有全局中间件且设置了编译器,预编译处理链\n if (this.compiler && middleware.length === 0) {\n routeHandler.compiled = this.compiler([], handler);\n }\n\n node.handlers[method] = routeHandler;\n }\n\n /** 预编译所有路由(在添加全局中间件后调用) */\n precompileAll(globalMiddleware: Middleware[]): void {\n if (!this.compiler) return;\n this.precompileNode(this.root, globalMiddleware);\n }\n\n private precompileNode(\n node: RadixNode,\n globalMiddleware: Middleware[],\n ): void {\n for (const method in node.handlers) {\n const routeHandler = node.handlers[method as Method];\n if (routeHandler) {\n const allMiddleware = [...globalMiddleware, ...routeHandler.middleware];\n routeHandler.compiled = this.compiler!(\n allMiddleware,\n routeHandler.handler,\n );\n }\n }\n\n for (const key in node.children) {\n this.precompileNode(node.children[key], globalMiddleware);\n }\n\n if (node.paramChild) {\n this.precompileNode(node.paramChild, globalMiddleware);\n }\n\n if (node.wildcardChild) {\n this.precompileNode(node.wildcardChild, globalMiddleware);\n }\n }\n\n /** 匹配路由 */\n match(method: Method, path: string): MatchResult | null {\n const segments = this.splitPath(path);\n // 使用数组按位置收集参数值\n const paramValues: string[] = [];\n\n const node = this.matchNode(this.root, segments, 0, paramValues);\n if (!node) return null;\n\n const routeHandler = node.handlers[method];\n if (!routeHandler) return null;\n\n // 根据路由自己的 paramNames 构建 params 对象\n const params: Record<string, string> = Object.create(null);\n for (let i = 0; i < routeHandler.paramNames.length; i++) {\n params[routeHandler.paramNames[i]] = paramValues[i];\n }\n\n return {\n handler: routeHandler.handler,\n middleware: routeHandler.middleware,\n params,\n compiled: routeHandler.compiled,\n };\n }\n\n /** 递归匹配节点 (优先级: 静态 > 动态参数 > 通配符) */\n private matchNode(\n node: RadixNode,\n segments: string[],\n index: number,\n paramValues: string[],\n ): RadixNode | null {\n if (index === segments.length) {\n for (const method in node.handlers) {\n if (node.handlers[method as Method]) return node;\n }\n return null;\n }\n\n const segment = segments[index];\n\n // 1. 静态路径\n const staticChild = node.children[segment];\n if (staticChild) {\n const result = this.matchNode(staticChild, segments, index + 1, paramValues);\n if (result) return result;\n }\n\n // 2. 动态参数\n if (node.paramChild) {\n const valueIndex = paramValues.length;\n paramValues.push(segment);\n \n const result = this.matchNode(\n node.paramChild,\n segments,\n index + 1,\n paramValues,\n );\n\n if (result) return result;\n\n // 回溯:移除添加的参数值\n paramValues.length = valueIndex;\n }\n\n // 3. 通配符\n if (node.wildcardChild) {\n paramValues.push(segments.slice(index).join(\"/\"));\n return node.wildcardChild;\n }\n\n return null;\n }\n\n /** 获取路径允许的 HTTP 方法 */\n getAllowedMethods(path: string): Method[] {\n const segments = this.splitPath(path);\n const node = this.findNode(segments);\n if (!node) return [];\n\n const methods: Method[] = [];\n for (const method in node.handlers) {\n if (node.handlers[method as Method]) {\n methods.push(method as Method);\n }\n }\n return methods;\n }\n\n /** 查找节点(不提取参数) */\n private findNode(segments: string[]): RadixNode | null {\n let node = this.root;\n\n for (const segment of segments) {\n if (node.children[segment]) {\n node = node.children[segment];\n } else if (node.paramChild) {\n node = node.paramChild;\n } else if (node.wildcardChild) {\n return node.wildcardChild;\n } else {\n return null;\n }\n }\n\n return node;\n }\n\n /** 获取所有已注册的路由 */\n getRoutes(): Array<{ method: Method; path: string }> {\n const routes: Array<{ method: Method; path: string }> = [];\n this.collectRoutes(this.root, \"\", routes);\n return routes;\n }\n\n private collectRoutes(\n node: RadixNode,\n prefix: string,\n routes: Array<{ method: Method; path: string }>,\n ): void {\n const currentPath = prefix + (node.path ? \"/\" + node.path : \"\");\n\n for (const method in node.handlers) {\n if (node.handlers[method as Method]) {\n routes.push({ method: method as Method, path: currentPath || \"/\" });\n }\n }\n\n for (const key in node.children) {\n this.collectRoutes(node.children[key], currentPath, routes);\n }\n\n if (node.paramChild) {\n this.collectRoutes(node.paramChild, currentPath, routes);\n }\n\n if (node.wildcardChild) {\n this.collectRoutes(node.wildcardChild, currentPath, routes);\n }\n }\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"request-D5oMj6sH.mjs","names":[],"sources":["../src/node-server/request.ts"],"sourcesContent":["/**\n * 优化的 Request 代理\n * 延迟创建真实 Request,减少不必要的对象分配\n */\n\nimport { Readable } from \"node:stream\";\nimport type { ReadableStream as NodeReadableStream } from \"node:stream/web\";\nimport type { IncomingMessage } from \"node:http\";\nimport type { Socket } from \"node:net\";\n\n// 内部 Symbol\nconst requestCache = Symbol(\"requestCache\");\nconst incomingKey = Symbol(\"incoming\");\nconst urlKey = Symbol(\"url\");\nconst headersKey = Symbol(\"headers\");\nconst ipKey = Symbol(\"ip\");\nconst ipsKey = Symbol(\"ips\");\n\n/** 信任代理配置类型 */\nexport type TrustProxyOption = boolean | string | string[];\n\n/**\n * IP 解析优先级列表\n * 参考业界标准和各大云厂商\n */\nconst IP_HEADERS = [\n \"x-forwarded-for\", // 标准代理头(优先级最高)\n \"x-real-ip\", // Nginx\n \"x-client-ip\", // Apache\n \"cf-connecting-ip\", // Cloudflare\n \"fastly-client-ip\", // Fastly\n \"x-cluster-client-ip\", // GCP\n \"true-client-ip\", // Akamai & Cloudflare\n \"fly-client-ip\", // Fly.io\n \"x-forwarded\", // RFC 7239\n \"forwarded-for\", // RFC 7239\n \"forwarded\", // RFC 7239\n \"appengine-user-ip\", // GCP AppEngine\n \"cf-pseudo-ipv4\", // Cloudflare IPv6 兼容\n];\n\n/**\n * 检查 IP 是否在 CIDR 范围内\n */\nfunction isIpInCidr(ip: string, cidr: string): boolean {\n // 简单实现:只支持精确匹配和 /8, /16, /24 掩码\n if (!cidr.includes(\"/\")) {\n return ip === cidr;\n }\n \n const [network, maskStr] = cidr.split(\"/\");\n const mask = parseInt(maskStr, 10);\n \n const ipParts = ip.split(\".\").map(Number);\n const networkParts = network.split(\".\").map(Number);\n \n if (ipParts.length !== 4 || networkParts.length !== 4) {\n return false;\n }\n \n const ipNum = (ipParts[0] << 24) | (ipParts[1] << 16) | (ipParts[2] << 8) | ipParts[3];\n const networkNum = (networkParts[0] << 24) | (networkParts[1] << 16) | (networkParts[2] << 8) | networkParts[3];\n const maskNum = ~((1 << (32 - mask)) - 1);\n \n return (ipNum & maskNum) === (networkNum & maskNum);\n}\n\n/**\n * 检查是否应该信任代理\n */\nfunction shouldTrustProxy(\n socketIp: string | undefined,\n trustProxy: TrustProxyOption,\n): boolean {\n if (trustProxy === true) return true;\n if (trustProxy === false || !trustProxy) return false;\n if (!socketIp) return false;\n \n const trustedList = Array.isArray(trustProxy) ? trustProxy : [trustProxy];\n return trustedList.some((trusted) => isIpInCidr(socketIp, trusted));\n}\n\n/**\n * 从请求头解析客户端 IP\n */\nfunction parseClientIp(\n incoming: IncomingMessage,\n trustProxy: TrustProxyOption,\n): { ip: string; ips: string[] } {\n const socket = incoming.socket as Socket;\n const socketIp = socket.remoteAddress || \"\";\n \n // 如果不信任代理,直接返回 socket IP\n if (!shouldTrustProxy(socketIp, trustProxy)) {\n return { ip: socketIp, ips: [socketIp] };\n }\n \n // 尝试从各种头中获取 IP\n for (const header of IP_HEADERS) {\n const value = incoming.headers[header];\n if (value) {\n const headerValue = Array.isArray(value) ? value[0] : value;\n // X-Forwarded-For 可能包含多个 IP,逗号分隔\n if (header === \"x-forwarded-for\") {\n const ips = headerValue.split(\",\").map((ip) => ip.trim()).filter(Boolean);\n if (ips.length > 0) {\n return { ip: ips[0], ips };\n }\n } else {\n return { ip: headerValue.trim(), ips: [headerValue.trim()] };\n }\n }\n }\n \n // 回退到 socket IP\n return { ip: socketIp, ips: [socketIp] };\n}\n\n/**\n * 从 rawHeaders 高效解析 Headers\n */\nfunction parseHeaders(rawHeaders: string[]): Headers {\n const headers = new Headers();\n for (let i = 0; i < rawHeaders.length; i += 2) {\n const key = rawHeaders[i];\n const value = rawHeaders[i + 1];\n // 跳过 HTTP/2 伪头 (以 : 开头)\n if (key.charCodeAt(0) !== 58) {\n headers.append(key, value);\n }\n }\n return headers;\n}\n\n/**\n * 将 Node.js ReadableStream 转换为 Web 标准 ReadableStream\n * Node.js 和 Web 标准的 ReadableStream 在运行时兼容,但 TypeScript 类型不同\n */\nfunction toWebStream(\n nodeStream: NodeReadableStream<Uint8Array>,\n): ReadableStream<Uint8Array> {\n // Node.js ReadableStream 和 Web ReadableStream 在运行时是兼容的\n // 这里使用类型断言是安全的,因为 Node.js >= 18 的 stream/web 完全实现了 WHATWG Streams 标准\n return nodeStream as unknown as ReadableStream<Uint8Array>;\n}\n\n/** 代理 Request 内部接口 */\ninterface ProxyRequestInternal {\n [requestCache]?: Request;\n [incomingKey]: IncomingMessage;\n [urlKey]: string;\n [headersKey]?: Headers;\n [ipKey]?: string;\n [ipsKey]?: string[];\n _getRequest(): Request;\n}\n\n/**\n * 创建真实的 Request 对象\n */\nfunction createRealRequest(proxy: ProxyRequestInternal): Request {\n const incoming = proxy[incomingKey];\n const method = incoming.method || \"GET\";\n const init: RequestInit & { duplex?: string } = {\n method,\n headers: proxy[headersKey] || parseHeaders(incoming.rawHeaders),\n };\n\n // 只有非 GET/HEAD 请求才有 body\n if (method !== \"GET\" && method !== \"HEAD\") {\n // 使用 Node.js 原生流转换,避免收集 chunks\n const nodeWebStream = Readable.toWeb(\n incoming,\n ) as NodeReadableStream<Uint8Array>;\n init.body = toWebStream(nodeWebStream);\n init.duplex = \"half\";\n }\n\n return new Request(proxy[urlKey], init);\n}\n\n/**\n * Request 代理原型\n * 使用 Object.defineProperty 定义属性以支持 this 绑定\n */\nconst requestPrototype: object = {};\n\n// 定义 method 属性\nObject.defineProperty(requestPrototype, \"method\", {\n get() {\n const self = this as ProxyRequestInternal;\n return self[incomingKey].method || \"GET\";\n },\n enumerable: true,\n});\n\n// 定义 url 属性\nObject.defineProperty(requestPrototype, \"url\", {\n get() {\n const self = this as ProxyRequestInternal;\n return self[urlKey];\n },\n enumerable: true,\n});\n\n// 定义 headers 属性(延迟解析)\nObject.defineProperty(requestPrototype, \"headers\", {\n get() {\n const self = this as ProxyRequestInternal;\n if (!self[headersKey]) {\n self[headersKey] = parseHeaders(self[incomingKey].rawHeaders);\n }\n return self[headersKey];\n },\n enumerable: true,\n});\n\n// 定义 _getRequest 方法(获取或创建真实 Request)\nObject.defineProperty(requestPrototype, \"_getRequest\", {\n value: function () {\n const self = this as ProxyRequestInternal;\n if (!self[requestCache]) {\n self[requestCache] = createRealRequest(self);\n }\n return self[requestCache]!;\n },\n enumerable: false,\n});\n\n// 代理需要访问真实 Request 的属性\nconst proxyGetters = [\n \"body\",\n \"bodyUsed\",\n \"signal\",\n \"cache\",\n \"credentials\",\n \"destination\",\n \"integrity\",\n \"mode\",\n \"redirect\",\n \"referrer\",\n \"referrerPolicy\",\n \"keepalive\",\n];\n\nproxyGetters.forEach((key) => {\n Object.defineProperty(requestPrototype, key, {\n get() {\n const self = this as ProxyRequestInternal;\n return self._getRequest()[key as keyof Request];\n },\n enumerable: true,\n });\n});\n\n// 代理需要调用真实 Request 的方法\n// 注意:对于 body 相关方法(json/text/arrayBuffer/blob/formData),GET/HEAD 请求没有 body\n// 直接返回空值,避免用户误调用导致流读取异常\n// 这是框架级防御,参考业界标准:Fastify \"for GET and HEAD requests, the payload is never parsed\"\n\n// 定义 clone 方法(所有请求都可以克隆)\nObject.defineProperty(requestPrototype, \"clone\", {\n value: function () {\n const self = this as ProxyRequestInternal;\n const req = self._getRequest();\n return req.clone();\n },\n enumerable: true,\n});\n\n// 定义 body 相关方法(GET/HEAD 返回空值)\nconst bodyMethods = [\"arrayBuffer\", \"blob\", \"formData\", \"json\", \"text\"] as const;\n\nbodyMethods.forEach((key) => {\n Object.defineProperty(requestPrototype, key, {\n value: function () {\n const self = this as ProxyRequestInternal;\n const method = self[incomingKey].method || \"GET\";\n \n // GET/HEAD 请求没有 body,直接返回空值\n // 这样即使用户误调用 req.json() 也不会出错\n if (method === \"GET\" || method === \"HEAD\") {\n // json() 返回 null,text() 返回空字符串,其他返回对应的空值\n if (key === \"json\") return Promise.resolve(null);\n if (key === \"text\") return Promise.resolve(\"\");\n if (key === \"arrayBuffer\") return Promise.resolve(new ArrayBuffer(0));\n if (key === \"blob\") return Promise.resolve(new Blob([]));\n if (key === \"formData\") return Promise.resolve(new FormData());\n }\n \n // 其他请求正常处理\n const req = self._getRequest();\n return (req[key as keyof Request] as () => Promise<unknown>).call(req);\n },\n enumerable: true,\n });\n});\n\n// 定义 ip 属性(客户端真实 IP)\nObject.defineProperty(requestPrototype, \"ip\", {\n get() {\n const self = this as ProxyRequestInternal;\n return self[ipKey] || \"\";\n },\n enumerable: true,\n});\n\n// 定义 ips 属性(代理链中的所有 IP)\nObject.defineProperty(requestPrototype, \"ips\", {\n get() {\n const self = this as ProxyRequestInternal;\n return self[ipsKey] || [];\n },\n enumerable: true,\n});\n\n// 设置原型链\nObject.setPrototypeOf(requestPrototype, Request.prototype);\n\n/** 创建代理 Request 的选项 */\nexport interface CreateProxyRequestOptions {\n /** 信任代理配置 */\n trustProxy?: TrustProxyOption;\n}\n\n/**\n * 创建代理 Request\n * @param incoming Node.js IncomingMessage\n * @param defaultHost 默认主机名\n * @param options 可选配置\n */\nexport function createProxyRequest(\n incoming: IncomingMessage,\n defaultHost: string,\n options?: CreateProxyRequestOptions,\n): Request {\n const req = Object.create(requestPrototype) as ProxyRequestInternal;\n req[incomingKey] = incoming;\n\n // 构建 URL\n const host = incoming.headers.host || defaultHost;\n const protocol = (incoming.socket as { encrypted?: boolean }).encrypted\n ? \"https\"\n : \"http\";\n req[urlKey] = `${protocol}://${host}${incoming.url || \"/\"}`;\n\n // 解析客户端 IP(如果启用了 trustProxy)\n if (options?.trustProxy) {\n const { ip, ips } = parseClientIp(incoming, options.trustProxy);\n req[ipKey] = ip;\n req[ipsKey] = ips;\n } else {\n // 默认使用 socket IP\n const socketIp = (incoming.socket as Socket).remoteAddress || \"\";\n req[ipKey] = socketIp;\n req[ipsKey] = [socketIp];\n }\n\n return req as unknown as Request;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"request-COqaDa-c.mjs","names":[],"sources":["../src/node-server/request.ts"],"sourcesContent":["/**\n * 优化的 Request 代理\n * 延迟创建真实 Request,减少不必要的对象分配\n */\n\nimport { Readable } from \"node:stream\";\nimport type { ReadableStream as NodeReadableStream } from \"node:stream/web\";\nimport type { IncomingMessage } from \"node:http\";\nimport type { Socket } from \"node:net\";\n\n// 内部 Symbol\nconst requestCache = Symbol(\"requestCache\");\nconst incomingKey = Symbol(\"incoming\");\nconst urlKey = Symbol(\"url\");\nconst headersKey = Symbol(\"headers\");\nconst ipKey = Symbol(\"ip\");\nconst ipsKey = Symbol(\"ips\");\n\n/** 信任代理配置类型 */\nexport type TrustProxyOption = boolean | string | string[];\n\n/**\n * IP 解析优先级列表\n * 参考业界标准和各大云厂商\n */\nconst IP_HEADERS = [\n \"x-forwarded-for\", // 标准代理头(优先级最高)\n \"x-real-ip\", // Nginx\n \"x-client-ip\", // Apache\n \"cf-connecting-ip\", // Cloudflare\n \"fastly-client-ip\", // Fastly\n \"x-cluster-client-ip\", // GCP\n \"true-client-ip\", // Akamai & Cloudflare\n \"fly-client-ip\", // Fly.io\n \"x-forwarded\", // RFC 7239\n \"forwarded-for\", // RFC 7239\n \"forwarded\", // RFC 7239\n \"appengine-user-ip\", // GCP AppEngine\n \"cf-pseudo-ipv4\", // Cloudflare IPv6 兼容\n];\n\n/**\n * 检查 IP 是否在 CIDR 范围内\n */\nfunction isIpInCidr(ip: string, cidr: string): boolean {\n // 简单实现:只支持精确匹配和 /8, /16, /24 掩码\n if (!cidr.includes(\"/\")) {\n return ip === cidr;\n }\n \n const [network, maskStr] = cidr.split(\"/\");\n const mask = parseInt(maskStr, 10);\n \n const ipParts = ip.split(\".\").map(Number);\n const networkParts = network.split(\".\").map(Number);\n \n if (ipParts.length !== 4 || networkParts.length !== 4) {\n return false;\n }\n \n const ipNum = (ipParts[0] << 24) | (ipParts[1] << 16) | (ipParts[2] << 8) | ipParts[3];\n const networkNum = (networkParts[0] << 24) | (networkParts[1] << 16) | (networkParts[2] << 8) | networkParts[3];\n const maskNum = ~((1 << (32 - mask)) - 1);\n \n return (ipNum & maskNum) === (networkNum & maskNum);\n}\n\n/**\n * 检查是否应该信任代理\n */\nfunction shouldTrustProxy(\n socketIp: string | undefined,\n trustProxy: TrustProxyOption,\n): boolean {\n if (trustProxy === true) return true;\n if (trustProxy === false || !trustProxy) return false;\n if (!socketIp) return false;\n \n const trustedList = Array.isArray(trustProxy) ? trustProxy : [trustProxy];\n return trustedList.some((trusted) => isIpInCidr(socketIp, trusted));\n}\n\n/**\n * 从请求头解析客户端 IP\n */\nfunction parseClientIp(\n incoming: IncomingMessage,\n trustProxy: TrustProxyOption,\n): { ip: string; ips: string[] } {\n const socket = incoming.socket as Socket;\n const socketIp = socket.remoteAddress || \"\";\n \n // 如果不信任代理,直接返回 socket IP\n if (!shouldTrustProxy(socketIp, trustProxy)) {\n return { ip: socketIp, ips: [socketIp] };\n }\n \n // 尝试从各种头中获取 IP\n for (const header of IP_HEADERS) {\n const value = incoming.headers[header];\n if (value) {\n const headerValue = Array.isArray(value) ? value[0] : value;\n // X-Forwarded-For 可能包含多个 IP,逗号分隔\n if (header === \"x-forwarded-for\") {\n const ips = headerValue.split(\",\").map((ip) => ip.trim()).filter(Boolean);\n if (ips.length > 0) {\n return { ip: ips[0], ips };\n }\n } else {\n return { ip: headerValue.trim(), ips: [headerValue.trim()] };\n }\n }\n }\n \n // 回退到 socket IP\n return { ip: socketIp, ips: [socketIp] };\n}\n\n/**\n * 从 rawHeaders 高效解析 Headers\n */\nfunction parseHeaders(rawHeaders: string[]): Headers {\n const headers = new Headers();\n for (let i = 0; i < rawHeaders.length; i += 2) {\n const key = rawHeaders[i];\n const value = rawHeaders[i + 1];\n // 跳过 HTTP/2 伪头 (以 : 开头)\n if (key.charCodeAt(0) !== 58) {\n headers.append(key, value);\n }\n }\n return headers;\n}\n\n/**\n * 将 Node.js ReadableStream 转换为 Web 标准 ReadableStream\n * Node.js 和 Web 标准的 ReadableStream 在运行时兼容,但 TypeScript 类型不同\n */\nfunction toWebStream(\n nodeStream: NodeReadableStream<Uint8Array>,\n): ReadableStream<Uint8Array> {\n // Node.js ReadableStream 和 Web ReadableStream 在运行时是兼容的\n // 这里使用类型断言是安全的,因为 Node.js >= 18 的 stream/web 完全实现了 WHATWG Streams 标准\n return nodeStream as unknown as ReadableStream<Uint8Array>;\n}\n\n/** 代理 Request 内部接口 */\ninterface ProxyRequestInternal {\n [requestCache]?: Request;\n [incomingKey]: IncomingMessage;\n [urlKey]: string;\n [headersKey]?: Headers;\n [ipKey]?: string;\n [ipsKey]?: string[];\n _getRequest(): Request;\n}\n\n/**\n * 创建真实的 Request 对象\n */\nfunction createRealRequest(proxy: ProxyRequestInternal): Request {\n const incoming = proxy[incomingKey];\n const method = incoming.method || \"GET\";\n const init: RequestInit & { duplex?: string } = {\n method,\n headers: proxy[headersKey] || parseHeaders(incoming.rawHeaders),\n };\n\n // 只有非 GET/HEAD 请求才有 body\n if (method !== \"GET\" && method !== \"HEAD\") {\n // 使用 Node.js 原生流转换,避免收集 chunks\n const nodeWebStream = Readable.toWeb(\n incoming,\n ) as NodeReadableStream<Uint8Array>;\n init.body = toWebStream(nodeWebStream);\n init.duplex = \"half\";\n }\n\n return new Request(proxy[urlKey], init);\n}\n\n/**\n * Request 代理原型\n * 使用 Object.defineProperty 定义属性以支持 this 绑定\n */\nconst requestPrototype: object = {};\n\n// 定义 method 属性\nObject.defineProperty(requestPrototype, \"method\", {\n get() {\n const self = this as ProxyRequestInternal;\n return self[incomingKey].method || \"GET\";\n },\n enumerable: true,\n});\n\n// 定义 url 属性\nObject.defineProperty(requestPrototype, \"url\", {\n get() {\n const self = this as ProxyRequestInternal;\n return self[urlKey];\n },\n enumerable: true,\n});\n\n// 定义 headers 属性(延迟解析)\nObject.defineProperty(requestPrototype, \"headers\", {\n get() {\n const self = this as ProxyRequestInternal;\n if (!self[headersKey]) {\n self[headersKey] = parseHeaders(self[incomingKey].rawHeaders);\n }\n return self[headersKey];\n },\n enumerable: true,\n});\n\n// 定义 _getRequest 方法(获取或创建真实 Request)\nObject.defineProperty(requestPrototype, \"_getRequest\", {\n value: function () {\n const self = this as ProxyRequestInternal;\n if (!self[requestCache]) {\n self[requestCache] = createRealRequest(self);\n }\n return self[requestCache]!;\n },\n enumerable: false,\n});\n\n// 代理需要访问真实 Request 的属性\nconst proxyGetters = [\n \"body\",\n \"bodyUsed\",\n \"signal\",\n \"cache\",\n \"credentials\",\n \"destination\",\n \"integrity\",\n \"mode\",\n \"redirect\",\n \"referrer\",\n \"referrerPolicy\",\n \"keepalive\",\n];\n\nproxyGetters.forEach((key) => {\n Object.defineProperty(requestPrototype, key, {\n get() {\n const self = this as ProxyRequestInternal;\n return self._getRequest()[key as keyof Request];\n },\n enumerable: true,\n });\n});\n\n// 代理需要调用真实 Request 的方法\n// 注意:对于 body 相关方法(json/text/arrayBuffer/blob/formData),GET/HEAD 请求没有 body\n// 直接返回空值,避免用户误调用导致流读取异常\n// 这是框架级防御,参考业界标准:Fastify \"for GET and HEAD requests, the payload is never parsed\"\n\n// 定义 clone 方法(所有请求都可以克隆)\nObject.defineProperty(requestPrototype, \"clone\", {\n value: function () {\n const self = this as ProxyRequestInternal;\n const req = self._getRequest();\n return req.clone();\n },\n enumerable: true,\n});\n\n// 定义 body 相关方法(GET/HEAD 返回空值)\nconst bodyMethods = [\"arrayBuffer\", \"blob\", \"formData\", \"json\", \"text\"] as const;\n\nbodyMethods.forEach((key) => {\n Object.defineProperty(requestPrototype, key, {\n value: function () {\n const self = this as ProxyRequestInternal;\n const method = self[incomingKey].method || \"GET\";\n \n // GET/HEAD 请求没有 body,直接返回空值\n // 这样即使用户误调用 req.json() 也不会出错\n if (method === \"GET\" || method === \"HEAD\") {\n // json() 返回 null,text() 返回空字符串,其他返回对应的空值\n if (key === \"json\") return Promise.resolve(null);\n if (key === \"text\") return Promise.resolve(\"\");\n if (key === \"arrayBuffer\") return Promise.resolve(new ArrayBuffer(0));\n if (key === \"blob\") return Promise.resolve(new Blob([]));\n if (key === \"formData\") return Promise.resolve(new FormData());\n }\n \n // 其他请求正常处理\n const req = self._getRequest();\n return (req[key as keyof Request] as () => Promise<unknown>).call(req);\n },\n enumerable: true,\n });\n});\n\n// 定义 ip 属性(客户端真实 IP)\nObject.defineProperty(requestPrototype, \"ip\", {\n get() {\n const self = this as ProxyRequestInternal;\n return self[ipKey] || \"\";\n },\n enumerable: true,\n});\n\n// 定义 ips 属性(代理链中的所有 IP)\nObject.defineProperty(requestPrototype, \"ips\", {\n get() {\n const self = this as ProxyRequestInternal;\n return self[ipsKey] || [];\n },\n enumerable: true,\n});\n\n// 设置原型链\nObject.setPrototypeOf(requestPrototype, Request.prototype);\n\n/** 创建代理 Request 的选项 */\nexport interface CreateProxyRequestOptions {\n /** 信任代理配置 */\n trustProxy?: TrustProxyOption;\n}\n\n/**\n * 创建代理 Request\n * @param incoming Node.js IncomingMessage\n * @param defaultHost 默认主机名\n * @param options 可选配置\n */\nexport function createProxyRequest(\n incoming: IncomingMessage,\n defaultHost: string,\n options?: CreateProxyRequestOptions,\n): Request {\n const req = Object.create(requestPrototype) as ProxyRequestInternal;\n req[incomingKey] = incoming;\n\n // 构建 URL\n const host = incoming.headers.host || defaultHost;\n const protocol = (incoming.socket as { encrypted?: boolean }).encrypted\n ? \"https\"\n : \"http\";\n req[urlKey] = `${protocol}://${host}${incoming.url || \"/\"}`;\n\n // 解析客户端 IP(如果启用了 trustProxy)\n if (options?.trustProxy) {\n const { ip, ips } = parseClientIp(incoming, options.trustProxy);\n req[ipKey] = ip;\n req[ipsKey] = ips;\n } else {\n // 默认使用 socket IP\n const socketIp = (incoming.socket as Socket).remoteAddress || \"\";\n req[ipKey] = socketIp;\n req[ipsKey] = [socketIp];\n }\n\n return req as unknown as Request;\n}\n"],"mappings":"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"}
|