vafast 0.3.2 → 0.3.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.
Files changed (136) hide show
  1. package/dist/auth/token.d.ts +13 -11
  2. package/dist/auth/token.js +118 -111
  3. package/dist/auth/token.js.map +1 -0
  4. package/dist/defineRoute.d.ts +5 -2
  5. package/dist/defineRoute.js +7 -2
  6. package/dist/defineRoute.js.map +1 -0
  7. package/dist/index.d.ts +30 -14
  8. package/dist/index.js +2247 -15
  9. package/dist/index.js.map +1 -0
  10. package/dist/middleware/auth.d.ts +8 -6
  11. package/dist/middleware/auth.js +198 -99
  12. package/dist/middleware/auth.js.map +1 -0
  13. package/dist/middleware/authMiddleware.d.ts +5 -2
  14. package/dist/middleware/authMiddleware.js +55 -11
  15. package/dist/middleware/authMiddleware.js.map +1 -0
  16. package/dist/middleware/component-renderer.d.ts +4 -2
  17. package/dist/middleware/component-renderer.js +87 -80
  18. package/dist/middleware/component-renderer.js.map +1 -0
  19. package/dist/middleware/component-router.d.ts +8 -3
  20. package/dist/middleware/component-router.js +33 -39
  21. package/dist/middleware/component-router.js.map +1 -0
  22. package/dist/middleware/cors.d.ts +6 -3
  23. package/dist/middleware/cors.js +42 -29
  24. package/dist/middleware/cors.js.map +1 -0
  25. package/dist/middleware/rateLimit.d.ts +5 -3
  26. package/dist/middleware/rateLimit.js +45 -29
  27. package/dist/middleware/rateLimit.js.map +1 -0
  28. package/dist/middleware.d.ts +6 -3
  29. package/dist/middleware.js +97 -51
  30. package/dist/middleware.js.map +1 -0
  31. package/dist/monitoring/index.d.ts +11 -4
  32. package/dist/monitoring/index.js +1299 -17
  33. package/dist/monitoring/index.js.map +1 -0
  34. package/dist/monitoring/native-monitor.d.ts +12 -6
  35. package/dist/monitoring/native-monitor.js +1258 -161
  36. package/dist/monitoring/native-monitor.js.map +1 -0
  37. package/dist/monitoring/types.d.ts +8 -6
  38. package/dist/monitoring/types.js +1 -1
  39. package/dist/monitoring/types.js.map +1 -0
  40. package/dist/node-server/index.d.ts +4 -22
  41. package/dist/node-server/index.js +254 -21
  42. package/dist/node-server/index.js.map +1 -0
  43. package/dist/node-server/request.d.ts +6 -2
  44. package/dist/node-server/request.js +102 -134
  45. package/dist/node-server/request.js.map +1 -0
  46. package/dist/node-server/response.d.ts +7 -3
  47. package/dist/node-server/response.js +67 -89
  48. package/dist/node-server/response.js.map +1 -0
  49. package/dist/node-server/serve.d.ts +11 -7
  50. package/dist/node-server/serve.js +231 -82
  51. package/dist/node-server/serve.js.map +1 -0
  52. package/dist/router/index.d.ts +3 -5
  53. package/dist/router/index.js +228 -7
  54. package/dist/router/index.js.map +1 -0
  55. package/dist/router/radix-tree.d.ts +7 -4
  56. package/dist/router/radix-tree.js +186 -218
  57. package/dist/router/radix-tree.js.map +1 -0
  58. package/dist/router.d.ts +7 -3
  59. package/dist/router.js +37 -83
  60. package/dist/router.js.map +1 -0
  61. package/dist/serve.d.ts +2 -12
  62. package/dist/serve.js +237 -11
  63. package/dist/serve.js.map +1 -0
  64. package/dist/server/base-server.d.ts +5 -2
  65. package/dist/server/base-server.js +124 -135
  66. package/dist/server/base-server.js.map +1 -0
  67. package/dist/server/component-server.d.ts +9 -4
  68. package/dist/server/component-server.js +481 -139
  69. package/dist/server/component-server.js.map +1 -0
  70. package/dist/server/index.d.ts +8 -7
  71. package/dist/server/index.js +985 -11
  72. package/dist/server/index.js.map +1 -0
  73. package/dist/server/server-factory.d.ts +11 -5
  74. package/dist/server/server-factory.js +979 -67
  75. package/dist/server/server-factory.js.map +1 -0
  76. package/dist/server/server.d.ts +7 -3
  77. package/dist/server/server.js +553 -112
  78. package/dist/server/server.js.map +1 -0
  79. package/dist/types/component-route.d.ts +8 -4
  80. package/dist/types/component-route.js +1 -1
  81. package/dist/types/component-route.js.map +1 -0
  82. package/dist/types/index.d.ts +5 -5
  83. package/dist/types/index.js +21 -4
  84. package/dist/types/index.js.map +1 -0
  85. package/dist/types/route.d.ts +13 -10
  86. package/dist/types/route.js +10 -9
  87. package/dist/types/route.js.map +1 -0
  88. package/dist/types/schema.d.ts +11 -7
  89. package/dist/types/schema.js +1 -1
  90. package/dist/types/schema.js.map +1 -0
  91. package/dist/types/types.d.ts +11 -9
  92. package/dist/types/types.js +1 -1
  93. package/dist/types/types.js.map +1 -0
  94. package/dist/utils/base64url.d.ts +4 -2
  95. package/dist/utils/base64url.js +12 -9
  96. package/dist/utils/base64url.js.map +1 -0
  97. package/dist/utils/create-handler.d.ts +11 -7
  98. package/dist/utils/create-handler.js +393 -217
  99. package/dist/utils/create-handler.js.map +1 -0
  100. package/dist/utils/dependency-manager.d.ts +3 -1
  101. package/dist/utils/dependency-manager.js +67 -69
  102. package/dist/utils/dependency-manager.js.map +1 -0
  103. package/dist/utils/go-await.d.ts +3 -1
  104. package/dist/utils/go-await.js +8 -22
  105. package/dist/utils/go-await.js.map +1 -0
  106. package/dist/utils/handle.d.ts +6 -4
  107. package/dist/utils/handle.js +44 -25
  108. package/dist/utils/handle.js.map +1 -0
  109. package/dist/utils/html-renderer.d.ts +3 -1
  110. package/dist/utils/html-renderer.js +25 -24
  111. package/dist/utils/html-renderer.js.map +1 -0
  112. package/dist/utils/index.d.ts +13 -13
  113. package/dist/utils/index.js +832 -21
  114. package/dist/utils/index.js.map +1 -0
  115. package/dist/utils/parsers.d.ts +15 -13
  116. package/dist/utils/parsers.js +138 -188
  117. package/dist/utils/parsers.js.map +1 -0
  118. package/dist/utils/path-matcher.d.ts +3 -1
  119. package/dist/utils/path-matcher.js +68 -78
  120. package/dist/utils/path-matcher.js.map +1 -0
  121. package/dist/utils/request-validator.d.ts +13 -10
  122. package/dist/utils/request-validator.js +234 -84
  123. package/dist/utils/request-validator.js.map +1 -0
  124. package/dist/utils/response.d.ts +9 -7
  125. package/dist/utils/response.js +93 -102
  126. package/dist/utils/response.js.map +1 -0
  127. package/dist/utils/validators/schema-validator.d.ts +13 -9
  128. package/dist/utils/validators/schema-validator.js +228 -209
  129. package/dist/utils/validators/schema-validator.js.map +1 -0
  130. package/dist/utils/validators/schema-validators-ultra.d.ts +15 -12
  131. package/dist/utils/validators/schema-validators-ultra.js +233 -256
  132. package/dist/utils/validators/schema-validators-ultra.js.map +1 -0
  133. package/dist/utils/validators/validators.d.ts +15 -12
  134. package/dist/utils/validators/validators.js +81 -122
  135. package/dist/utils/validators/validators.js.map +1 -0
  136. package/package.json +5 -4
@@ -1,106 +1,205 @@
1
+ // src/middleware.ts
2
+ var VafastError = class extends Error {
3
+ status;
4
+ type;
5
+ expose;
6
+ constructor(message, options = {}) {
7
+ super(message);
8
+ this.name = "VafastError";
9
+ this.status = options.status ?? 500;
10
+ this.type = options.type ?? "internal_error";
11
+ this.expose = options.expose ?? false;
12
+ if (options.cause) this.cause = options.cause;
13
+ }
14
+ };
15
+
16
+ // src/utils/parsers.ts
17
+ import qs from "qs";
18
+ import cookie from "cookie";
19
+ function parseCookies(req) {
20
+ const cookieHeader = req.headers.get("cookie");
21
+ if (!cookieHeader) return {};
22
+ try {
23
+ const parsed = cookie.parse(cookieHeader);
24
+ const result = {};
25
+ for (const [key, value] of Object.entries(parsed)) {
26
+ if (value !== void 0 && value !== null) {
27
+ result[key] = value;
28
+ }
29
+ }
30
+ return result;
31
+ } catch {
32
+ return {};
33
+ }
34
+ }
35
+
36
+ // src/utils/handle.ts
37
+ function getCookie(req, key) {
38
+ const cookies = parseCookies(req);
39
+ return cookies[key] || null;
40
+ }
41
+
42
+ // src/utils/base64url.ts
43
+ function base64urlEncode(str) {
44
+ return btoa(str).replace(/=/g, "").replace(/\+/g, "-").replace(/\//g, "_");
45
+ }
46
+ function base64urlDecode(str) {
47
+ const pad = str.length % 4 === 0 ? "" : "=".repeat(4 - str.length % 4);
48
+ const base64 = str.replace(/-/g, "+").replace(/_/g, "/") + pad;
49
+ return atob(base64);
50
+ }
51
+
52
+ // src/auth/token.ts
53
+ var TokenError = class extends Error {
54
+ constructor(message, code) {
55
+ super(message);
56
+ this.code = code;
57
+ this.name = "TokenError";
58
+ }
59
+ };
60
+ var encoder = new TextEncoder();
61
+ async function sign(data, secret) {
62
+ const key = await crypto.subtle.importKey(
63
+ "raw",
64
+ encoder.encode(secret),
65
+ { name: "HMAC", hash: "SHA-256" },
66
+ false,
67
+ ["sign"]
68
+ );
69
+ const signature = await crypto.subtle.sign("HMAC", key, encoder.encode(data));
70
+ return btoa(
71
+ String.fromCharCode.apply(null, Array.from(new Uint8Array(signature)))
72
+ );
73
+ }
74
+ async function verifyToken(token, secret) {
75
+ try {
76
+ const [data, sig] = token.split(".");
77
+ if (!data || !sig) {
78
+ throw new TokenError("\u4EE4\u724C\u683C\u5F0F\u65E0\u6548", "MALFORMED_TOKEN");
79
+ }
80
+ const expectedSig = await sign(data, secret);
81
+ const expected = base64urlEncode(expectedSig);
82
+ if (sig !== expected) {
83
+ throw new TokenError("\u4EE4\u724C\u7B7E\u540D\u65E0\u6548", "INVALID_SIGNATURE");
84
+ }
85
+ const payload = JSON.parse(base64urlDecode(data));
86
+ if (payload.exp && Date.now() / 1e3 > payload.exp) {
87
+ throw new TokenError("\u4EE4\u724C\u5DF2\u8FC7\u671F", "EXPIRED_TOKEN");
88
+ }
89
+ return payload;
90
+ } catch (error) {
91
+ if (error instanceof TokenError) {
92
+ throw error;
93
+ }
94
+ throw new TokenError("\u4EE4\u724C\u9A8C\u8BC1\u5931\u8D25", "INVALID_TOKEN");
95
+ }
96
+ }
97
+
1
98
  // src/middleware/auth.ts
2
- import { VafastError } from "../middleware";
3
- import { getCookie } from "../utils/handle";
4
- import { verifyToken, TokenError } from "../auth/token";
5
- export function createAuth(options) {
6
- const { secret, cookieName = "auth", headerName = "authorization", required = true, roles = [], permissions = [], } = options;
7
- return async (req, next) => {
8
- const token = getCookie(req, cookieName) ||
9
- req.headers.get(headerName)?.replace("Bearer ", "") ||
10
- "";
11
- if (!token && required) {
12
- throw new VafastError("缺少认证令牌", {
13
- status: 401,
14
- type: "unauthorized",
15
- expose: true,
16
- });
17
- }
18
- if (!token && !required) {
19
- return next();
20
- }
21
- try {
22
- const user = (await verifyToken(token, secret));
23
- if (!user) {
24
- throw new VafastError("令牌验证失败", {
25
- status: 401,
26
- type: "unauthorized",
27
- expose: true,
28
- });
29
- }
30
- // 检查角色权限
31
- if (roles.length > 0 && user.role && !roles.includes(user.role)) {
32
- throw new VafastError("权限不足", {
33
- status: 403,
34
- type: "forbidden",
35
- expose: true,
36
- });
37
- }
38
- // 检查具体权限
39
- if (permissions.length > 0 && user.permissions) {
40
- const userPermissions = Array.isArray(user.permissions)
41
- ? user.permissions
42
- : [user.permissions];
43
- const hasPermission = permissions.some((permission) => userPermissions.includes(permission));
44
- if (!hasPermission) {
45
- throw new VafastError("权限不足", {
46
- status: 403,
47
- type: "forbidden",
48
- expose: true,
49
- });
50
- }
51
- }
52
- // 🪄 在这里扩展 Request 对象
53
- req.user = user;
54
- req.token = token;
55
- return next();
99
+ function createAuth(options) {
100
+ const {
101
+ secret,
102
+ cookieName = "auth",
103
+ headerName = "authorization",
104
+ required = true,
105
+ roles = [],
106
+ permissions = []
107
+ } = options;
108
+ return async (req, next) => {
109
+ const token = getCookie(req, cookieName) || req.headers.get(headerName)?.replace("Bearer ", "") || "";
110
+ if (!token && required) {
111
+ throw new VafastError("\u7F3A\u5C11\u8BA4\u8BC1\u4EE4\u724C", {
112
+ status: 401,
113
+ type: "unauthorized",
114
+ expose: true
115
+ });
116
+ }
117
+ if (!token && !required) {
118
+ return next();
119
+ }
120
+ try {
121
+ const user = await verifyToken(token, secret);
122
+ if (!user) {
123
+ throw new VafastError("\u4EE4\u724C\u9A8C\u8BC1\u5931\u8D25", {
124
+ status: 401,
125
+ type: "unauthorized",
126
+ expose: true
127
+ });
128
+ }
129
+ if (roles.length > 0 && user.role && !roles.includes(user.role)) {
130
+ throw new VafastError("\u6743\u9650\u4E0D\u8DB3", {
131
+ status: 403,
132
+ type: "forbidden",
133
+ expose: true
134
+ });
135
+ }
136
+ if (permissions.length > 0 && user.permissions) {
137
+ const userPermissions = Array.isArray(user.permissions) ? user.permissions : [user.permissions];
138
+ const hasPermission = permissions.some(
139
+ (permission) => userPermissions.includes(permission)
140
+ );
141
+ if (!hasPermission) {
142
+ throw new VafastError("\u6743\u9650\u4E0D\u8DB3", {
143
+ status: 403,
144
+ type: "forbidden",
145
+ expose: true
146
+ });
56
147
  }
57
- catch (error) {
58
- if (error instanceof TokenError) {
59
- let status = 401;
60
- let message = "认证失败";
61
- switch (error.code) {
62
- case "EXPIRED_TOKEN":
63
- status = 401;
64
- message = "令牌已过期";
65
- break;
66
- case "INVALID_SIGNATURE":
67
- status = 401;
68
- message = "令牌签名无效";
69
- break;
70
- case "MALFORMED_TOKEN":
71
- status = 400;
72
- message = "令牌格式错误";
73
- break;
74
- default:
75
- status = 401;
76
- message = "令牌验证失败";
77
- }
78
- throw new VafastError(message, {
79
- status,
80
- type: "unauthorized",
81
- expose: true,
82
- });
83
- }
84
- if (error instanceof VafastError) {
85
- throw error;
86
- }
87
- throw new VafastError("认证过程中发生错误", {
88
- status: 500,
89
- type: "internal_error",
90
- expose: false,
91
- });
148
+ }
149
+ req.user = user;
150
+ req.token = token;
151
+ return next();
152
+ } catch (error) {
153
+ if (error instanceof TokenError) {
154
+ let status = 401;
155
+ let message = "\u8BA4\u8BC1\u5931\u8D25";
156
+ switch (error.code) {
157
+ case "EXPIRED_TOKEN":
158
+ status = 401;
159
+ message = "\u4EE4\u724C\u5DF2\u8FC7\u671F";
160
+ break;
161
+ case "INVALID_SIGNATURE":
162
+ status = 401;
163
+ message = "\u4EE4\u724C\u7B7E\u540D\u65E0\u6548";
164
+ break;
165
+ case "MALFORMED_TOKEN":
166
+ status = 400;
167
+ message = "\u4EE4\u724C\u683C\u5F0F\u9519\u8BEF";
168
+ break;
169
+ default:
170
+ status = 401;
171
+ message = "\u4EE4\u724C\u9A8C\u8BC1\u5931\u8D25";
92
172
  }
93
- };
173
+ throw new VafastError(message, {
174
+ status,
175
+ type: "unauthorized",
176
+ expose: true
177
+ });
178
+ }
179
+ if (error instanceof VafastError) {
180
+ throw error;
181
+ }
182
+ throw new VafastError("\u8BA4\u8BC1\u8FC7\u7A0B\u4E2D\u53D1\u751F\u9519\u8BEF", {
183
+ status: 500,
184
+ type: "internal_error",
185
+ expose: false
186
+ });
187
+ }
188
+ };
94
189
  }
95
- // 可选认证中间件
96
- export function createOptionalAuth(options) {
97
- return createAuth({ ...options, required: false });
190
+ function createOptionalAuth(options) {
191
+ return createAuth({ ...options, required: false });
98
192
  }
99
- // 角色验证中间件
100
- export function createRoleAuth(roles, options) {
101
- return createAuth({ ...options, roles });
193
+ function createRoleAuth(roles, options) {
194
+ return createAuth({ ...options, roles });
102
195
  }
103
- // 权限验证中间件
104
- export function createPermissionAuth(permissions, options) {
105
- return createAuth({ ...options, permissions });
196
+ function createPermissionAuth(permissions, options) {
197
+ return createAuth({ ...options, permissions });
106
198
  }
199
+ export {
200
+ createAuth,
201
+ createOptionalAuth,
202
+ createPermissionAuth,
203
+ createRoleAuth
204
+ };
205
+ //# sourceMappingURL=auth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/middleware.ts","../../src/utils/parsers.ts","../../src/utils/handle.ts","../../src/utils/base64url.ts","../../src/auth/token.ts","../../src/middleware/auth.ts"],"sourcesContent":["// src/middleware.ts\n\nimport { json, mapResponse } from \"./utils/response\";\n\nimport type { Handler, Middleware } from \"./types\";\n/** 中间件类型:使用 next() 传递给下一个处理 */\n\n/** Vafast 自定义错误类型 */\nexport class VafastError extends Error {\n status: number;\n type: string;\n expose: boolean;\n\n constructor(\n message: string,\n options: {\n status?: number;\n type?: string;\n expose?: boolean;\n cause?: unknown;\n } = {},\n ) {\n super(message);\n this.name = \"VafastError\";\n this.status = options.status ?? 500;\n this.type = options.type ?? \"internal_error\";\n this.expose = options.expose ?? false;\n if (options.cause) (this as any).cause = options.cause;\n }\n}\n\n/**\n * 组合类型: 自动注入错误处理器进行中间件组合\n */\nexport function composeMiddleware(\n middleware: Middleware[],\n finalHandler: Handler,\n): (req: Request) => Promise<Response> {\n const all = [errorHandler, ...middleware];\n\n return function composedHandler(req: Request): Promise<Response> {\n let i = -1;\n\n const dispatch = (index: number): Promise<Response> => {\n if (index <= i)\n return Promise.reject(new Error(\"next() called multiple times\"));\n i = index;\n\n // 中间件阶段\n if (index < all.length) {\n const mw = all[index];\n return Promise.resolve(mw(req, () => dispatch(index + 1)));\n }\n\n // 最终 handler - 使用 mapResponse 转换返回值\n return Promise.resolve(finalHandler(req)).then(mapResponse);\n };\n\n return dispatch(0);\n };\n}\n\n/** 默认包含的全局错误处理器 */\nconst errorHandler: Middleware = async (req, next) => {\n try {\n return await next();\n } catch (err) {\n console.error(\"未处理的错误:\", err);\n\n if (err instanceof VafastError) {\n return json(\n {\n error: err.type,\n message: err.expose ? err.message : \"发生了一个错误\",\n },\n err.status,\n );\n }\n\n return json({ error: \"internal_error\", message: \"出现了一些问题\" }, 500);\n }\n};\n","// src/parsers.ts\nimport qs from \"qs\";\nimport cookie from \"cookie\";\n\n// 文件信息接口\nexport interface FileInfo {\n name: string;\n type: string;\n size: number;\n data: ArrayBuffer;\n}\n\n// 表单数据接口\nexport interface FormData {\n fields: Record<string, string>;\n files: Record<string, FileInfo>;\n}\n\n/**\n * 简化的请求体解析函数\n * 优先简洁性,处理最常见的场景\n */\nexport async function parseBody(req: Request): Promise<unknown> {\n const contentType = req.headers.get(\"content-type\") || \"\";\n if (contentType.includes(\"application/json\")) {\n return await req.json();\n }\n if (contentType.includes(\"application/x-www-form-urlencoded\")) {\n const text = await req.text();\n return Object.fromEntries(new URLSearchParams(text));\n }\n return await req.text(); // fallback\n}\n\n/**\n * 解析 multipart/form-data 格式\n * 支持文件上传和普通表单字段\n */\nasync function parseMultipartFormData(req: Request): Promise<FormData> {\n const formData = await req.formData();\n const result: FormData = {\n fields: {},\n files: {},\n };\n\n for (const [key, value] of formData.entries()) {\n if (\n typeof value === \"object\" &&\n value !== null &&\n \"name\" in value &&\n \"type\" in value &&\n \"size\" in value\n ) {\n // 处理文件\n const file = value as any;\n const arrayBuffer = await file.arrayBuffer();\n result.files[key] = {\n name: file.name,\n type: file.type,\n size: file.size,\n data: arrayBuffer,\n };\n } else {\n // 处理普通字段\n result.fields[key] = value as string;\n }\n }\n\n return result;\n}\n\n/**\n * 解析请求体为特定类型\n * 提供类型安全的解析方法\n */\nexport async function parseBodyAs<T>(req: Request): Promise<T> {\n const body = await parseBody(req);\n return body as T;\n}\n\n/**\n * 解析请求体为表单数据\n * 专门用于处理 multipart/form-data\n */\nexport async function parseFormData(req: Request): Promise<FormData> {\n const contentType = req.headers.get(\"content-type\") || \"\";\n\n if (!contentType.includes(\"multipart/form-data\")) {\n throw new Error(\"请求不是 multipart/form-data 格式\");\n }\n\n return await parseMultipartFormData(req);\n}\n\n/**\n * 解析请求体为文件\n * 专门用于处理文件上传\n */\nexport async function parseFile(req: Request): Promise<FileInfo> {\n const contentType = req.headers.get(\"content-type\") || \"\";\n\n if (!contentType.includes(\"multipart/form-data\")) {\n throw new Error(\"请求不是 multipart/form-data 格式\");\n }\n\n const formData = await parseMultipartFormData(req);\n const fileKeys = Object.keys(formData.files);\n\n if (fileKeys.length === 0) {\n throw new Error(\"请求中没有文件\");\n }\n\n if (fileKeys.length > 1) {\n throw new Error(\"请求中包含多个文件,请使用 parseFormData\");\n }\n\n return formData.files[fileKeys[0]];\n}\n\n/**\n * 快速提取 query string(避免创建 URL 对象)\n */\nfunction extractQueryString(url: string): string {\n const qIndex = url.indexOf(\"?\");\n if (qIndex === -1) return \"\";\n\n const hashIndex = url.indexOf(\"#\", qIndex);\n return hashIndex === -1\n ? url.substring(qIndex + 1)\n : url.substring(qIndex + 1, hashIndex);\n}\n\n/** 获取查询字符串,直接返回对象 */\nexport function parseQuery(req: Request): Record<string, unknown> {\n const queryString = extractQueryString(req.url);\n if (!queryString) return {};\n return qs.parse(queryString);\n}\n\n/**\n * 快速解析简单查询字符串(不支持嵌套,但更快)\n * 适用于简单的 key=value&key2=value2 场景\n */\nexport function parseQueryFast(req: Request): Record<string, string> {\n const queryString = extractQueryString(req.url);\n if (!queryString) return {};\n\n const result: Record<string, string> = Object.create(null);\n const pairs = queryString.split(\"&\");\n\n for (const pair of pairs) {\n const eqIndex = pair.indexOf(\"=\");\n if (eqIndex === -1) {\n result[decodeURIComponent(pair)] = \"\";\n } else {\n const key = decodeURIComponent(pair.substring(0, eqIndex));\n const value = decodeURIComponent(pair.substring(eqIndex + 1));\n result[key] = value;\n }\n }\n\n return result;\n}\n\n/** 解析请求头,返回对象 */\nexport function parseHeaders(req: Request): Record<string, string> {\n const headers: Record<string, string> = Object.create(null);\n req.headers.forEach((value, key) => {\n headers[key] = value;\n });\n return headers;\n}\n\n/**\n * 获取单个请求头(避免解析全部)\n */\nexport function getHeader(req: Request, name: string): string | null {\n return req.headers.get(name);\n}\n\n/** 使用cookie库解析Cookie,保证可靠性 */\nexport function parseCookies(req: Request): Record<string, string> {\n const cookieHeader = req.headers.get(\"cookie\");\n if (!cookieHeader) return {};\n\n try {\n const parsed = cookie.parse(cookieHeader);\n // 过滤掉undefined和null值\n const result: Record<string, string> = {};\n for (const [key, value] of Object.entries(parsed)) {\n if (value !== undefined && value !== null) {\n result[key] = value;\n }\n }\n return result;\n } catch {\n return {};\n }\n}\n\n/**\n * 快速解析 Cookie(简化版,不使用外部库)\n * 适用于简单的 cookie 场景\n */\nexport function parseCookiesFast(req: Request): Record<string, string> {\n const cookieHeader = req.headers.get(\"cookie\");\n if (!cookieHeader) return {};\n\n const result: Record<string, string> = Object.create(null);\n const pairs = cookieHeader.split(\";\");\n\n for (const pair of pairs) {\n const trimmed = pair.trim();\n const eqIndex = trimmed.indexOf(\"=\");\n if (eqIndex > 0) {\n const key = trimmed.substring(0, eqIndex).trim();\n const value = trimmed.substring(eqIndex + 1).trim();\n // 移除引号\n result[key] =\n value.startsWith('\"') && value.endsWith('\"')\n ? value.slice(1, -1)\n : value;\n }\n }\n\n return result;\n}\n\n/**\n * 获取单个 Cookie 值(避免解析全部)\n */\nexport function getCookie(req: Request, name: string): string | null {\n const cookieHeader = req.headers.get(\"cookie\");\n if (!cookieHeader) return null;\n\n const prefix = `${name}=`;\n const pairs = cookieHeader.split(\";\");\n\n for (const pair of pairs) {\n const trimmed = pair.trim();\n if (trimmed.startsWith(prefix)) {\n const value = trimmed.substring(prefix.length).trim();\n return value.startsWith('\"') && value.endsWith('\"')\n ? value.slice(1, -1)\n : value;\n }\n }\n\n return null;\n}\n","import { parseCookies } from \"./parsers\";\n\n/** 获取单个 Cookie 值 */\nexport function getCookie(req: Request, key: string): string | null {\n const cookies = parseCookies(req);\n return cookies[key] || null;\n}\n\n/** 生成 Set-Cookie 头 */\nexport function setCookie(\n key: string,\n value: string,\n options: {\n path?: string;\n httpOnly?: boolean;\n maxAge?: number;\n secure?: boolean;\n } = {},\n): string {\n let cookie = `${key}=${encodeURIComponent(value)}`;\n\n if (options.path) cookie += `; Path=${options.path}`;\n if (options.httpOnly) cookie += `; HttpOnly`;\n if (options.secure) cookie += `; Secure`;\n if (options.maxAge) cookie += `; Max-Age=${options.maxAge}`;\n\n return cookie;\n}\n\n// 提供给中间件写入\"局部上下文\"的工具函数\nexport function setLocals<T extends object>(req: Request, extras: T) {\n const target = req as unknown as Record<string, unknown>;\n target.__locals = { ...((target.__locals as object) ?? {}), ...extras };\n}\n\n// 获取中间件注入的局部上下文\nexport function getLocals<T extends object>(req: Request): T {\n const target = req as unknown as Record<string, unknown>;\n return (target.__locals ?? {}) as T;\n}\n","export function base64urlEncode(str: string): string {\n return btoa(str)\n .replace(/=/g, \"\") // ✅ 删除填充\n .replace(/\\+/g, \"-\")\n .replace(/\\//g, \"_\");\n}\n\nexport function base64urlDecode(str: string): string {\n const pad = str.length % 4 === 0 ? \"\" : \"=\".repeat(4 - (str.length % 4));\n const base64 = str.replace(/-/g, \"+\").replace(/_/g, \"/\") + pad;\n return atob(base64);\n}\n","// src/auth/token.ts\nimport { base64urlEncode, base64urlDecode } from \"../utils/base64url\";\n\n// 类型定义\nexport interface TokenPayload {\n [key: string]: any;\n exp?: number; // 过期时间戳\n iat?: number; // 签发时间戳\n sub?: string; // 主题(通常是用户ID)\n aud?: string; // 受众\n iss?: string; // 签发者\n}\n\nexport interface TokenResult {\n payload: TokenPayload;\n token: string;\n expiresAt: number;\n}\n\nexport interface TokenOptions {\n expiresIn?: number; // 过期时间(秒)\n issuer?: string; // 签发者\n audience?: string; // 受众\n subject?: string; // 主题\n}\n\nexport class TokenError extends Error {\n constructor(\n message: string,\n public code:\n | \"INVALID_TOKEN\"\n | \"EXPIRED_TOKEN\"\n | \"INVALID_SIGNATURE\"\n | \"MALFORMED_TOKEN\"\n | \"INVALID_PAYLOAD\",\n ) {\n super(message);\n this.name = \"TokenError\";\n }\n}\n\nconst encoder = new TextEncoder();\n\n/** 使用 HMAC-SHA256 进行签名 */\nasync function sign(data: string, secret: string): Promise<string> {\n const key = await crypto.subtle.importKey(\n \"raw\",\n encoder.encode(secret),\n { name: \"HMAC\", hash: \"SHA-256\" },\n false,\n [\"sign\"],\n );\n\n const signature = await crypto.subtle.sign(\"HMAC\", key, encoder.encode(data));\n return btoa(\n String.fromCharCode.apply(null, Array.from(new Uint8Array(signature))),\n );\n}\n\n/** 生成令牌 */\nexport async function generateToken(\n payload: TokenPayload,\n secret: string,\n options: TokenOptions = {},\n): Promise<TokenResult> {\n const { expiresIn = 3600, issuer, audience, subject } = options;\n\n // 创建令牌载荷,强制使用当前时间\n const now = Math.floor(Date.now() / 1000);\n const tokenPayload: TokenPayload = {\n ...payload,\n iat: now,\n exp: now + expiresIn,\n };\n\n // 添加可选字段\n if (issuer) tokenPayload.iss = issuer;\n if (audience) tokenPayload.aud = audience;\n if (subject) tokenPayload.sub = subject;\n\n const data = base64urlEncode(JSON.stringify(tokenPayload));\n const sig = await sign(data, secret);\n const token = `${data}.${base64urlEncode(sig)}`;\n\n return {\n payload: tokenPayload,\n token,\n expiresAt: tokenPayload.exp! * 1000, // 转换为毫秒\n };\n}\n\n/** 验证令牌 */\nexport async function verifyToken(\n token: string,\n secret: string,\n): Promise<TokenPayload | null> {\n try {\n const [data, sig] = token.split(\".\");\n if (!data || !sig) {\n throw new TokenError(\"令牌格式无效\", \"MALFORMED_TOKEN\");\n }\n\n const expectedSig = await sign(data, secret);\n const expected = base64urlEncode(expectedSig);\n\n if (sig !== expected) {\n throw new TokenError(\"令牌签名无效\", \"INVALID_SIGNATURE\");\n }\n\n const payload = JSON.parse(base64urlDecode(data)) as TokenPayload;\n\n // 检查过期时间\n if (payload.exp && Date.now() / 1000 > payload.exp) {\n throw new TokenError(\"令牌已过期\", \"EXPIRED_TOKEN\");\n }\n\n return payload;\n } catch (error) {\n if (error instanceof TokenError) {\n throw error;\n }\n throw new TokenError(\"令牌验证失败\", \"INVALID_TOKEN\");\n }\n}\n\n/** 解析令牌(不验证签名) */\nexport function parseToken(token: string): TokenPayload | null {\n try {\n const [data] = token.split(\".\");\n if (!data) return null;\n\n return JSON.parse(base64urlDecode(data));\n } catch {\n return null;\n }\n}\n\n/** 检查令牌是否过期 */\nexport function isTokenExpired(token: string): boolean {\n const payload = parseToken(token);\n if (!payload || !payload.exp) return true;\n\n return Date.now() / 1000 > payload.exp;\n}\n\n/** 获取令牌剩余有效时间(秒) */\nexport function getTokenTimeRemaining(token: string): number {\n const payload = parseToken(token);\n if (!payload || !payload.exp) return 0;\n\n const remaining = payload.exp - Date.now() / 1000;\n return Math.max(0, Math.floor(remaining));\n}\n\n/** 刷新令牌 */\nexport async function refreshToken(\n token: string,\n secret: string,\n options: TokenOptions = {},\n): Promise<TokenResult | null> {\n try {\n const payload = await verifyToken(token, secret);\n if (!payload) return null;\n\n // 移除时间相关字段,重新生成\n const { exp, iat, ...cleanPayload } = payload;\n\n // 添加延迟确保时间戳不同\n await new Promise((resolve) => setTimeout(resolve, 10));\n\n return await generateToken(cleanPayload, secret, options);\n } catch {\n return null;\n }\n}\n\n/** 创建访问令牌和刷新令牌对 */\nexport async function createTokenPair(\n payload: TokenPayload,\n secret: string,\n options: TokenOptions = {},\n): Promise<{\n accessToken: TokenResult;\n refreshToken: TokenResult;\n}> {\n const accessToken = await generateToken(payload, secret, {\n ...options,\n expiresIn: options.expiresIn || 3600, // 1小时\n });\n\n const refreshToken = await generateToken(payload, secret, {\n ...options,\n expiresIn: 7 * 24 * 3600, // 7天\n });\n\n return { accessToken, refreshToken };\n}\n","// src/middleware/auth.ts\n\nimport type { Middleware } from \"../types\";\nimport { VafastError } from \"../middleware\";\nimport { getCookie } from \"../utils/handle\";\nimport { verifyToken, TokenError, type TokenPayload } from \"../auth/token\";\n\ninterface AuthOptions {\n secret: string;\n cookieName?: string;\n headerName?: string;\n required?: boolean; // 是否必需认证\n roles?: string[]; // 允许的角色\n permissions?: string[]; // 允许的权限\n}\n\nexport function createAuth(options: AuthOptions): Middleware {\n const {\n secret,\n cookieName = \"auth\",\n headerName = \"authorization\",\n required = true,\n roles = [],\n permissions = [],\n } = options;\n\n return async (req, next) => {\n const token =\n getCookie(req, cookieName) ||\n req.headers.get(headerName)?.replace(\"Bearer \", \"\") ||\n \"\";\n\n if (!token && required) {\n throw new VafastError(\"缺少认证令牌\", {\n status: 401,\n type: \"unauthorized\",\n expose: true,\n });\n }\n\n if (!token && !required) {\n return next();\n }\n\n try {\n const user = (await verifyToken(token, secret)) as TokenPayload;\n\n if (!user) {\n throw new VafastError(\"令牌验证失败\", {\n status: 401,\n type: \"unauthorized\",\n expose: true,\n });\n }\n\n // 检查角色权限\n if (roles.length > 0 && user.role && !roles.includes(user.role)) {\n throw new VafastError(\"权限不足\", {\n status: 403,\n type: \"forbidden\",\n expose: true,\n });\n }\n\n // 检查具体权限\n if (permissions.length > 0 && user.permissions) {\n const userPermissions = Array.isArray(user.permissions)\n ? user.permissions\n : [user.permissions];\n\n const hasPermission = permissions.some((permission) =>\n userPermissions.includes(permission),\n );\n\n if (!hasPermission) {\n throw new VafastError(\"权限不足\", {\n status: 403,\n type: \"forbidden\",\n expose: true,\n });\n }\n }\n\n // 🪄 在这里扩展 Request 对象\n (req as any).user = user;\n (req as any).token = token;\n\n return next();\n } catch (error) {\n if (error instanceof TokenError) {\n let status = 401;\n let message = \"认证失败\";\n\n switch (error.code) {\n case \"EXPIRED_TOKEN\":\n status = 401;\n message = \"令牌已过期\";\n break;\n case \"INVALID_SIGNATURE\":\n status = 401;\n message = \"令牌签名无效\";\n break;\n case \"MALFORMED_TOKEN\":\n status = 400;\n message = \"令牌格式错误\";\n break;\n default:\n status = 401;\n message = \"令牌验证失败\";\n }\n\n throw new VafastError(message, {\n status,\n type: \"unauthorized\",\n expose: true,\n });\n }\n\n if (error instanceof VafastError) {\n throw error;\n }\n\n throw new VafastError(\"认证过程中发生错误\", {\n status: 500,\n type: \"internal_error\",\n expose: false,\n });\n }\n };\n}\n\n// 可选认证中间件\nexport function createOptionalAuth(\n options: Omit<AuthOptions, \"required\">,\n): Middleware {\n return createAuth({ ...options, required: false });\n}\n\n// 角色验证中间件\nexport function createRoleAuth(\n roles: string[],\n options: Omit<AuthOptions, \"roles\">,\n): Middleware {\n return createAuth({ ...options, roles });\n}\n\n// 权限验证中间件\nexport function createPermissionAuth(\n permissions: string[],\n options: Omit<AuthOptions, \"permissions\">,\n): Middleware {\n return createAuth({ ...options, permissions });\n}\n"],"mappings":";AAQO,IAAM,cAAN,cAA0B,MAAM;AAAA,EACrC;AAAA,EACA;AAAA,EACA;AAAA,EAEA,YACE,SACA,UAKI,CAAC,GACL;AACA,UAAM,OAAO;AACb,SAAK,OAAO;AACZ,SAAK,SAAS,QAAQ,UAAU;AAChC,SAAK,OAAO,QAAQ,QAAQ;AAC5B,SAAK,SAAS,QAAQ,UAAU;AAChC,QAAI,QAAQ,MAAO,CAAC,KAAa,QAAQ,QAAQ;AAAA,EACnD;AACF;;;AC5BA,OAAO,QAAQ;AACf,OAAO,YAAY;AAmLZ,SAAS,aAAa,KAAsC;AACjE,QAAM,eAAe,IAAI,QAAQ,IAAI,QAAQ;AAC7C,MAAI,CAAC,aAAc,QAAO,CAAC;AAE3B,MAAI;AACF,UAAM,SAAS,OAAO,MAAM,YAAY;AAExC,UAAM,SAAiC,CAAC;AACxC,eAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,MAAM,GAAG;AACjD,UAAI,UAAU,UAAa,UAAU,MAAM;AACzC,eAAO,GAAG,IAAI;AAAA,MAChB;AAAA,IACF;AACA,WAAO;AAAA,EACT,QAAQ;AACN,WAAO,CAAC;AAAA,EACV;AACF;;;ACnMO,SAAS,UAAU,KAAc,KAA4B;AAClE,QAAM,UAAU,aAAa,GAAG;AAChC,SAAO,QAAQ,GAAG,KAAK;AACzB;;;ACNO,SAAS,gBAAgB,KAAqB;AACnD,SAAO,KAAK,GAAG,EACZ,QAAQ,MAAM,EAAE,EAChB,QAAQ,OAAO,GAAG,EAClB,QAAQ,OAAO,GAAG;AACvB;AAEO,SAAS,gBAAgB,KAAqB;AACnD,QAAM,MAAM,IAAI,SAAS,MAAM,IAAI,KAAK,IAAI,OAAO,IAAK,IAAI,SAAS,CAAE;AACvE,QAAM,SAAS,IAAI,QAAQ,MAAM,GAAG,EAAE,QAAQ,MAAM,GAAG,IAAI;AAC3D,SAAO,KAAK,MAAM;AACpB;;;ACeO,IAAM,aAAN,cAAyB,MAAM;AAAA,EACpC,YACE,SACO,MAMP;AACA,UAAM,OAAO;AAPN;AAQP,SAAK,OAAO;AAAA,EACd;AACF;AAEA,IAAM,UAAU,IAAI,YAAY;AAGhC,eAAe,KAAK,MAAc,QAAiC;AACjE,QAAM,MAAM,MAAM,OAAO,OAAO;AAAA,IAC9B;AAAA,IACA,QAAQ,OAAO,MAAM;AAAA,IACrB,EAAE,MAAM,QAAQ,MAAM,UAAU;AAAA,IAChC;AAAA,IACA,CAAC,MAAM;AAAA,EACT;AAEA,QAAM,YAAY,MAAM,OAAO,OAAO,KAAK,QAAQ,KAAK,QAAQ,OAAO,IAAI,CAAC;AAC5E,SAAO;AAAA,IACL,OAAO,aAAa,MAAM,MAAM,MAAM,KAAK,IAAI,WAAW,SAAS,CAAC,CAAC;AAAA,EACvE;AACF;AAmCA,eAAsB,YACpB,OACA,QAC8B;AAC9B,MAAI;AACF,UAAM,CAAC,MAAM,GAAG,IAAI,MAAM,MAAM,GAAG;AACnC,QAAI,CAAC,QAAQ,CAAC,KAAK;AACjB,YAAM,IAAI,WAAW,wCAAU,iBAAiB;AAAA,IAClD;AAEA,UAAM,cAAc,MAAM,KAAK,MAAM,MAAM;AAC3C,UAAM,WAAW,gBAAgB,WAAW;AAE5C,QAAI,QAAQ,UAAU;AACpB,YAAM,IAAI,WAAW,wCAAU,mBAAmB;AAAA,IACpD;AAEA,UAAM,UAAU,KAAK,MAAM,gBAAgB,IAAI,CAAC;AAGhD,QAAI,QAAQ,OAAO,KAAK,IAAI,IAAI,MAAO,QAAQ,KAAK;AAClD,YAAM,IAAI,WAAW,kCAAS,eAAe;AAAA,IAC/C;AAEA,WAAO;AAAA,EACT,SAAS,OAAO;AACd,QAAI,iBAAiB,YAAY;AAC/B,YAAM;AAAA,IACR;AACA,UAAM,IAAI,WAAW,wCAAU,eAAe;AAAA,EAChD;AACF;;;AC3GO,SAAS,WAAW,SAAkC;AAC3D,QAAM;AAAA,IACJ;AAAA,IACA,aAAa;AAAA,IACb,aAAa;AAAA,IACb,WAAW;AAAA,IACX,QAAQ,CAAC;AAAA,IACT,cAAc,CAAC;AAAA,EACjB,IAAI;AAEJ,SAAO,OAAO,KAAK,SAAS;AAC1B,UAAM,QACJ,UAAU,KAAK,UAAU,KACzB,IAAI,QAAQ,IAAI,UAAU,GAAG,QAAQ,WAAW,EAAE,KAClD;AAEF,QAAI,CAAC,SAAS,UAAU;AACtB,YAAM,IAAI,YAAY,wCAAU;AAAA,QAC9B,QAAQ;AAAA,QACR,MAAM;AAAA,QACN,QAAQ;AAAA,MACV,CAAC;AAAA,IACH;AAEA,QAAI,CAAC,SAAS,CAAC,UAAU;AACvB,aAAO,KAAK;AAAA,IACd;AAEA,QAAI;AACF,YAAM,OAAQ,MAAM,YAAY,OAAO,MAAM;AAE7C,UAAI,CAAC,MAAM;AACT,cAAM,IAAI,YAAY,wCAAU;AAAA,UAC9B,QAAQ;AAAA,UACR,MAAM;AAAA,UACN,QAAQ;AAAA,QACV,CAAC;AAAA,MACH;AAGA,UAAI,MAAM,SAAS,KAAK,KAAK,QAAQ,CAAC,MAAM,SAAS,KAAK,IAAI,GAAG;AAC/D,cAAM,IAAI,YAAY,4BAAQ;AAAA,UAC5B,QAAQ;AAAA,UACR,MAAM;AAAA,UACN,QAAQ;AAAA,QACV,CAAC;AAAA,MACH;AAGA,UAAI,YAAY,SAAS,KAAK,KAAK,aAAa;AAC9C,cAAM,kBAAkB,MAAM,QAAQ,KAAK,WAAW,IAClD,KAAK,cACL,CAAC,KAAK,WAAW;AAErB,cAAM,gBAAgB,YAAY;AAAA,UAAK,CAAC,eACtC,gBAAgB,SAAS,UAAU;AAAA,QACrC;AAEA,YAAI,CAAC,eAAe;AAClB,gBAAM,IAAI,YAAY,4BAAQ;AAAA,YAC5B,QAAQ;AAAA,YACR,MAAM;AAAA,YACN,QAAQ;AAAA,UACV,CAAC;AAAA,QACH;AAAA,MACF;AAGA,MAAC,IAAY,OAAO;AACpB,MAAC,IAAY,QAAQ;AAErB,aAAO,KAAK;AAAA,IACd,SAAS,OAAO;AACd,UAAI,iBAAiB,YAAY;AAC/B,YAAI,SAAS;AACb,YAAI,UAAU;AAEd,gBAAQ,MAAM,MAAM;AAAA,UAClB,KAAK;AACH,qBAAS;AACT,sBAAU;AACV;AAAA,UACF,KAAK;AACH,qBAAS;AACT,sBAAU;AACV;AAAA,UACF,KAAK;AACH,qBAAS;AACT,sBAAU;AACV;AAAA,UACF;AACE,qBAAS;AACT,sBAAU;AAAA,QACd;AAEA,cAAM,IAAI,YAAY,SAAS;AAAA,UAC7B;AAAA,UACA,MAAM;AAAA,UACN,QAAQ;AAAA,QACV,CAAC;AAAA,MACH;AAEA,UAAI,iBAAiB,aAAa;AAChC,cAAM;AAAA,MACR;AAEA,YAAM,IAAI,YAAY,0DAAa;AAAA,QACjC,QAAQ;AAAA,QACR,MAAM;AAAA,QACN,QAAQ;AAAA,MACV,CAAC;AAAA,IACH;AAAA,EACF;AACF;AAGO,SAAS,mBACd,SACY;AACZ,SAAO,WAAW,EAAE,GAAG,SAAS,UAAU,MAAM,CAAC;AACnD;AAGO,SAAS,eACd,OACA,SACY;AACZ,SAAO,WAAW,EAAE,GAAG,SAAS,MAAM,CAAC;AACzC;AAGO,SAAS,qBACd,aACA,SACY;AACZ,SAAO,WAAW,EAAE,GAAG,SAAS,YAAY,CAAC;AAC/C;","names":[]}
@@ -1,2 +1,5 @@
1
- import type { Middleware } from "../types";
2
- export declare const requireAuth: Middleware;
1
+ import { Middleware } from '../types/types.js';
2
+
3
+ declare const requireAuth: Middleware;
4
+
5
+ export { requireAuth };
@@ -1,13 +1,57 @@
1
- import { VafastError } from "../middleware";
2
- import { getCookie } from "../utils/handle";
3
- export const requireAuth = async (req, next) => {
4
- const token = getCookie(req, "auth");
5
- if (!token || token !== "valid-token") {
6
- throw new VafastError("Unauthorized", {
7
- status: 401,
8
- type: "unauthorized",
9
- expose: true,
10
- });
1
+ // src/middleware.ts
2
+ var VafastError = class extends Error {
3
+ status;
4
+ type;
5
+ expose;
6
+ constructor(message, options = {}) {
7
+ super(message);
8
+ this.name = "VafastError";
9
+ this.status = options.status ?? 500;
10
+ this.type = options.type ?? "internal_error";
11
+ this.expose = options.expose ?? false;
12
+ if (options.cause) this.cause = options.cause;
13
+ }
14
+ };
15
+
16
+ // src/utils/parsers.ts
17
+ import qs from "qs";
18
+ import cookie from "cookie";
19
+ function parseCookies(req) {
20
+ const cookieHeader = req.headers.get("cookie");
21
+ if (!cookieHeader) return {};
22
+ try {
23
+ const parsed = cookie.parse(cookieHeader);
24
+ const result = {};
25
+ for (const [key, value] of Object.entries(parsed)) {
26
+ if (value !== void 0 && value !== null) {
27
+ result[key] = value;
28
+ }
11
29
  }
12
- return next();
30
+ return result;
31
+ } catch {
32
+ return {};
33
+ }
34
+ }
35
+
36
+ // src/utils/handle.ts
37
+ function getCookie(req, key) {
38
+ const cookies = parseCookies(req);
39
+ return cookies[key] || null;
40
+ }
41
+
42
+ // src/middleware/authMiddleware.ts
43
+ var requireAuth = async (req, next) => {
44
+ const token = getCookie(req, "auth");
45
+ if (!token || token !== "valid-token") {
46
+ throw new VafastError("Unauthorized", {
47
+ status: 401,
48
+ type: "unauthorized",
49
+ expose: true
50
+ });
51
+ }
52
+ return next();
53
+ };
54
+ export {
55
+ requireAuth
13
56
  };
57
+ //# sourceMappingURL=authMiddleware.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/middleware.ts","../../src/utils/parsers.ts","../../src/utils/handle.ts","../../src/middleware/authMiddleware.ts"],"sourcesContent":["// src/middleware.ts\n\nimport { json, mapResponse } from \"./utils/response\";\n\nimport type { Handler, Middleware } from \"./types\";\n/** 中间件类型:使用 next() 传递给下一个处理 */\n\n/** Vafast 自定义错误类型 */\nexport class VafastError extends Error {\n status: number;\n type: string;\n expose: boolean;\n\n constructor(\n message: string,\n options: {\n status?: number;\n type?: string;\n expose?: boolean;\n cause?: unknown;\n } = {},\n ) {\n super(message);\n this.name = \"VafastError\";\n this.status = options.status ?? 500;\n this.type = options.type ?? \"internal_error\";\n this.expose = options.expose ?? false;\n if (options.cause) (this as any).cause = options.cause;\n }\n}\n\n/**\n * 组合类型: 自动注入错误处理器进行中间件组合\n */\nexport function composeMiddleware(\n middleware: Middleware[],\n finalHandler: Handler,\n): (req: Request) => Promise<Response> {\n const all = [errorHandler, ...middleware];\n\n return function composedHandler(req: Request): Promise<Response> {\n let i = -1;\n\n const dispatch = (index: number): Promise<Response> => {\n if (index <= i)\n return Promise.reject(new Error(\"next() called multiple times\"));\n i = index;\n\n // 中间件阶段\n if (index < all.length) {\n const mw = all[index];\n return Promise.resolve(mw(req, () => dispatch(index + 1)));\n }\n\n // 最终 handler - 使用 mapResponse 转换返回值\n return Promise.resolve(finalHandler(req)).then(mapResponse);\n };\n\n return dispatch(0);\n };\n}\n\n/** 默认包含的全局错误处理器 */\nconst errorHandler: Middleware = async (req, next) => {\n try {\n return await next();\n } catch (err) {\n console.error(\"未处理的错误:\", err);\n\n if (err instanceof VafastError) {\n return json(\n {\n error: err.type,\n message: err.expose ? err.message : \"发生了一个错误\",\n },\n err.status,\n );\n }\n\n return json({ error: \"internal_error\", message: \"出现了一些问题\" }, 500);\n }\n};\n","// src/parsers.ts\nimport qs from \"qs\";\nimport cookie from \"cookie\";\n\n// 文件信息接口\nexport interface FileInfo {\n name: string;\n type: string;\n size: number;\n data: ArrayBuffer;\n}\n\n// 表单数据接口\nexport interface FormData {\n fields: Record<string, string>;\n files: Record<string, FileInfo>;\n}\n\n/**\n * 简化的请求体解析函数\n * 优先简洁性,处理最常见的场景\n */\nexport async function parseBody(req: Request): Promise<unknown> {\n const contentType = req.headers.get(\"content-type\") || \"\";\n if (contentType.includes(\"application/json\")) {\n return await req.json();\n }\n if (contentType.includes(\"application/x-www-form-urlencoded\")) {\n const text = await req.text();\n return Object.fromEntries(new URLSearchParams(text));\n }\n return await req.text(); // fallback\n}\n\n/**\n * 解析 multipart/form-data 格式\n * 支持文件上传和普通表单字段\n */\nasync function parseMultipartFormData(req: Request): Promise<FormData> {\n const formData = await req.formData();\n const result: FormData = {\n fields: {},\n files: {},\n };\n\n for (const [key, value] of formData.entries()) {\n if (\n typeof value === \"object\" &&\n value !== null &&\n \"name\" in value &&\n \"type\" in value &&\n \"size\" in value\n ) {\n // 处理文件\n const file = value as any;\n const arrayBuffer = await file.arrayBuffer();\n result.files[key] = {\n name: file.name,\n type: file.type,\n size: file.size,\n data: arrayBuffer,\n };\n } else {\n // 处理普通字段\n result.fields[key] = value as string;\n }\n }\n\n return result;\n}\n\n/**\n * 解析请求体为特定类型\n * 提供类型安全的解析方法\n */\nexport async function parseBodyAs<T>(req: Request): Promise<T> {\n const body = await parseBody(req);\n return body as T;\n}\n\n/**\n * 解析请求体为表单数据\n * 专门用于处理 multipart/form-data\n */\nexport async function parseFormData(req: Request): Promise<FormData> {\n const contentType = req.headers.get(\"content-type\") || \"\";\n\n if (!contentType.includes(\"multipart/form-data\")) {\n throw new Error(\"请求不是 multipart/form-data 格式\");\n }\n\n return await parseMultipartFormData(req);\n}\n\n/**\n * 解析请求体为文件\n * 专门用于处理文件上传\n */\nexport async function parseFile(req: Request): Promise<FileInfo> {\n const contentType = req.headers.get(\"content-type\") || \"\";\n\n if (!contentType.includes(\"multipart/form-data\")) {\n throw new Error(\"请求不是 multipart/form-data 格式\");\n }\n\n const formData = await parseMultipartFormData(req);\n const fileKeys = Object.keys(formData.files);\n\n if (fileKeys.length === 0) {\n throw new Error(\"请求中没有文件\");\n }\n\n if (fileKeys.length > 1) {\n throw new Error(\"请求中包含多个文件,请使用 parseFormData\");\n }\n\n return formData.files[fileKeys[0]];\n}\n\n/**\n * 快速提取 query string(避免创建 URL 对象)\n */\nfunction extractQueryString(url: string): string {\n const qIndex = url.indexOf(\"?\");\n if (qIndex === -1) return \"\";\n\n const hashIndex = url.indexOf(\"#\", qIndex);\n return hashIndex === -1\n ? url.substring(qIndex + 1)\n : url.substring(qIndex + 1, hashIndex);\n}\n\n/** 获取查询字符串,直接返回对象 */\nexport function parseQuery(req: Request): Record<string, unknown> {\n const queryString = extractQueryString(req.url);\n if (!queryString) return {};\n return qs.parse(queryString);\n}\n\n/**\n * 快速解析简单查询字符串(不支持嵌套,但更快)\n * 适用于简单的 key=value&key2=value2 场景\n */\nexport function parseQueryFast(req: Request): Record<string, string> {\n const queryString = extractQueryString(req.url);\n if (!queryString) return {};\n\n const result: Record<string, string> = Object.create(null);\n const pairs = queryString.split(\"&\");\n\n for (const pair of pairs) {\n const eqIndex = pair.indexOf(\"=\");\n if (eqIndex === -1) {\n result[decodeURIComponent(pair)] = \"\";\n } else {\n const key = decodeURIComponent(pair.substring(0, eqIndex));\n const value = decodeURIComponent(pair.substring(eqIndex + 1));\n result[key] = value;\n }\n }\n\n return result;\n}\n\n/** 解析请求头,返回对象 */\nexport function parseHeaders(req: Request): Record<string, string> {\n const headers: Record<string, string> = Object.create(null);\n req.headers.forEach((value, key) => {\n headers[key] = value;\n });\n return headers;\n}\n\n/**\n * 获取单个请求头(避免解析全部)\n */\nexport function getHeader(req: Request, name: string): string | null {\n return req.headers.get(name);\n}\n\n/** 使用cookie库解析Cookie,保证可靠性 */\nexport function parseCookies(req: Request): Record<string, string> {\n const cookieHeader = req.headers.get(\"cookie\");\n if (!cookieHeader) return {};\n\n try {\n const parsed = cookie.parse(cookieHeader);\n // 过滤掉undefined和null值\n const result: Record<string, string> = {};\n for (const [key, value] of Object.entries(parsed)) {\n if (value !== undefined && value !== null) {\n result[key] = value;\n }\n }\n return result;\n } catch {\n return {};\n }\n}\n\n/**\n * 快速解析 Cookie(简化版,不使用外部库)\n * 适用于简单的 cookie 场景\n */\nexport function parseCookiesFast(req: Request): Record<string, string> {\n const cookieHeader = req.headers.get(\"cookie\");\n if (!cookieHeader) return {};\n\n const result: Record<string, string> = Object.create(null);\n const pairs = cookieHeader.split(\";\");\n\n for (const pair of pairs) {\n const trimmed = pair.trim();\n const eqIndex = trimmed.indexOf(\"=\");\n if (eqIndex > 0) {\n const key = trimmed.substring(0, eqIndex).trim();\n const value = trimmed.substring(eqIndex + 1).trim();\n // 移除引号\n result[key] =\n value.startsWith('\"') && value.endsWith('\"')\n ? value.slice(1, -1)\n : value;\n }\n }\n\n return result;\n}\n\n/**\n * 获取单个 Cookie 值(避免解析全部)\n */\nexport function getCookie(req: Request, name: string): string | null {\n const cookieHeader = req.headers.get(\"cookie\");\n if (!cookieHeader) return null;\n\n const prefix = `${name}=`;\n const pairs = cookieHeader.split(\";\");\n\n for (const pair of pairs) {\n const trimmed = pair.trim();\n if (trimmed.startsWith(prefix)) {\n const value = trimmed.substring(prefix.length).trim();\n return value.startsWith('\"') && value.endsWith('\"')\n ? value.slice(1, -1)\n : value;\n }\n }\n\n return null;\n}\n","import { parseCookies } from \"./parsers\";\n\n/** 获取单个 Cookie 值 */\nexport function getCookie(req: Request, key: string): string | null {\n const cookies = parseCookies(req);\n return cookies[key] || null;\n}\n\n/** 生成 Set-Cookie 头 */\nexport function setCookie(\n key: string,\n value: string,\n options: {\n path?: string;\n httpOnly?: boolean;\n maxAge?: number;\n secure?: boolean;\n } = {},\n): string {\n let cookie = `${key}=${encodeURIComponent(value)}`;\n\n if (options.path) cookie += `; Path=${options.path}`;\n if (options.httpOnly) cookie += `; HttpOnly`;\n if (options.secure) cookie += `; Secure`;\n if (options.maxAge) cookie += `; Max-Age=${options.maxAge}`;\n\n return cookie;\n}\n\n// 提供给中间件写入\"局部上下文\"的工具函数\nexport function setLocals<T extends object>(req: Request, extras: T) {\n const target = req as unknown as Record<string, unknown>;\n target.__locals = { ...((target.__locals as object) ?? {}), ...extras };\n}\n\n// 获取中间件注入的局部上下文\nexport function getLocals<T extends object>(req: Request): T {\n const target = req as unknown as Record<string, unknown>;\n return (target.__locals ?? {}) as T;\n}\n","// src/middleware/authMiddleware.ts\nimport type { Middleware } from \"../types\";\nimport { VafastError } from \"../middleware\";\nimport { getCookie } from \"../utils/handle\";\n\nexport const requireAuth: Middleware = async (req, next) => {\n const token = getCookie(req, \"auth\");\n\n if (!token || token !== \"valid-token\") {\n throw new VafastError(\"Unauthorized\", {\n status: 401,\n type: \"unauthorized\",\n expose: true,\n });\n }\n\n return next();\n};\n"],"mappings":";AAQO,IAAM,cAAN,cAA0B,MAAM;AAAA,EACrC;AAAA,EACA;AAAA,EACA;AAAA,EAEA,YACE,SACA,UAKI,CAAC,GACL;AACA,UAAM,OAAO;AACb,SAAK,OAAO;AACZ,SAAK,SAAS,QAAQ,UAAU;AAChC,SAAK,OAAO,QAAQ,QAAQ;AAC5B,SAAK,SAAS,QAAQ,UAAU;AAChC,QAAI,QAAQ,MAAO,CAAC,KAAa,QAAQ,QAAQ;AAAA,EACnD;AACF;;;AC5BA,OAAO,QAAQ;AACf,OAAO,YAAY;AAmLZ,SAAS,aAAa,KAAsC;AACjE,QAAM,eAAe,IAAI,QAAQ,IAAI,QAAQ;AAC7C,MAAI,CAAC,aAAc,QAAO,CAAC;AAE3B,MAAI;AACF,UAAM,SAAS,OAAO,MAAM,YAAY;AAExC,UAAM,SAAiC,CAAC;AACxC,eAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,MAAM,GAAG;AACjD,UAAI,UAAU,UAAa,UAAU,MAAM;AACzC,eAAO,GAAG,IAAI;AAAA,MAChB;AAAA,IACF;AACA,WAAO;AAAA,EACT,QAAQ;AACN,WAAO,CAAC;AAAA,EACV;AACF;;;ACnMO,SAAS,UAAU,KAAc,KAA4B;AAClE,QAAM,UAAU,aAAa,GAAG;AAChC,SAAO,QAAQ,GAAG,KAAK;AACzB;;;ACDO,IAAM,cAA0B,OAAO,KAAK,SAAS;AAC1D,QAAM,QAAQ,UAAU,KAAK,MAAM;AAEnC,MAAI,CAAC,SAAS,UAAU,eAAe;AACrC,UAAM,IAAI,YAAY,gBAAgB;AAAA,MACpC,QAAQ;AAAA,MACR,MAAM;AAAA,MACN,QAAQ;AAAA,IACV,CAAC;AAAA,EACH;AAEA,SAAO,KAAK;AACd;","names":[]}
@@ -2,5 +2,7 @@
2
2
  * 组件渲染中间件 - 专注 SSR
3
3
  * 支持 Vue 和 React 的服务端渲染
4
4
  */
5
- export declare const vueRenderer: (preloadedDeps?: any) => (req: Request, next: () => Promise<Response>) => Promise<Response>;
6
- export declare const reactRenderer: (preloadedDeps?: any) => (req: Request, next: () => Promise<Response>) => Promise<Response>;
5
+ declare const vueRenderer: (preloadedDeps?: any) => (req: Request, next: () => Promise<Response>) => Promise<Response>;
6
+ declare const reactRenderer: (preloadedDeps?: any) => (req: Request, next: () => Promise<Response>) => Promise<Response>;
7
+
8
+ export { reactRenderer, vueRenderer };