vafast 0.4.2 → 0.4.4
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 +6 -10
- package/dist/base-server-CSkyjEaY.mjs +113 -0
- package/dist/base-server-CSkyjEaY.mjs.map +1 -0
- package/dist/base-server-DvGS6ATg.d.mts +40 -0
- package/dist/base64url-0N9uQPjZ.d.mts +6 -0
- package/dist/base64url-CwQnvZyp.mjs +13 -0
- package/dist/base64url-CwQnvZyp.mjs.map +1 -0
- package/dist/chunk-DW4-Jl94.mjs +37 -0
- package/dist/component-route-CYxLOnci.mjs +1 -0
- package/dist/component-route-CZawYn61.d.mts +31 -0
- package/dist/component-router-CErbGh2A.mjs +33 -0
- package/dist/component-router-CErbGh2A.mjs.map +1 -0
- package/dist/component-server-CGzU4bss.d.mts +38 -0
- package/dist/component-server-DAbIyIPI.mjs +124 -0
- package/dist/component-server-DAbIyIPI.mjs.map +1 -0
- package/dist/create-handler-B_pfxh3m.mjs +166 -0
- package/dist/create-handler-B_pfxh3m.mjs.map +1 -0
- package/dist/{utils/create-handler.d.ts → create-handler-Bo-hvWp-.d.mts} +24 -24
- package/dist/{defineRoute.d.ts → defineRoute.d.mts} +20 -17
- package/dist/defineRoute.mjs +93 -0
- package/dist/defineRoute.mjs.map +1 -0
- package/dist/dependency-manager-C6vFWP2L.d.mts +27 -0
- package/dist/dependency-manager-Czg7Po2j.mjs +61 -0
- package/dist/dependency-manager-Czg7Po2j.mjs.map +1 -0
- package/dist/formats-CYLwo9GJ.d.mts +42 -0
- package/dist/go-await-BGAGJfrB.mjs +33 -0
- package/dist/go-await-BGAGJfrB.mjs.map +1 -0
- package/dist/{utils/go-await.d.ts → go-await-DRItVwwh.d.mts} +4 -2
- package/dist/handle-Csjtywdc.mjs +30 -0
- package/dist/handle-Csjtywdc.mjs.map +1 -0
- package/dist/{utils/handle.d.ts → handle-D0TFoOiX.d.mts} +8 -6
- package/dist/html-renderer-CMGKJoIy.d.mts +22 -0
- package/dist/{utils/html-renderer.js → html-renderer-CczE8mwC.mjs} +34 -29
- package/dist/html-renderer-CczE8mwC.mjs.map +1 -0
- package/dist/index-Ci52vt55.d.mts +48 -0
- package/dist/index-CzItj62a.d.mts +1 -0
- package/dist/index.d.mts +30 -0
- package/dist/index.mjs +37 -0
- package/dist/index.mjs.map +1 -0
- package/dist/middleware/{component-renderer.d.ts → component-renderer.d.mts} +3 -1
- package/dist/middleware/component-renderer.mjs +119 -0
- package/dist/middleware/component-renderer.mjs.map +1 -0
- package/dist/middleware/{component-router.d.ts → component-router.d.mts} +5 -4
- package/dist/middleware/component-router.mjs +4 -0
- package/dist/middleware-Byp3Hjae.mjs +59 -0
- package/dist/middleware-Byp3Hjae.mjs.map +1 -0
- package/dist/middleware.d.mts +25 -0
- package/dist/middleware.mjs +4 -0
- package/dist/monitoring/index.d.mts +33 -0
- package/dist/monitoring/index.mjs +27 -0
- package/dist/monitoring/index.mjs.map +1 -0
- package/dist/monitoring/native-monitor.d.mts +48 -0
- package/dist/monitoring/native-monitor.mjs +154 -0
- package/dist/monitoring/native-monitor.mjs.map +1 -0
- package/dist/monitoring/types.d.mts +150 -0
- package/dist/monitoring/types.mjs +1 -0
- package/dist/node-server/index.d.mts +4 -0
- package/dist/node-server/index.mjs +5 -0
- package/dist/node-server/{request.d.ts → request.d.mts} +4 -6
- package/dist/node-server/request.mjs +3 -0
- package/dist/node-server/{response.d.ts → response.d.mts} +4 -6
- package/dist/node-server/response.mjs +3 -0
- package/dist/node-server/serve.d.mts +2 -0
- package/dist/node-server/serve.mjs +4 -0
- package/dist/{utils/parsers.d.ts → parsers-7lvt3Oss.d.mts} +10 -8
- package/dist/parsers-CI_TZ7pO.mjs +168 -0
- package/dist/parsers-CI_TZ7pO.mjs.map +1 -0
- package/dist/path-matcher-73cJd5Y7.mjs +62 -0
- package/dist/path-matcher-73cJd5Y7.mjs.map +1 -0
- package/dist/radix-tree-D0XYaJKb.mjs +157 -0
- package/dist/radix-tree-D0XYaJKb.mjs.map +1 -0
- package/dist/request-CKC3Ox6M.mjs +133 -0
- package/dist/request-CKC3Ox6M.mjs.map +1 -0
- package/dist/request-validator-42lY21gn.d.mts +67 -0
- package/dist/request-validator-_J5HloRq.mjs +77 -0
- package/dist/request-validator-_J5HloRq.mjs.map +1 -0
- package/dist/{utils/response.d.ts → response-A-sZZiJ7.d.mts} +4 -2
- package/dist/response-Cf5FgtmE.mjs +72 -0
- package/dist/response-Cf5FgtmE.mjs.map +1 -0
- package/dist/response-DXg4i_yh.mjs +97 -0
- package/dist/response-DXg4i_yh.mjs.map +1 -0
- package/dist/route-9pAVP1GC.mjs +11 -0
- package/dist/route-9pAVP1GC.mjs.map +1 -0
- package/dist/route-BJ-40LNI.d.mts +44 -0
- package/dist/route-registry--tx1PNui.d.mts +176 -0
- package/dist/route-registry-mcTG0M-t.mjs +225 -0
- package/dist/route-registry-mcTG0M-t.mjs.map +1 -0
- package/dist/router/index.d.mts +5 -0
- package/dist/router/index.mjs +10 -0
- package/dist/router/index.mjs.map +1 -0
- package/dist/router/radix-tree.d.mts +60 -0
- package/dist/router/radix-tree.mjs +4 -0
- package/dist/router-B9HUUCkR.mjs +71 -0
- package/dist/router-B9HUUCkR.mjs.map +1 -0
- package/dist/{router.d.ts → router.d.mts} +6 -7
- package/dist/router.mjs +4 -0
- package/dist/schema-CPQudJpE.d.mts +81 -0
- package/dist/schema-CtVBNfnQ.mjs +1 -0
- package/dist/serve-B5WmhK6m.d.mts +69 -0
- package/dist/serve-Be6NvuQk.mjs +107 -0
- package/dist/serve-Be6NvuQk.mjs.map +1 -0
- package/dist/serve.d.mts +2 -0
- package/dist/serve.mjs +4 -0
- package/dist/server/base-server.d.mts +4 -0
- package/dist/server/base-server.mjs +4 -0
- package/dist/server/component-server.d.mts +5 -0
- package/dist/server/component-server.mjs +5 -0
- package/dist/server/index.d.mts +7 -0
- package/dist/server/index.mjs +8 -0
- package/dist/server/server-factory.d.mts +7 -0
- package/dist/server/server-factory.mjs +6 -0
- package/dist/server/server.d.mts +5 -0
- package/dist/server/server.mjs +4 -0
- package/dist/server-BmPKs8oM.mjs +137 -0
- package/dist/server-BmPKs8oM.mjs.map +1 -0
- package/dist/server-Cbd3Ia51.mjs +88 -0
- package/dist/server-Cbd3Ia51.mjs.map +1 -0
- package/dist/server-D9gjszHe.d.mts +60 -0
- package/dist/sse-BMM6KTfy.d.mts +65 -0
- package/dist/sse-BT5yyLgX.mjs +87 -0
- package/dist/sse-BT5yyLgX.mjs.map +1 -0
- package/dist/types/component-route.d.mts +2 -0
- package/dist/types/component-route.mjs +1 -0
- package/dist/types/index.d.mts +6 -0
- package/dist/types/index.mjs +3 -0
- package/dist/types/route.d.mts +2 -0
- package/dist/types/route.mjs +3 -0
- package/dist/types/schema.d.mts +2 -0
- package/dist/types/schema.mjs +1 -0
- package/dist/types/types.d.mts +2 -0
- package/dist/types/types.mjs +1 -0
- package/dist/{types/types.d.ts → types-DuTa8AVN.d.mts} +27 -25
- package/dist/utils/base64url.d.mts +2 -0
- package/dist/utils/base64url.mjs +3 -0
- package/dist/utils/create-handler.d.mts +3 -0
- package/dist/utils/create-handler.mjs +5 -0
- package/dist/utils/dependency-manager.d.mts +2 -0
- package/dist/utils/dependency-manager.mjs +4 -0
- package/dist/utils/formats.d.mts +2 -0
- package/dist/utils/formats.mjs +129 -0
- package/dist/utils/formats.mjs.map +1 -0
- package/dist/utils/go-await.d.mts +2 -0
- package/dist/utils/go-await.mjs +3 -0
- package/dist/utils/handle.d.mts +2 -0
- package/dist/utils/handle.mjs +4 -0
- package/dist/utils/html-renderer.d.mts +2 -0
- package/dist/utils/html-renderer.mjs +4 -0
- package/dist/utils/index.d.mts +16 -0
- package/dist/utils/index.mjs +23 -0
- package/dist/utils/index.mjs.map +1 -0
- package/dist/utils/parsers.d.mts +2 -0
- package/dist/utils/parsers.mjs +3 -0
- package/dist/utils/path-matcher.d.mts +27 -0
- package/dist/utils/path-matcher.mjs +4 -0
- package/dist/utils/request-validator.d.mts +3 -0
- package/dist/utils/request-validator.mjs +5 -0
- package/dist/utils/response.d.mts +2 -0
- package/dist/utils/response.mjs +4 -0
- package/dist/utils/route-registry.d.mts +4 -0
- package/dist/utils/route-registry.mjs +4 -0
- package/dist/utils/sse.d.mts +3 -0
- package/dist/utils/sse.mjs +5 -0
- package/dist/utils/validators/validators.d.mts +2 -0
- package/dist/utils/validators/validators.mjs +3 -0
- package/dist/validators-BBrGePBr.mjs +112 -0
- package/dist/validators-BBrGePBr.mjs.map +1 -0
- package/dist/validators-CPmnj_y9.d.mts +67 -0
- package/package.json +16 -18
- package/dist/auth/token.d.ts +0 -42
- package/dist/auth/token.js +0 -131
- package/dist/auth/token.js.map +0 -1
- package/dist/defineRoute.js +0 -37
- package/dist/defineRoute.js.map +0 -1
- package/dist/index.d.ts +0 -32
- package/dist/index.js +0 -2578
- package/dist/index.js.map +0 -1
- package/dist/middleware/auth.d.ts +0 -16
- package/dist/middleware/auth.js +0 -205
- package/dist/middleware/auth.js.map +0 -1
- package/dist/middleware/authMiddleware.d.ts +0 -5
- package/dist/middleware/authMiddleware.js +0 -57
- package/dist/middleware/authMiddleware.js.map +0 -1
- package/dist/middleware/component-renderer.js +0 -139
- package/dist/middleware/component-renderer.js.map +0 -1
- package/dist/middleware/component-router.js +0 -36
- package/dist/middleware/component-router.js.map +0 -1
- package/dist/middleware/cors.d.ts +0 -12
- package/dist/middleware/cors.js +0 -43
- package/dist/middleware/cors.js.map +0 -1
- package/dist/middleware/rateLimit.d.ts +0 -10
- package/dist/middleware/rateLimit.js +0 -49
- package/dist/middleware/rateLimit.js.map +0 -1
- package/dist/middleware.d.ts +0 -21
- package/dist/middleware.js +0 -102
- package/dist/middleware.js.map +0 -1
- package/dist/monitoring/index.d.ts +0 -36
- package/dist/monitoring/index.js +0 -1487
- package/dist/monitoring/index.js.map +0 -1
- package/dist/monitoring/native-monitor.d.ts +0 -44
- package/dist/monitoring/native-monitor.js +0 -1454
- package/dist/monitoring/native-monitor.js.map +0 -1
- package/dist/monitoring/types.d.ts +0 -148
- package/dist/monitoring/types.js +0 -8
- package/dist/monitoring/types.js.map +0 -1
- package/dist/node-server/index.d.ts +0 -4
- package/dist/node-server/index.js +0 -298
- package/dist/node-server/index.js.map +0 -1
- package/dist/node-server/request.js +0 -125
- package/dist/node-server/request.js.map +0 -1
- package/dist/node-server/response.js +0 -76
- package/dist/node-server/response.js.map +0 -1
- package/dist/node-server/serve.d.ts +0 -71
- package/dist/node-server/serve.js +0 -281
- package/dist/node-server/serve.js.map +0 -1
- package/dist/router/index.d.ts +0 -3
- package/dist/router/index.js +0 -232
- package/dist/router/index.js.map +0 -1
- package/dist/router/radix-tree.d.ts +0 -66
- package/dist/router/radix-tree.js +0 -190
- package/dist/router/radix-tree.js.map +0 -1
- package/dist/router.js +0 -44
- package/dist/router.js.map +0 -1
- package/dist/serve.d.ts +0 -2
- package/dist/serve.js +0 -281
- package/dist/serve.js.map +0 -1
- package/dist/server/base-server.d.ts +0 -37
- package/dist/server/base-server.js +0 -134
- package/dist/server/base-server.js.map +0 -1
- package/dist/server/component-server.d.ts +0 -37
- package/dist/server/component-server.js +0 -488
- package/dist/server/component-server.js.map +0 -1
- package/dist/server/index.d.ts +0 -8
- package/dist/server/index.js +0 -1159
- package/dist/server/index.js.map +0 -1
- package/dist/server/server-factory.d.ts +0 -48
- package/dist/server/server-factory.js +0 -1156
- package/dist/server/server-factory.js.map +0 -1
- package/dist/server/server.d.ts +0 -64
- package/dist/server/server.js +0 -737
- package/dist/server/server.js.map +0 -1
- package/dist/types/component-route.d.ts +0 -29
- package/dist/types/component-route.js +0 -1
- package/dist/types/component-route.js.map +0 -1
- package/dist/types/index.d.ts +0 -5
- package/dist/types/index.js +0 -21
- package/dist/types/index.js.map +0 -1
- package/dist/types/route.d.ts +0 -42
- package/dist/types/route.js +0 -12
- package/dist/types/route.js.map +0 -1
- package/dist/types/schema.d.ts +0 -79
- package/dist/types/schema.js +0 -10
- package/dist/types/schema.js.map +0 -1
- package/dist/types/types.js +0 -1
- package/dist/types/types.js.map +0 -1
- package/dist/utils/base64url.d.ts +0 -4
- package/dist/utils/base64url.js +0 -14
- package/dist/utils/base64url.js.map +0 -1
- package/dist/utils/create-handler.js +0 -299
- package/dist/utils/create-handler.js.map +0 -1
- package/dist/utils/dependency-manager.d.ts +0 -25
- package/dist/utils/dependency-manager.js +0 -71
- package/dist/utils/dependency-manager.js.map +0 -1
- package/dist/utils/formats.d.ts +0 -40
- package/dist/utils/formats.js +0 -116
- package/dist/utils/formats.js.map +0 -1
- package/dist/utils/go-await.js +0 -16
- package/dist/utils/go-await.js.map +0 -1
- package/dist/utils/handle.js +0 -48
- package/dist/utils/handle.js.map +0 -1
- package/dist/utils/html-renderer.d.ts +0 -20
- package/dist/utils/html-renderer.js.map +0 -1
- package/dist/utils/index.d.ts +0 -16
- package/dist/utils/index.js +0 -1038
- package/dist/utils/index.js.map +0 -1
- package/dist/utils/parsers.js +0 -160
- package/dist/utils/parsers.js.map +0 -1
- package/dist/utils/path-matcher.d.ts +0 -25
- package/dist/utils/path-matcher.js +0 -73
- package/dist/utils/path-matcher.js.map +0 -1
- package/dist/utils/request-validator.d.ts +0 -66
- package/dist/utils/request-validator.js +0 -158
- package/dist/utils/request-validator.js.map +0 -1
- package/dist/utils/response.js +0 -102
- package/dist/utils/response.js.map +0 -1
- package/dist/utils/route-registry.d.ts +0 -195
- package/dist/utils/route-registry.js +0 -152
- package/dist/utils/route-registry.js.map +0 -1
- package/dist/utils/sse.d.ts +0 -87
- package/dist/utils/sse.js +0 -181
- package/dist/utils/sse.js.map +0 -1
- package/dist/utils/validators/validators.d.ts +0 -76
- package/dist/utils/validators/validators.js +0 -97
- package/dist/utils/validators/validators.js.map +0 -1
package/dist/index.js
DELETED
|
@@ -1,2578 +0,0 @@
|
|
|
1
|
-
// src/router.ts
|
|
2
|
-
function flattenNestedRoutes(routes) {
|
|
3
|
-
const flattened = [];
|
|
4
|
-
function processRoute(route2, parentPath = "", parentMiddleware = [], parentName) {
|
|
5
|
-
const currentPath = normalizePath(parentPath + route2.path);
|
|
6
|
-
const currentMiddleware = [
|
|
7
|
-
...parentMiddleware,
|
|
8
|
-
...route2.middleware || []
|
|
9
|
-
];
|
|
10
|
-
const currentName = route2.name || parentName;
|
|
11
|
-
if ("method" in route2 && "handler" in route2) {
|
|
12
|
-
const leafRoute = route2;
|
|
13
|
-
flattened.push({
|
|
14
|
-
...leafRoute,
|
|
15
|
-
fullPath: currentPath,
|
|
16
|
-
middlewareChain: currentMiddleware,
|
|
17
|
-
parentName
|
|
18
|
-
// 保存父级名称
|
|
19
|
-
});
|
|
20
|
-
} else if ("children" in route2 && route2.children) {
|
|
21
|
-
for (const child of route2.children) {
|
|
22
|
-
processRoute(child, currentPath, currentMiddleware, currentName);
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
for (const route2 of routes) {
|
|
27
|
-
processRoute(route2);
|
|
28
|
-
}
|
|
29
|
-
return flattened;
|
|
30
|
-
}
|
|
31
|
-
function normalizePath(path) {
|
|
32
|
-
let normalized = decodeURIComponent(path);
|
|
33
|
-
normalized = normalized.replace(/\/+/g, "/");
|
|
34
|
-
if (normalized === "") return "/";
|
|
35
|
-
if (normalized !== "/" && normalized.endsWith("/")) {
|
|
36
|
-
normalized = normalized.slice(0, -1);
|
|
37
|
-
}
|
|
38
|
-
return normalized;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
// src/utils/response.ts
|
|
42
|
-
function json(data, status = 200, headers = {}) {
|
|
43
|
-
const body = JSON.stringify(data);
|
|
44
|
-
if (Object.keys(headers).length === 0) {
|
|
45
|
-
return new Response(body, {
|
|
46
|
-
status,
|
|
47
|
-
headers: { "Content-Type": "application/json" }
|
|
48
|
-
});
|
|
49
|
-
}
|
|
50
|
-
const h = new Headers({
|
|
51
|
-
"Content-Type": "application/json",
|
|
52
|
-
...headers
|
|
53
|
-
});
|
|
54
|
-
return new Response(body, {
|
|
55
|
-
status,
|
|
56
|
-
headers: h
|
|
57
|
-
});
|
|
58
|
-
}
|
|
59
|
-
var JSON_HEADERS = { "Content-Type": "application/json" };
|
|
60
|
-
var TEXT_HEADERS = { "Content-Type": "text/plain" };
|
|
61
|
-
function mapResponse(response) {
|
|
62
|
-
if (response instanceof Response) return response;
|
|
63
|
-
switch (response?.constructor?.name) {
|
|
64
|
-
case "String":
|
|
65
|
-
return new Response(response, { headers: TEXT_HEADERS });
|
|
66
|
-
case "Object":
|
|
67
|
-
case "Array":
|
|
68
|
-
return new Response(JSON.stringify(response), { headers: JSON_HEADERS });
|
|
69
|
-
case "Number":
|
|
70
|
-
case "Boolean":
|
|
71
|
-
return new Response(String(response), { headers: TEXT_HEADERS });
|
|
72
|
-
case void 0:
|
|
73
|
-
return new Response(null, { status: 204 });
|
|
74
|
-
case "ReadableStream":
|
|
75
|
-
return new Response(response);
|
|
76
|
-
case "Blob":
|
|
77
|
-
return new Response(response);
|
|
78
|
-
case "ArrayBuffer":
|
|
79
|
-
return new Response(response);
|
|
80
|
-
case "Uint8Array":
|
|
81
|
-
return new Response(response);
|
|
82
|
-
default:
|
|
83
|
-
if (response instanceof Promise) {
|
|
84
|
-
return response.then(mapResponse);
|
|
85
|
-
}
|
|
86
|
-
return new Response(JSON.stringify(response), { headers: JSON_HEADERS });
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
function redirect(location, status = 302) {
|
|
90
|
-
return new Response(null, {
|
|
91
|
-
status,
|
|
92
|
-
headers: {
|
|
93
|
-
Location: location
|
|
94
|
-
}
|
|
95
|
-
});
|
|
96
|
-
}
|
|
97
|
-
function text(content, status = 200, headers = {}) {
|
|
98
|
-
const h = new Headers({
|
|
99
|
-
"Content-Type": "text/plain; charset=utf-8",
|
|
100
|
-
...headers
|
|
101
|
-
});
|
|
102
|
-
return new Response(content, {
|
|
103
|
-
status,
|
|
104
|
-
headers: h
|
|
105
|
-
});
|
|
106
|
-
}
|
|
107
|
-
function html(content, status = 200, headers = {}) {
|
|
108
|
-
const h = new Headers({
|
|
109
|
-
"Content-Type": "text/html; charset=utf-8",
|
|
110
|
-
...headers
|
|
111
|
-
});
|
|
112
|
-
return new Response(content, {
|
|
113
|
-
status,
|
|
114
|
-
headers: h
|
|
115
|
-
});
|
|
116
|
-
}
|
|
117
|
-
function empty(status = 204, headers = {}) {
|
|
118
|
-
return new Response(null, {
|
|
119
|
-
status,
|
|
120
|
-
headers
|
|
121
|
-
});
|
|
122
|
-
}
|
|
123
|
-
function stream(stream2, status = 200, headers = {}) {
|
|
124
|
-
const h = new Headers({
|
|
125
|
-
"Content-Type": "application/octet-stream",
|
|
126
|
-
...headers
|
|
127
|
-
});
|
|
128
|
-
return new Response(stream2, {
|
|
129
|
-
status,
|
|
130
|
-
headers: h
|
|
131
|
-
});
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
// src/middleware.ts
|
|
135
|
-
var VafastError = class extends Error {
|
|
136
|
-
status;
|
|
137
|
-
type;
|
|
138
|
-
expose;
|
|
139
|
-
constructor(message, options = {}) {
|
|
140
|
-
super(message);
|
|
141
|
-
this.name = "VafastError";
|
|
142
|
-
this.status = options.status ?? 500;
|
|
143
|
-
this.type = options.type ?? "internal_error";
|
|
144
|
-
this.expose = options.expose ?? false;
|
|
145
|
-
if (options.cause) this.cause = options.cause;
|
|
146
|
-
}
|
|
147
|
-
};
|
|
148
|
-
function composeMiddleware(middleware, finalHandler) {
|
|
149
|
-
const all = [errorHandler, ...middleware];
|
|
150
|
-
return function composedHandler(req) {
|
|
151
|
-
let i = -1;
|
|
152
|
-
const dispatch = (index) => {
|
|
153
|
-
if (index <= i)
|
|
154
|
-
return Promise.reject(new Error("next() called multiple times"));
|
|
155
|
-
i = index;
|
|
156
|
-
if (index < all.length) {
|
|
157
|
-
const mw = all[index];
|
|
158
|
-
return Promise.resolve(mw(req, () => dispatch(index + 1)));
|
|
159
|
-
}
|
|
160
|
-
return Promise.resolve(finalHandler(req)).then(mapResponse);
|
|
161
|
-
};
|
|
162
|
-
return dispatch(0);
|
|
163
|
-
};
|
|
164
|
-
}
|
|
165
|
-
var errorHandler = async (req, next) => {
|
|
166
|
-
try {
|
|
167
|
-
return await next();
|
|
168
|
-
} catch (err) {
|
|
169
|
-
console.error("\u672A\u5904\u7406\u7684\u9519\u8BEF:", err);
|
|
170
|
-
if (err instanceof VafastError) {
|
|
171
|
-
return json(
|
|
172
|
-
{
|
|
173
|
-
error: err.type,
|
|
174
|
-
message: err.expose ? err.message : "\u53D1\u751F\u4E86\u4E00\u4E2A\u9519\u8BEF"
|
|
175
|
-
},
|
|
176
|
-
err.status
|
|
177
|
-
);
|
|
178
|
-
}
|
|
179
|
-
return json({ error: "internal_error", message: "\u51FA\u73B0\u4E86\u4E00\u4E9B\u95EE\u9898" }, 500);
|
|
180
|
-
}
|
|
181
|
-
};
|
|
182
|
-
|
|
183
|
-
// src/server/base-server.ts
|
|
184
|
-
var BaseServer = class {
|
|
185
|
-
globalMiddleware = [];
|
|
186
|
-
use(mw) {
|
|
187
|
-
this.globalMiddleware.push(mw);
|
|
188
|
-
}
|
|
189
|
-
/**
|
|
190
|
-
* 打印扁平化后的路由信息,用于调试
|
|
191
|
-
*/
|
|
192
|
-
logFlattenedRoutes(routes, type = "\u8DEF\u7531") {
|
|
193
|
-
console.log(`\u{1F680} \u6241\u5E73\u5316\u540E\u7684${type}:`);
|
|
194
|
-
for (const route2 of routes) {
|
|
195
|
-
const method = route2.method || "GET";
|
|
196
|
-
const path = route2.fullPath || route2.path;
|
|
197
|
-
console.log(` ${method} ${path}`);
|
|
198
|
-
if (route2.middlewareChain && route2.middlewareChain.length > 0) {
|
|
199
|
-
console.log(` \u4E2D\u95F4\u4EF6\u94FE: ${route2.middlewareChain.length} \u4E2A`);
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
console.log("");
|
|
203
|
-
}
|
|
204
|
-
/**
|
|
205
|
-
* 检测路由冲突
|
|
206
|
-
* 检查是否有路径相同但方法不同的路由,以及潜在的路径冲突
|
|
207
|
-
*/
|
|
208
|
-
detectRouteConflicts(routes) {
|
|
209
|
-
const pathGroups = /* @__PURE__ */ new Map();
|
|
210
|
-
for (const route2 of routes) {
|
|
211
|
-
const path = route2.fullPath || route2.path;
|
|
212
|
-
const method = route2.method || "GET";
|
|
213
|
-
if (!pathGroups.has(path)) {
|
|
214
|
-
pathGroups.set(path, []);
|
|
215
|
-
}
|
|
216
|
-
pathGroups.get(path).push({ ...route2, method });
|
|
217
|
-
}
|
|
218
|
-
for (const [path, routeList] of pathGroups) {
|
|
219
|
-
if (routeList.length > 1) {
|
|
220
|
-
const methods = routeList.map((r) => r.method);
|
|
221
|
-
const uniqueMethods = [...new Set(methods)];
|
|
222
|
-
if (uniqueMethods.length === 1) {
|
|
223
|
-
console.warn(
|
|
224
|
-
`\u26A0\uFE0F \u8DEF\u7531\u51B2\u7A81: ${uniqueMethods[0]} ${path} \u5B9A\u4E49\u4E86 ${routeList.length} \u6B21`
|
|
225
|
-
);
|
|
226
|
-
routeList.forEach((route2, index) => {
|
|
227
|
-
console.warn(` ${index + 1}. ${route2.method} ${path}`);
|
|
228
|
-
});
|
|
229
|
-
} else {
|
|
230
|
-
console.log(`\u2139\uFE0F \u8DEF\u5F84 ${path} \u652F\u6301\u65B9\u6CD5: ${uniqueMethods.join(", ")}`);
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
this.detectDynamicRouteConflicts(routes);
|
|
235
|
-
}
|
|
236
|
-
/**
|
|
237
|
-
* 检测动态路由的潜在冲突
|
|
238
|
-
*/
|
|
239
|
-
detectDynamicRouteConflicts(routes) {
|
|
240
|
-
const dynamicRoutes = routes.filter((r) => {
|
|
241
|
-
const path = r.fullPath || r.path;
|
|
242
|
-
return path.includes(":") || path.includes("*");
|
|
243
|
-
});
|
|
244
|
-
for (let i = 0; i < dynamicRoutes.length; i++) {
|
|
245
|
-
for (let j = i + 1; j < dynamicRoutes.length; j++) {
|
|
246
|
-
const route1 = dynamicRoutes[i];
|
|
247
|
-
const route2 = dynamicRoutes[j];
|
|
248
|
-
const method1 = route1.method || "GET";
|
|
249
|
-
const method2 = route2.method || "GET";
|
|
250
|
-
if (method1 === method2) {
|
|
251
|
-
const path1 = route1.fullPath || route1.path;
|
|
252
|
-
const path2 = route2.fullPath || route2.path;
|
|
253
|
-
if (this.pathsMayConflict(path1, path2)) {
|
|
254
|
-
console.warn(
|
|
255
|
-
`\u26A0\uFE0F \u6F5C\u5728\u8DEF\u7531\u51B2\u7A81: ${method1} ${path1} \u53EF\u80FD\u4E0E ${path2} \u51B2\u7A81`
|
|
256
|
-
);
|
|
257
|
-
}
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
|
-
}
|
|
262
|
-
/**
|
|
263
|
-
* 判断两个路径是否可能冲突
|
|
264
|
-
*/
|
|
265
|
-
pathsMayConflict(path1, path2) {
|
|
266
|
-
const parts1 = path1.split("/").filter(Boolean);
|
|
267
|
-
const parts2 = path2.split("/").filter(Boolean);
|
|
268
|
-
if (parts1.length !== parts2.length) return false;
|
|
269
|
-
for (let i = 0; i < parts1.length; i++) {
|
|
270
|
-
const p1 = parts1[i];
|
|
271
|
-
const p2 = parts2[i];
|
|
272
|
-
if (!p1.startsWith(":") && !p1.startsWith("*") && !p2.startsWith(":") && !p2.startsWith("*") && p1 !== p2) {
|
|
273
|
-
return false;
|
|
274
|
-
}
|
|
275
|
-
if (p1 === "*" && p2.startsWith(":") || p2 === "*" && p1.startsWith(":")) {
|
|
276
|
-
return true;
|
|
277
|
-
}
|
|
278
|
-
}
|
|
279
|
-
return false;
|
|
280
|
-
}
|
|
281
|
-
/**
|
|
282
|
-
* 路径匹配
|
|
283
|
-
*/
|
|
284
|
-
matchPath(pattern, path) {
|
|
285
|
-
const patternParts = pattern.split("/").filter(Boolean);
|
|
286
|
-
const pathParts = path.split("/").filter(Boolean);
|
|
287
|
-
if (patternParts.length !== pathParts.length) {
|
|
288
|
-
return false;
|
|
289
|
-
}
|
|
290
|
-
for (let i = 0; i < patternParts.length; i++) {
|
|
291
|
-
if (patternParts[i] !== pathParts[i] && !patternParts[i].startsWith(":")) {
|
|
292
|
-
return false;
|
|
293
|
-
}
|
|
294
|
-
}
|
|
295
|
-
return true;
|
|
296
|
-
}
|
|
297
|
-
/**
|
|
298
|
-
* 提取路径参数
|
|
299
|
-
*/
|
|
300
|
-
extractParams(pattern, path) {
|
|
301
|
-
const params = {};
|
|
302
|
-
const patternParts = pattern.split("/").filter(Boolean);
|
|
303
|
-
const pathParts = path.split("/").filter(Boolean);
|
|
304
|
-
for (let i = 0; i < patternParts.length; i++) {
|
|
305
|
-
if (patternParts[i].startsWith(":")) {
|
|
306
|
-
const paramName = patternParts[i].slice(1);
|
|
307
|
-
params[paramName] = pathParts[i];
|
|
308
|
-
}
|
|
309
|
-
}
|
|
310
|
-
return params;
|
|
311
|
-
}
|
|
312
|
-
};
|
|
313
|
-
|
|
314
|
-
// src/router/radix-tree.ts
|
|
315
|
-
var RadixRouter = class {
|
|
316
|
-
root;
|
|
317
|
-
constructor() {
|
|
318
|
-
this.root = this.createNode("");
|
|
319
|
-
}
|
|
320
|
-
createNode(path) {
|
|
321
|
-
return {
|
|
322
|
-
path,
|
|
323
|
-
children: /* @__PURE__ */ Object.create(null),
|
|
324
|
-
handlers: /* @__PURE__ */ Object.create(null)
|
|
325
|
-
};
|
|
326
|
-
}
|
|
327
|
-
/** 分割路径 */
|
|
328
|
-
splitPath(path) {
|
|
329
|
-
return path.split("/").filter(Boolean);
|
|
330
|
-
}
|
|
331
|
-
/** 编译器函数 - 用于预编译中间件链 */
|
|
332
|
-
compiler;
|
|
333
|
-
/** 设置中间件编译器 */
|
|
334
|
-
setCompiler(compiler) {
|
|
335
|
-
this.compiler = compiler;
|
|
336
|
-
}
|
|
337
|
-
/** 注册路由 */
|
|
338
|
-
register(method, pattern, handler, middleware = []) {
|
|
339
|
-
const segments = this.splitPath(pattern);
|
|
340
|
-
let node = this.root;
|
|
341
|
-
for (const segment of segments) {
|
|
342
|
-
const firstChar = segment[0];
|
|
343
|
-
if (firstChar === ":") {
|
|
344
|
-
if (!node.paramChild) {
|
|
345
|
-
node.paramChild = this.createNode(segment);
|
|
346
|
-
node.paramChild.paramName = segment.substring(1);
|
|
347
|
-
}
|
|
348
|
-
node = node.paramChild;
|
|
349
|
-
} else if (firstChar === "*") {
|
|
350
|
-
if (!node.wildcardChild) {
|
|
351
|
-
node.wildcardChild = this.createNode(segment);
|
|
352
|
-
node.wildcardChild.paramName = segment.length > 1 ? segment.substring(1) : "*";
|
|
353
|
-
}
|
|
354
|
-
node = node.wildcardChild;
|
|
355
|
-
break;
|
|
356
|
-
} else {
|
|
357
|
-
if (!node.children[segment]) {
|
|
358
|
-
node.children[segment] = this.createNode(segment);
|
|
359
|
-
}
|
|
360
|
-
node = node.children[segment];
|
|
361
|
-
}
|
|
362
|
-
}
|
|
363
|
-
const routeHandler = { handler, middleware };
|
|
364
|
-
if (this.compiler && middleware.length === 0) {
|
|
365
|
-
routeHandler.compiled = this.compiler([], handler);
|
|
366
|
-
}
|
|
367
|
-
node.handlers[method] = routeHandler;
|
|
368
|
-
}
|
|
369
|
-
/** 预编译所有路由(在添加全局中间件后调用) */
|
|
370
|
-
precompileAll(globalMiddleware) {
|
|
371
|
-
if (!this.compiler) return;
|
|
372
|
-
this.precompileNode(this.root, globalMiddleware);
|
|
373
|
-
}
|
|
374
|
-
precompileNode(node, globalMiddleware) {
|
|
375
|
-
for (const method in node.handlers) {
|
|
376
|
-
const routeHandler = node.handlers[method];
|
|
377
|
-
if (routeHandler) {
|
|
378
|
-
const allMiddleware = [...globalMiddleware, ...routeHandler.middleware];
|
|
379
|
-
routeHandler.compiled = this.compiler(
|
|
380
|
-
allMiddleware,
|
|
381
|
-
routeHandler.handler
|
|
382
|
-
);
|
|
383
|
-
}
|
|
384
|
-
}
|
|
385
|
-
for (const key in node.children) {
|
|
386
|
-
this.precompileNode(node.children[key], globalMiddleware);
|
|
387
|
-
}
|
|
388
|
-
if (node.paramChild) {
|
|
389
|
-
this.precompileNode(node.paramChild, globalMiddleware);
|
|
390
|
-
}
|
|
391
|
-
if (node.wildcardChild) {
|
|
392
|
-
this.precompileNode(node.wildcardChild, globalMiddleware);
|
|
393
|
-
}
|
|
394
|
-
}
|
|
395
|
-
/** 匹配路由 */
|
|
396
|
-
match(method, path) {
|
|
397
|
-
const segments = this.splitPath(path);
|
|
398
|
-
const params = /* @__PURE__ */ Object.create(null);
|
|
399
|
-
const node = this.matchNode(this.root, segments, 0, params);
|
|
400
|
-
if (!node) return null;
|
|
401
|
-
const routeHandler = node.handlers[method];
|
|
402
|
-
if (!routeHandler) return null;
|
|
403
|
-
return {
|
|
404
|
-
handler: routeHandler.handler,
|
|
405
|
-
middleware: routeHandler.middleware,
|
|
406
|
-
params,
|
|
407
|
-
compiled: routeHandler.compiled
|
|
408
|
-
};
|
|
409
|
-
}
|
|
410
|
-
/** 递归匹配节点 (优先级: 静态 > 动态参数 > 通配符) */
|
|
411
|
-
matchNode(node, segments, index, params) {
|
|
412
|
-
if (index === segments.length) {
|
|
413
|
-
for (const method in node.handlers) {
|
|
414
|
-
if (node.handlers[method]) return node;
|
|
415
|
-
}
|
|
416
|
-
return null;
|
|
417
|
-
}
|
|
418
|
-
const segment = segments[index];
|
|
419
|
-
const staticChild = node.children[segment];
|
|
420
|
-
if (staticChild) {
|
|
421
|
-
const result = this.matchNode(staticChild, segments, index + 1, params);
|
|
422
|
-
if (result) return result;
|
|
423
|
-
}
|
|
424
|
-
if (node.paramChild) {
|
|
425
|
-
const paramName = node.paramChild.paramName;
|
|
426
|
-
const oldValue = params[paramName];
|
|
427
|
-
params[paramName] = segment;
|
|
428
|
-
const result = this.matchNode(
|
|
429
|
-
node.paramChild,
|
|
430
|
-
segments,
|
|
431
|
-
index + 1,
|
|
432
|
-
params
|
|
433
|
-
);
|
|
434
|
-
if (result) return result;
|
|
435
|
-
if (oldValue === void 0) {
|
|
436
|
-
delete params[paramName];
|
|
437
|
-
} else {
|
|
438
|
-
params[paramName] = oldValue;
|
|
439
|
-
}
|
|
440
|
-
}
|
|
441
|
-
if (node.wildcardChild) {
|
|
442
|
-
params[node.wildcardChild.paramName || "*"] = segments.slice(index).join("/");
|
|
443
|
-
return node.wildcardChild;
|
|
444
|
-
}
|
|
445
|
-
return null;
|
|
446
|
-
}
|
|
447
|
-
/** 获取路径允许的 HTTP 方法 */
|
|
448
|
-
getAllowedMethods(path) {
|
|
449
|
-
const segments = this.splitPath(path);
|
|
450
|
-
const node = this.findNode(segments);
|
|
451
|
-
if (!node) return [];
|
|
452
|
-
const methods = [];
|
|
453
|
-
for (const method in node.handlers) {
|
|
454
|
-
if (node.handlers[method]) {
|
|
455
|
-
methods.push(method);
|
|
456
|
-
}
|
|
457
|
-
}
|
|
458
|
-
return methods;
|
|
459
|
-
}
|
|
460
|
-
/** 查找节点(不提取参数) */
|
|
461
|
-
findNode(segments) {
|
|
462
|
-
let node = this.root;
|
|
463
|
-
for (const segment of segments) {
|
|
464
|
-
if (node.children[segment]) {
|
|
465
|
-
node = node.children[segment];
|
|
466
|
-
} else if (node.paramChild) {
|
|
467
|
-
node = node.paramChild;
|
|
468
|
-
} else if (node.wildcardChild) {
|
|
469
|
-
return node.wildcardChild;
|
|
470
|
-
} else {
|
|
471
|
-
return null;
|
|
472
|
-
}
|
|
473
|
-
}
|
|
474
|
-
return node;
|
|
475
|
-
}
|
|
476
|
-
/** 获取所有已注册的路由 */
|
|
477
|
-
getRoutes() {
|
|
478
|
-
const routes = [];
|
|
479
|
-
this.collectRoutes(this.root, "", routes);
|
|
480
|
-
return routes;
|
|
481
|
-
}
|
|
482
|
-
collectRoutes(node, prefix, routes) {
|
|
483
|
-
const currentPath = prefix + (node.path ? "/" + node.path : "");
|
|
484
|
-
for (const method in node.handlers) {
|
|
485
|
-
if (node.handlers[method]) {
|
|
486
|
-
routes.push({ method, path: currentPath || "/" });
|
|
487
|
-
}
|
|
488
|
-
}
|
|
489
|
-
for (const key in node.children) {
|
|
490
|
-
this.collectRoutes(node.children[key], currentPath, routes);
|
|
491
|
-
}
|
|
492
|
-
if (node.paramChild) {
|
|
493
|
-
this.collectRoutes(node.paramChild, currentPath, routes);
|
|
494
|
-
}
|
|
495
|
-
if (node.wildcardChild) {
|
|
496
|
-
this.collectRoutes(node.wildcardChild, currentPath, routes);
|
|
497
|
-
}
|
|
498
|
-
}
|
|
499
|
-
};
|
|
500
|
-
|
|
501
|
-
// src/utils/route-registry.ts
|
|
502
|
-
var RouteRegistry = class {
|
|
503
|
-
/** 所有路由元信息 */
|
|
504
|
-
routes = [];
|
|
505
|
-
/** 路由映射表:METHOD:fullPath -> RouteMeta */
|
|
506
|
-
routeMap = /* @__PURE__ */ new Map();
|
|
507
|
-
/** 分类映射表:category -> RouteMeta[] */
|
|
508
|
-
categoryMap = /* @__PURE__ */ new Map();
|
|
509
|
-
constructor(routes) {
|
|
510
|
-
this.buildRegistry(routes);
|
|
511
|
-
}
|
|
512
|
-
/**
|
|
513
|
-
* 构建注册表
|
|
514
|
-
*/
|
|
515
|
-
buildRegistry(routes) {
|
|
516
|
-
for (const route2 of routes) {
|
|
517
|
-
const meta = {
|
|
518
|
-
method: route2.method,
|
|
519
|
-
path: route2.path,
|
|
520
|
-
fullPath: route2.fullPath,
|
|
521
|
-
name: route2.name,
|
|
522
|
-
description: route2.description
|
|
523
|
-
};
|
|
524
|
-
for (const key of Object.keys(route2)) {
|
|
525
|
-
if (!["method", "path", "fullPath", "name", "description", "handler", "middleware", "middlewareChain"].includes(key)) {
|
|
526
|
-
meta[key] = route2[key];
|
|
527
|
-
}
|
|
528
|
-
}
|
|
529
|
-
this.routes.push(meta);
|
|
530
|
-
this.routeMap.set(`${route2.method}:${route2.fullPath}`, meta);
|
|
531
|
-
const category = this.extractCategory(route2.fullPath);
|
|
532
|
-
if (!this.categoryMap.has(category)) {
|
|
533
|
-
this.categoryMap.set(category, []);
|
|
534
|
-
}
|
|
535
|
-
this.categoryMap.get(category).push(meta);
|
|
536
|
-
}
|
|
537
|
-
}
|
|
538
|
-
/**
|
|
539
|
-
* 提取分类(第一段路径)
|
|
540
|
-
*/
|
|
541
|
-
extractCategory(path) {
|
|
542
|
-
const segments = path.split("/").filter(Boolean);
|
|
543
|
-
return segments[0] || "root";
|
|
544
|
-
}
|
|
545
|
-
// ============================================
|
|
546
|
-
// 查询接口
|
|
547
|
-
// ============================================
|
|
548
|
-
/**
|
|
549
|
-
* 获取所有路由元信息
|
|
550
|
-
*/
|
|
551
|
-
getAll() {
|
|
552
|
-
return [...this.routes];
|
|
553
|
-
}
|
|
554
|
-
/**
|
|
555
|
-
* 按 method + path 查询路由
|
|
556
|
-
*/
|
|
557
|
-
get(method, path) {
|
|
558
|
-
return this.routeMap.get(`${method}:${path}`);
|
|
559
|
-
}
|
|
560
|
-
/**
|
|
561
|
-
* 检查路由是否存在
|
|
562
|
-
*/
|
|
563
|
-
has(method, path) {
|
|
564
|
-
return this.routeMap.has(`${method}:${path}`);
|
|
565
|
-
}
|
|
566
|
-
/**
|
|
567
|
-
* 按分类获取路由
|
|
568
|
-
*/
|
|
569
|
-
getByCategory(category) {
|
|
570
|
-
return this.categoryMap.get(category) || [];
|
|
571
|
-
}
|
|
572
|
-
/**
|
|
573
|
-
* 获取所有分类
|
|
574
|
-
*/
|
|
575
|
-
getCategories() {
|
|
576
|
-
return Array.from(this.categoryMap.keys()).sort();
|
|
577
|
-
}
|
|
578
|
-
/**
|
|
579
|
-
* 筛选有特定字段的路由
|
|
580
|
-
*
|
|
581
|
-
* @example
|
|
582
|
-
* ```typescript
|
|
583
|
-
* // 获取所有配置了 webhook 的路由
|
|
584
|
-
* const webhookRoutes = registry.filter('webhook')
|
|
585
|
-
* ```
|
|
586
|
-
*/
|
|
587
|
-
filter(field) {
|
|
588
|
-
return this.routes.filter((r) => field in r && r[field] !== void 0);
|
|
589
|
-
}
|
|
590
|
-
/**
|
|
591
|
-
* 按条件筛选路由
|
|
592
|
-
*
|
|
593
|
-
* @example
|
|
594
|
-
* ```typescript
|
|
595
|
-
* // 获取所有 POST 请求
|
|
596
|
-
* const postRoutes = registry.filterBy(r => r.method === 'POST')
|
|
597
|
-
* ```
|
|
598
|
-
*/
|
|
599
|
-
filterBy(predicate) {
|
|
600
|
-
return this.routes.filter(predicate);
|
|
601
|
-
}
|
|
602
|
-
/**
|
|
603
|
-
* 获取路由数量
|
|
604
|
-
*/
|
|
605
|
-
get size() {
|
|
606
|
-
return this.routes.length;
|
|
607
|
-
}
|
|
608
|
-
/**
|
|
609
|
-
* 遍历所有路由
|
|
610
|
-
*/
|
|
611
|
-
forEach(callback) {
|
|
612
|
-
this.routes.forEach(callback);
|
|
613
|
-
}
|
|
614
|
-
/**
|
|
615
|
-
* 映射所有路由
|
|
616
|
-
*/
|
|
617
|
-
map(callback) {
|
|
618
|
-
return this.routes.map(callback);
|
|
619
|
-
}
|
|
620
|
-
};
|
|
621
|
-
function createRouteRegistry(routes) {
|
|
622
|
-
return new RouteRegistry(routes);
|
|
623
|
-
}
|
|
624
|
-
var globalRegistry = null;
|
|
625
|
-
function setGlobalRegistry(registry) {
|
|
626
|
-
globalRegistry = registry;
|
|
627
|
-
}
|
|
628
|
-
function getRouteRegistry() {
|
|
629
|
-
if (!globalRegistry) {
|
|
630
|
-
throw new Error("RouteRegistry not initialized. Make sure Server is created first.");
|
|
631
|
-
}
|
|
632
|
-
return globalRegistry;
|
|
633
|
-
}
|
|
634
|
-
function getRoute(method, path) {
|
|
635
|
-
return getRouteRegistry().get(method, path);
|
|
636
|
-
}
|
|
637
|
-
function getAllRoutes() {
|
|
638
|
-
return getRouteRegistry().getAll();
|
|
639
|
-
}
|
|
640
|
-
function filterRoutes(field) {
|
|
641
|
-
return getRouteRegistry().filter(field);
|
|
642
|
-
}
|
|
643
|
-
|
|
644
|
-
// src/server/server.ts
|
|
645
|
-
var Server = class extends BaseServer {
|
|
646
|
-
router;
|
|
647
|
-
routes;
|
|
648
|
-
/** 是否已预编译 */
|
|
649
|
-
isCompiled = false;
|
|
650
|
-
/** 预编译时的全局中间件数量 */
|
|
651
|
-
compiledWithMiddlewareCount = 0;
|
|
652
|
-
constructor(routes = []) {
|
|
653
|
-
super();
|
|
654
|
-
this.router = new RadixRouter();
|
|
655
|
-
this.routes = [];
|
|
656
|
-
this.router.setCompiler(
|
|
657
|
-
(middleware, handler) => composeMiddleware(middleware, handler)
|
|
658
|
-
);
|
|
659
|
-
if (routes.length > 0) {
|
|
660
|
-
this.registerRoutes(routes);
|
|
661
|
-
}
|
|
662
|
-
}
|
|
663
|
-
/**
|
|
664
|
-
* 预编译所有路由处理链
|
|
665
|
-
* 在添加所有路由和全局中间件后调用,可提升运行时性能
|
|
666
|
-
*/
|
|
667
|
-
compile() {
|
|
668
|
-
this.router.precompileAll(this.globalMiddleware);
|
|
669
|
-
this.isCompiled = true;
|
|
670
|
-
this.compiledWithMiddlewareCount = this.globalMiddleware.length;
|
|
671
|
-
return this;
|
|
672
|
-
}
|
|
673
|
-
registerRoutes(routes) {
|
|
674
|
-
const flattened = flattenNestedRoutes(routes);
|
|
675
|
-
this.routes.push(...flattened);
|
|
676
|
-
for (const route2 of flattened) {
|
|
677
|
-
this.router.register(
|
|
678
|
-
route2.method,
|
|
679
|
-
route2.fullPath,
|
|
680
|
-
route2.handler,
|
|
681
|
-
route2.middlewareChain || []
|
|
682
|
-
);
|
|
683
|
-
}
|
|
684
|
-
this.detectRouteConflicts(flattened);
|
|
685
|
-
this.logFlattenedRoutes(flattened);
|
|
686
|
-
if (this.globalMiddleware.length === 0 && !this.isCompiled) {
|
|
687
|
-
this.compile();
|
|
688
|
-
}
|
|
689
|
-
setGlobalRegistry(new RouteRegistry(this.routes));
|
|
690
|
-
}
|
|
691
|
-
/** 快速提取 pathname */
|
|
692
|
-
extractPathname(url) {
|
|
693
|
-
let start = url.indexOf("://");
|
|
694
|
-
start = start === -1 ? 0 : start + 3;
|
|
695
|
-
const pathStart = url.indexOf("/", start);
|
|
696
|
-
if (pathStart === -1) return "/";
|
|
697
|
-
let end = url.indexOf("?", pathStart);
|
|
698
|
-
if (end === -1) end = url.indexOf("#", pathStart);
|
|
699
|
-
if (end === -1) end = url.length;
|
|
700
|
-
return url.substring(pathStart, end) || "/";
|
|
701
|
-
}
|
|
702
|
-
/** 生成 404/405 响应 */
|
|
703
|
-
createErrorResponse(method, pathname) {
|
|
704
|
-
const allowedMethods = this.router.getAllowedMethods(pathname);
|
|
705
|
-
if (allowedMethods.length > 0) {
|
|
706
|
-
return json(
|
|
707
|
-
{
|
|
708
|
-
success: false,
|
|
709
|
-
error: "Method Not Allowed",
|
|
710
|
-
message: `Method ${method} not allowed for this endpoint`,
|
|
711
|
-
allowedMethods
|
|
712
|
-
},
|
|
713
|
-
405,
|
|
714
|
-
{ Allow: allowedMethods.join(", ") }
|
|
715
|
-
);
|
|
716
|
-
}
|
|
717
|
-
return json({ success: false, error: "Not Found" }, 404);
|
|
718
|
-
}
|
|
719
|
-
/** 处理请求 */
|
|
720
|
-
fetch = async (req) => {
|
|
721
|
-
const pathname = this.extractPathname(req.url);
|
|
722
|
-
const method = req.method;
|
|
723
|
-
const match = this.router.match(method, pathname);
|
|
724
|
-
if (match) {
|
|
725
|
-
req.params = match.params;
|
|
726
|
-
if (match.compiled && this.globalMiddleware.length === this.compiledWithMiddlewareCount) {
|
|
727
|
-
return match.compiled(req);
|
|
728
|
-
}
|
|
729
|
-
const allMiddleware = [...this.globalMiddleware, ...match.middleware];
|
|
730
|
-
const handler = composeMiddleware(allMiddleware, match.handler);
|
|
731
|
-
return handler(req);
|
|
732
|
-
}
|
|
733
|
-
if (method === "OPTIONS") {
|
|
734
|
-
const allowedMethods = this.router.getAllowedMethods(pathname);
|
|
735
|
-
if (allowedMethods.length > 0) {
|
|
736
|
-
const anyMatch = this.router.match(
|
|
737
|
-
allowedMethods[0],
|
|
738
|
-
pathname
|
|
739
|
-
);
|
|
740
|
-
const routeMiddleware = anyMatch?.middleware || [];
|
|
741
|
-
const allMiddleware = [...this.globalMiddleware, ...routeMiddleware];
|
|
742
|
-
const optionsHandler = () => new Response(null, {
|
|
743
|
-
status: 204,
|
|
744
|
-
headers: { Allow: allowedMethods.join(", ") }
|
|
745
|
-
});
|
|
746
|
-
const handler = composeMiddleware(allMiddleware, optionsHandler);
|
|
747
|
-
return handler(req);
|
|
748
|
-
}
|
|
749
|
-
}
|
|
750
|
-
if (this.globalMiddleware.length > 0) {
|
|
751
|
-
const handler = composeMiddleware(
|
|
752
|
-
this.globalMiddleware,
|
|
753
|
-
() => this.createErrorResponse(method, pathname)
|
|
754
|
-
);
|
|
755
|
-
return handler(req);
|
|
756
|
-
}
|
|
757
|
-
return this.createErrorResponse(method, pathname);
|
|
758
|
-
};
|
|
759
|
-
addRoute(route2) {
|
|
760
|
-
const flattenedRoute = {
|
|
761
|
-
...route2,
|
|
762
|
-
fullPath: route2.path,
|
|
763
|
-
middlewareChain: route2.middleware || []
|
|
764
|
-
};
|
|
765
|
-
this.routes.push(flattenedRoute);
|
|
766
|
-
this.router.register(
|
|
767
|
-
route2.method,
|
|
768
|
-
route2.path,
|
|
769
|
-
route2.handler,
|
|
770
|
-
route2.middleware || []
|
|
771
|
-
);
|
|
772
|
-
}
|
|
773
|
-
addRoutes(routes) {
|
|
774
|
-
this.registerRoutes(routes);
|
|
775
|
-
}
|
|
776
|
-
getRoutes() {
|
|
777
|
-
return this.router.getRoutes();
|
|
778
|
-
}
|
|
779
|
-
/**
|
|
780
|
-
* 获取完整的路由元信息(不含 handler 和 middleware)
|
|
781
|
-
*
|
|
782
|
-
* 用于 API 文档生成、Webhook 事件注册、权限检查等场景
|
|
783
|
-
*
|
|
784
|
-
* @example
|
|
785
|
-
* ```typescript
|
|
786
|
-
* const routes = server.getRoutesWithMeta()
|
|
787
|
-
* for (const route of routes) {
|
|
788
|
-
* console.log(route.fullPath, route.name, route.description)
|
|
789
|
-
* }
|
|
790
|
-
* ```
|
|
791
|
-
*/
|
|
792
|
-
getRoutesWithMeta() {
|
|
793
|
-
return this.routes;
|
|
794
|
-
}
|
|
795
|
-
};
|
|
796
|
-
|
|
797
|
-
// src/middleware/component-router.ts
|
|
798
|
-
function flattenComponentRoutes(routes) {
|
|
799
|
-
const flattened = [];
|
|
800
|
-
function processRoute(route2, parentPath = "", parentMiddleware = []) {
|
|
801
|
-
const currentPath = parentPath + route2.path;
|
|
802
|
-
const currentMiddleware = [
|
|
803
|
-
...parentMiddleware,
|
|
804
|
-
...route2.middleware || []
|
|
805
|
-
];
|
|
806
|
-
if ("component" in route2) {
|
|
807
|
-
flattened.push({
|
|
808
|
-
...route2,
|
|
809
|
-
fullPath: currentPath,
|
|
810
|
-
middlewareChain: currentMiddleware
|
|
811
|
-
});
|
|
812
|
-
} else if ("children" in route2 && route2.children) {
|
|
813
|
-
for (const child of route2.children) {
|
|
814
|
-
processRoute(child, currentPath, currentMiddleware);
|
|
815
|
-
}
|
|
816
|
-
}
|
|
817
|
-
}
|
|
818
|
-
for (const route2 of routes) {
|
|
819
|
-
processRoute(route2);
|
|
820
|
-
}
|
|
821
|
-
return flattened;
|
|
822
|
-
}
|
|
823
|
-
|
|
824
|
-
// src/utils/path-matcher.ts
|
|
825
|
-
var PathMatcher = class {
|
|
826
|
-
/**
|
|
827
|
-
* 路径匹配
|
|
828
|
-
*/
|
|
829
|
-
static matchPath(pattern, path) {
|
|
830
|
-
const patternParts = pattern.split("/").filter(Boolean);
|
|
831
|
-
const pathParts = path.split("/").filter(Boolean);
|
|
832
|
-
if (patternParts.length !== pathParts.length) {
|
|
833
|
-
return false;
|
|
834
|
-
}
|
|
835
|
-
for (let i = 0; i < patternParts.length; i++) {
|
|
836
|
-
if (patternParts[i] !== pathParts[i] && !patternParts[i].startsWith(":")) {
|
|
837
|
-
return false;
|
|
838
|
-
}
|
|
839
|
-
}
|
|
840
|
-
return true;
|
|
841
|
-
}
|
|
842
|
-
/**
|
|
843
|
-
* 提取路径参数
|
|
844
|
-
*/
|
|
845
|
-
static extractParams(pattern, path) {
|
|
846
|
-
const params = {};
|
|
847
|
-
const patternParts = pattern.split("/").filter(Boolean);
|
|
848
|
-
const pathParts = path.split("/").filter(Boolean);
|
|
849
|
-
for (let i = 0; i < patternParts.length; i++) {
|
|
850
|
-
if (patternParts[i].startsWith(":")) {
|
|
851
|
-
const paramName = patternParts[i].slice(1);
|
|
852
|
-
params[paramName] = pathParts[i];
|
|
853
|
-
}
|
|
854
|
-
}
|
|
855
|
-
return params;
|
|
856
|
-
}
|
|
857
|
-
/**
|
|
858
|
-
* 计算路径特异性分数
|
|
859
|
-
* 用于路由排序:静态 > 动态(:param) > 通配符(*)
|
|
860
|
-
*/
|
|
861
|
-
static calculatePathScore(path) {
|
|
862
|
-
const parts = path.split("/").filter(Boolean);
|
|
863
|
-
let score = 0;
|
|
864
|
-
for (const p of parts) {
|
|
865
|
-
if (p === "*")
|
|
866
|
-
score += 1;
|
|
867
|
-
else if (p.startsWith(":"))
|
|
868
|
-
score += 2;
|
|
869
|
-
else score += 3;
|
|
870
|
-
}
|
|
871
|
-
return score * 10 + parts.length;
|
|
872
|
-
}
|
|
873
|
-
/**
|
|
874
|
-
* 判断两个路径是否可能冲突
|
|
875
|
-
*/
|
|
876
|
-
static pathsMayConflict(path1, path2) {
|
|
877
|
-
const parts1 = path1.split("/").filter(Boolean);
|
|
878
|
-
const parts2 = path2.split("/").filter(Boolean);
|
|
879
|
-
if (parts1.length !== parts2.length) return false;
|
|
880
|
-
for (let i = 0; i < parts1.length; i++) {
|
|
881
|
-
const p1 = parts1[i];
|
|
882
|
-
const p2 = parts2[i];
|
|
883
|
-
if (!p1.startsWith(":") && !p1.startsWith("*") && !p2.startsWith(":") && !p2.startsWith("*") && p1 !== p2) {
|
|
884
|
-
return false;
|
|
885
|
-
}
|
|
886
|
-
if (p1 === "*" && p2.startsWith(":") || p2 === "*" && p1.startsWith(":")) {
|
|
887
|
-
return true;
|
|
888
|
-
}
|
|
889
|
-
}
|
|
890
|
-
return false;
|
|
891
|
-
}
|
|
892
|
-
};
|
|
893
|
-
|
|
894
|
-
// src/utils/html-renderer.ts
|
|
895
|
-
var HtmlRenderer = class {
|
|
896
|
-
/**
|
|
897
|
-
* 生成基础HTML模板
|
|
898
|
-
*/
|
|
899
|
-
static generateBaseHtml(content, context, clientScriptPath = "/client.js") {
|
|
900
|
-
return `
|
|
901
|
-
<!doctype html>
|
|
902
|
-
<html>
|
|
903
|
-
<head>
|
|
904
|
-
<meta charset="utf-8">
|
|
905
|
-
<title>Vafast SSR App</title>
|
|
906
|
-
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
907
|
-
</head>
|
|
908
|
-
<body>
|
|
909
|
-
<div id="app">${content}</div>
|
|
910
|
-
<script>
|
|
911
|
-
window.__ROUTE_INFO__ = {
|
|
912
|
-
params: ${JSON.stringify(context.params || {})},
|
|
913
|
-
query: ${JSON.stringify(context.query || {})},
|
|
914
|
-
pathname: '${context.pathname}'
|
|
915
|
-
};
|
|
916
|
-
</script>
|
|
917
|
-
<script type="module" src="${clientScriptPath}"></script>
|
|
918
|
-
</body>
|
|
919
|
-
</html>
|
|
920
|
-
`;
|
|
921
|
-
}
|
|
922
|
-
/**
|
|
923
|
-
* 生成Vue组件HTML
|
|
924
|
-
*/
|
|
925
|
-
static generateVueHtml(content, context, clientScriptPath = "/client.js") {
|
|
926
|
-
return this.generateBaseHtml(content, context, clientScriptPath);
|
|
927
|
-
}
|
|
928
|
-
/**
|
|
929
|
-
* 生成React组件HTML
|
|
930
|
-
*/
|
|
931
|
-
static generateReactHtml(content, context, clientScriptPath = "/client.js") {
|
|
932
|
-
return `
|
|
933
|
-
<!doctype html>
|
|
934
|
-
<html>
|
|
935
|
-
<head>
|
|
936
|
-
<meta charset="utf-8">
|
|
937
|
-
<title>Vafast SSR App</title>
|
|
938
|
-
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
939
|
-
</head>
|
|
940
|
-
<body>
|
|
941
|
-
<div id="root">${content}</div>
|
|
942
|
-
<script>
|
|
943
|
-
window.__ROUTE_INFO__ = {
|
|
944
|
-
params: ${JSON.stringify(context.params || {})},
|
|
945
|
-
query: ${JSON.stringify(context.query || {})},
|
|
946
|
-
pathname: '${context.pathname}'
|
|
947
|
-
};
|
|
948
|
-
</script>
|
|
949
|
-
<script type="module" src="${clientScriptPath}"></script>
|
|
950
|
-
</body>
|
|
951
|
-
</html>
|
|
952
|
-
`;
|
|
953
|
-
}
|
|
954
|
-
};
|
|
955
|
-
|
|
956
|
-
// src/utils/dependency-manager.ts
|
|
957
|
-
var DependencyManager = class {
|
|
958
|
-
dependencyCache = /* @__PURE__ */ new Map();
|
|
959
|
-
/**
|
|
960
|
-
* 按需获取框架依赖
|
|
961
|
-
*/
|
|
962
|
-
async getFrameworkDeps(framework) {
|
|
963
|
-
if (this.dependencyCache.has(framework)) {
|
|
964
|
-
return this.dependencyCache.get(framework);
|
|
965
|
-
}
|
|
966
|
-
console.log(`\u{1F4E6} \u6309\u9700\u52A0\u8F7D ${framework} \u4F9D\u8D56...`);
|
|
967
|
-
try {
|
|
968
|
-
let deps;
|
|
969
|
-
switch (framework) {
|
|
970
|
-
case "vue":
|
|
971
|
-
deps = await Promise.all([
|
|
972
|
-
import("vue"),
|
|
973
|
-
import("@vue/server-renderer")
|
|
974
|
-
]);
|
|
975
|
-
break;
|
|
976
|
-
case "react":
|
|
977
|
-
deps = await Promise.all([
|
|
978
|
-
import("react"),
|
|
979
|
-
import("react-dom/server")
|
|
980
|
-
]);
|
|
981
|
-
break;
|
|
982
|
-
default:
|
|
983
|
-
throw new Error(`\u4E0D\u652F\u6301\u7684\u6846\u67B6: ${framework}`);
|
|
984
|
-
}
|
|
985
|
-
this.dependencyCache.set(framework, deps);
|
|
986
|
-
console.log(`\u2705 ${framework} \u4F9D\u8D56\u52A0\u8F7D\u5B8C\u6210`);
|
|
987
|
-
return deps;
|
|
988
|
-
} catch (error) {
|
|
989
|
-
console.error(`\u274C ${framework} \u4F9D\u8D56\u52A0\u8F7D\u5931\u8D25:`, error);
|
|
990
|
-
throw error;
|
|
991
|
-
}
|
|
992
|
-
}
|
|
993
|
-
/**
|
|
994
|
-
* 检测组件类型
|
|
995
|
-
*/
|
|
996
|
-
detectComponentType(component) {
|
|
997
|
-
if (component.render && typeof component.render === "function") {
|
|
998
|
-
return "vue";
|
|
999
|
-
}
|
|
1000
|
-
if (component.$$typeof) {
|
|
1001
|
-
return "react";
|
|
1002
|
-
}
|
|
1003
|
-
return "vue";
|
|
1004
|
-
}
|
|
1005
|
-
/**
|
|
1006
|
-
* 清除缓存
|
|
1007
|
-
*/
|
|
1008
|
-
clearCache() {
|
|
1009
|
-
this.dependencyCache.clear();
|
|
1010
|
-
console.log("\u{1F9F9} \u4F9D\u8D56\u7F13\u5B58\u5DF2\u6E05\u9664");
|
|
1011
|
-
}
|
|
1012
|
-
/**
|
|
1013
|
-
* 获取缓存状态
|
|
1014
|
-
*/
|
|
1015
|
-
getCacheStatus() {
|
|
1016
|
-
const status = {};
|
|
1017
|
-
for (const [framework] of this.dependencyCache) {
|
|
1018
|
-
status[framework] = true;
|
|
1019
|
-
}
|
|
1020
|
-
return status;
|
|
1021
|
-
}
|
|
1022
|
-
};
|
|
1023
|
-
|
|
1024
|
-
// src/server/component-server.ts
|
|
1025
|
-
var ComponentServer = class extends BaseServer {
|
|
1026
|
-
routes;
|
|
1027
|
-
dependencyManager;
|
|
1028
|
-
constructor(routes) {
|
|
1029
|
-
super();
|
|
1030
|
-
this.routes = flattenComponentRoutes(routes);
|
|
1031
|
-
this.dependencyManager = new DependencyManager();
|
|
1032
|
-
this.detectRouteConflicts(this.routes);
|
|
1033
|
-
this.logFlattenedRoutes(this.routes, "\u7EC4\u4EF6\u8DEF\u7531");
|
|
1034
|
-
console.log("\u{1F680} \u4F9D\u8D56\u6309\u9700\u52A0\u8F7D\uFF0C\u670D\u52A1\u5668\u542F\u52A8\u5B8C\u6210");
|
|
1035
|
-
}
|
|
1036
|
-
/**
|
|
1037
|
-
* 处理请求
|
|
1038
|
-
*/
|
|
1039
|
-
async fetch(req) {
|
|
1040
|
-
const url = new URL(req.url);
|
|
1041
|
-
const pathname = url.pathname;
|
|
1042
|
-
const method = req.method;
|
|
1043
|
-
if (method !== "GET") {
|
|
1044
|
-
return new Response("Method Not Allowed", { status: 405 });
|
|
1045
|
-
}
|
|
1046
|
-
let matchedRoute = null;
|
|
1047
|
-
for (const route2 of this.routes) {
|
|
1048
|
-
if (PathMatcher.matchPath(route2.fullPath, pathname)) {
|
|
1049
|
-
matchedRoute = route2;
|
|
1050
|
-
break;
|
|
1051
|
-
}
|
|
1052
|
-
}
|
|
1053
|
-
if (!matchedRoute) {
|
|
1054
|
-
return new Response("Not Found", { status: 404 });
|
|
1055
|
-
}
|
|
1056
|
-
try {
|
|
1057
|
-
const context = {
|
|
1058
|
-
req,
|
|
1059
|
-
params: PathMatcher.extractParams(matchedRoute.fullPath, pathname),
|
|
1060
|
-
query: Object.fromEntries(url.searchParams),
|
|
1061
|
-
pathname
|
|
1062
|
-
};
|
|
1063
|
-
return await this.executeMiddlewareChain(
|
|
1064
|
-
matchedRoute.middlewareChain,
|
|
1065
|
-
context,
|
|
1066
|
-
matchedRoute.component
|
|
1067
|
-
);
|
|
1068
|
-
} catch (error) {
|
|
1069
|
-
console.error("\u7EC4\u4EF6\u6E32\u67D3\u5931\u8D25:", error);
|
|
1070
|
-
return new Response("Internal Server Error", { status: 500 });
|
|
1071
|
-
}
|
|
1072
|
-
}
|
|
1073
|
-
/**
|
|
1074
|
-
* 执行中间件链
|
|
1075
|
-
*/
|
|
1076
|
-
async executeMiddlewareChain(middlewareChain, context, componentImport) {
|
|
1077
|
-
const renderComponent = async () => {
|
|
1078
|
-
const componentModule = await componentImport();
|
|
1079
|
-
const component = componentModule.default || componentModule;
|
|
1080
|
-
const componentType = this.dependencyManager.detectComponentType(component);
|
|
1081
|
-
const deps = await this.dependencyManager.getFrameworkDeps(componentType);
|
|
1082
|
-
if (componentType === "vue") {
|
|
1083
|
-
return await this.renderVueComponent(component, context, deps);
|
|
1084
|
-
} else if (componentType === "react") {
|
|
1085
|
-
return await this.renderReactComponent(component, context, deps);
|
|
1086
|
-
} else {
|
|
1087
|
-
throw new Error(`\u4E0D\u652F\u6301\u7684\u7EC4\u4EF6\u7C7B\u578B: ${componentType}`);
|
|
1088
|
-
}
|
|
1089
|
-
};
|
|
1090
|
-
let index = 0;
|
|
1091
|
-
const next = async () => {
|
|
1092
|
-
if (index >= middlewareChain.length) {
|
|
1093
|
-
return await renderComponent();
|
|
1094
|
-
}
|
|
1095
|
-
const middleware = middlewareChain[index++];
|
|
1096
|
-
return await middleware(context.req, next);
|
|
1097
|
-
};
|
|
1098
|
-
return await next();
|
|
1099
|
-
}
|
|
1100
|
-
/**
|
|
1101
|
-
* 渲染 Vue 组件
|
|
1102
|
-
*/
|
|
1103
|
-
async renderVueComponent(component, context, deps) {
|
|
1104
|
-
try {
|
|
1105
|
-
const [vue, renderer] = deps;
|
|
1106
|
-
const app = vue.createSSRApp(component);
|
|
1107
|
-
app.provide("routeInfo", {
|
|
1108
|
-
params: context.params || {},
|
|
1109
|
-
query: context.query || {},
|
|
1110
|
-
pathname: context.pathname
|
|
1111
|
-
});
|
|
1112
|
-
const html2 = await renderer.renderToString(app);
|
|
1113
|
-
const fullHtml = HtmlRenderer.generateVueHtml(html2, context);
|
|
1114
|
-
return new Response(fullHtml, {
|
|
1115
|
-
headers: { "Content-Type": "text/html; charset=utf-8" }
|
|
1116
|
-
});
|
|
1117
|
-
} catch (error) {
|
|
1118
|
-
console.error("Vue \u7EC4\u4EF6\u6E32\u67D3\u5931\u8D25:", error);
|
|
1119
|
-
return new Response("Vue Component Render Error", { status: 500 });
|
|
1120
|
-
}
|
|
1121
|
-
}
|
|
1122
|
-
/**
|
|
1123
|
-
* 渲染 React 组件
|
|
1124
|
-
*/
|
|
1125
|
-
async renderReactComponent(component, context, deps) {
|
|
1126
|
-
try {
|
|
1127
|
-
const [react, renderer] = deps;
|
|
1128
|
-
const content = react.createElement(component, {
|
|
1129
|
-
req: context.req,
|
|
1130
|
-
params: context.params || {},
|
|
1131
|
-
query: context.query || {}
|
|
1132
|
-
});
|
|
1133
|
-
const html2 = renderer.renderToString(content);
|
|
1134
|
-
const fullHtml = HtmlRenderer.generateReactHtml(html2, context);
|
|
1135
|
-
return new Response(fullHtml, {
|
|
1136
|
-
headers: { "Content-Type": "text/html; charset=utf-8" }
|
|
1137
|
-
});
|
|
1138
|
-
} catch (error) {
|
|
1139
|
-
console.error("React \u7EC4\u4EF6\u6E32\u67D3\u5931\u8D25:", error);
|
|
1140
|
-
return new Response("React Component Render Error", { status: 500 });
|
|
1141
|
-
}
|
|
1142
|
-
}
|
|
1143
|
-
/**
|
|
1144
|
-
* 获取依赖管理器(用于外部访问)
|
|
1145
|
-
*/
|
|
1146
|
-
getDependencyManager() {
|
|
1147
|
-
return this.dependencyManager;
|
|
1148
|
-
}
|
|
1149
|
-
};
|
|
1150
|
-
|
|
1151
|
-
// src/server/server-factory.ts
|
|
1152
|
-
var ServerFactory = class {
|
|
1153
|
-
servers = /* @__PURE__ */ new Map();
|
|
1154
|
-
/**
|
|
1155
|
-
* 创建标准REST API服务器
|
|
1156
|
-
*/
|
|
1157
|
-
createRestServer(routes) {
|
|
1158
|
-
const server = new Server(routes);
|
|
1159
|
-
this.servers.set("rest", server);
|
|
1160
|
-
return server;
|
|
1161
|
-
}
|
|
1162
|
-
/**
|
|
1163
|
-
* 创建组件服务器
|
|
1164
|
-
*/
|
|
1165
|
-
createComponentServer(routes) {
|
|
1166
|
-
const server = new ComponentServer(routes);
|
|
1167
|
-
this.servers.set("component", server);
|
|
1168
|
-
return server;
|
|
1169
|
-
}
|
|
1170
|
-
/**
|
|
1171
|
-
* 获取指定类型的服务器
|
|
1172
|
-
*/
|
|
1173
|
-
getServer(type) {
|
|
1174
|
-
return this.servers.get(type);
|
|
1175
|
-
}
|
|
1176
|
-
/**
|
|
1177
|
-
* 获取所有服务器
|
|
1178
|
-
*/
|
|
1179
|
-
getAllServers() {
|
|
1180
|
-
return this.servers;
|
|
1181
|
-
}
|
|
1182
|
-
/**
|
|
1183
|
-
* 移除指定类型的服务器
|
|
1184
|
-
*/
|
|
1185
|
-
removeServer(type) {
|
|
1186
|
-
return this.servers.delete(type);
|
|
1187
|
-
}
|
|
1188
|
-
/**
|
|
1189
|
-
* 清除所有服务器
|
|
1190
|
-
*/
|
|
1191
|
-
clearServers() {
|
|
1192
|
-
this.servers.clear();
|
|
1193
|
-
}
|
|
1194
|
-
/**
|
|
1195
|
-
* 获取服务器状态信息
|
|
1196
|
-
*/
|
|
1197
|
-
getServerStatus() {
|
|
1198
|
-
const status = {};
|
|
1199
|
-
for (const [name, server] of this.servers) {
|
|
1200
|
-
if (server instanceof Server) {
|
|
1201
|
-
status[name] = {
|
|
1202
|
-
type: "REST API",
|
|
1203
|
-
routes: server.routes?.length || 0
|
|
1204
|
-
};
|
|
1205
|
-
} else if (server instanceof ComponentServer) {
|
|
1206
|
-
status[name] = {
|
|
1207
|
-
type: "Component",
|
|
1208
|
-
routes: server.routes?.length || 0
|
|
1209
|
-
};
|
|
1210
|
-
}
|
|
1211
|
-
}
|
|
1212
|
-
return status;
|
|
1213
|
-
}
|
|
1214
|
-
};
|
|
1215
|
-
|
|
1216
|
-
// src/utils/parsers.ts
|
|
1217
|
-
import qs from "qs";
|
|
1218
|
-
import cookie from "cookie";
|
|
1219
|
-
async function parseBody(req) {
|
|
1220
|
-
const contentType = req.headers.get("content-type") || "";
|
|
1221
|
-
if (contentType.includes("application/json")) {
|
|
1222
|
-
return await req.json();
|
|
1223
|
-
}
|
|
1224
|
-
if (contentType.includes("application/x-www-form-urlencoded")) {
|
|
1225
|
-
const text2 = await req.text();
|
|
1226
|
-
return Object.fromEntries(new URLSearchParams(text2));
|
|
1227
|
-
}
|
|
1228
|
-
return await req.text();
|
|
1229
|
-
}
|
|
1230
|
-
function extractQueryString(url) {
|
|
1231
|
-
const qIndex = url.indexOf("?");
|
|
1232
|
-
if (qIndex === -1) return "";
|
|
1233
|
-
const hashIndex = url.indexOf("#", qIndex);
|
|
1234
|
-
return hashIndex === -1 ? url.substring(qIndex + 1) : url.substring(qIndex + 1, hashIndex);
|
|
1235
|
-
}
|
|
1236
|
-
function parseQuery(req) {
|
|
1237
|
-
const queryString = extractQueryString(req.url);
|
|
1238
|
-
if (!queryString) return {};
|
|
1239
|
-
return qs.parse(queryString);
|
|
1240
|
-
}
|
|
1241
|
-
function parseQueryFast(req) {
|
|
1242
|
-
const queryString = extractQueryString(req.url);
|
|
1243
|
-
if (!queryString) return {};
|
|
1244
|
-
const result = /* @__PURE__ */ Object.create(null);
|
|
1245
|
-
const pairs = queryString.split("&");
|
|
1246
|
-
for (const pair of pairs) {
|
|
1247
|
-
const eqIndex = pair.indexOf("=");
|
|
1248
|
-
if (eqIndex === -1) {
|
|
1249
|
-
result[decodeURIComponent(pair)] = "";
|
|
1250
|
-
} else {
|
|
1251
|
-
const key = decodeURIComponent(pair.substring(0, eqIndex));
|
|
1252
|
-
const value = decodeURIComponent(pair.substring(eqIndex + 1));
|
|
1253
|
-
result[key] = value;
|
|
1254
|
-
}
|
|
1255
|
-
}
|
|
1256
|
-
return result;
|
|
1257
|
-
}
|
|
1258
|
-
function parseHeaders(req) {
|
|
1259
|
-
const headers = /* @__PURE__ */ Object.create(null);
|
|
1260
|
-
req.headers.forEach((value, key) => {
|
|
1261
|
-
headers[key] = value;
|
|
1262
|
-
});
|
|
1263
|
-
return headers;
|
|
1264
|
-
}
|
|
1265
|
-
function getHeader(req, name) {
|
|
1266
|
-
return req.headers.get(name);
|
|
1267
|
-
}
|
|
1268
|
-
function parseCookies(req) {
|
|
1269
|
-
const cookieHeader = req.headers.get("cookie");
|
|
1270
|
-
if (!cookieHeader) return {};
|
|
1271
|
-
try {
|
|
1272
|
-
const parsed = cookie.parse(cookieHeader);
|
|
1273
|
-
const result = {};
|
|
1274
|
-
for (const [key, value] of Object.entries(parsed)) {
|
|
1275
|
-
if (value !== void 0 && value !== null) {
|
|
1276
|
-
result[key] = value;
|
|
1277
|
-
}
|
|
1278
|
-
}
|
|
1279
|
-
return result;
|
|
1280
|
-
} catch {
|
|
1281
|
-
return {};
|
|
1282
|
-
}
|
|
1283
|
-
}
|
|
1284
|
-
function parseCookiesFast(req) {
|
|
1285
|
-
const cookieHeader = req.headers.get("cookie");
|
|
1286
|
-
if (!cookieHeader) return {};
|
|
1287
|
-
const result = /* @__PURE__ */ Object.create(null);
|
|
1288
|
-
const pairs = cookieHeader.split(";");
|
|
1289
|
-
for (const pair of pairs) {
|
|
1290
|
-
const trimmed = pair.trim();
|
|
1291
|
-
const eqIndex = trimmed.indexOf("=");
|
|
1292
|
-
if (eqIndex > 0) {
|
|
1293
|
-
const key = trimmed.substring(0, eqIndex).trim();
|
|
1294
|
-
const value = trimmed.substring(eqIndex + 1).trim();
|
|
1295
|
-
result[key] = value.startsWith('"') && value.endsWith('"') ? value.slice(1, -1) : value;
|
|
1296
|
-
}
|
|
1297
|
-
}
|
|
1298
|
-
return result;
|
|
1299
|
-
}
|
|
1300
|
-
function getCookie(req, name) {
|
|
1301
|
-
const cookieHeader = req.headers.get("cookie");
|
|
1302
|
-
if (!cookieHeader) return null;
|
|
1303
|
-
const prefix = `${name}=`;
|
|
1304
|
-
const pairs = cookieHeader.split(";");
|
|
1305
|
-
for (const pair of pairs) {
|
|
1306
|
-
const trimmed = pair.trim();
|
|
1307
|
-
if (trimmed.startsWith(prefix)) {
|
|
1308
|
-
const value = trimmed.substring(prefix.length).trim();
|
|
1309
|
-
return value.startsWith('"') && value.endsWith('"') ? value.slice(1, -1) : value;
|
|
1310
|
-
}
|
|
1311
|
-
}
|
|
1312
|
-
return null;
|
|
1313
|
-
}
|
|
1314
|
-
|
|
1315
|
-
// src/utils/go-await.ts
|
|
1316
|
-
function goAwait(promise) {
|
|
1317
|
-
return promise.then((data) => [null, data]).catch((err) => [err instanceof Error ? err : new Error(String(err)), void 0]);
|
|
1318
|
-
}
|
|
1319
|
-
|
|
1320
|
-
// src/utils/validators/validators.ts
|
|
1321
|
-
import { Type } from "@sinclair/typebox";
|
|
1322
|
-
import { TypeCompiler } from "@sinclair/typebox/compiler";
|
|
1323
|
-
var compilerCache = /* @__PURE__ */ new WeakMap();
|
|
1324
|
-
function getCompiledValidator(schema) {
|
|
1325
|
-
let compiler = compilerCache.get(schema);
|
|
1326
|
-
if (!compiler) {
|
|
1327
|
-
compiler = TypeCompiler.Compile(schema);
|
|
1328
|
-
compilerCache.set(schema, compiler);
|
|
1329
|
-
}
|
|
1330
|
-
return compiler;
|
|
1331
|
-
}
|
|
1332
|
-
function validateSchema(schema, data) {
|
|
1333
|
-
try {
|
|
1334
|
-
const compiler = getCompiledValidator(schema);
|
|
1335
|
-
if (compiler.Check(data)) {
|
|
1336
|
-
return { success: true, data };
|
|
1337
|
-
}
|
|
1338
|
-
const errors = [];
|
|
1339
|
-
for (const error of compiler.Errors(data)) {
|
|
1340
|
-
errors.push({
|
|
1341
|
-
path: error.path,
|
|
1342
|
-
message: error.message,
|
|
1343
|
-
code: "VALIDATION_FAILED",
|
|
1344
|
-
value: error.value
|
|
1345
|
-
});
|
|
1346
|
-
}
|
|
1347
|
-
return { success: false, errors };
|
|
1348
|
-
} catch (error) {
|
|
1349
|
-
return {
|
|
1350
|
-
success: false,
|
|
1351
|
-
errors: [
|
|
1352
|
-
{
|
|
1353
|
-
path: "",
|
|
1354
|
-
message: error instanceof Error ? error.message : "\u9A8C\u8BC1\u5F02\u5E38",
|
|
1355
|
-
code: "VALIDATION_EXCEPTION"
|
|
1356
|
-
}
|
|
1357
|
-
]
|
|
1358
|
-
};
|
|
1359
|
-
}
|
|
1360
|
-
}
|
|
1361
|
-
function validateSchemaOrThrow(schema, data, context) {
|
|
1362
|
-
const compiler = getCompiledValidator(schema);
|
|
1363
|
-
if (!compiler.Check(data)) {
|
|
1364
|
-
throw new Error(`${context}\u9A8C\u8BC1\u5931\u8D25`);
|
|
1365
|
-
}
|
|
1366
|
-
return data;
|
|
1367
|
-
}
|
|
1368
|
-
function validateFast(schema, data) {
|
|
1369
|
-
const compiler = getCompiledValidator(schema);
|
|
1370
|
-
return compiler.Check(data);
|
|
1371
|
-
}
|
|
1372
|
-
function validateAllSchemas(config, data) {
|
|
1373
|
-
if (config.body) {
|
|
1374
|
-
validateSchemaOrThrow(config.body, data.body, "\u8BF7\u6C42\u4F53");
|
|
1375
|
-
}
|
|
1376
|
-
if (config.query) {
|
|
1377
|
-
validateSchemaOrThrow(config.query, data.query, "Query\u53C2\u6570");
|
|
1378
|
-
}
|
|
1379
|
-
if (config.params) {
|
|
1380
|
-
validateSchemaOrThrow(config.params, data.params, "\u8DEF\u5F84\u53C2\u6570");
|
|
1381
|
-
}
|
|
1382
|
-
if (config.headers) {
|
|
1383
|
-
validateSchemaOrThrow(config.headers, data.headers, "\u8BF7\u6C42\u5934");
|
|
1384
|
-
}
|
|
1385
|
-
if (config.cookies) {
|
|
1386
|
-
validateSchemaOrThrow(config.cookies, data.cookies, "Cookie");
|
|
1387
|
-
}
|
|
1388
|
-
return data;
|
|
1389
|
-
}
|
|
1390
|
-
function precompileSchemas(config) {
|
|
1391
|
-
if (config.body) getCompiledValidator(config.body);
|
|
1392
|
-
if (config.query) getCompiledValidator(config.query);
|
|
1393
|
-
if (config.params) getCompiledValidator(config.params);
|
|
1394
|
-
if (config.headers) getCompiledValidator(config.headers);
|
|
1395
|
-
if (config.cookies) getCompiledValidator(config.cookies);
|
|
1396
|
-
}
|
|
1397
|
-
function createValidator(schema) {
|
|
1398
|
-
return (data) => validateSchema(schema, data);
|
|
1399
|
-
}
|
|
1400
|
-
function getValidatorCacheStats() {
|
|
1401
|
-
return {
|
|
1402
|
-
cacheType: "WeakMap",
|
|
1403
|
-
note: "WeakMap \u7F13\u5B58\u4F1A\u968F Schema \u5BF9\u8C61\u81EA\u52A8\u6E05\u7406\uFF0C\u65E0\u5185\u5B58\u6CC4\u6F0F\u98CE\u9669"
|
|
1404
|
-
};
|
|
1405
|
-
}
|
|
1406
|
-
|
|
1407
|
-
// src/utils/create-handler.ts
|
|
1408
|
-
function autoResponse(result) {
|
|
1409
|
-
if (result instanceof Response) {
|
|
1410
|
-
return result;
|
|
1411
|
-
}
|
|
1412
|
-
if (result === null || result === void 0) {
|
|
1413
|
-
return new Response(null, { status: 204 });
|
|
1414
|
-
}
|
|
1415
|
-
if (typeof result === "string") {
|
|
1416
|
-
return new Response(result, {
|
|
1417
|
-
headers: { "Content-Type": "text/plain; charset=utf-8" }
|
|
1418
|
-
});
|
|
1419
|
-
}
|
|
1420
|
-
if (typeof result === "number" || typeof result === "boolean") {
|
|
1421
|
-
return new Response(String(result), {
|
|
1422
|
-
headers: { "Content-Type": "text/plain; charset=utf-8" }
|
|
1423
|
-
});
|
|
1424
|
-
}
|
|
1425
|
-
if (typeof result === "object") {
|
|
1426
|
-
const obj = result;
|
|
1427
|
-
if ("data" in obj && ("status" in obj || "headers" in obj)) {
|
|
1428
|
-
const { data, status = 200, headers = {} } = obj;
|
|
1429
|
-
if (data === null || data === void 0) {
|
|
1430
|
-
return new Response(null, {
|
|
1431
|
-
status: status === 200 ? 204 : status,
|
|
1432
|
-
headers
|
|
1433
|
-
});
|
|
1434
|
-
}
|
|
1435
|
-
if (typeof data === "string" || typeof data === "number" || typeof data === "boolean") {
|
|
1436
|
-
return new Response(String(data), {
|
|
1437
|
-
status,
|
|
1438
|
-
headers: {
|
|
1439
|
-
"Content-Type": "text/plain; charset=utf-8",
|
|
1440
|
-
...headers
|
|
1441
|
-
}
|
|
1442
|
-
});
|
|
1443
|
-
}
|
|
1444
|
-
return json(data, status, headers);
|
|
1445
|
-
}
|
|
1446
|
-
return json(result);
|
|
1447
|
-
}
|
|
1448
|
-
return new Response(null, { status: 204 });
|
|
1449
|
-
}
|
|
1450
|
-
function handleValidationError(error) {
|
|
1451
|
-
return json(
|
|
1452
|
-
{
|
|
1453
|
-
success: false,
|
|
1454
|
-
error: "Validation Error",
|
|
1455
|
-
message: error.message,
|
|
1456
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
1457
|
-
},
|
|
1458
|
-
400
|
|
1459
|
-
);
|
|
1460
|
-
}
|
|
1461
|
-
function handleInternalError(error) {
|
|
1462
|
-
return json(
|
|
1463
|
-
{
|
|
1464
|
-
success: false,
|
|
1465
|
-
error: "Internal Error",
|
|
1466
|
-
message: error instanceof Error ? error.message : "\u672A\u77E5\u9519\u8BEF"
|
|
1467
|
-
},
|
|
1468
|
-
500
|
|
1469
|
-
);
|
|
1470
|
-
}
|
|
1471
|
-
function isHandler(value) {
|
|
1472
|
-
return typeof value === "function";
|
|
1473
|
-
}
|
|
1474
|
-
function createHandler(schemaOrHandler, maybeHandler) {
|
|
1475
|
-
const hasSchema = !isHandler(schemaOrHandler);
|
|
1476
|
-
const schema = hasSchema ? schemaOrHandler : {};
|
|
1477
|
-
const handler = hasSchema ? maybeHandler : schemaOrHandler;
|
|
1478
|
-
if (schema.body || schema.query || schema.params || schema.headers || schema.cookies) {
|
|
1479
|
-
precompileSchemas(schema);
|
|
1480
|
-
}
|
|
1481
|
-
const handlerFn = async (req) => {
|
|
1482
|
-
try {
|
|
1483
|
-
const query = parseQuery(req);
|
|
1484
|
-
const headers = parseHeaders(req);
|
|
1485
|
-
const cookies = parseCookies(req);
|
|
1486
|
-
const params = req.params || {};
|
|
1487
|
-
let body = void 0;
|
|
1488
|
-
if (req.method !== "GET" && req.method !== "HEAD") {
|
|
1489
|
-
const [, parsedBody] = await goAwait(parseBody(req));
|
|
1490
|
-
body = parsedBody;
|
|
1491
|
-
}
|
|
1492
|
-
const data = { body, query, params, headers, cookies };
|
|
1493
|
-
if (schema.body || schema.query || schema.params || schema.headers || schema.cookies) {
|
|
1494
|
-
validateAllSchemas(schema, data);
|
|
1495
|
-
}
|
|
1496
|
-
const result = await handler({
|
|
1497
|
-
req,
|
|
1498
|
-
body,
|
|
1499
|
-
query,
|
|
1500
|
-
params,
|
|
1501
|
-
headers,
|
|
1502
|
-
cookies
|
|
1503
|
-
});
|
|
1504
|
-
return autoResponse(result);
|
|
1505
|
-
} catch (error) {
|
|
1506
|
-
if (error instanceof Error && error.message.includes("\u9A8C\u8BC1\u5931\u8D25")) {
|
|
1507
|
-
return handleValidationError(error);
|
|
1508
|
-
}
|
|
1509
|
-
return handleInternalError(error);
|
|
1510
|
-
}
|
|
1511
|
-
};
|
|
1512
|
-
return handlerFn;
|
|
1513
|
-
}
|
|
1514
|
-
function createHandlerWithExtra(schemaOrHandler, maybeHandler) {
|
|
1515
|
-
const hasSchema = !isHandler(schemaOrHandler);
|
|
1516
|
-
const schema = hasSchema ? schemaOrHandler : {};
|
|
1517
|
-
const handler = hasSchema ? maybeHandler : schemaOrHandler;
|
|
1518
|
-
if (schema.body || schema.query || schema.params || schema.headers || schema.cookies) {
|
|
1519
|
-
precompileSchemas(schema);
|
|
1520
|
-
}
|
|
1521
|
-
return async (req) => {
|
|
1522
|
-
try {
|
|
1523
|
-
const query = parseQuery(req);
|
|
1524
|
-
const headers = parseHeaders(req);
|
|
1525
|
-
const cookies = parseCookies(req);
|
|
1526
|
-
const params = req.params || {};
|
|
1527
|
-
let body = void 0;
|
|
1528
|
-
if (req.method !== "GET" && req.method !== "HEAD") {
|
|
1529
|
-
const [, parsedBody] = await goAwait(parseBody(req));
|
|
1530
|
-
body = parsedBody;
|
|
1531
|
-
}
|
|
1532
|
-
const data = { body, query, params, headers, cookies };
|
|
1533
|
-
if (schema.body || schema.query || schema.params || schema.headers || schema.cookies) {
|
|
1534
|
-
validateAllSchemas(schema, data);
|
|
1535
|
-
}
|
|
1536
|
-
const extras = req.__locals ?? {};
|
|
1537
|
-
const result = await handler({
|
|
1538
|
-
req,
|
|
1539
|
-
body,
|
|
1540
|
-
query,
|
|
1541
|
-
params,
|
|
1542
|
-
headers,
|
|
1543
|
-
cookies,
|
|
1544
|
-
...extras
|
|
1545
|
-
});
|
|
1546
|
-
return autoResponse(result);
|
|
1547
|
-
} catch (error) {
|
|
1548
|
-
if (error instanceof Error && error.message.includes("\u9A8C\u8BC1\u5931\u8D25")) {
|
|
1549
|
-
return handleValidationError(error);
|
|
1550
|
-
}
|
|
1551
|
-
return handleInternalError(error);
|
|
1552
|
-
}
|
|
1553
|
-
};
|
|
1554
|
-
}
|
|
1555
|
-
function simpleHandler(handler) {
|
|
1556
|
-
return async (req) => {
|
|
1557
|
-
try {
|
|
1558
|
-
const result = await handler({ req });
|
|
1559
|
-
return autoResponse(result);
|
|
1560
|
-
} catch (error) {
|
|
1561
|
-
return handleInternalError(error);
|
|
1562
|
-
}
|
|
1563
|
-
};
|
|
1564
|
-
}
|
|
1565
|
-
|
|
1566
|
-
// src/utils/base64url.ts
|
|
1567
|
-
function base64urlEncode(str) {
|
|
1568
|
-
return btoa(str).replace(/=/g, "").replace(/\+/g, "-").replace(/\//g, "_");
|
|
1569
|
-
}
|
|
1570
|
-
function base64urlDecode(str) {
|
|
1571
|
-
const pad = str.length % 4 === 0 ? "" : "=".repeat(4 - str.length % 4);
|
|
1572
|
-
const base64 = str.replace(/-/g, "+").replace(/_/g, "/") + pad;
|
|
1573
|
-
return atob(base64);
|
|
1574
|
-
}
|
|
1575
|
-
|
|
1576
|
-
// src/utils/handle.ts
|
|
1577
|
-
function getCookie2(req, key) {
|
|
1578
|
-
const cookies = parseCookies(req);
|
|
1579
|
-
return cookies[key] || null;
|
|
1580
|
-
}
|
|
1581
|
-
function setLocals(req, extras) {
|
|
1582
|
-
const target = req;
|
|
1583
|
-
target.__locals = { ...target.__locals ?? {}, ...extras };
|
|
1584
|
-
}
|
|
1585
|
-
function getLocals(req) {
|
|
1586
|
-
const target = req;
|
|
1587
|
-
return target.__locals ?? {};
|
|
1588
|
-
}
|
|
1589
|
-
|
|
1590
|
-
// src/utils/request-validator.ts
|
|
1591
|
-
async function parseRequest(request, params) {
|
|
1592
|
-
const requestData = {
|
|
1593
|
-
body: void 0,
|
|
1594
|
-
query: parseQuery(request),
|
|
1595
|
-
params: params || {},
|
|
1596
|
-
headers: parseHeaders(request),
|
|
1597
|
-
cookies: parseCookies(request)
|
|
1598
|
-
};
|
|
1599
|
-
if (request.method !== "GET" && request.method !== "HEAD") {
|
|
1600
|
-
requestData.body = await parseBody(request);
|
|
1601
|
-
}
|
|
1602
|
-
return requestData;
|
|
1603
|
-
}
|
|
1604
|
-
function validateRequest(config, requestData) {
|
|
1605
|
-
try {
|
|
1606
|
-
const validatedData = validateAllSchemas(config, requestData);
|
|
1607
|
-
return {
|
|
1608
|
-
success: true,
|
|
1609
|
-
data: validatedData
|
|
1610
|
-
};
|
|
1611
|
-
} catch (error) {
|
|
1612
|
-
return {
|
|
1613
|
-
success: false,
|
|
1614
|
-
errors: [
|
|
1615
|
-
{
|
|
1616
|
-
field: "unknown",
|
|
1617
|
-
message: error instanceof Error ? error.message : "\u9A8C\u8BC1\u5931\u8D25"
|
|
1618
|
-
}
|
|
1619
|
-
]
|
|
1620
|
-
};
|
|
1621
|
-
}
|
|
1622
|
-
}
|
|
1623
|
-
async function parseAndValidateRequest(request, config, params) {
|
|
1624
|
-
try {
|
|
1625
|
-
const requestData = await parseRequest(request, params);
|
|
1626
|
-
return validateRequest(config, requestData);
|
|
1627
|
-
} catch (error) {
|
|
1628
|
-
return {
|
|
1629
|
-
success: false,
|
|
1630
|
-
errors: [
|
|
1631
|
-
{
|
|
1632
|
-
field: "unknown",
|
|
1633
|
-
message: error instanceof Error ? error.message : "\u8BF7\u6C42\u89E3\u6790\u5931\u8D25"
|
|
1634
|
-
}
|
|
1635
|
-
]
|
|
1636
|
-
};
|
|
1637
|
-
}
|
|
1638
|
-
}
|
|
1639
|
-
function createRequestValidator(config) {
|
|
1640
|
-
return async (request, params) => {
|
|
1641
|
-
return parseAndValidateRequest(request, config, params);
|
|
1642
|
-
};
|
|
1643
|
-
}
|
|
1644
|
-
|
|
1645
|
-
// src/utils/formats.ts
|
|
1646
|
-
import { FormatRegistry } from "@sinclair/typebox";
|
|
1647
|
-
var EMAIL_REGEX = /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/;
|
|
1648
|
-
var UUID_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
1649
|
-
var UUID_ANY_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
1650
|
-
var CUID_REGEX = /^c[^\s-]{8,}$/i;
|
|
1651
|
-
var CUID2_REGEX = /^[0-9a-z]+$/;
|
|
1652
|
-
var ULID_REGEX = /^[0-9A-HJKMNP-TV-Z]{26}$/i;
|
|
1653
|
-
var NANOID_REGEX = /^[A-Za-z0-9_-]{21}$/;
|
|
1654
|
-
var URL_REGEX = /^https?:\/\/(?:www\.)?[-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b[-a-zA-Z0-9()@:%_+.~#?&/=]*$/;
|
|
1655
|
-
var IPV4_REGEX = /^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/;
|
|
1656
|
-
var IPV6_REGEX = /^(?:[0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}$|^::(?:[0-9a-fA-F]{1,4}:){0,6}[0-9a-fA-F]{1,4}$|^(?:[0-9a-fA-F]{1,4}:){1,7}:$|^(?:[0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}$|^(?:[0-9a-fA-F]{1,4}:){1,5}(?::[0-9a-fA-F]{1,4}){1,2}$|^(?:[0-9a-fA-F]{1,4}:){1,4}(?::[0-9a-fA-F]{1,4}){1,3}$|^(?:[0-9a-fA-F]{1,4}:){1,3}(?::[0-9a-fA-F]{1,4}){1,4}$|^(?:[0-9a-fA-F]{1,4}:){1,2}(?::[0-9a-fA-F]{1,4}){1,5}$|^[0-9a-fA-F]{1,4}:(?::[0-9a-fA-F]{1,4}){1,6}$/;
|
|
1657
|
-
var CIDR_REGEX = /^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\/(?:3[0-2]|[12]?[0-9])$/;
|
|
1658
|
-
var DATE_REGEX = /^\d{4}-(?:0[1-9]|1[0-2])-(?:0[1-9]|[12][0-9]|3[01])$/;
|
|
1659
|
-
var TIME_REGEX = /^(?:[01][0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9](?:\.\d{1,3})?$/;
|
|
1660
|
-
var DATE_TIME_REGEX = /^\d{4}-(?:0[1-9]|1[0-2])-(?:0[1-9]|[12][0-9]|3[01])T(?:[01][0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9](?:\.\d{1,3})?(?:Z|[+-](?:[01][0-9]|2[0-3]):[0-5][0-9])?$/;
|
|
1661
|
-
var DURATION_REGEX = /^P(?:\d+Y)?(?:\d+M)?(?:\d+W)?(?:\d+D)?(?:T(?:\d+H)?(?:\d+M)?(?:\d+(?:\.\d+)?S)?)?$/;
|
|
1662
|
-
var HOSTNAME_REGEX = /^(?=.{1,253}$)(?:(?!-)[a-zA-Z0-9-]{1,63}(?<!-)\.)*(?!-)[a-zA-Z0-9-]{1,63}(?<!-)$/;
|
|
1663
|
-
var PHONE_CN_REGEX = /^1[3-9]\d{9}$/;
|
|
1664
|
-
var PHONE_E164_REGEX = /^\+[1-9]\d{6,14}$/;
|
|
1665
|
-
var OBJECTID_REGEX = /^[0-9a-fA-F]{24}$/;
|
|
1666
|
-
var HEX_COLOR_REGEX = /^#(?:[0-9a-fA-F]{3}){1,2}$/;
|
|
1667
|
-
var RGB_COLOR_REGEX = /^rgba?\(\s*(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\s*,\s*(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\s*,\s*(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(?:\s*,\s*(?:0|1|0?\.\d+))?\s*\)$/;
|
|
1668
|
-
var BASE64_REGEX = /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/;
|
|
1669
|
-
var BASE64URL_REGEX = /^[A-Za-z0-9_-]+$/;
|
|
1670
|
-
var JWT_REGEX = /^[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+$/;
|
|
1671
|
-
var EMOJI_REGEX = /^[\p{Emoji}]+$/u;
|
|
1672
|
-
var SLUG_REGEX = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
1673
|
-
var SEMVER_REGEX = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$/;
|
|
1674
|
-
function isValidCreditCard(value) {
|
|
1675
|
-
const digits = value.replace(/\D/g, "");
|
|
1676
|
-
if (digits.length < 13 || digits.length > 19) return false;
|
|
1677
|
-
let sum = 0;
|
|
1678
|
-
let isEven = false;
|
|
1679
|
-
for (let i = digits.length - 1; i >= 0; i--) {
|
|
1680
|
-
let digit = parseInt(digits[i], 10);
|
|
1681
|
-
if (isEven) {
|
|
1682
|
-
digit *= 2;
|
|
1683
|
-
if (digit > 9) digit -= 9;
|
|
1684
|
-
}
|
|
1685
|
-
sum += digit;
|
|
1686
|
-
isEven = !isEven;
|
|
1687
|
-
}
|
|
1688
|
-
return sum % 10 === 0;
|
|
1689
|
-
}
|
|
1690
|
-
var isRegistered = false;
|
|
1691
|
-
function registerFormats() {
|
|
1692
|
-
if (isRegistered) return;
|
|
1693
|
-
isRegistered = true;
|
|
1694
|
-
FormatRegistry.Set("email", (v) => EMAIL_REGEX.test(v));
|
|
1695
|
-
FormatRegistry.Set("uuid", (v) => UUID_REGEX.test(v));
|
|
1696
|
-
FormatRegistry.Set("uuid-any", (v) => UUID_ANY_REGEX.test(v));
|
|
1697
|
-
FormatRegistry.Set("cuid", (v) => CUID_REGEX.test(v));
|
|
1698
|
-
FormatRegistry.Set("cuid2", (v) => CUID2_REGEX.test(v) && v.length >= 1);
|
|
1699
|
-
FormatRegistry.Set("ulid", (v) => ULID_REGEX.test(v));
|
|
1700
|
-
FormatRegistry.Set("nanoid", (v) => NANOID_REGEX.test(v));
|
|
1701
|
-
FormatRegistry.Set("objectid", (v) => OBJECTID_REGEX.test(v));
|
|
1702
|
-
FormatRegistry.Set("slug", (v) => SLUG_REGEX.test(v));
|
|
1703
|
-
FormatRegistry.Set("url", (v) => URL_REGEX.test(v));
|
|
1704
|
-
FormatRegistry.Set("uri", (v) => URL_REGEX.test(v));
|
|
1705
|
-
FormatRegistry.Set("ipv4", (v) => IPV4_REGEX.test(v));
|
|
1706
|
-
FormatRegistry.Set("ipv6", (v) => IPV6_REGEX.test(v));
|
|
1707
|
-
FormatRegistry.Set("ip", (v) => IPV4_REGEX.test(v) || IPV6_REGEX.test(v));
|
|
1708
|
-
FormatRegistry.Set("cidr", (v) => CIDR_REGEX.test(v));
|
|
1709
|
-
FormatRegistry.Set("hostname", (v) => HOSTNAME_REGEX.test(v));
|
|
1710
|
-
FormatRegistry.Set("date", (v) => DATE_REGEX.test(v));
|
|
1711
|
-
FormatRegistry.Set("time", (v) => TIME_REGEX.test(v));
|
|
1712
|
-
FormatRegistry.Set("date-time", (v) => DATE_TIME_REGEX.test(v));
|
|
1713
|
-
FormatRegistry.Set("datetime", (v) => DATE_TIME_REGEX.test(v));
|
|
1714
|
-
FormatRegistry.Set("duration", (v) => DURATION_REGEX.test(v));
|
|
1715
|
-
FormatRegistry.Set("phone", (v) => PHONE_CN_REGEX.test(v));
|
|
1716
|
-
FormatRegistry.Set("phone-cn", (v) => PHONE_CN_REGEX.test(v));
|
|
1717
|
-
FormatRegistry.Set("phone-e164", (v) => PHONE_E164_REGEX.test(v));
|
|
1718
|
-
FormatRegistry.Set("base64", (v) => BASE64_REGEX.test(v));
|
|
1719
|
-
FormatRegistry.Set("base64url", (v) => BASE64URL_REGEX.test(v));
|
|
1720
|
-
FormatRegistry.Set("jwt", (v) => JWT_REGEX.test(v));
|
|
1721
|
-
FormatRegistry.Set("hex-color", (v) => HEX_COLOR_REGEX.test(v));
|
|
1722
|
-
FormatRegistry.Set("rgb-color", (v) => RGB_COLOR_REGEX.test(v));
|
|
1723
|
-
FormatRegistry.Set(
|
|
1724
|
-
"color",
|
|
1725
|
-
(v) => HEX_COLOR_REGEX.test(v) || RGB_COLOR_REGEX.test(v)
|
|
1726
|
-
);
|
|
1727
|
-
FormatRegistry.Set("emoji", (v) => EMOJI_REGEX.test(v));
|
|
1728
|
-
FormatRegistry.Set("semver", (v) => SEMVER_REGEX.test(v));
|
|
1729
|
-
FormatRegistry.Set("credit-card", isValidCreditCard);
|
|
1730
|
-
}
|
|
1731
|
-
function registerFormat(name, validator) {
|
|
1732
|
-
FormatRegistry.Set(name, validator);
|
|
1733
|
-
}
|
|
1734
|
-
function hasFormat(name) {
|
|
1735
|
-
return FormatRegistry.Has(name);
|
|
1736
|
-
}
|
|
1737
|
-
var Patterns = {
|
|
1738
|
-
EMAIL: EMAIL_REGEX,
|
|
1739
|
-
UUID: UUID_REGEX,
|
|
1740
|
-
URL: URL_REGEX,
|
|
1741
|
-
IPV4: IPV4_REGEX,
|
|
1742
|
-
IPV6: IPV6_REGEX,
|
|
1743
|
-
DATE: DATE_REGEX,
|
|
1744
|
-
TIME: TIME_REGEX,
|
|
1745
|
-
DATE_TIME: DATE_TIME_REGEX,
|
|
1746
|
-
PHONE_CN: PHONE_CN_REGEX,
|
|
1747
|
-
PHONE_E164: PHONE_E164_REGEX,
|
|
1748
|
-
OBJECTID: OBJECTID_REGEX,
|
|
1749
|
-
HEX_COLOR: HEX_COLOR_REGEX,
|
|
1750
|
-
SLUG: SLUG_REGEX,
|
|
1751
|
-
SEMVER: SEMVER_REGEX,
|
|
1752
|
-
JWT: JWT_REGEX
|
|
1753
|
-
};
|
|
1754
|
-
|
|
1755
|
-
// src/utils/sse.ts
|
|
1756
|
-
function formatSSEEvent(event) {
|
|
1757
|
-
const lines = [];
|
|
1758
|
-
if (event.id !== void 0) {
|
|
1759
|
-
lines.push(`id: ${event.id}`);
|
|
1760
|
-
}
|
|
1761
|
-
if (event.event !== void 0) {
|
|
1762
|
-
lines.push(`event: ${event.event}`);
|
|
1763
|
-
}
|
|
1764
|
-
if (event.retry !== void 0) {
|
|
1765
|
-
lines.push(`retry: ${event.retry}`);
|
|
1766
|
-
}
|
|
1767
|
-
const dataStr = typeof event.data === "string" ? event.data : JSON.stringify(event.data);
|
|
1768
|
-
const dataLines = dataStr.split("\n");
|
|
1769
|
-
for (const line of dataLines) {
|
|
1770
|
-
lines.push(`data: ${line}`);
|
|
1771
|
-
}
|
|
1772
|
-
return lines.join("\n") + "\n\n";
|
|
1773
|
-
}
|
|
1774
|
-
function createSSEHandler(schemaOrGenerator, maybeGenerator) {
|
|
1775
|
-
const hasSchema = typeof schemaOrGenerator !== "function";
|
|
1776
|
-
const schema = hasSchema ? schemaOrGenerator : {};
|
|
1777
|
-
const generator = hasSchema ? maybeGenerator : schemaOrGenerator;
|
|
1778
|
-
if (schema.body || schema.query || schema.params || schema.headers || schema.cookies) {
|
|
1779
|
-
precompileSchemas(schema);
|
|
1780
|
-
}
|
|
1781
|
-
const handlerFn = async (req) => {
|
|
1782
|
-
try {
|
|
1783
|
-
const query = parseQuery(req);
|
|
1784
|
-
const headers = parseHeaders(req);
|
|
1785
|
-
const cookies = parseCookies(req);
|
|
1786
|
-
const params = req.params || {};
|
|
1787
|
-
const data = { body: void 0, query, params, headers, cookies };
|
|
1788
|
-
if (schema.body || schema.query || schema.params || schema.headers || schema.cookies) {
|
|
1789
|
-
validateAllSchemas(schema, data);
|
|
1790
|
-
}
|
|
1791
|
-
const stream2 = new ReadableStream({
|
|
1792
|
-
async start(controller) {
|
|
1793
|
-
const encoder2 = new TextEncoder();
|
|
1794
|
-
try {
|
|
1795
|
-
const gen = generator({
|
|
1796
|
-
req,
|
|
1797
|
-
body: void 0,
|
|
1798
|
-
query,
|
|
1799
|
-
params,
|
|
1800
|
-
headers,
|
|
1801
|
-
cookies
|
|
1802
|
-
});
|
|
1803
|
-
for await (const event of gen) {
|
|
1804
|
-
const formatted = formatSSEEvent(event);
|
|
1805
|
-
controller.enqueue(encoder2.encode(formatted));
|
|
1806
|
-
}
|
|
1807
|
-
} catch (error) {
|
|
1808
|
-
const errorEvent = formatSSEEvent({
|
|
1809
|
-
event: "error",
|
|
1810
|
-
data: {
|
|
1811
|
-
message: error instanceof Error ? error.message : "Unknown error"
|
|
1812
|
-
}
|
|
1813
|
-
});
|
|
1814
|
-
controller.enqueue(encoder2.encode(errorEvent));
|
|
1815
|
-
} finally {
|
|
1816
|
-
controller.close();
|
|
1817
|
-
}
|
|
1818
|
-
}
|
|
1819
|
-
});
|
|
1820
|
-
return new Response(stream2, {
|
|
1821
|
-
headers: {
|
|
1822
|
-
"Content-Type": "text/event-stream",
|
|
1823
|
-
"Cache-Control": "no-cache",
|
|
1824
|
-
"Connection": "keep-alive",
|
|
1825
|
-
"X-Accel-Buffering": "no"
|
|
1826
|
-
// Nginx 禁用缓冲
|
|
1827
|
-
}
|
|
1828
|
-
});
|
|
1829
|
-
} catch (error) {
|
|
1830
|
-
return new Response(
|
|
1831
|
-
JSON.stringify({
|
|
1832
|
-
success: false,
|
|
1833
|
-
error: "Validation Error",
|
|
1834
|
-
message: error instanceof Error ? error.message : "Unknown error"
|
|
1835
|
-
}),
|
|
1836
|
-
{
|
|
1837
|
-
status: 400,
|
|
1838
|
-
headers: { "Content-Type": "application/json" }
|
|
1839
|
-
}
|
|
1840
|
-
);
|
|
1841
|
-
}
|
|
1842
|
-
};
|
|
1843
|
-
const handler = handlerFn;
|
|
1844
|
-
handler.__sse = { __brand: "SSE" };
|
|
1845
|
-
handler.__schema = schema;
|
|
1846
|
-
handler.__returnType = void 0;
|
|
1847
|
-
return handler;
|
|
1848
|
-
}
|
|
1849
|
-
|
|
1850
|
-
// src/middleware/authMiddleware.ts
|
|
1851
|
-
var requireAuth = async (req, next) => {
|
|
1852
|
-
const token = getCookie2(req, "auth");
|
|
1853
|
-
if (!token || token !== "valid-token") {
|
|
1854
|
-
throw new VafastError("Unauthorized", {
|
|
1855
|
-
status: 401,
|
|
1856
|
-
type: "unauthorized",
|
|
1857
|
-
expose: true
|
|
1858
|
-
});
|
|
1859
|
-
}
|
|
1860
|
-
return next();
|
|
1861
|
-
};
|
|
1862
|
-
|
|
1863
|
-
// src/middleware/rateLimit.ts
|
|
1864
|
-
var store = /* @__PURE__ */ new Map();
|
|
1865
|
-
function rateLimit(options = {}) {
|
|
1866
|
-
const windowMs = options.windowMs ?? 6e4;
|
|
1867
|
-
const max = options.max ?? 30;
|
|
1868
|
-
const keyFn = options.keyFn ?? getIP;
|
|
1869
|
-
return async (req, next) => {
|
|
1870
|
-
const key = keyFn(req);
|
|
1871
|
-
const now = Date.now();
|
|
1872
|
-
const entry = store.get(key);
|
|
1873
|
-
if (entry && entry.expires > now) {
|
|
1874
|
-
if (entry.count >= max) {
|
|
1875
|
-
throw new VafastError("Too many requests", {
|
|
1876
|
-
status: 429,
|
|
1877
|
-
type: "rate_limit",
|
|
1878
|
-
expose: true
|
|
1879
|
-
});
|
|
1880
|
-
}
|
|
1881
|
-
entry.count += 1;
|
|
1882
|
-
} else {
|
|
1883
|
-
store.set(key, { count: 1, expires: now + windowMs });
|
|
1884
|
-
}
|
|
1885
|
-
return next();
|
|
1886
|
-
};
|
|
1887
|
-
}
|
|
1888
|
-
function getIP(req) {
|
|
1889
|
-
return req.headers.get("cf-connecting-ip") || // Cloudflare
|
|
1890
|
-
req.headers.get("x-forwarded-for")?.split(",")[0]?.trim() || // Vercel
|
|
1891
|
-
"unknown";
|
|
1892
|
-
}
|
|
1893
|
-
|
|
1894
|
-
// src/middleware/cors.ts
|
|
1895
|
-
function createCORS(options = {}) {
|
|
1896
|
-
const {
|
|
1897
|
-
origin = [],
|
|
1898
|
-
methods = ["GET", "POST", "PUT", "DELETE", "OPTIONS"],
|
|
1899
|
-
headers = [],
|
|
1900
|
-
credentials = false,
|
|
1901
|
-
maxAge
|
|
1902
|
-
} = options;
|
|
1903
|
-
return async (req, next) => {
|
|
1904
|
-
const reqOrigin = req.headers.get("Origin") || "";
|
|
1905
|
-
const isAllowedOrigin = origin === "*" || origin.includes(reqOrigin);
|
|
1906
|
-
if (req.method === "OPTIONS") {
|
|
1907
|
-
const resHeaders = new Headers();
|
|
1908
|
-
if (isAllowedOrigin) {
|
|
1909
|
-
resHeaders.set(
|
|
1910
|
-
"Access-Control-Allow-Origin",
|
|
1911
|
-
origin === "*" ? "*" : reqOrigin
|
|
1912
|
-
);
|
|
1913
|
-
resHeaders.set("Access-Control-Allow-Methods", methods.join(","));
|
|
1914
|
-
resHeaders.set("Access-Control-Allow-Headers", headers.join(","));
|
|
1915
|
-
if (credentials)
|
|
1916
|
-
resHeaders.set("Access-Control-Allow-Credentials", "true");
|
|
1917
|
-
if (maxAge) resHeaders.set("Access-Control-Max-Age", maxAge.toString());
|
|
1918
|
-
}
|
|
1919
|
-
return new Response(null, { status: 204, headers: resHeaders });
|
|
1920
|
-
}
|
|
1921
|
-
const res = await next();
|
|
1922
|
-
if (isAllowedOrigin) {
|
|
1923
|
-
res.headers.set(
|
|
1924
|
-
"Access-Control-Allow-Origin",
|
|
1925
|
-
origin === "*" ? "*" : reqOrigin
|
|
1926
|
-
);
|
|
1927
|
-
if (credentials)
|
|
1928
|
-
res.headers.set("Access-Control-Allow-Credentials", "true");
|
|
1929
|
-
}
|
|
1930
|
-
return res;
|
|
1931
|
-
};
|
|
1932
|
-
}
|
|
1933
|
-
|
|
1934
|
-
// src/auth/token.ts
|
|
1935
|
-
var TokenError = class extends Error {
|
|
1936
|
-
constructor(message, code) {
|
|
1937
|
-
super(message);
|
|
1938
|
-
this.code = code;
|
|
1939
|
-
this.name = "TokenError";
|
|
1940
|
-
}
|
|
1941
|
-
};
|
|
1942
|
-
var encoder = new TextEncoder();
|
|
1943
|
-
async function sign(data, secret) {
|
|
1944
|
-
const key = await crypto.subtle.importKey(
|
|
1945
|
-
"raw",
|
|
1946
|
-
encoder.encode(secret),
|
|
1947
|
-
{ name: "HMAC", hash: "SHA-256" },
|
|
1948
|
-
false,
|
|
1949
|
-
["sign"]
|
|
1950
|
-
);
|
|
1951
|
-
const signature = await crypto.subtle.sign("HMAC", key, encoder.encode(data));
|
|
1952
|
-
return btoa(
|
|
1953
|
-
String.fromCharCode.apply(null, Array.from(new Uint8Array(signature)))
|
|
1954
|
-
);
|
|
1955
|
-
}
|
|
1956
|
-
async function generateToken(payload, secret, options = {}) {
|
|
1957
|
-
const { expiresIn = 3600, issuer, audience, subject } = options;
|
|
1958
|
-
const now = Math.floor(Date.now() / 1e3);
|
|
1959
|
-
const tokenPayload = {
|
|
1960
|
-
...payload,
|
|
1961
|
-
iat: now,
|
|
1962
|
-
exp: now + expiresIn
|
|
1963
|
-
};
|
|
1964
|
-
if (issuer) tokenPayload.iss = issuer;
|
|
1965
|
-
if (audience) tokenPayload.aud = audience;
|
|
1966
|
-
if (subject) tokenPayload.sub = subject;
|
|
1967
|
-
const data = base64urlEncode(JSON.stringify(tokenPayload));
|
|
1968
|
-
const sig = await sign(data, secret);
|
|
1969
|
-
const token = `${data}.${base64urlEncode(sig)}`;
|
|
1970
|
-
return {
|
|
1971
|
-
payload: tokenPayload,
|
|
1972
|
-
token,
|
|
1973
|
-
expiresAt: tokenPayload.exp * 1e3
|
|
1974
|
-
// 转换为毫秒
|
|
1975
|
-
};
|
|
1976
|
-
}
|
|
1977
|
-
async function verifyToken(token, secret) {
|
|
1978
|
-
try {
|
|
1979
|
-
const [data, sig] = token.split(".");
|
|
1980
|
-
if (!data || !sig) {
|
|
1981
|
-
throw new TokenError("\u4EE4\u724C\u683C\u5F0F\u65E0\u6548", "MALFORMED_TOKEN");
|
|
1982
|
-
}
|
|
1983
|
-
const expectedSig = await sign(data, secret);
|
|
1984
|
-
const expected = base64urlEncode(expectedSig);
|
|
1985
|
-
if (sig !== expected) {
|
|
1986
|
-
throw new TokenError("\u4EE4\u724C\u7B7E\u540D\u65E0\u6548", "INVALID_SIGNATURE");
|
|
1987
|
-
}
|
|
1988
|
-
const payload = JSON.parse(base64urlDecode(data));
|
|
1989
|
-
if (payload.exp && Date.now() / 1e3 > payload.exp) {
|
|
1990
|
-
throw new TokenError("\u4EE4\u724C\u5DF2\u8FC7\u671F", "EXPIRED_TOKEN");
|
|
1991
|
-
}
|
|
1992
|
-
return payload;
|
|
1993
|
-
} catch (error) {
|
|
1994
|
-
if (error instanceof TokenError) {
|
|
1995
|
-
throw error;
|
|
1996
|
-
}
|
|
1997
|
-
throw new TokenError("\u4EE4\u724C\u9A8C\u8BC1\u5931\u8D25", "INVALID_TOKEN");
|
|
1998
|
-
}
|
|
1999
|
-
}
|
|
2000
|
-
function parseToken(token) {
|
|
2001
|
-
try {
|
|
2002
|
-
const [data] = token.split(".");
|
|
2003
|
-
if (!data) return null;
|
|
2004
|
-
return JSON.parse(base64urlDecode(data));
|
|
2005
|
-
} catch {
|
|
2006
|
-
return null;
|
|
2007
|
-
}
|
|
2008
|
-
}
|
|
2009
|
-
function isTokenExpired(token) {
|
|
2010
|
-
const payload = parseToken(token);
|
|
2011
|
-
if (!payload || !payload.exp) return true;
|
|
2012
|
-
return Date.now() / 1e3 > payload.exp;
|
|
2013
|
-
}
|
|
2014
|
-
function getTokenTimeRemaining(token) {
|
|
2015
|
-
const payload = parseToken(token);
|
|
2016
|
-
if (!payload || !payload.exp) return 0;
|
|
2017
|
-
const remaining = payload.exp - Date.now() / 1e3;
|
|
2018
|
-
return Math.max(0, Math.floor(remaining));
|
|
2019
|
-
}
|
|
2020
|
-
async function refreshToken(token, secret, options = {}) {
|
|
2021
|
-
try {
|
|
2022
|
-
const payload = await verifyToken(token, secret);
|
|
2023
|
-
if (!payload) return null;
|
|
2024
|
-
const { exp, iat, ...cleanPayload } = payload;
|
|
2025
|
-
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
2026
|
-
return await generateToken(cleanPayload, secret, options);
|
|
2027
|
-
} catch {
|
|
2028
|
-
return null;
|
|
2029
|
-
}
|
|
2030
|
-
}
|
|
2031
|
-
async function createTokenPair(payload, secret, options = {}) {
|
|
2032
|
-
const accessToken = await generateToken(payload, secret, {
|
|
2033
|
-
...options,
|
|
2034
|
-
expiresIn: options.expiresIn || 3600
|
|
2035
|
-
// 1小时
|
|
2036
|
-
});
|
|
2037
|
-
const refreshToken2 = await generateToken(payload, secret, {
|
|
2038
|
-
...options,
|
|
2039
|
-
expiresIn: 7 * 24 * 3600
|
|
2040
|
-
// 7天
|
|
2041
|
-
});
|
|
2042
|
-
return { accessToken, refreshToken: refreshToken2 };
|
|
2043
|
-
}
|
|
2044
|
-
|
|
2045
|
-
// src/middleware/auth.ts
|
|
2046
|
-
function createAuth(options) {
|
|
2047
|
-
const {
|
|
2048
|
-
secret,
|
|
2049
|
-
cookieName = "auth",
|
|
2050
|
-
headerName = "authorization",
|
|
2051
|
-
required = true,
|
|
2052
|
-
roles = [],
|
|
2053
|
-
permissions = []
|
|
2054
|
-
} = options;
|
|
2055
|
-
return async (req, next) => {
|
|
2056
|
-
const token = getCookie2(req, cookieName) || req.headers.get(headerName)?.replace("Bearer ", "") || "";
|
|
2057
|
-
if (!token && required) {
|
|
2058
|
-
throw new VafastError("\u7F3A\u5C11\u8BA4\u8BC1\u4EE4\u724C", {
|
|
2059
|
-
status: 401,
|
|
2060
|
-
type: "unauthorized",
|
|
2061
|
-
expose: true
|
|
2062
|
-
});
|
|
2063
|
-
}
|
|
2064
|
-
if (!token && !required) {
|
|
2065
|
-
return next();
|
|
2066
|
-
}
|
|
2067
|
-
try {
|
|
2068
|
-
const user = await verifyToken(token, secret);
|
|
2069
|
-
if (!user) {
|
|
2070
|
-
throw new VafastError("\u4EE4\u724C\u9A8C\u8BC1\u5931\u8D25", {
|
|
2071
|
-
status: 401,
|
|
2072
|
-
type: "unauthorized",
|
|
2073
|
-
expose: true
|
|
2074
|
-
});
|
|
2075
|
-
}
|
|
2076
|
-
if (roles.length > 0 && user.role && !roles.includes(user.role)) {
|
|
2077
|
-
throw new VafastError("\u6743\u9650\u4E0D\u8DB3", {
|
|
2078
|
-
status: 403,
|
|
2079
|
-
type: "forbidden",
|
|
2080
|
-
expose: true
|
|
2081
|
-
});
|
|
2082
|
-
}
|
|
2083
|
-
if (permissions.length > 0 && user.permissions) {
|
|
2084
|
-
const userPermissions = Array.isArray(user.permissions) ? user.permissions : [user.permissions];
|
|
2085
|
-
const hasPermission = permissions.some(
|
|
2086
|
-
(permission) => userPermissions.includes(permission)
|
|
2087
|
-
);
|
|
2088
|
-
if (!hasPermission) {
|
|
2089
|
-
throw new VafastError("\u6743\u9650\u4E0D\u8DB3", {
|
|
2090
|
-
status: 403,
|
|
2091
|
-
type: "forbidden",
|
|
2092
|
-
expose: true
|
|
2093
|
-
});
|
|
2094
|
-
}
|
|
2095
|
-
}
|
|
2096
|
-
req.user = user;
|
|
2097
|
-
req.token = token;
|
|
2098
|
-
return next();
|
|
2099
|
-
} catch (error) {
|
|
2100
|
-
if (error instanceof TokenError) {
|
|
2101
|
-
let status = 401;
|
|
2102
|
-
let message = "\u8BA4\u8BC1\u5931\u8D25";
|
|
2103
|
-
switch (error.code) {
|
|
2104
|
-
case "EXPIRED_TOKEN":
|
|
2105
|
-
status = 401;
|
|
2106
|
-
message = "\u4EE4\u724C\u5DF2\u8FC7\u671F";
|
|
2107
|
-
break;
|
|
2108
|
-
case "INVALID_SIGNATURE":
|
|
2109
|
-
status = 401;
|
|
2110
|
-
message = "\u4EE4\u724C\u7B7E\u540D\u65E0\u6548";
|
|
2111
|
-
break;
|
|
2112
|
-
case "MALFORMED_TOKEN":
|
|
2113
|
-
status = 400;
|
|
2114
|
-
message = "\u4EE4\u724C\u683C\u5F0F\u9519\u8BEF";
|
|
2115
|
-
break;
|
|
2116
|
-
default:
|
|
2117
|
-
status = 401;
|
|
2118
|
-
message = "\u4EE4\u724C\u9A8C\u8BC1\u5931\u8D25";
|
|
2119
|
-
}
|
|
2120
|
-
throw new VafastError(message, {
|
|
2121
|
-
status,
|
|
2122
|
-
type: "unauthorized",
|
|
2123
|
-
expose: true
|
|
2124
|
-
});
|
|
2125
|
-
}
|
|
2126
|
-
if (error instanceof VafastError) {
|
|
2127
|
-
throw error;
|
|
2128
|
-
}
|
|
2129
|
-
throw new VafastError("\u8BA4\u8BC1\u8FC7\u7A0B\u4E2D\u53D1\u751F\u9519\u8BEF", {
|
|
2130
|
-
status: 500,
|
|
2131
|
-
type: "internal_error",
|
|
2132
|
-
expose: false
|
|
2133
|
-
});
|
|
2134
|
-
}
|
|
2135
|
-
};
|
|
2136
|
-
}
|
|
2137
|
-
function createOptionalAuth(options) {
|
|
2138
|
-
return createAuth({ ...options, required: false });
|
|
2139
|
-
}
|
|
2140
|
-
function createRoleAuth(roles, options) {
|
|
2141
|
-
return createAuth({ ...options, roles });
|
|
2142
|
-
}
|
|
2143
|
-
function createPermissionAuth(permissions, options) {
|
|
2144
|
-
return createAuth({ ...options, permissions });
|
|
2145
|
-
}
|
|
2146
|
-
|
|
2147
|
-
// src/defineRoute.ts
|
|
2148
|
-
function route(method, path, handler, middleware) {
|
|
2149
|
-
return {
|
|
2150
|
-
method,
|
|
2151
|
-
path,
|
|
2152
|
-
handler,
|
|
2153
|
-
middleware
|
|
2154
|
-
};
|
|
2155
|
-
}
|
|
2156
|
-
function get(path, handler, middleware) {
|
|
2157
|
-
return route("GET", path, handler, middleware);
|
|
2158
|
-
}
|
|
2159
|
-
function post(path, handler, middleware) {
|
|
2160
|
-
return route("POST", path, handler, middleware);
|
|
2161
|
-
}
|
|
2162
|
-
function put(path, handler, middleware) {
|
|
2163
|
-
return route("PUT", path, handler, middleware);
|
|
2164
|
-
}
|
|
2165
|
-
function del(path, handler, middleware) {
|
|
2166
|
-
return route("DELETE", path, handler, middleware);
|
|
2167
|
-
}
|
|
2168
|
-
function patch(path, handler, middleware) {
|
|
2169
|
-
return route("PATCH", path, handler, middleware);
|
|
2170
|
-
}
|
|
2171
|
-
function defineRoutes(routes) {
|
|
2172
|
-
return routes;
|
|
2173
|
-
}
|
|
2174
|
-
|
|
2175
|
-
// src/types/route.ts
|
|
2176
|
-
function createTypedRoute(config) {
|
|
2177
|
-
return config;
|
|
2178
|
-
}
|
|
2179
|
-
function isTypedRoute(route2) {
|
|
2180
|
-
return route2 && typeof route2 === "object" && "method" in route2 && "path" in route2 && "handler" in route2;
|
|
2181
|
-
}
|
|
2182
|
-
|
|
2183
|
-
// src/node-server/serve.ts
|
|
2184
|
-
import {
|
|
2185
|
-
createServer
|
|
2186
|
-
} from "http";
|
|
2187
|
-
|
|
2188
|
-
// src/node-server/request.ts
|
|
2189
|
-
import { Readable } from "stream";
|
|
2190
|
-
var requestCache = /* @__PURE__ */ Symbol("requestCache");
|
|
2191
|
-
var incomingKey = /* @__PURE__ */ Symbol("incoming");
|
|
2192
|
-
var urlKey = /* @__PURE__ */ Symbol("url");
|
|
2193
|
-
var headersKey = /* @__PURE__ */ Symbol("headers");
|
|
2194
|
-
function parseHeaders2(rawHeaders) {
|
|
2195
|
-
const headers = new Headers();
|
|
2196
|
-
for (let i = 0; i < rawHeaders.length; i += 2) {
|
|
2197
|
-
const key = rawHeaders[i];
|
|
2198
|
-
const value = rawHeaders[i + 1];
|
|
2199
|
-
if (key.charCodeAt(0) !== 58) {
|
|
2200
|
-
headers.append(key, value);
|
|
2201
|
-
}
|
|
2202
|
-
}
|
|
2203
|
-
return headers;
|
|
2204
|
-
}
|
|
2205
|
-
function toWebStream(nodeStream) {
|
|
2206
|
-
return nodeStream;
|
|
2207
|
-
}
|
|
2208
|
-
function createRealRequest(proxy) {
|
|
2209
|
-
const incoming = proxy[incomingKey];
|
|
2210
|
-
const method = incoming.method || "GET";
|
|
2211
|
-
const init = {
|
|
2212
|
-
method,
|
|
2213
|
-
headers: proxy[headersKey] || parseHeaders2(incoming.rawHeaders)
|
|
2214
|
-
};
|
|
2215
|
-
if (method !== "GET" && method !== "HEAD") {
|
|
2216
|
-
const nodeWebStream = Readable.toWeb(
|
|
2217
|
-
incoming
|
|
2218
|
-
);
|
|
2219
|
-
init.body = toWebStream(nodeWebStream);
|
|
2220
|
-
init.duplex = "half";
|
|
2221
|
-
}
|
|
2222
|
-
return new Request(proxy[urlKey], init);
|
|
2223
|
-
}
|
|
2224
|
-
var requestPrototype = {};
|
|
2225
|
-
Object.defineProperty(requestPrototype, "method", {
|
|
2226
|
-
get() {
|
|
2227
|
-
const self = this;
|
|
2228
|
-
return self[incomingKey].method || "GET";
|
|
2229
|
-
},
|
|
2230
|
-
enumerable: true
|
|
2231
|
-
});
|
|
2232
|
-
Object.defineProperty(requestPrototype, "url", {
|
|
2233
|
-
get() {
|
|
2234
|
-
const self = this;
|
|
2235
|
-
return self[urlKey];
|
|
2236
|
-
},
|
|
2237
|
-
enumerable: true
|
|
2238
|
-
});
|
|
2239
|
-
Object.defineProperty(requestPrototype, "headers", {
|
|
2240
|
-
get() {
|
|
2241
|
-
const self = this;
|
|
2242
|
-
if (!self[headersKey]) {
|
|
2243
|
-
self[headersKey] = parseHeaders2(self[incomingKey].rawHeaders);
|
|
2244
|
-
}
|
|
2245
|
-
return self[headersKey];
|
|
2246
|
-
},
|
|
2247
|
-
enumerable: true
|
|
2248
|
-
});
|
|
2249
|
-
Object.defineProperty(requestPrototype, "_getRequest", {
|
|
2250
|
-
value: function() {
|
|
2251
|
-
const self = this;
|
|
2252
|
-
if (!self[requestCache]) {
|
|
2253
|
-
self[requestCache] = createRealRequest(self);
|
|
2254
|
-
}
|
|
2255
|
-
return self[requestCache];
|
|
2256
|
-
},
|
|
2257
|
-
enumerable: false
|
|
2258
|
-
});
|
|
2259
|
-
var proxyGetters = [
|
|
2260
|
-
"body",
|
|
2261
|
-
"bodyUsed",
|
|
2262
|
-
"signal",
|
|
2263
|
-
"cache",
|
|
2264
|
-
"credentials",
|
|
2265
|
-
"destination",
|
|
2266
|
-
"integrity",
|
|
2267
|
-
"mode",
|
|
2268
|
-
"redirect",
|
|
2269
|
-
"referrer",
|
|
2270
|
-
"referrerPolicy",
|
|
2271
|
-
"keepalive"
|
|
2272
|
-
];
|
|
2273
|
-
proxyGetters.forEach((key) => {
|
|
2274
|
-
Object.defineProperty(requestPrototype, key, {
|
|
2275
|
-
get() {
|
|
2276
|
-
const self = this;
|
|
2277
|
-
return self._getRequest()[key];
|
|
2278
|
-
},
|
|
2279
|
-
enumerable: true
|
|
2280
|
-
});
|
|
2281
|
-
});
|
|
2282
|
-
var proxyMethods = [
|
|
2283
|
-
"arrayBuffer",
|
|
2284
|
-
"blob",
|
|
2285
|
-
"clone",
|
|
2286
|
-
"formData",
|
|
2287
|
-
"json",
|
|
2288
|
-
"text"
|
|
2289
|
-
];
|
|
2290
|
-
proxyMethods.forEach((key) => {
|
|
2291
|
-
Object.defineProperty(requestPrototype, key, {
|
|
2292
|
-
value: function() {
|
|
2293
|
-
const self = this;
|
|
2294
|
-
const req = self._getRequest();
|
|
2295
|
-
return req[key].call(req);
|
|
2296
|
-
},
|
|
2297
|
-
enumerable: true
|
|
2298
|
-
});
|
|
2299
|
-
});
|
|
2300
|
-
Object.setPrototypeOf(requestPrototype, Request.prototype);
|
|
2301
|
-
function createProxyRequest(incoming, defaultHost) {
|
|
2302
|
-
const req = Object.create(requestPrototype);
|
|
2303
|
-
req[incomingKey] = incoming;
|
|
2304
|
-
const host = incoming.headers.host || defaultHost;
|
|
2305
|
-
const protocol = incoming.socket.encrypted ? "https" : "http";
|
|
2306
|
-
req[urlKey] = `${protocol}://${host}${incoming.url || "/"}`;
|
|
2307
|
-
return req;
|
|
2308
|
-
}
|
|
2309
|
-
|
|
2310
|
-
// src/node-server/response.ts
|
|
2311
|
-
function buildOutgoingHeaders(headers) {
|
|
2312
|
-
const result = {};
|
|
2313
|
-
const cookies = [];
|
|
2314
|
-
headers.forEach((value, key) => {
|
|
2315
|
-
if (key === "set-cookie") {
|
|
2316
|
-
cookies.push(value);
|
|
2317
|
-
} else {
|
|
2318
|
-
result[key] = value;
|
|
2319
|
-
}
|
|
2320
|
-
});
|
|
2321
|
-
if (cookies.length > 0) {
|
|
2322
|
-
result["set-cookie"] = cookies;
|
|
2323
|
-
}
|
|
2324
|
-
return result;
|
|
2325
|
-
}
|
|
2326
|
-
async function writeBodyStream(body, outgoing) {
|
|
2327
|
-
const reader = body.getReader();
|
|
2328
|
-
try {
|
|
2329
|
-
while (true) {
|
|
2330
|
-
const { done, value } = await reader.read();
|
|
2331
|
-
if (done) {
|
|
2332
|
-
break;
|
|
2333
|
-
}
|
|
2334
|
-
const canContinue = outgoing.write(value);
|
|
2335
|
-
if (!canContinue) {
|
|
2336
|
-
await new Promise((resolve) => {
|
|
2337
|
-
outgoing.once("drain", resolve);
|
|
2338
|
-
});
|
|
2339
|
-
}
|
|
2340
|
-
}
|
|
2341
|
-
} finally {
|
|
2342
|
-
reader.releaseLock();
|
|
2343
|
-
}
|
|
2344
|
-
}
|
|
2345
|
-
async function writeResponse(response, outgoing) {
|
|
2346
|
-
outgoing.statusCode = response.status;
|
|
2347
|
-
const headers = buildOutgoingHeaders(response.headers);
|
|
2348
|
-
for (const [key, value] of Object.entries(headers)) {
|
|
2349
|
-
outgoing.setHeader(key, value);
|
|
2350
|
-
}
|
|
2351
|
-
const body = response.body;
|
|
2352
|
-
if (!body) {
|
|
2353
|
-
outgoing.end();
|
|
2354
|
-
return;
|
|
2355
|
-
}
|
|
2356
|
-
try {
|
|
2357
|
-
await writeBodyStream(body, outgoing);
|
|
2358
|
-
outgoing.end();
|
|
2359
|
-
} catch (error) {
|
|
2360
|
-
if (!outgoing.destroyed) {
|
|
2361
|
-
outgoing.destroy(
|
|
2362
|
-
error instanceof Error ? error : new Error(String(error))
|
|
2363
|
-
);
|
|
2364
|
-
}
|
|
2365
|
-
}
|
|
2366
|
-
}
|
|
2367
|
-
|
|
2368
|
-
// src/node-server/serve.ts
|
|
2369
|
-
function createRequestHandler(fetch, defaultHost, onError) {
|
|
2370
|
-
return async (incoming, outgoing) => {
|
|
2371
|
-
try {
|
|
2372
|
-
const request = createProxyRequest(incoming, defaultHost);
|
|
2373
|
-
const response = await fetch(request);
|
|
2374
|
-
await writeResponse(response, outgoing);
|
|
2375
|
-
} catch (error) {
|
|
2376
|
-
const err = error instanceof Error ? error : new Error(String(error));
|
|
2377
|
-
if (onError) {
|
|
2378
|
-
try {
|
|
2379
|
-
const errorResponse = await onError(err);
|
|
2380
|
-
await writeResponse(errorResponse, outgoing);
|
|
2381
|
-
return;
|
|
2382
|
-
} catch {
|
|
2383
|
-
}
|
|
2384
|
-
}
|
|
2385
|
-
if (!outgoing.headersSent) {
|
|
2386
|
-
outgoing.statusCode = 500;
|
|
2387
|
-
outgoing.setHeader("Content-Type", "text/plain");
|
|
2388
|
-
outgoing.end("Internal Server Error");
|
|
2389
|
-
}
|
|
2390
|
-
}
|
|
2391
|
-
};
|
|
2392
|
-
}
|
|
2393
|
-
function serve(options, callback) {
|
|
2394
|
-
const { fetch, port = 3e3, hostname = "0.0.0.0", onError, gracefulShutdown } = options;
|
|
2395
|
-
const defaultHost = `${hostname === "0.0.0.0" ? "localhost" : hostname}:${port}`;
|
|
2396
|
-
const handler = createRequestHandler(fetch, defaultHost, onError);
|
|
2397
|
-
const server = createServer(handler);
|
|
2398
|
-
const connections = /* @__PURE__ */ new Set();
|
|
2399
|
-
server.on("connection", (socket) => {
|
|
2400
|
-
connections.add(socket);
|
|
2401
|
-
socket.on("close", () => connections.delete(socket));
|
|
2402
|
-
});
|
|
2403
|
-
let isShuttingDown = false;
|
|
2404
|
-
const shutdown = async () => {
|
|
2405
|
-
if (isShuttingDown) return;
|
|
2406
|
-
isShuttingDown = true;
|
|
2407
|
-
const shutdownOptions = typeof gracefulShutdown === "object" ? gracefulShutdown : {};
|
|
2408
|
-
const timeout = shutdownOptions.timeout ?? 3e4;
|
|
2409
|
-
if (shutdownOptions.onShutdown) {
|
|
2410
|
-
await shutdownOptions.onShutdown();
|
|
2411
|
-
}
|
|
2412
|
-
return new Promise((resolve) => {
|
|
2413
|
-
const forceCloseTimer = setTimeout(() => {
|
|
2414
|
-
for (const socket of connections) {
|
|
2415
|
-
socket.destroy();
|
|
2416
|
-
}
|
|
2417
|
-
connections.clear();
|
|
2418
|
-
resolve();
|
|
2419
|
-
}, timeout);
|
|
2420
|
-
server.close(() => {
|
|
2421
|
-
clearTimeout(forceCloseTimer);
|
|
2422
|
-
shutdownOptions.onShutdownComplete?.();
|
|
2423
|
-
resolve();
|
|
2424
|
-
});
|
|
2425
|
-
for (const socket of connections) {
|
|
2426
|
-
if (!socket.writableLength) {
|
|
2427
|
-
socket.end();
|
|
2428
|
-
}
|
|
2429
|
-
}
|
|
2430
|
-
});
|
|
2431
|
-
};
|
|
2432
|
-
if (gracefulShutdown) {
|
|
2433
|
-
const shutdownOptions = typeof gracefulShutdown === "object" ? gracefulShutdown : {};
|
|
2434
|
-
const signals = shutdownOptions.signals ?? ["SIGINT", "SIGTERM"];
|
|
2435
|
-
for (const signal of signals) {
|
|
2436
|
-
process.on(signal, () => {
|
|
2437
|
-
shutdown().then(() => process.exit(0));
|
|
2438
|
-
});
|
|
2439
|
-
}
|
|
2440
|
-
}
|
|
2441
|
-
server.listen(port, hostname, callback);
|
|
2442
|
-
return {
|
|
2443
|
-
server,
|
|
2444
|
-
port,
|
|
2445
|
-
hostname,
|
|
2446
|
-
stop: () => new Promise((resolve, reject) => {
|
|
2447
|
-
server.close((err) => {
|
|
2448
|
-
if (err) reject(err);
|
|
2449
|
-
else resolve();
|
|
2450
|
-
});
|
|
2451
|
-
}),
|
|
2452
|
-
shutdown
|
|
2453
|
-
};
|
|
2454
|
-
}
|
|
2455
|
-
|
|
2456
|
-
// src/index.ts
|
|
2457
|
-
import { Type as Type2, FormatRegistry as FormatRegistry2 } from "@sinclair/typebox";
|
|
2458
|
-
registerFormats();
|
|
2459
|
-
export {
|
|
2460
|
-
BaseServer,
|
|
2461
|
-
ComponentServer,
|
|
2462
|
-
DependencyManager,
|
|
2463
|
-
FormatRegistry2 as FormatRegistry,
|
|
2464
|
-
HtmlRenderer,
|
|
2465
|
-
Patterns,
|
|
2466
|
-
RouteRegistry,
|
|
2467
|
-
Server,
|
|
2468
|
-
ServerFactory,
|
|
2469
|
-
TokenError,
|
|
2470
|
-
Type2 as Type,
|
|
2471
|
-
VafastError,
|
|
2472
|
-
base64urlDecode,
|
|
2473
|
-
base64urlEncode,
|
|
2474
|
-
composeMiddleware,
|
|
2475
|
-
createAuth,
|
|
2476
|
-
createCORS,
|
|
2477
|
-
createHandler,
|
|
2478
|
-
createHandlerWithExtra,
|
|
2479
|
-
createOptionalAuth,
|
|
2480
|
-
createPermissionAuth,
|
|
2481
|
-
createRequestValidator,
|
|
2482
|
-
createRoleAuth,
|
|
2483
|
-
createRouteRegistry,
|
|
2484
|
-
createSSEHandler,
|
|
2485
|
-
createTokenPair,
|
|
2486
|
-
createTypedRoute,
|
|
2487
|
-
createValidator,
|
|
2488
|
-
defineRoutes,
|
|
2489
|
-
del,
|
|
2490
|
-
empty,
|
|
2491
|
-
filterRoutes,
|
|
2492
|
-
flattenNestedRoutes,
|
|
2493
|
-
generateToken,
|
|
2494
|
-
get,
|
|
2495
|
-
getAllRoutes,
|
|
2496
|
-
getCookie,
|
|
2497
|
-
getHeader,
|
|
2498
|
-
getLocals,
|
|
2499
|
-
getRoute,
|
|
2500
|
-
getRouteRegistry,
|
|
2501
|
-
getTokenTimeRemaining,
|
|
2502
|
-
getValidatorCacheStats,
|
|
2503
|
-
goAwait,
|
|
2504
|
-
hasFormat,
|
|
2505
|
-
html,
|
|
2506
|
-
isTokenExpired,
|
|
2507
|
-
isTypedRoute,
|
|
2508
|
-
json,
|
|
2509
|
-
normalizePath,
|
|
2510
|
-
parseAndValidateRequest,
|
|
2511
|
-
parseBody,
|
|
2512
|
-
parseCookies,
|
|
2513
|
-
parseCookiesFast,
|
|
2514
|
-
parseHeaders,
|
|
2515
|
-
parseQuery,
|
|
2516
|
-
parseQueryFast,
|
|
2517
|
-
parseRequest,
|
|
2518
|
-
parseToken,
|
|
2519
|
-
patch,
|
|
2520
|
-
post,
|
|
2521
|
-
precompileSchemas,
|
|
2522
|
-
put,
|
|
2523
|
-
rateLimit,
|
|
2524
|
-
redirect,
|
|
2525
|
-
refreshToken,
|
|
2526
|
-
registerFormat,
|
|
2527
|
-
registerFormats,
|
|
2528
|
-
requireAuth,
|
|
2529
|
-
route,
|
|
2530
|
-
serve,
|
|
2531
|
-
setLocals,
|
|
2532
|
-
simpleHandler,
|
|
2533
|
-
stream,
|
|
2534
|
-
text,
|
|
2535
|
-
validateAllSchemas,
|
|
2536
|
-
validateFast,
|
|
2537
|
-
validateRequest,
|
|
2538
|
-
validateSchema,
|
|
2539
|
-
validateSchemaOrThrow,
|
|
2540
|
-
verifyToken
|
|
2541
|
-
};
|
|
2542
|
-
/**
|
|
2543
|
-
* Go 风格的错误处理工具
|
|
2544
|
-
* 将 Promise 转换为 [Error | null, T | undefined] 格式
|
|
2545
|
-
*
|
|
2546
|
-
* @author Framework Team
|
|
2547
|
-
* @version 1.0.0
|
|
2548
|
-
* @license MIT
|
|
2549
|
-
*/
|
|
2550
|
-
/**
|
|
2551
|
-
* 类型安全的路由处理器工厂
|
|
2552
|
-
*
|
|
2553
|
-
* 非柯里化设计,API 更简洁
|
|
2554
|
-
*
|
|
2555
|
-
* @author Framework Team
|
|
2556
|
-
* @version 3.0.0
|
|
2557
|
-
* @license MIT
|
|
2558
|
-
*/
|
|
2559
|
-
/**
|
|
2560
|
-
* 请求解析和验证器
|
|
2561
|
-
*
|
|
2562
|
-
* 解析handler的req参数,使用Ultra验证器进行验证,
|
|
2563
|
-
* 并类型安全地返回解析出来的值
|
|
2564
|
-
*
|
|
2565
|
-
* @author Framework Team
|
|
2566
|
-
* @version 1.0.0
|
|
2567
|
-
* @license MIT
|
|
2568
|
-
*/
|
|
2569
|
-
/**
|
|
2570
|
-
* Schema 类型定义
|
|
2571
|
-
*
|
|
2572
|
-
* 使用 TSchema 约束替代 any,提供完整的类型安全
|
|
2573
|
-
*
|
|
2574
|
-
* @author Framework Team
|
|
2575
|
-
* @version 1.0.0
|
|
2576
|
-
* @license MIT
|
|
2577
|
-
*/
|
|
2578
|
-
//# sourceMappingURL=index.js.map
|