vafast 0.5.4 → 0.5.6
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 +418 -163
- package/dist/{defineRoute-BNqVD0_t.d.mts → defineRoute-FhAN4ivP.d.mts} +20 -3
- package/dist/defineRoute.d.mts +2 -2
- package/dist/defineRoute.mjs +15 -1
- package/dist/defineRoute.mjs.map +1 -1
- package/dist/{index-DCJOWsRE.d.mts → index-XcXGKQqt.d.mts} +3 -3
- package/dist/index.d.mts +6 -6
- package/dist/monitoring/index.d.mts +3 -3
- package/dist/monitoring/native-monitor.d.mts +3 -3
- package/dist/{route-registry-DZv4vAjT.d.mts → route-registry-YIco7opr.d.mts} +2 -2
- package/dist/server/index.d.mts +3 -3
- package/dist/server/server-factory.d.mts +3 -3
- package/dist/server/server.d.mts +2 -2
- package/dist/{server-Bicf_7Hx.d.mts → server-8X5Hgu7h.d.mts} +2 -2
- package/dist/{sse-DYuFPif9.d.mts → sse-DyI21Jqk.d.mts} +2 -2
- package/dist/utils/index.d.mts +3 -3
- package/dist/utils/route-registry.d.mts +2 -2
- package/dist/utils/sse.d.mts +2 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -8,21 +8,24 @@
|
|
|
8
8
|
|
|
9
9
|
> Vafast 不只是框架,更是一种 **结构、清晰、可控** 的开发哲学。
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
import { Server, createHandler } from 'vafast';
|
|
13
|
-
|
|
14
|
-
const server = new Server([
|
|
15
|
-
{ method: 'GET', path: '/', handler: createHandler(() => 'Hello Vafast!') }
|
|
16
|
-
]);
|
|
11
|
+
## 🚀 快速开始
|
|
17
12
|
|
|
18
|
-
|
|
13
|
+
```bash
|
|
14
|
+
npx create-vafast-app
|
|
19
15
|
```
|
|
20
16
|
|
|
17
|
+
按照提示输入项目名称,然后运行:
|
|
18
|
+
|
|
21
19
|
```bash
|
|
22
|
-
|
|
23
|
-
|
|
20
|
+
cd my-vafast-app
|
|
21
|
+
npm install
|
|
22
|
+
npm run dev
|
|
24
23
|
```
|
|
25
24
|
|
|
25
|
+
访问 [http://localhost:3000](http://localhost:3000) 即可看到 "Hello Vafast!"。
|
|
26
|
+
|
|
27
|
+
> 💡 想要手动配置?查看 [安装](#-安装) 部分。
|
|
28
|
+
|
|
26
29
|
## ⚡ 性能
|
|
27
30
|
|
|
28
31
|
| 框架 | RPS | 相对性能 |
|
|
@@ -37,10 +40,47 @@ npx tsx index.ts
|
|
|
37
40
|
|
|
38
41
|
## 📦 安装
|
|
39
42
|
|
|
43
|
+
### 方式一:使用脚手架(推荐)
|
|
44
|
+
|
|
45
|
+
使用官方脚手架快速创建项目:
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
npx create-vafast-app
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### 方式二:手动安装
|
|
52
|
+
|
|
53
|
+
在现有项目中安装:
|
|
54
|
+
|
|
40
55
|
```bash
|
|
41
56
|
npm install vafast
|
|
42
57
|
```
|
|
43
58
|
|
|
59
|
+
然后创建 `index.ts`:
|
|
60
|
+
|
|
61
|
+
```typescript
|
|
62
|
+
import { Server, defineRoute, defineRoutes, serve } from 'vafast';
|
|
63
|
+
|
|
64
|
+
const routes = defineRoutes([
|
|
65
|
+
defineRoute({
|
|
66
|
+
method: 'GET',
|
|
67
|
+
path: '/',
|
|
68
|
+
handler: () => 'Hello Vafast!'
|
|
69
|
+
})
|
|
70
|
+
]);
|
|
71
|
+
|
|
72
|
+
const server = new Server(routes);
|
|
73
|
+
serve({ fetch: server.fetch, port: 3000 }, (info) => {
|
|
74
|
+
console.log(`🚀 Server running at http://localhost:${info.port}`);
|
|
75
|
+
});
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
运行:
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
npx tsx index.ts
|
|
82
|
+
```
|
|
83
|
+
|
|
44
84
|
## 💡 设计哲学
|
|
45
85
|
|
|
46
86
|
### 结构即真相 — 无装饰器,无链式魔法
|
|
@@ -72,14 +112,25 @@ export default app;
|
|
|
72
112
|
|
|
73
113
|
**Vafast 完整示例:**
|
|
74
114
|
```typescript
|
|
75
|
-
import { Server,
|
|
76
|
-
import type { Route } from 'vafast';
|
|
115
|
+
import { Server, defineRoute, defineRoutes } from 'vafast';
|
|
77
116
|
|
|
78
|
-
const routes
|
|
79
|
-
{
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
117
|
+
const routes = defineRoutes([
|
|
118
|
+
defineRoute({
|
|
119
|
+
method: 'GET',
|
|
120
|
+
path: '/users',
|
|
121
|
+
handler: () => 'list users'
|
|
122
|
+
}),
|
|
123
|
+
defineRoute({
|
|
124
|
+
method: 'POST',
|
|
125
|
+
path: '/users',
|
|
126
|
+
handler: ({ body }) => body
|
|
127
|
+
}),
|
|
128
|
+
defineRoute({
|
|
129
|
+
method: 'GET',
|
|
130
|
+
path: '/users/:id',
|
|
131
|
+
handler: ({ params }) => `User ${params.id}`
|
|
132
|
+
}),
|
|
133
|
+
]);
|
|
83
134
|
|
|
84
135
|
const server = new Server(routes);
|
|
85
136
|
export default { fetch: server.fetch };
|
|
@@ -110,22 +161,21 @@ export default app;
|
|
|
110
161
|
|
|
111
162
|
**Vafast 完整示例:**
|
|
112
163
|
```typescript
|
|
113
|
-
import { Server,
|
|
114
|
-
import type { Route } from 'vafast';
|
|
164
|
+
import { Server, defineRoute, defineRoutes, err } from 'vafast';
|
|
115
165
|
|
|
116
|
-
const routes
|
|
117
|
-
{
|
|
166
|
+
const routes = defineRoutes([
|
|
167
|
+
defineRoute({
|
|
118
168
|
method: 'GET',
|
|
119
169
|
path: '/user',
|
|
120
|
-
handler:
|
|
121
|
-
const name =
|
|
170
|
+
handler: ({ query }) => {
|
|
171
|
+
const name = query.name;
|
|
122
172
|
if (!name) {
|
|
123
173
|
throw err.badRequest('Missing name'); // ✨ 简洁!
|
|
124
174
|
}
|
|
125
175
|
return `Hello, ${name}`;
|
|
126
|
-
}
|
|
127
|
-
},
|
|
128
|
-
];
|
|
176
|
+
},
|
|
177
|
+
}),
|
|
178
|
+
]);
|
|
129
179
|
|
|
130
180
|
const server = new Server(routes);
|
|
131
181
|
export default { fetch: server.fetch };
|
|
@@ -155,21 +205,29 @@ export default app;
|
|
|
155
205
|
|
|
156
206
|
**Vafast 完整示例:**
|
|
157
207
|
```typescript
|
|
158
|
-
import { Server,
|
|
159
|
-
import type { Route, Middleware } from 'vafast';
|
|
208
|
+
import { Server, defineRoute, defineRoutes, defineMiddleware } from 'vafast';
|
|
160
209
|
|
|
161
|
-
const authMiddleware
|
|
210
|
+
const authMiddleware = defineMiddleware(async (req, next) => {
|
|
162
211
|
const token = req.headers.get('Authorization');
|
|
163
212
|
if (!token) return new Response('Unauthorized', { status: 401 });
|
|
164
213
|
return next();
|
|
165
|
-
};
|
|
214
|
+
});
|
|
166
215
|
|
|
167
|
-
const routes
|
|
216
|
+
const routes = defineRoutes([
|
|
168
217
|
// 无中间件
|
|
169
|
-
{
|
|
218
|
+
defineRoute({
|
|
219
|
+
method: 'GET',
|
|
220
|
+
path: '/public',
|
|
221
|
+
handler: () => 'public'
|
|
222
|
+
}),
|
|
170
223
|
// 仅 auth
|
|
171
|
-
{
|
|
172
|
-
|
|
224
|
+
defineRoute({
|
|
225
|
+
method: 'GET',
|
|
226
|
+
path: '/api/users',
|
|
227
|
+
middleware: [authMiddleware],
|
|
228
|
+
handler: () => 'users'
|
|
229
|
+
}),
|
|
230
|
+
]);
|
|
173
231
|
|
|
174
232
|
const server = new Server(routes);
|
|
175
233
|
export default { fetch: server.fetch };
|
|
@@ -177,6 +235,69 @@ export default { fetch: server.fetch };
|
|
|
177
235
|
|
|
178
236
|
**对比:Vafast 的中间件直接声明在路由上,一目了然。**
|
|
179
237
|
|
|
238
|
+
### 扩展字段 — 声明式元数据,赋能业务逻辑
|
|
239
|
+
|
|
240
|
+
**其他框架的问题:**
|
|
241
|
+
- 路由定义和元数据分离,难以统一管理
|
|
242
|
+
- 需要额外的配置文件或装饰器来存储 webhook、权限、计费等信息
|
|
243
|
+
- 元数据查询需要遍历路由或维护独立映射表
|
|
244
|
+
|
|
245
|
+
**Vafast 完整示例:**
|
|
246
|
+
```typescript
|
|
247
|
+
import { Server, defineRoute, defineRoutes, getRouteRegistry, defineMiddleware } from 'vafast';
|
|
248
|
+
|
|
249
|
+
// 计费中间件(基于路由元数据)
|
|
250
|
+
const billingMiddleware = defineMiddleware(async (req, next) => {
|
|
251
|
+
const route = getRouteRegistry().get(req.method, new URL(req.url).pathname);
|
|
252
|
+
|
|
253
|
+
// 从路由元数据读取计费配置
|
|
254
|
+
if (route?.billing) {
|
|
255
|
+
const { price, currency } = route.billing;
|
|
256
|
+
// 执行计费逻辑
|
|
257
|
+
await chargeUser(req, price, currency);
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
return next();
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
const routes = defineRoutes([
|
|
264
|
+
defineRoute({
|
|
265
|
+
method: 'POST',
|
|
266
|
+
path: '/ai/generate',
|
|
267
|
+
name: 'AI 生成',
|
|
268
|
+
description: '生成 AI 内容',
|
|
269
|
+
// ✨ 扩展字段:计费配置
|
|
270
|
+
billing: { price: 0.01, currency: 'USD' },
|
|
271
|
+
// ✨ 扩展字段:Webhook 事件
|
|
272
|
+
webhook: { eventKey: 'ai.generate', enabled: true },
|
|
273
|
+
// ✨ 扩展字段:权限要求
|
|
274
|
+
permission: 'ai.generate',
|
|
275
|
+
middleware: [billingMiddleware],
|
|
276
|
+
handler: async ({ body }) => {
|
|
277
|
+
const result = await generateAI(body.prompt);
|
|
278
|
+
return { result };
|
|
279
|
+
}
|
|
280
|
+
}),
|
|
281
|
+
defineRoute({
|
|
282
|
+
method: 'GET',
|
|
283
|
+
path: '/users',
|
|
284
|
+
// 免费 API,无需计费
|
|
285
|
+
handler: () => ({ users: [] })
|
|
286
|
+
}),
|
|
287
|
+
]);
|
|
288
|
+
|
|
289
|
+
const server = new Server(routes);
|
|
290
|
+
|
|
291
|
+
// 查询所有需要计费的 API
|
|
292
|
+
const paidRoutes = getRouteRegistry().filter('billing');
|
|
293
|
+
// 查询所有 Webhook 事件
|
|
294
|
+
const webhookRoutes = getRouteRegistry().filter('webhook');
|
|
295
|
+
// 按权限筛选
|
|
296
|
+
const aiRoutes = getRouteRegistry().filterBy(r => r.permission?.startsWith('ai.'));
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
**对比:Vafast 的扩展字段让路由定义成为单一数据源,元数据查询、中间件配置、业务逻辑都基于声明式配置。**
|
|
300
|
+
|
|
180
301
|
### 类型注入 — 跨文件不丢失
|
|
181
302
|
|
|
182
303
|
**Hono 跨文件类型问题:**
|
|
@@ -205,33 +326,37 @@ export function setupRoutes(app: Hono) {
|
|
|
205
326
|
export type AuthContext = { user: { id: string; role: string } };
|
|
206
327
|
|
|
207
328
|
// -------- file: middleware/auth.ts --------
|
|
208
|
-
import
|
|
329
|
+
import { defineMiddleware } from 'vafast';
|
|
330
|
+
import type { AuthContext } from '../types';
|
|
209
331
|
|
|
210
|
-
|
|
332
|
+
// 使用 defineMiddleware 定义带类型的中间件
|
|
333
|
+
export const authMiddleware = defineMiddleware<AuthContext>(async (req, next) => {
|
|
211
334
|
const user = await verifyToken(req.headers.get('Authorization'));
|
|
212
|
-
(
|
|
213
|
-
|
|
214
|
-
};
|
|
335
|
+
return next({ user }); // 通过 next 传递上下文
|
|
336
|
+
});
|
|
215
337
|
|
|
216
338
|
// -------- file: handlers/profile.ts --------
|
|
217
|
-
import {
|
|
218
|
-
import
|
|
339
|
+
import { defineRoute } from 'vafast';
|
|
340
|
+
import { authMiddleware } from '../middleware/auth';
|
|
219
341
|
|
|
220
|
-
//
|
|
221
|
-
export const
|
|
222
|
-
|
|
223
|
-
|
|
342
|
+
// 类型在路由级别定义,任意文件都能用!
|
|
343
|
+
export const getProfileRoute = defineRoute({
|
|
344
|
+
method: 'GET',
|
|
345
|
+
path: '/profile',
|
|
346
|
+
middleware: [authMiddleware],
|
|
347
|
+
handler: ({ user }) => {
|
|
348
|
+
// ✅ user 自动有类型: { id: string; role: string }
|
|
349
|
+
return { profile: user, isAdmin: user.role === 'admin' };
|
|
350
|
+
}
|
|
224
351
|
});
|
|
225
352
|
|
|
226
353
|
// -------- file: routes.ts --------
|
|
227
|
-
import { Server } from 'vafast';
|
|
228
|
-
import
|
|
229
|
-
import { authMiddleware } from './middleware/auth';
|
|
230
|
-
import { getProfile } from './handlers/profile';
|
|
354
|
+
import { Server, defineRoutes } from 'vafast';
|
|
355
|
+
import { getProfileRoute } from './handlers/profile';
|
|
231
356
|
|
|
232
|
-
const routes
|
|
233
|
-
|
|
234
|
-
];
|
|
357
|
+
const routes = defineRoutes([
|
|
358
|
+
getProfileRoute,
|
|
359
|
+
]);
|
|
235
360
|
|
|
236
361
|
const server = new Server(routes);
|
|
237
362
|
export default { fetch: server.fetch };
|
|
@@ -243,34 +368,49 @@ export default { fetch: server.fetch };
|
|
|
243
368
|
|
|
244
369
|
**Bun 环境完整示例:**
|
|
245
370
|
```typescript
|
|
246
|
-
import { Server,
|
|
371
|
+
import { Server, defineRoute, defineRoutes } from 'vafast';
|
|
247
372
|
|
|
248
|
-
const
|
|
249
|
-
{
|
|
373
|
+
const routes = defineRoutes([
|
|
374
|
+
defineRoute({
|
|
375
|
+
method: 'GET',
|
|
376
|
+
path: '/',
|
|
377
|
+
handler: () => 'Hello Bun!'
|
|
378
|
+
})
|
|
250
379
|
]);
|
|
251
380
|
|
|
381
|
+
const server = new Server(routes);
|
|
252
382
|
export default { port: 3000, fetch: server.fetch };
|
|
253
383
|
```
|
|
254
384
|
|
|
255
385
|
**Cloudflare Workers 完整示例:**
|
|
256
386
|
```typescript
|
|
257
|
-
import { Server,
|
|
387
|
+
import { Server, defineRoute, defineRoutes } from 'vafast';
|
|
258
388
|
|
|
259
|
-
const
|
|
260
|
-
{
|
|
389
|
+
const routes = defineRoutes([
|
|
390
|
+
defineRoute({
|
|
391
|
+
method: 'GET',
|
|
392
|
+
path: '/',
|
|
393
|
+
handler: () => 'Hello Workers!'
|
|
394
|
+
})
|
|
261
395
|
]);
|
|
262
396
|
|
|
397
|
+
const server = new Server(routes);
|
|
263
398
|
export default { fetch: server.fetch };
|
|
264
399
|
```
|
|
265
400
|
|
|
266
401
|
**Node.js 完整示例:**
|
|
267
402
|
```typescript
|
|
268
|
-
import { Server,
|
|
403
|
+
import { Server, defineRoute, defineRoutes, serve } from 'vafast';
|
|
269
404
|
|
|
270
|
-
const
|
|
271
|
-
{
|
|
405
|
+
const routes = defineRoutes([
|
|
406
|
+
defineRoute({
|
|
407
|
+
method: 'GET',
|
|
408
|
+
path: '/',
|
|
409
|
+
handler: () => 'Hello Node!'
|
|
410
|
+
})
|
|
272
411
|
]);
|
|
273
412
|
|
|
413
|
+
const server = new Server(routes);
|
|
274
414
|
serve({ fetch: server.fetch, port: 3000 }, () => {
|
|
275
415
|
console.log('Server running on http://localhost:3000');
|
|
276
416
|
});
|
|
@@ -287,9 +427,13 @@ nest new my-app # 生成 20+ 文件
|
|
|
287
427
|
# ❌ Express - 需要配置和样板代码
|
|
288
428
|
npm init && npm install express && mkdir routes controllers...
|
|
289
429
|
|
|
290
|
-
# ✅ Vafast -
|
|
291
|
-
|
|
292
|
-
|
|
430
|
+
# ✅ Vafast - 使用脚手架一键创建
|
|
431
|
+
npx create-vafast-app
|
|
432
|
+
|
|
433
|
+
# ✅ 或手动创建 - 一个文件搞定
|
|
434
|
+
echo "import { Server, defineRoute, defineRoutes } from 'vafast';
|
|
435
|
+
const routes = defineRoutes([defineRoute({ method: 'GET', path: '/', handler: () => 'Hi' })]);
|
|
436
|
+
const server = new Server(routes);
|
|
293
437
|
export default { fetch: server.fetch };" > index.ts && bun index.ts
|
|
294
438
|
```
|
|
295
439
|
|
|
@@ -304,6 +448,9 @@ export default { fetch: server.fetch };" > index.ts && bun index.ts
|
|
|
304
448
|
| **类型推断** | 优秀 | 良好 | **优秀 (TypeBox)** |
|
|
305
449
|
| **跨文件类型** | ⚠️ 链断裂丢失 | ❌ 实例绑定丢失 | **✅ Handler 级独立** |
|
|
306
450
|
| **类型定义位置** | 链式调用上下文 | App 实例泛型 | **Handler 泛型参数** |
|
|
451
|
+
| **扩展字段** | ❌ 不支持 | ❌ 不支持 | **✅ 任意扩展字段** |
|
|
452
|
+
| **元数据查询** | ❌ 需遍历 | ❌ 需遍历 | **✅ RouteRegistry API** |
|
|
453
|
+
| **业务集成** | ⚠️ 需额外配置 | ⚠️ 需额外配置 | **✅ 声明式集成** |
|
|
307
454
|
| **性能 (RPS)** | ~118K | ~56K | **~101K** |
|
|
308
455
|
| **学习曲线** | 中等 | 简单 | **简单** |
|
|
309
456
|
| **API 风格** | 函数式链 | Express-like | **配置式** |
|
|
@@ -317,8 +464,11 @@ export default { fetch: server.fetch };" > index.ts && bun index.ts
|
|
|
317
464
|
| **需要路由一览表** | **✅ Vafast** |
|
|
318
465
|
| **需要精确中间件控制** | **✅ Vafast** |
|
|
319
466
|
| **需要结构化错误** | **✅ Vafast** |
|
|
467
|
+
| **需要扩展字段(webhook、计费、权限)** | **✅ Vafast** |
|
|
468
|
+
| **需要元数据查询和筛选** | **✅ Vafast (RouteRegistry)** |
|
|
320
469
|
| **大型项目多文件拆分** | **✅ Vafast (类型不丢失)** |
|
|
321
470
|
| **团队协作类型安全** | **✅ Vafast** |
|
|
471
|
+
| **API 网关/微服务场景** | **✅ Vafast (声明式配置)** |
|
|
322
472
|
| 从 Express 迁移 | Hono (API 相似) |
|
|
323
473
|
|
|
324
474
|
## 🎯 核心功能
|
|
@@ -335,25 +485,31 @@ export default { fetch: server.fetch };" > index.ts && bun index.ts
|
|
|
335
485
|
Vafast 提供简洁、对称的响应 API:
|
|
336
486
|
|
|
337
487
|
```typescript
|
|
338
|
-
import {
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
488
|
+
import { defineRoute, json, err } from 'vafast';
|
|
489
|
+
|
|
490
|
+
defineRoute({
|
|
491
|
+
method: 'POST',
|
|
492
|
+
path: '/users',
|
|
493
|
+
handler: ({ body }) => {
|
|
494
|
+
// ==================== 成功响应 ====================
|
|
495
|
+
return body // 200 + JSON(自动转换)
|
|
496
|
+
return json(body, 201) // 201 Created
|
|
497
|
+
return json(body, 200, { // 自定义头部
|
|
498
|
+
'X-Request-Id': 'abc123'
|
|
499
|
+
})
|
|
500
|
+
return 'Hello' // 200 + text/plain
|
|
501
|
+
return new Response(...) // 完全控制
|
|
502
|
+
|
|
503
|
+
// ==================== 错误响应 ====================
|
|
504
|
+
throw err.badRequest('参数错误') // 400
|
|
505
|
+
throw err.unauthorized('请先登录') // 401
|
|
506
|
+
throw err.forbidden('无权限') // 403
|
|
507
|
+
throw err.notFound('用户不存在') // 404
|
|
508
|
+
throw err.conflict('用户名已存在') // 409
|
|
509
|
+
throw err.internal('服务器错误') // 500
|
|
510
|
+
throw err('自定义错误', 422, 'CUSTOM_TYPE') // 自定义
|
|
511
|
+
}
|
|
345
512
|
})
|
|
346
|
-
return 'Hello' // 200 + text/plain
|
|
347
|
-
return new Response(...) // 完全控制
|
|
348
|
-
|
|
349
|
-
// ==================== 错误响应 ====================
|
|
350
|
-
throw err.badRequest('参数错误') // 400
|
|
351
|
-
throw err.unauthorized('请先登录') // 401
|
|
352
|
-
throw err.forbidden('无权限') // 403
|
|
353
|
-
throw err.notFound('用户不存在') // 404
|
|
354
|
-
throw err.conflict('用户名已存在') // 409
|
|
355
|
-
throw err.internal('服务器错误') // 500
|
|
356
|
-
throw err('自定义错误', 422, 'CUSTOM_TYPE') // 自定义
|
|
357
513
|
```
|
|
358
514
|
|
|
359
515
|
**API 速查表:**
|
|
@@ -372,20 +528,20 @@ throw err('自定义错误', 422, 'CUSTOM_TYPE') // 自定义
|
|
|
372
528
|
### 类型安全的路由
|
|
373
529
|
|
|
374
530
|
```typescript
|
|
375
|
-
import { Server,
|
|
531
|
+
import { Server, defineRoute, defineRoutes, Type } from 'vafast';
|
|
376
532
|
|
|
377
533
|
const routes = defineRoutes([
|
|
378
|
-
{
|
|
534
|
+
defineRoute({
|
|
379
535
|
method: 'POST',
|
|
380
536
|
path: '/users',
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
}
|
|
387
|
-
|
|
388
|
-
}
|
|
537
|
+
schema: {
|
|
538
|
+
body: Type.Object({ name: Type.String(), email: Type.String() })
|
|
539
|
+
},
|
|
540
|
+
handler: ({ body }) => {
|
|
541
|
+
// body.name 和 body.email 自动类型推断
|
|
542
|
+
return { success: true, user: body };
|
|
543
|
+
}
|
|
544
|
+
})
|
|
389
545
|
]);
|
|
390
546
|
|
|
391
547
|
const server = new Server(routes);
|
|
@@ -395,32 +551,34 @@ export default { port: 3000, fetch: server.fetch };
|
|
|
395
551
|
### 路径参数
|
|
396
552
|
|
|
397
553
|
```typescript
|
|
398
|
-
{
|
|
554
|
+
defineRoute({
|
|
399
555
|
method: 'GET',
|
|
400
556
|
path: '/users/:id',
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
)
|
|
405
|
-
}
|
|
557
|
+
schema: {
|
|
558
|
+
params: Type.Object({ id: Type.String() })
|
|
559
|
+
},
|
|
560
|
+
handler: ({ params }) => ({ userId: params.id })
|
|
561
|
+
})
|
|
406
562
|
```
|
|
407
563
|
|
|
408
564
|
### 中间件
|
|
409
565
|
|
|
410
566
|
```typescript
|
|
411
|
-
|
|
567
|
+
import { defineMiddleware } from 'vafast';
|
|
568
|
+
|
|
569
|
+
const authMiddleware = defineMiddleware(async (req, next) => {
|
|
412
570
|
const token = req.headers.get('Authorization');
|
|
413
571
|
if (!token) return new Response('Unauthorized', { status: 401 });
|
|
414
|
-
return next(
|
|
415
|
-
};
|
|
572
|
+
return next();
|
|
573
|
+
});
|
|
416
574
|
|
|
417
575
|
const routes = defineRoutes([
|
|
418
|
-
{
|
|
576
|
+
defineRoute({
|
|
419
577
|
method: 'GET',
|
|
420
578
|
path: '/protected',
|
|
421
579
|
middleware: [authMiddleware],
|
|
422
|
-
handler:
|
|
423
|
-
}
|
|
580
|
+
handler: () => ({ secret: 'data' })
|
|
581
|
+
})
|
|
424
582
|
]);
|
|
425
583
|
```
|
|
426
584
|
|
|
@@ -428,22 +586,42 @@ const routes = defineRoutes([
|
|
|
428
586
|
|
|
429
587
|
```typescript
|
|
430
588
|
const routes = defineRoutes([
|
|
431
|
-
{
|
|
589
|
+
defineRoute({
|
|
432
590
|
path: '/api',
|
|
433
591
|
middleware: [apiMiddleware],
|
|
434
592
|
children: [
|
|
435
|
-
{
|
|
436
|
-
|
|
437
|
-
|
|
593
|
+
defineRoute({
|
|
594
|
+
method: 'GET',
|
|
595
|
+
path: '/users',
|
|
596
|
+
handler: getUsers
|
|
597
|
+
}),
|
|
598
|
+
defineRoute({
|
|
599
|
+
method: 'POST',
|
|
600
|
+
path: '/users',
|
|
601
|
+
handler: createUser
|
|
602
|
+
}),
|
|
603
|
+
defineRoute({
|
|
438
604
|
path: '/users/:id',
|
|
439
605
|
children: [
|
|
440
|
-
{
|
|
441
|
-
|
|
442
|
-
|
|
606
|
+
defineRoute({
|
|
607
|
+
method: 'GET',
|
|
608
|
+
path: '/',
|
|
609
|
+
handler: getUser
|
|
610
|
+
}),
|
|
611
|
+
defineRoute({
|
|
612
|
+
method: 'PUT',
|
|
613
|
+
path: '/',
|
|
614
|
+
handler: updateUser
|
|
615
|
+
}),
|
|
616
|
+
defineRoute({
|
|
617
|
+
method: 'DELETE',
|
|
618
|
+
path: '/',
|
|
619
|
+
handler: deleteUser
|
|
620
|
+
}),
|
|
443
621
|
]
|
|
444
|
-
}
|
|
622
|
+
})
|
|
445
623
|
]
|
|
446
|
-
}
|
|
624
|
+
})
|
|
447
625
|
]);
|
|
448
626
|
```
|
|
449
627
|
|
|
@@ -509,7 +687,7 @@ precompileSchemas([UserSchema, PostSchema, CommentSchema]);
|
|
|
509
687
|
Vafast 内置 30+ 常用 format 验证器,**导入框架时自动注册**,对标 Zod 的内置验证:
|
|
510
688
|
|
|
511
689
|
```typescript
|
|
512
|
-
import {
|
|
690
|
+
import { defineRoute, defineRoutes, Type } from 'vafast';
|
|
513
691
|
|
|
514
692
|
// 直接使用内置 format,无需手动注册
|
|
515
693
|
const UserSchema = Type.Object({
|
|
@@ -520,9 +698,16 @@ const UserSchema = Type.Object({
|
|
|
520
698
|
createdAt: Type.String({ format: 'date-time' }),
|
|
521
699
|
});
|
|
522
700
|
|
|
523
|
-
const
|
|
524
|
-
|
|
525
|
-
|
|
701
|
+
const routes = defineRoutes([
|
|
702
|
+
defineRoute({
|
|
703
|
+
method: 'POST',
|
|
704
|
+
path: '/users',
|
|
705
|
+
schema: { body: UserSchema },
|
|
706
|
+
handler: ({ body }) => {
|
|
707
|
+
return { success: true, user: body };
|
|
708
|
+
}
|
|
709
|
+
})
|
|
710
|
+
]);
|
|
526
711
|
```
|
|
527
712
|
|
|
528
713
|
**支持的 Format 列表:**
|
|
@@ -551,52 +736,113 @@ registerFormat('order-id', (v) => /^ORD-\d{8}$/.test(v));
|
|
|
551
736
|
const isEmail = Patterns.EMAIL.test('test@example.com');
|
|
552
737
|
```
|
|
553
738
|
|
|
554
|
-
### 路由注册表 (RouteRegistry)
|
|
739
|
+
### 路由注册表 (RouteRegistry) — 声明式元数据,赋能业务逻辑
|
|
555
740
|
|
|
556
|
-
Vafast
|
|
741
|
+
Vafast 的声明式路由支持**任意扩展字段**,让路由定义成为业务逻辑的单一数据源。适用于 API 文档生成、Webhook 事件注册、权限检查、**按 API 计费**等场景:
|
|
557
742
|
|
|
558
743
|
```typescript
|
|
559
|
-
import { Server,
|
|
560
|
-
|
|
744
|
+
import { Server, defineRoute, defineRoutes, getRouteRegistry, defineMiddleware } from 'vafast';
|
|
745
|
+
|
|
746
|
+
// 计费中间件:基于路由元数据自动计费
|
|
747
|
+
const billingMiddleware = defineMiddleware(async (req, next) => {
|
|
748
|
+
const registry = getRouteRegistry();
|
|
749
|
+
const url = new URL(req.url);
|
|
750
|
+
const route = registry.get(req.method, url.pathname);
|
|
751
|
+
|
|
752
|
+
if (route?.billing) {
|
|
753
|
+
const { price, currency, unit } = route.billing;
|
|
754
|
+
const userId = getUserId(req);
|
|
755
|
+
|
|
756
|
+
// 执行计费逻辑
|
|
757
|
+
await chargeUser(userId, {
|
|
758
|
+
api: `${req.method} ${url.pathname}`,
|
|
759
|
+
price,
|
|
760
|
+
currency,
|
|
761
|
+
unit, // 'request' | 'token' | 'minute'
|
|
762
|
+
});
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
return next();
|
|
766
|
+
});
|
|
561
767
|
|
|
562
768
|
// 定义带扩展字段的路由
|
|
563
|
-
const routes
|
|
564
|
-
{
|
|
769
|
+
const routes = defineRoutes([
|
|
770
|
+
defineRoute({
|
|
565
771
|
method: 'POST',
|
|
566
772
|
path: '/auth/signIn',
|
|
773
|
+
name: '用户登录',
|
|
774
|
+
description: '用户通过邮箱密码登录',
|
|
567
775
|
handler: signInHandler,
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
776
|
+
// ✨ 扩展字段:Webhook 事件
|
|
777
|
+
webhook: { eventKey: 'auth.signIn', enabled: true },
|
|
778
|
+
}),
|
|
779
|
+
defineRoute({
|
|
780
|
+
method: 'POST',
|
|
781
|
+
path: '/ai/generate',
|
|
782
|
+
name: 'AI 生成',
|
|
783
|
+
description: '生成 AI 内容',
|
|
784
|
+
// ✨ 扩展字段:按请求计费
|
|
785
|
+
billing: { price: 0.01, currency: 'USD', unit: 'request' },
|
|
786
|
+
// ✨ 扩展字段:权限要求
|
|
787
|
+
permission: 'ai.generate',
|
|
788
|
+
middleware: [billingMiddleware],
|
|
789
|
+
handler: async ({ body }) => {
|
|
790
|
+
return await generateAI(body.prompt);
|
|
791
|
+
}
|
|
792
|
+
}),
|
|
793
|
+
defineRoute({
|
|
794
|
+
method: 'POST',
|
|
795
|
+
path: '/ai/chat',
|
|
796
|
+
name: 'AI 对话',
|
|
797
|
+
// ✨ 扩展字段:按 token 计费
|
|
798
|
+
billing: { price: 0.0001, currency: 'USD', unit: 'token' },
|
|
799
|
+
permission: 'ai.chat',
|
|
800
|
+
middleware: [billingMiddleware],
|
|
801
|
+
handler: async ({ body }) => {
|
|
802
|
+
return await chatAI(body.message);
|
|
803
|
+
}
|
|
804
|
+
}),
|
|
805
|
+
defineRoute({
|
|
573
806
|
method: 'GET',
|
|
574
807
|
path: '/users',
|
|
575
808
|
handler: getUsersHandler,
|
|
576
|
-
permission: 'users.read',
|
|
577
|
-
|
|
578
|
-
|
|
809
|
+
permission: 'users.read',
|
|
810
|
+
// 免费 API,无需计费配置
|
|
811
|
+
}),
|
|
812
|
+
]);
|
|
579
813
|
|
|
580
814
|
const server = new Server(routes);
|
|
581
815
|
|
|
582
|
-
//
|
|
583
|
-
const registry =
|
|
816
|
+
// Server 创建时自动设置全局注册表,直接使用即可
|
|
817
|
+
const registry = getRouteRegistry();
|
|
584
818
|
|
|
585
|
-
//
|
|
586
|
-
const route = registry.get('POST', '/
|
|
587
|
-
console.log(route?.name);
|
|
819
|
+
// 查询路由元信息
|
|
820
|
+
const route = registry.get('POST', '/ai/generate');
|
|
821
|
+
console.log(route?.name); // 'AI 生成'
|
|
822
|
+
console.log(route?.billing); // { price: 0.01, currency: 'USD', unit: 'request' }
|
|
823
|
+
console.log(route?.permission); // 'ai.generate'
|
|
824
|
+
|
|
825
|
+
// 筛选有特定字段的路由
|
|
826
|
+
const webhookRoutes = registry.filter('webhook'); // 所有 Webhook 事件
|
|
827
|
+
const paidRoutes = registry.filter('billing'); // 所有付费 API
|
|
828
|
+
const aiRoutes = registry.filterBy(r => r.permission?.startsWith('ai.')); // AI 相关 API
|
|
588
829
|
|
|
589
830
|
// 按分类获取
|
|
590
831
|
const authRoutes = registry.getByCategory('auth');
|
|
591
|
-
|
|
592
|
-
// 筛选有特定字段的路由
|
|
593
|
-
const webhookRoutes = registry.filter('webhook');
|
|
594
|
-
const permissionRoutes = registry.filter('permission');
|
|
832
|
+
const aiCategoryRoutes = registry.getByCategory('ai');
|
|
595
833
|
|
|
596
834
|
// 获取所有分类
|
|
597
|
-
const categories = registry.getCategories(); // ['auth', 'users']
|
|
835
|
+
const categories = registry.getCategories(); // ['auth', 'ai', 'users']
|
|
598
836
|
```
|
|
599
837
|
|
|
838
|
+
**扩展字段的优势:**
|
|
839
|
+
|
|
840
|
+
1. **单一数据源**:路由定义包含所有元数据,无需额外配置文件
|
|
841
|
+
2. **类型安全**:扩展字段在 TypeScript 中完全类型化
|
|
842
|
+
3. **运行时查询**:通过 `RouteRegistry` API 动态查询和筛选
|
|
843
|
+
4. **业务集成**:中间件可直接读取路由元数据,实现计费、权限、审计等功能
|
|
844
|
+
5. **API 网关友好**:声明式配置完美适配网关场景
|
|
845
|
+
|
|
600
846
|
**Registry 实例方法:**
|
|
601
847
|
|
|
602
848
|
| 方法 | 说明 |
|
|
@@ -612,52 +858,61 @@ const categories = registry.getCategories(); // ['auth', 'users']
|
|
|
612
858
|
| `map(callback)` | 映射所有路由 |
|
|
613
859
|
| `size` | 路由数量 |
|
|
614
860
|
|
|
615
|
-
|
|
861
|
+
**全局便捷函数(Server 创建后自动可用):**
|
|
616
862
|
|
|
617
863
|
```typescript
|
|
618
864
|
import {
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
865
|
+
getRouteRegistry, // 获取全局注册表实例
|
|
866
|
+
getRoute, // 快速查询单个路由
|
|
867
|
+
getAllRoutes, // 获取所有路由
|
|
868
|
+
filterRoutes, // 按字段筛选
|
|
869
|
+
getRoutesByMethod, // 按 HTTP 方法获取
|
|
623
870
|
} from 'vafast'
|
|
624
871
|
|
|
625
|
-
//
|
|
626
|
-
const
|
|
872
|
+
// 方式一:使用全局注册表实例
|
|
873
|
+
const registry = getRouteRegistry()
|
|
874
|
+
const route = registry.get('POST', '/users')
|
|
627
875
|
|
|
628
|
-
//
|
|
876
|
+
// 方式二:使用便捷函数(推荐,更简洁)
|
|
877
|
+
const route = getRoute('POST', '/users')
|
|
629
878
|
const allRoutes = getAllRoutes()
|
|
630
|
-
|
|
631
|
-
// 按字段筛选
|
|
632
879
|
const webhookRoutes = filterRoutes('webhook')
|
|
633
|
-
|
|
634
|
-
// 按 HTTP 方法获取
|
|
635
880
|
const getRoutes = getRoutesByMethod('GET')
|
|
636
881
|
const postRoutes = getRoutesByMethod('POST')
|
|
637
882
|
|
|
638
|
-
//
|
|
883
|
+
// 按路径前缀筛选
|
|
639
884
|
const authRoutes = getAllRoutes().filter(r => r.path.startsWith('/auth'))
|
|
640
885
|
```
|
|
641
886
|
|
|
887
|
+
> 💡 **提示**:Server 创建时会自动设置全局 RouteRegistry,无需手动创建。在任意文件中导入 `getRouteRegistry()` 即可访问。
|
|
888
|
+
|
|
642
889
|
### API Spec 生成
|
|
643
890
|
|
|
644
891
|
Vafast 提供 `getApiSpec` 用于生成 API 规范,支持跨仓库类型同步和 AI 工具函数生成:
|
|
645
892
|
|
|
646
893
|
```typescript
|
|
647
|
-
import { Server, defineRoutes, getApiSpec } from 'vafast';
|
|
894
|
+
import { Server, defineRoute, defineRoutes, getApiSpec } from 'vafast';
|
|
648
895
|
|
|
649
896
|
const routes = defineRoutes([
|
|
650
|
-
{
|
|
651
|
-
|
|
897
|
+
defineRoute({
|
|
898
|
+
method: 'GET',
|
|
899
|
+
path: '/users',
|
|
900
|
+
handler: getUsers
|
|
901
|
+
}),
|
|
902
|
+
defineRoute({
|
|
903
|
+
method: 'POST',
|
|
904
|
+
path: '/users',
|
|
905
|
+
handler: createUser
|
|
906
|
+
}),
|
|
907
|
+
// 添加 API Spec 接口
|
|
908
|
+
defineRoute({
|
|
909
|
+
method: 'GET',
|
|
910
|
+
path: '/api-spec',
|
|
911
|
+
handler: getApiSpec // 直接作为 handler
|
|
912
|
+
}),
|
|
652
913
|
]);
|
|
653
914
|
|
|
654
|
-
|
|
655
|
-
const allRoutes = [
|
|
656
|
-
...routes,
|
|
657
|
-
{ method: 'GET', path: '/api-spec', handler: getApiSpec } // 直接作为 handler
|
|
658
|
-
];
|
|
659
|
-
|
|
660
|
-
const server = new Server(allRoutes);
|
|
915
|
+
const server = new Server(routes);
|
|
661
916
|
```
|
|
662
917
|
|
|
663
918
|
**三种使用方式:**
|
|
@@ -186,10 +186,27 @@ declare function withContext<TContext extends object>(): <const TSchema$1 extend
|
|
|
186
186
|
responses?: Record<string, unknown>;
|
|
187
187
|
};
|
|
188
188
|
}) => LeafRouteConfig<TMethod, TPath, TSchema$1, TReturn, TMiddleware>;
|
|
189
|
+
/** 带原始类型信息的路由数组 */
|
|
190
|
+
type RoutesWithSource<T extends readonly RouteConfigResult[]> = ProcessedRoute[] & {
|
|
191
|
+
__source: T;
|
|
192
|
+
};
|
|
189
193
|
/**
|
|
190
194
|
* 定义路由数组,支持嵌套路由
|
|
195
|
+
*
|
|
196
|
+
* 使用 `const T` 泛型自动保留字面量类型,无需手动添加 `as const`
|
|
197
|
+
*
|
|
198
|
+
* @example
|
|
199
|
+
* ```typescript
|
|
200
|
+
* const routes = defineRoutes([
|
|
201
|
+
* defineRoute({ method: 'GET', path: '/users', handler: ... }),
|
|
202
|
+
* defineRoute({ method: 'POST', path: '/users', handler: ... }),
|
|
203
|
+
* ])
|
|
204
|
+
*
|
|
205
|
+
* // 类型推断自动工作,无需 as const
|
|
206
|
+
* type Api = InferEden<typeof routes>
|
|
207
|
+
* ```
|
|
191
208
|
*/
|
|
192
|
-
declare function defineRoutes(routes:
|
|
209
|
+
declare function defineRoutes<const T extends readonly RouteConfigResult[]>(routes: T): RoutesWithSource<T>;
|
|
193
210
|
/** 可推断的路由类型(供 vafast-api-client 使用) */
|
|
194
211
|
type InferableRoute<TMethod extends string = string, TPath extends string = string, TReturn = unknown, TSchema$1 extends RouteSchema = RouteSchema> = {
|
|
195
212
|
readonly method: TMethod;
|
|
@@ -204,5 +221,5 @@ type InferableRoute<TMethod extends string = string, TPath extends string = stri
|
|
|
204
221
|
readonly middleware?: ReadonlyArray<AnyMiddleware>;
|
|
205
222
|
};
|
|
206
223
|
//#endregion
|
|
207
|
-
export { RouteSchema as a,
|
|
208
|
-
//# sourceMappingURL=defineRoute-
|
|
224
|
+
export { RouteSchema as a, defineMiddleware as c, withContext as d, ProcessedRoute as i, defineRoute as l, InferableRoute as n, RoutesWithSource as o, LeafRouteConfig as r, TypedMiddleware as s, HandlerContext as t, defineRoutes as u };
|
|
225
|
+
//# sourceMappingURL=defineRoute-FhAN4ivP.d.mts.map
|
package/dist/defineRoute.d.mts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as RouteSchema, c as
|
|
2
|
-
export { HandlerContext, InferableRoute, LeafRouteConfig, ProcessedRoute, RouteSchema, TypedMiddleware, defineMiddleware, defineRoute, defineRoutes, withContext };
|
|
1
|
+
import { a as RouteSchema, c as defineMiddleware, d as withContext, i as ProcessedRoute, l as defineRoute, n as InferableRoute, o as RoutesWithSource, r as LeafRouteConfig, s as TypedMiddleware, t as HandlerContext, u as defineRoutes } from "./defineRoute-FhAN4ivP.mjs";
|
|
2
|
+
export { HandlerContext, InferableRoute, LeafRouteConfig, ProcessedRoute, RouteSchema, RoutesWithSource, TypedMiddleware, defineMiddleware, defineRoute, defineRoutes, withContext };
|
package/dist/defineRoute.mjs
CHANGED
|
@@ -164,9 +164,23 @@ function flattenRoutes(routes, parentPath = "", parentMiddleware = []) {
|
|
|
164
164
|
}
|
|
165
165
|
/**
|
|
166
166
|
* 定义路由数组,支持嵌套路由
|
|
167
|
+
*
|
|
168
|
+
* 使用 `const T` 泛型自动保留字面量类型,无需手动添加 `as const`
|
|
169
|
+
*
|
|
170
|
+
* @example
|
|
171
|
+
* ```typescript
|
|
172
|
+
* const routes = defineRoutes([
|
|
173
|
+
* defineRoute({ method: 'GET', path: '/users', handler: ... }),
|
|
174
|
+
* defineRoute({ method: 'POST', path: '/users', handler: ... }),
|
|
175
|
+
* ])
|
|
176
|
+
*
|
|
177
|
+
* // 类型推断自动工作,无需 as const
|
|
178
|
+
* type Api = InferEden<typeof routes>
|
|
179
|
+
* ```
|
|
167
180
|
*/
|
|
168
181
|
function defineRoutes(routes) {
|
|
169
|
-
|
|
182
|
+
const processed = flattenRoutes(routes);
|
|
183
|
+
return Object.assign(processed, { __source: routes });
|
|
170
184
|
}
|
|
171
185
|
|
|
172
186
|
//#endregion
|
package/dist/defineRoute.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"defineRoute.mjs","names":[],"sources":["../src/defineRoute.ts"],"sourcesContent":["/**\n * 路由定义 - Schema 在路由级别定义,支持嵌套路由和中间件类型推断\n *\n * @example\n * ```typescript\n * // 定义带类型的中间件(函数式风格,通过 next 传递上下文)\n * const authMiddleware = defineMiddleware<{ user: User }>((req, next) => {\n * const user = getUser(req)\n * return next({ user }) // 通过 next 参数传递上下文\n * })\n *\n * // 路由自动推断中间件注入的类型\n * const routes = defineRoutes([\n * defineRoute({\n * path: '/api',\n * middleware: [authMiddleware],\n * children: [\n * defineRoute({\n * method: 'GET',\n * path: '/profile',\n * handler: ({ user }) => ({ name: user.name }) // ✅ user 有类型\n * })\n * ]\n * })\n * ])\n * ```\n */\n\nimport type { TSchema, Static } from \"@sinclair/typebox\";\nimport { parseBody, parseQuery, parseHeaders, parseCookies } from \"./utils/parsers\";\nimport { validateAllSchemas, precompileSchemas } from \"./utils/validators/validators\";\nimport { json } from \"./utils/response\";\nimport { VafastError } from \"./middleware\";\n\n// ============= Schema 类型 =============\n\n/** 路由 Schema 配置 */\nexport interface RouteSchema {\n body?: TSchema;\n query?: TSchema;\n params?: TSchema;\n headers?: TSchema;\n cookies?: TSchema;\n}\n\n/** 从 Schema 推断类型 */\ntype InferSchemaType<T extends RouteSchema> = {\n body: T[\"body\"] extends TSchema ? Static<T[\"body\"]> : unknown;\n query: T[\"query\"] extends TSchema ? Static<T[\"query\"]> : Record<string, string>;\n params: T[\"params\"] extends TSchema ? Static<T[\"params\"]> : Record<string, string>;\n headers: T[\"headers\"] extends TSchema ? Static<T[\"headers\"]> : Record<string, string>;\n cookies: T[\"cookies\"] extends TSchema ? Static<T[\"cookies\"]> : Record<string, string>;\n};\n\n// ============= 中间件类型系统 =============\n\n/** 带类型标记的中间件 */\nexport interface TypedMiddleware<TContext extends object = object> {\n (req: Request, next: (ctx?: TContext) => Promise<Response>): Response | Promise<Response>;\n /** 类型标记(仅编译时使用) */\n __context?: TContext;\n}\n\n/** 普通中间件(无类型注入) */\ntype PlainMiddleware = (req: Request, next: () => Promise<Response>) => Response | Promise<Response>;\n\n/** 任意中间件类型 */\ntype AnyMiddleware = TypedMiddleware<object> | PlainMiddleware;\n\n/** 从中间件提取上下文类型 */\ntype ExtractMiddlewareContext<T> = T extends TypedMiddleware<infer C> ? C : object;\n\n/** 合并中间件数组的上下文类型 */\ntype MergeMiddlewareContexts<T extends readonly unknown[]> =\n T extends readonly [infer First, ...infer Rest]\n ? ExtractMiddlewareContext<First> & MergeMiddlewareContexts<Rest>\n : object;\n\n// ============= Handler 上下文 =============\n\n/** Handler 上下文(包含 schema 推断) */\nexport interface HandlerContext<TSchema extends RouteSchema = RouteSchema> {\n req: Request;\n body: InferSchemaType<TSchema>[\"body\"];\n query: InferSchemaType<TSchema>[\"query\"];\n params: InferSchemaType<TSchema>[\"params\"];\n headers: InferSchemaType<TSchema>[\"headers\"];\n cookies: InferSchemaType<TSchema>[\"cookies\"];\n}\n\n/** Handler 上下文(带中间件注入的额外类型) */\ntype HandlerContextWithExtra<TSchema extends RouteSchema, TExtra> =\n HandlerContext<TSchema> & TExtra;\n\n// ============= 路由配置类型 =============\n\n/** HTTP 方法 */\ntype HTTPMethod = \"GET\" | \"POST\" | \"PUT\" | \"DELETE\" | \"PATCH\" | \"OPTIONS\" | \"HEAD\";\n\n/** 叶子路由配置(有 method 和 handler) */\nexport interface LeafRouteConfig<\n TMethod extends HTTPMethod = HTTPMethod,\n TPath extends string = string,\n TSchema extends RouteSchema = RouteSchema,\n TReturn = unknown,\n TMiddleware extends readonly AnyMiddleware[] = readonly AnyMiddleware[]\n> {\n readonly method: TMethod;\n readonly path: TPath;\n readonly name?: string;\n readonly description?: string;\n readonly schema?: TSchema;\n readonly handler: (\n ctx: HandlerContextWithExtra<TSchema, MergeMiddlewareContexts<TMiddleware>>\n ) => TReturn | Promise<TReturn>;\n readonly middleware?: TMiddleware;\n readonly docs?: {\n tags?: string[];\n security?: unknown[];\n responses?: Record<string, unknown>;\n };\n}\n\n/** 嵌套路由配置(有 children,无 method 和 handler) */\ninterface NestedRouteConfig<\n TPath extends string = string,\n TMiddleware extends readonly AnyMiddleware[] = readonly AnyMiddleware[]\n> {\n readonly path: TPath;\n readonly name?: string;\n readonly description?: string;\n readonly middleware?: TMiddleware;\n readonly children: ReadonlyArray<RouteConfigResult>;\n}\n\n/** defineRoute 返回的类型 */\ntype RouteConfigResult =\n | LeafRouteConfig<HTTPMethod, string, RouteSchema, unknown, readonly AnyMiddleware[]>\n | NestedRouteConfig<string, readonly AnyMiddleware[]>;\n\n/** 处理后的扁平路由 */\nexport interface ProcessedRoute {\n method: HTTPMethod;\n path: string;\n name?: string;\n description?: string;\n schema?: RouteSchema;\n handler: (req: Request) => Promise<Response>;\n middleware?: readonly AnyMiddleware[];\n docs?: {\n tags?: string[];\n security?: unknown[];\n responses?: Record<string, unknown>;\n };\n /** 允许任意扩展(兼容 Route 类型) */\n [key: string]: unknown;\n}\n\n// ============= defineMiddleware =============\n\n/**\n * 定义带类型的中间件(函数式风格)\n *\n * 通过 next() 参数传递上下文,更符合函数式编程风格\n *\n * @example\n * ```typescript\n * type AuthContext = { user: { id: string; name: string } }\n *\n * const authMiddleware = defineMiddleware<AuthContext>((req, next) => {\n * const user = getUserFromToken(req)\n * return next({ user }) // 通过 next 传递上下文\n * })\n * ```\n */\nexport function defineMiddleware<TContext extends object = object>(\n handler: (\n req: Request,\n next: (ctx?: TContext) => Promise<Response>\n ) => Promise<Response>\n): TypedMiddleware<TContext> {\n // 包装成标准中间件签名\n const middleware = ((req: Request, originalNext: () => Promise<Response>) => {\n // 包装 next,接收上下文参数并存储到 req.__locals\n const nextWithContext = (ctx?: TContext): Promise<Response> => {\n if (ctx) {\n const target = req as unknown as { __locals?: object };\n target.__locals = { ...(target.__locals || {}), ...ctx };\n }\n return originalNext();\n };\n return handler(req, nextWithContext);\n }) as TypedMiddleware<TContext>;\n\n return middleware;\n}\n\n// ============= 响应处理 =============\n\n/** 自动转换返回值为 Response */\nfunction autoResponse(result: unknown): Response {\n if (result instanceof Response) return result;\n if (result === null || result === undefined) return new Response(null, { status: 204 });\n if (typeof result === \"string\") {\n return new Response(result, { headers: { \"Content-Type\": \"text/plain; charset=utf-8\" } });\n }\n if (typeof result === \"number\" || typeof result === \"boolean\") {\n return new Response(String(result), { headers: { \"Content-Type\": \"text/plain; charset=utf-8\" } });\n }\n if (typeof result === \"object\") {\n const obj = result as Record<string, unknown>;\n if (\"data\" in obj && (\"status\" in obj || \"headers\" in obj)) {\n const { data, status = 200, headers = {} } = obj;\n if (data === null || data === undefined) {\n return new Response(null, { status: status === 200 ? 204 : (status as number), headers: headers as HeadersInit });\n }\n return json(data, status as number, headers as Record<string, string>);\n }\n return json(result);\n }\n return new Response(null, { status: 204 });\n}\n\n/** 创建包装后的 handler */\nfunction wrapHandler<TSchema extends RouteSchema>(\n schema: TSchema | undefined,\n userHandler: (ctx: HandlerContext<TSchema>) => unknown | Promise<unknown>\n): (req: Request) => Promise<Response> {\n if (schema && (schema.body || schema.query || schema.params || schema.headers || schema.cookies)) {\n precompileSchemas(schema);\n }\n\n return async (req: Request): Promise<Response> => {\n try {\n const query = parseQuery(req);\n const headers = parseHeaders(req);\n const cookies = parseCookies(req);\n const params = ((req as unknown as Record<string, unknown>).params as Record<string, string>) || {};\n\n let body: unknown = undefined;\n if (req.method !== \"GET\" && req.method !== \"HEAD\") {\n try {\n body = await parseBody(req);\n } catch {\n // 忽略解析错误\n }\n }\n\n const data = { body, query, params, headers, cookies };\n if (schema && (schema.body || schema.query || schema.params || schema.headers || schema.cookies)) {\n validateAllSchemas(schema, data);\n }\n\n // 获取中间件注入的上下文\n const extraCtx = (req as unknown as { __locals?: unknown }).__locals || {};\n\n const result = await userHandler({\n req,\n body: body as HandlerContext<TSchema>[\"body\"],\n query: query as HandlerContext<TSchema>[\"query\"],\n params: params as HandlerContext<TSchema>[\"params\"],\n headers: headers as HandlerContext<TSchema>[\"headers\"],\n cookies: cookies as HandlerContext<TSchema>[\"cookies\"],\n ...extraCtx,\n } as HandlerContext<TSchema>);\n\n return autoResponse(result);\n } catch (error) {\n // 如果是 VafastError,重新抛出让错误处理中间件处理\n if (error instanceof VafastError) {\n throw error;\n }\n if (error instanceof Error && error.message.includes(\"验证失败\")) {\n return json({ success: false, error: \"Validation Error\", message: error.message }, 400);\n }\n return json({ success: false, error: \"Internal Error\", message: error instanceof Error ? error.message : \"未知错误\" }, 500);\n }\n };\n}\n\n// ============= 判断路由类型 =============\n\n/** 判断是否为叶子路由 */\nfunction isLeafRoute(route: RouteConfigResult): route is LeafRouteConfig {\n return \"method\" in route && \"handler\" in route;\n}\n\n/** 判断是否为嵌套路由 */\nfunction isNestedRoute(route: RouteConfigResult): route is NestedRouteConfig {\n return \"children\" in route;\n}\n\n// ============= defineRoute 函数(支持重载) =============\n\n/**\n * 定义叶子路由(有 method 和 handler),支持中间件类型推断和显式上下文类型\n *\n * @example\n * ```typescript\n * // 方式1:通过中间件自动推断上下文\n * defineRoute({\n * method: 'GET',\n * path: '/profile',\n * middleware: [authMiddleware],\n * handler: ({ user }) => { ... } // user 来自 authMiddleware\n * })\n *\n * // 方式2:显式声明上下文类型(用于父级中间件注入的场景)\n * defineRoute({\n * method: 'GET',\n * path: '/profile',\n * context: {} as { userInfo: UserInfo },\n * handler: ({ userInfo }) => { ... } // userInfo 有类型\n * })\n * ```\n */\nexport function defineRoute<\n const TSchema extends RouteSchema,\n TReturn,\n const TMiddleware extends readonly AnyMiddleware[],\n TContext extends object = object,\n TMethod extends HTTPMethod = HTTPMethod,\n TPath extends string = string\n>(config: {\n readonly method: TMethod;\n readonly path: TPath;\n readonly name?: string;\n readonly description?: string;\n readonly schema?: TSchema;\n /** 显式声明上下文类型(用于父级中间件注入的场景) */\n readonly context?: TContext;\n readonly handler: (\n ctx: HandlerContextWithExtra<TSchema, TContext & MergeMiddlewareContexts<TMiddleware>>\n ) => TReturn | Promise<TReturn>;\n readonly middleware?: TMiddleware;\n readonly docs?: {\n tags?: string[];\n security?: unknown[];\n responses?: Record<string, unknown>;\n };\n}): LeafRouteConfig<TMethod, TPath, TSchema, TReturn, TMiddleware>;\n\n/**\n * 定义嵌套路由(有 children),支持中间件类型推断\n */\nexport function defineRoute<\n const TMiddleware extends readonly AnyMiddleware[],\n TPath extends string = string\n>(config: {\n readonly path: TPath;\n readonly name?: string;\n readonly description?: string;\n readonly middleware?: TMiddleware;\n readonly children: ReadonlyArray<RouteConfigResult>;\n}): NestedRouteConfig<TPath, TMiddleware>;\n\n/**\n * defineRoute 实现\n */\nexport function defineRoute(config: {\n readonly method?: HTTPMethod;\n readonly path: string;\n readonly name?: string;\n readonly description?: string;\n readonly schema?: RouteSchema;\n readonly context?: object;\n readonly handler?: (ctx: HandlerContext<RouteSchema>) => unknown | Promise<unknown>;\n readonly middleware?: readonly AnyMiddleware[];\n readonly children?: ReadonlyArray<RouteConfigResult>;\n readonly docs?: {\n tags?: string[];\n security?: unknown[];\n responses?: Record<string, unknown>;\n };\n}): RouteConfigResult {\n return config as RouteConfigResult;\n}\n\n// ============= withContext 工厂函数 =============\n\n/**\n * 创建带预设上下文类型的路由定义器\n *\n * 用于父级中间件注入上下文的场景,定义一次,多处复用\n *\n * @example\n * ```typescript\n * // 1. 在 middleware/index.ts 中定义\n * export const defineAuthRoute = withContext<{ userInfo: UserInfo }>()\n *\n * // 2. 在路由文件中使用\n * defineAuthRoute({\n * method: 'GET',\n * path: '/profile',\n * handler: ({ userInfo }) => {\n * // userInfo 自动有类型!\n * return { id: userInfo.id }\n * }\n * })\n * ```\n */\nexport function withContext<TContext extends object>() {\n return <\n const TSchema extends RouteSchema,\n TReturn,\n const TMiddleware extends readonly AnyMiddleware[],\n TMethod extends HTTPMethod = HTTPMethod,\n TPath extends string = string\n >(config: {\n readonly method: TMethod;\n readonly path: TPath;\n readonly name?: string;\n readonly description?: string;\n readonly schema?: TSchema;\n readonly handler: (\n ctx: HandlerContextWithExtra<TSchema, TContext & MergeMiddlewareContexts<TMiddleware>>\n ) => TReturn | Promise<TReturn>;\n readonly middleware?: TMiddleware;\n readonly docs?: {\n tags?: string[];\n security?: unknown[];\n responses?: Record<string, unknown>;\n };\n }): LeafRouteConfig<TMethod, TPath, TSchema, TReturn, TMiddleware> => {\n return config as LeafRouteConfig<TMethod, TPath, TSchema, TReturn, TMiddleware>;\n };\n}\n\n// ============= 扁平化嵌套路由 =============\n\n/**\n * 递归扁平化路由,合并路径和中间件\n */\nfunction flattenRoutes(\n routes: ReadonlyArray<RouteConfigResult>,\n parentPath: string = \"\",\n parentMiddleware: readonly AnyMiddleware[] = []\n): ProcessedRoute[] {\n const result: ProcessedRoute[] = [];\n\n for (const route of routes) {\n const fullPath = parentPath + route.path;\n const mergedMiddleware = [...parentMiddleware, ...(route.middleware || [])];\n\n if (isLeafRoute(route)) {\n result.push({\n method: route.method,\n path: fullPath,\n name: route.name,\n description: route.description,\n schema: route.schema,\n handler: wrapHandler(route.schema, route.handler as (ctx: HandlerContext<RouteSchema>) => unknown),\n middleware: mergedMiddleware.length > 0 ? mergedMiddleware : undefined,\n docs: route.docs,\n });\n } else if (isNestedRoute(route)) {\n result.push(...flattenRoutes(route.children, fullPath, mergedMiddleware));\n }\n }\n\n return result;\n}\n\n// ============= defineRoutes 函数 =============\n\n/**\n * 定义路由数组,支持嵌套路由\n */\nexport function defineRoutes(routes: ReadonlyArray<RouteConfigResult>): ProcessedRoute[] {\n return flattenRoutes(routes);\n}\n\n// ============= 用于 API Client 的类型推断 =============\n\n/** 可推断的路由类型(供 vafast-api-client 使用) */\nexport type InferableRoute<\n TMethod extends string = string,\n TPath extends string = string,\n TReturn = unknown,\n TSchema extends RouteSchema = RouteSchema\n> = {\n readonly method: TMethod;\n readonly path: TPath;\n readonly name?: string;\n readonly description?: string;\n readonly schema?: TSchema;\n readonly handler: {\n __returnType: TReturn;\n __schema: TSchema;\n };\n readonly middleware?: ReadonlyArray<AnyMiddleware>;\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"defineRoute.mjs","names":[],"sources":["../src/defineRoute.ts"],"sourcesContent":["/**\n * 路由定义 - Schema 在路由级别定义,支持嵌套路由和中间件类型推断\n *\n * @example\n * ```typescript\n * // 定义带类型的中间件(函数式风格,通过 next 传递上下文)\n * const authMiddleware = defineMiddleware<{ user: User }>((req, next) => {\n * const user = getUser(req)\n * return next({ user }) // 通过 next 参数传递上下文\n * })\n *\n * // 路由自动推断中间件注入的类型\n * const routes = defineRoutes([\n * defineRoute({\n * path: '/api',\n * middleware: [authMiddleware],\n * children: [\n * defineRoute({\n * method: 'GET',\n * path: '/profile',\n * handler: ({ user }) => ({ name: user.name }) // ✅ user 有类型\n * })\n * ]\n * })\n * ])\n * ```\n */\n\nimport type { TSchema, Static } from \"@sinclair/typebox\";\nimport { parseBody, parseQuery, parseHeaders, parseCookies } from \"./utils/parsers\";\nimport { validateAllSchemas, precompileSchemas } from \"./utils/validators/validators\";\nimport { json } from \"./utils/response\";\nimport { VafastError } from \"./middleware\";\n\n// ============= Schema 类型 =============\n\n/** 路由 Schema 配置 */\nexport interface RouteSchema {\n body?: TSchema;\n query?: TSchema;\n params?: TSchema;\n headers?: TSchema;\n cookies?: TSchema;\n}\n\n/** 从 Schema 推断类型 */\ntype InferSchemaType<T extends RouteSchema> = {\n body: T[\"body\"] extends TSchema ? Static<T[\"body\"]> : unknown;\n query: T[\"query\"] extends TSchema ? Static<T[\"query\"]> : Record<string, string>;\n params: T[\"params\"] extends TSchema ? Static<T[\"params\"]> : Record<string, string>;\n headers: T[\"headers\"] extends TSchema ? Static<T[\"headers\"]> : Record<string, string>;\n cookies: T[\"cookies\"] extends TSchema ? Static<T[\"cookies\"]> : Record<string, string>;\n};\n\n// ============= 中间件类型系统 =============\n\n/** 带类型标记的中间件 */\nexport interface TypedMiddleware<TContext extends object = object> {\n (req: Request, next: (ctx?: TContext) => Promise<Response>): Response | Promise<Response>;\n /** 类型标记(仅编译时使用) */\n __context?: TContext;\n}\n\n/** 普通中间件(无类型注入) */\ntype PlainMiddleware = (req: Request, next: () => Promise<Response>) => Response | Promise<Response>;\n\n/** 任意中间件类型 */\ntype AnyMiddleware = TypedMiddleware<object> | PlainMiddleware;\n\n/** 从中间件提取上下文类型 */\ntype ExtractMiddlewareContext<T> = T extends TypedMiddleware<infer C> ? C : object;\n\n/** 合并中间件数组的上下文类型 */\ntype MergeMiddlewareContexts<T extends readonly unknown[]> =\n T extends readonly [infer First, ...infer Rest]\n ? ExtractMiddlewareContext<First> & MergeMiddlewareContexts<Rest>\n : object;\n\n// ============= Handler 上下文 =============\n\n/** Handler 上下文(包含 schema 推断) */\nexport interface HandlerContext<TSchema extends RouteSchema = RouteSchema> {\n req: Request;\n body: InferSchemaType<TSchema>[\"body\"];\n query: InferSchemaType<TSchema>[\"query\"];\n params: InferSchemaType<TSchema>[\"params\"];\n headers: InferSchemaType<TSchema>[\"headers\"];\n cookies: InferSchemaType<TSchema>[\"cookies\"];\n}\n\n/** Handler 上下文(带中间件注入的额外类型) */\ntype HandlerContextWithExtra<TSchema extends RouteSchema, TExtra> =\n HandlerContext<TSchema> & TExtra;\n\n// ============= 路由配置类型 =============\n\n/** HTTP 方法 */\ntype HTTPMethod = \"GET\" | \"POST\" | \"PUT\" | \"DELETE\" | \"PATCH\" | \"OPTIONS\" | \"HEAD\";\n\n/** 叶子路由配置(有 method 和 handler) */\nexport interface LeafRouteConfig<\n TMethod extends HTTPMethod = HTTPMethod,\n TPath extends string = string,\n TSchema extends RouteSchema = RouteSchema,\n TReturn = unknown,\n TMiddleware extends readonly AnyMiddleware[] = readonly AnyMiddleware[]\n> {\n readonly method: TMethod;\n readonly path: TPath;\n readonly name?: string;\n readonly description?: string;\n readonly schema?: TSchema;\n readonly handler: (\n ctx: HandlerContextWithExtra<TSchema, MergeMiddlewareContexts<TMiddleware>>\n ) => TReturn | Promise<TReturn>;\n readonly middleware?: TMiddleware;\n readonly docs?: {\n tags?: string[];\n security?: unknown[];\n responses?: Record<string, unknown>;\n };\n}\n\n/** 嵌套路由配置(有 children,无 method 和 handler) */\ninterface NestedRouteConfig<\n TPath extends string = string,\n TMiddleware extends readonly AnyMiddleware[] = readonly AnyMiddleware[]\n> {\n readonly path: TPath;\n readonly name?: string;\n readonly description?: string;\n readonly middleware?: TMiddleware;\n readonly children: ReadonlyArray<RouteConfigResult>;\n}\n\n/** defineRoute 返回的类型 */\ntype RouteConfigResult =\n | LeafRouteConfig<HTTPMethod, string, RouteSchema, unknown, readonly AnyMiddleware[]>\n | NestedRouteConfig<string, readonly AnyMiddleware[]>;\n\n/** 处理后的扁平路由 */\nexport interface ProcessedRoute {\n method: HTTPMethod;\n path: string;\n name?: string;\n description?: string;\n schema?: RouteSchema;\n handler: (req: Request) => Promise<Response>;\n middleware?: readonly AnyMiddleware[];\n docs?: {\n tags?: string[];\n security?: unknown[];\n responses?: Record<string, unknown>;\n };\n /** 允许任意扩展(兼容 Route 类型) */\n [key: string]: unknown;\n}\n\n// ============= defineMiddleware =============\n\n/**\n * 定义带类型的中间件(函数式风格)\n *\n * 通过 next() 参数传递上下文,更符合函数式编程风格\n *\n * @example\n * ```typescript\n * type AuthContext = { user: { id: string; name: string } }\n *\n * const authMiddleware = defineMiddleware<AuthContext>((req, next) => {\n * const user = getUserFromToken(req)\n * return next({ user }) // 通过 next 传递上下文\n * })\n * ```\n */\nexport function defineMiddleware<TContext extends object = object>(\n handler: (\n req: Request,\n next: (ctx?: TContext) => Promise<Response>\n ) => Promise<Response>\n): TypedMiddleware<TContext> {\n // 包装成标准中间件签名\n const middleware = ((req: Request, originalNext: () => Promise<Response>) => {\n // 包装 next,接收上下文参数并存储到 req.__locals\n const nextWithContext = (ctx?: TContext): Promise<Response> => {\n if (ctx) {\n const target = req as unknown as { __locals?: object };\n target.__locals = { ...(target.__locals || {}), ...ctx };\n }\n return originalNext();\n };\n return handler(req, nextWithContext);\n }) as TypedMiddleware<TContext>;\n\n return middleware;\n}\n\n// ============= 响应处理 =============\n\n/** 自动转换返回值为 Response */\nfunction autoResponse(result: unknown): Response {\n if (result instanceof Response) return result;\n if (result === null || result === undefined) return new Response(null, { status: 204 });\n if (typeof result === \"string\") {\n return new Response(result, { headers: { \"Content-Type\": \"text/plain; charset=utf-8\" } });\n }\n if (typeof result === \"number\" || typeof result === \"boolean\") {\n return new Response(String(result), { headers: { \"Content-Type\": \"text/plain; charset=utf-8\" } });\n }\n if (typeof result === \"object\") {\n const obj = result as Record<string, unknown>;\n if (\"data\" in obj && (\"status\" in obj || \"headers\" in obj)) {\n const { data, status = 200, headers = {} } = obj;\n if (data === null || data === undefined) {\n return new Response(null, { status: status === 200 ? 204 : (status as number), headers: headers as HeadersInit });\n }\n return json(data, status as number, headers as Record<string, string>);\n }\n return json(result);\n }\n return new Response(null, { status: 204 });\n}\n\n/** 创建包装后的 handler */\nfunction wrapHandler<TSchema extends RouteSchema>(\n schema: TSchema | undefined,\n userHandler: (ctx: HandlerContext<TSchema>) => unknown | Promise<unknown>\n): (req: Request) => Promise<Response> {\n if (schema && (schema.body || schema.query || schema.params || schema.headers || schema.cookies)) {\n precompileSchemas(schema);\n }\n\n return async (req: Request): Promise<Response> => {\n try {\n const query = parseQuery(req);\n const headers = parseHeaders(req);\n const cookies = parseCookies(req);\n const params = ((req as unknown as Record<string, unknown>).params as Record<string, string>) || {};\n\n let body: unknown = undefined;\n if (req.method !== \"GET\" && req.method !== \"HEAD\") {\n try {\n body = await parseBody(req);\n } catch {\n // 忽略解析错误\n }\n }\n\n const data = { body, query, params, headers, cookies };\n if (schema && (schema.body || schema.query || schema.params || schema.headers || schema.cookies)) {\n validateAllSchemas(schema, data);\n }\n\n // 获取中间件注入的上下文\n const extraCtx = (req as unknown as { __locals?: unknown }).__locals || {};\n\n const result = await userHandler({\n req,\n body: body as HandlerContext<TSchema>[\"body\"],\n query: query as HandlerContext<TSchema>[\"query\"],\n params: params as HandlerContext<TSchema>[\"params\"],\n headers: headers as HandlerContext<TSchema>[\"headers\"],\n cookies: cookies as HandlerContext<TSchema>[\"cookies\"],\n ...extraCtx,\n } as HandlerContext<TSchema>);\n\n return autoResponse(result);\n } catch (error) {\n // 如果是 VafastError,重新抛出让错误处理中间件处理\n if (error instanceof VafastError) {\n throw error;\n }\n if (error instanceof Error && error.message.includes(\"验证失败\")) {\n return json({ success: false, error: \"Validation Error\", message: error.message }, 400);\n }\n return json({ success: false, error: \"Internal Error\", message: error instanceof Error ? error.message : \"未知错误\" }, 500);\n }\n };\n}\n\n// ============= 判断路由类型 =============\n\n/** 判断是否为叶子路由 */\nfunction isLeafRoute(route: RouteConfigResult): route is LeafRouteConfig {\n return \"method\" in route && \"handler\" in route;\n}\n\n/** 判断是否为嵌套路由 */\nfunction isNestedRoute(route: RouteConfigResult): route is NestedRouteConfig {\n return \"children\" in route;\n}\n\n// ============= defineRoute 函数(支持重载) =============\n\n/**\n * 定义叶子路由(有 method 和 handler),支持中间件类型推断和显式上下文类型\n *\n * @example\n * ```typescript\n * // 方式1:通过中间件自动推断上下文\n * defineRoute({\n * method: 'GET',\n * path: '/profile',\n * middleware: [authMiddleware],\n * handler: ({ user }) => { ... } // user 来自 authMiddleware\n * })\n *\n * // 方式2:显式声明上下文类型(用于父级中间件注入的场景)\n * defineRoute({\n * method: 'GET',\n * path: '/profile',\n * context: {} as { userInfo: UserInfo },\n * handler: ({ userInfo }) => { ... } // userInfo 有类型\n * })\n * ```\n */\nexport function defineRoute<\n const TSchema extends RouteSchema,\n TReturn,\n const TMiddleware extends readonly AnyMiddleware[],\n TContext extends object = object,\n TMethod extends HTTPMethod = HTTPMethod,\n TPath extends string = string\n>(config: {\n readonly method: TMethod;\n readonly path: TPath;\n readonly name?: string;\n readonly description?: string;\n readonly schema?: TSchema;\n /** 显式声明上下文类型(用于父级中间件注入的场景) */\n readonly context?: TContext;\n readonly handler: (\n ctx: HandlerContextWithExtra<TSchema, TContext & MergeMiddlewareContexts<TMiddleware>>\n ) => TReturn | Promise<TReturn>;\n readonly middleware?: TMiddleware;\n readonly docs?: {\n tags?: string[];\n security?: unknown[];\n responses?: Record<string, unknown>;\n };\n}): LeafRouteConfig<TMethod, TPath, TSchema, TReturn, TMiddleware>;\n\n/**\n * 定义嵌套路由(有 children),支持中间件类型推断\n */\nexport function defineRoute<\n const TMiddleware extends readonly AnyMiddleware[],\n TPath extends string = string\n>(config: {\n readonly path: TPath;\n readonly name?: string;\n readonly description?: string;\n readonly middleware?: TMiddleware;\n readonly children: ReadonlyArray<RouteConfigResult>;\n}): NestedRouteConfig<TPath, TMiddleware>;\n\n/**\n * defineRoute 实现\n */\nexport function defineRoute(config: {\n readonly method?: HTTPMethod;\n readonly path: string;\n readonly name?: string;\n readonly description?: string;\n readonly schema?: RouteSchema;\n readonly context?: object;\n readonly handler?: (ctx: HandlerContext<RouteSchema>) => unknown | Promise<unknown>;\n readonly middleware?: readonly AnyMiddleware[];\n readonly children?: ReadonlyArray<RouteConfigResult>;\n readonly docs?: {\n tags?: string[];\n security?: unknown[];\n responses?: Record<string, unknown>;\n };\n}): RouteConfigResult {\n return config as RouteConfigResult;\n}\n\n// ============= withContext 工厂函数 =============\n\n/**\n * 创建带预设上下文类型的路由定义器\n *\n * 用于父级中间件注入上下文的场景,定义一次,多处复用\n *\n * @example\n * ```typescript\n * // 1. 在 middleware/index.ts 中定义\n * export const defineAuthRoute = withContext<{ userInfo: UserInfo }>()\n *\n * // 2. 在路由文件中使用\n * defineAuthRoute({\n * method: 'GET',\n * path: '/profile',\n * handler: ({ userInfo }) => {\n * // userInfo 自动有类型!\n * return { id: userInfo.id }\n * }\n * })\n * ```\n */\nexport function withContext<TContext extends object>() {\n return <\n const TSchema extends RouteSchema,\n TReturn,\n const TMiddleware extends readonly AnyMiddleware[],\n TMethod extends HTTPMethod = HTTPMethod,\n TPath extends string = string\n >(config: {\n readonly method: TMethod;\n readonly path: TPath;\n readonly name?: string;\n readonly description?: string;\n readonly schema?: TSchema;\n readonly handler: (\n ctx: HandlerContextWithExtra<TSchema, TContext & MergeMiddlewareContexts<TMiddleware>>\n ) => TReturn | Promise<TReturn>;\n readonly middleware?: TMiddleware;\n readonly docs?: {\n tags?: string[];\n security?: unknown[];\n responses?: Record<string, unknown>;\n };\n }): LeafRouteConfig<TMethod, TPath, TSchema, TReturn, TMiddleware> => {\n return config as LeafRouteConfig<TMethod, TPath, TSchema, TReturn, TMiddleware>;\n };\n}\n\n// ============= 扁平化嵌套路由 =============\n\n/**\n * 递归扁平化路由,合并路径和中间件\n */\nfunction flattenRoutes(\n routes: ReadonlyArray<RouteConfigResult>,\n parentPath: string = \"\",\n parentMiddleware: readonly AnyMiddleware[] = []\n): ProcessedRoute[] {\n const result: ProcessedRoute[] = [];\n\n for (const route of routes) {\n const fullPath = parentPath + route.path;\n const mergedMiddleware = [...parentMiddleware, ...(route.middleware || [])];\n\n if (isLeafRoute(route)) {\n result.push({\n method: route.method,\n path: fullPath,\n name: route.name,\n description: route.description,\n schema: route.schema,\n handler: wrapHandler(route.schema, route.handler as (ctx: HandlerContext<RouteSchema>) => unknown),\n middleware: mergedMiddleware.length > 0 ? mergedMiddleware : undefined,\n docs: route.docs,\n });\n } else if (isNestedRoute(route)) {\n result.push(...flattenRoutes(route.children, fullPath, mergedMiddleware));\n }\n }\n\n return result;\n}\n\n// ============= defineRoutes 函数 =============\n\n/** 带原始类型信息的路由数组 */\nexport type RoutesWithSource<T extends readonly RouteConfigResult[]> = ProcessedRoute[] & { __source: T };\n\n/**\n * 定义路由数组,支持嵌套路由\n * \n * 使用 `const T` 泛型自动保留字面量类型,无需手动添加 `as const`\n * \n * @example\n * ```typescript\n * const routes = defineRoutes([\n * defineRoute({ method: 'GET', path: '/users', handler: ... }),\n * defineRoute({ method: 'POST', path: '/users', handler: ... }),\n * ])\n * \n * // 类型推断自动工作,无需 as const\n * type Api = InferEden<typeof routes>\n * ```\n */\nexport function defineRoutes<const T extends readonly RouteConfigResult[]>(\n routes: T\n): RoutesWithSource<T> {\n const processed = flattenRoutes(routes);\n // 附加原始类型信息(仅用于类型推断,运行时不使用)\n return Object.assign(processed, { __source: routes }) as RoutesWithSource<T>;\n}\n\n// ============= 用于 API Client 的类型推断 =============\n\n/** 可推断的路由类型(供 vafast-api-client 使用) */\nexport type InferableRoute<\n TMethod extends string = string,\n TPath extends string = string,\n TReturn = unknown,\n TSchema extends RouteSchema = RouteSchema\n> = {\n readonly method: TMethod;\n readonly path: TPath;\n readonly name?: string;\n readonly description?: string;\n readonly schema?: TSchema;\n readonly handler: {\n __returnType: TReturn;\n __schema: TSchema;\n };\n readonly middleware?: ReadonlyArray<AnyMiddleware>;\n};\n"],"mappings":"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"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { i as ProcessedRoute } from "./defineRoute-
|
|
1
|
+
import { i as ProcessedRoute } from "./defineRoute-FhAN4ivP.mjs";
|
|
2
2
|
import { r as NestedComponentRoute, t as ComponentRoute } from "./component-route-Di7R40-2.mjs";
|
|
3
|
-
import { t as Server } from "./server-
|
|
3
|
+
import { t as Server } from "./server-8X5Hgu7h.mjs";
|
|
4
4
|
import { t as ComponentServer } from "./component-server-fR4UV6Jq.mjs";
|
|
5
5
|
|
|
6
6
|
//#region src/server/server-factory.d.ts
|
|
@@ -45,4 +45,4 @@ declare class ServerFactory {
|
|
|
45
45
|
}
|
|
46
46
|
//#endregion
|
|
47
47
|
export { ServerFactory as t };
|
|
48
|
-
//# sourceMappingURL=index-
|
|
48
|
+
//# sourceMappingURL=index-XcXGKQqt.d.mts.map
|
package/dist/index.d.mts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { a as RouteSchema, c as
|
|
1
|
+
import { a as RouteSchema, c as defineMiddleware, d as withContext, i as ProcessedRoute, l as defineRoute, n as InferableRoute, o as RoutesWithSource, r as LeafRouteConfig, s as TypedMiddleware, t as HandlerContext, u as defineRoutes } from "./defineRoute-FhAN4ivP.mjs";
|
|
2
2
|
import { i as ResponseBody, n as Method, r as Middleware, t as Handler } from "./types-B8Z3cMtZ.mjs";
|
|
3
3
|
import { n as FlattenedComponentRoute, r as NestedComponentRoute, t as ComponentRoute } from "./component-route-Di7R40-2.mjs";
|
|
4
4
|
import { t as BaseServer } from "./base-server-Contwrlf.mjs";
|
|
5
|
-
import { t as Server } from "./server-
|
|
5
|
+
import { t as Server } from "./server-8X5Hgu7h.mjs";
|
|
6
6
|
import { t as DependencyManager } from "./dependency-manager-DIN9X0Gj.mjs";
|
|
7
7
|
import { t as ComponentServer } from "./component-server-fR4UV6Jq.mjs";
|
|
8
|
-
import { t as ServerFactory } from "./index-
|
|
8
|
+
import { t as ServerFactory } from "./index-XcXGKQqt.mjs";
|
|
9
9
|
import { n as composeMiddleware, t as VafastError } from "./middleware-BR-R4p0M.mjs";
|
|
10
10
|
import { a as parseBody, c as parseCookiesFast, d as parseHeaders, f as parseQuery, i as getHeader, p as parseQueryFast, r as getCookie, s as parseCookies } from "./parsers-8hIAx0OV.mjs";
|
|
11
11
|
import { c as text, i as json, n as err, o as redirect, r as html, s as stream, t as empty } from "./response-BNLzz4Tq.mjs";
|
|
@@ -14,12 +14,12 @@ import { n as base64urlEncode, t as base64urlDecode } from "./base64url-DNUGwekK
|
|
|
14
14
|
import { t as HtmlRenderer } from "./html-renderer-DhQxRuyi.mjs";
|
|
15
15
|
import { a as ValidationError, c as getValidatorCacheStats, d as validateFast, f as validateSchema, l as precompileSchemas, o as ValidationResult, p as validateSchemaOrThrow, s as createValidator, t as SchemaConfig, u as validateAllSchemas } from "./validators-BFC6S_fr.mjs";
|
|
16
16
|
import { i as registerFormats, n as hasFormat, r as registerFormat, t as Patterns } from "./formats-DDDSFWP0.mjs";
|
|
17
|
-
import { a as createSSEHandler, i as SSEMarker, r as SSEHandler, t as SSEEvent } from "./sse-
|
|
18
|
-
import { a as getAllRoutes, c as getRoutesByMethod, i as filterRoutes, n as RouteRegistry, o as getRoute, r as createRouteRegistry, s as getRouteRegistry, t as RouteMeta } from "./route-registry-
|
|
17
|
+
import { a as createSSEHandler, i as SSEMarker, r as SSEHandler, t as SSEEvent } from "./sse-DyI21Jqk.mjs";
|
|
18
|
+
import { a as getAllRoutes, c as getRoutesByMethod, i as filterRoutes, n as RouteRegistry, o as getRoute, r as createRouteRegistry, s as getRouteRegistry, t as RouteMeta } from "./route-registry-YIco7opr.mjs";
|
|
19
19
|
import { n as getApiSpec, t as generateAITools } from "./contract-BL3JflJ7.mjs";
|
|
20
20
|
import "./utils/index.mjs";
|
|
21
21
|
import { normalizePath } from "./router.mjs";
|
|
22
22
|
import { i as ServeResult, o as serve, r as ServeOptions, t as FetchHandler } from "./serve-DlzxgOhz.mjs";
|
|
23
23
|
import "./serve.mjs";
|
|
24
24
|
import { FormatRegistry, Type } from "@sinclair/typebox";
|
|
25
|
-
export { BaseServer, ComponentRoute, ComponentServer, DependencyManager, type FetchHandler, FlattenedComponentRoute, FormatRegistry, Handler, HandlerContext, HtmlRenderer, InferableRoute, LeafRouteConfig, Method, Middleware, NestedComponentRoute, Patterns, ProcessedRoute, ResponseBody, RouteMeta, RouteRegistry, RouteSchema, SSEEvent, SSEHandler, SSEMarker, SchemaConfig, type ServeOptions, type ServeResult, Server, ServerFactory, Type, TypedMiddleware, VafastError, ValidationError, ValidationResult, base64urlDecode, base64urlEncode, composeMiddleware, createRouteRegistry, createSSEHandler, createValidator, defineMiddleware, defineRoute, defineRoutes, empty, err, filterRoutes, generateAITools, getAllRoutes, getApiSpec, getCookie, getHeader, getRoute, getRouteRegistry, getRoutesByMethod, getValidatorCacheStats, goAwait, hasFormat, html, json, normalizePath, parseBody, parseCookies, parseCookiesFast, parseHeaders, parseQuery, parseQueryFast, precompileSchemas, redirect, registerFormat, registerFormats, serve, stream, text, validateAllSchemas, validateFast, validateSchema, validateSchemaOrThrow, withContext };
|
|
25
|
+
export { BaseServer, ComponentRoute, ComponentServer, DependencyManager, type FetchHandler, FlattenedComponentRoute, FormatRegistry, Handler, HandlerContext, HtmlRenderer, InferableRoute, LeafRouteConfig, Method, Middleware, NestedComponentRoute, Patterns, ProcessedRoute, ResponseBody, RouteMeta, RouteRegistry, RouteSchema, RoutesWithSource, SSEEvent, SSEHandler, SSEMarker, SchemaConfig, type ServeOptions, type ServeResult, Server, ServerFactory, Type, TypedMiddleware, VafastError, ValidationError, ValidationResult, base64urlDecode, base64urlEncode, composeMiddleware, createRouteRegistry, createSSEHandler, createValidator, defineMiddleware, defineRoute, defineRoutes, empty, err, filterRoutes, generateAITools, getAllRoutes, getApiSpec, getCookie, getHeader, getRoute, getRouteRegistry, getRoutesByMethod, getValidatorCacheStats, goAwait, hasFormat, html, json, normalizePath, parseBody, parseCookies, parseCookiesFast, parseHeaders, parseQuery, parseQueryFast, precompileSchemas, redirect, registerFormat, registerFormats, serve, stream, text, validateAllSchemas, validateFast, validateSchema, validateSchemaOrThrow, withContext };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import "../defineRoute-
|
|
2
|
-
import "../server-
|
|
3
|
-
import "../index-
|
|
1
|
+
import "../defineRoute-FhAN4ivP.mjs";
|
|
2
|
+
import "../server-8X5Hgu7h.mjs";
|
|
3
|
+
import "../index-XcXGKQqt.mjs";
|
|
4
4
|
import { MemoryInfo, MonitoredServer, MonitoringConfig, MonitoringMetrics, MonitoringStatus, PathStats, StatusCodeDistribution, TimeWindowStats, createMonitoredServer, withMonitoring } from "./native-monitor.mjs";
|
|
5
5
|
export { type MemoryInfo, type MonitoredServer, type MonitoringConfig, type MonitoringMetrics, type MonitoringStatus, type PathStats, type StatusCodeDistribution, type TimeWindowStats, createMonitoredServer, withMonitoring };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import "../defineRoute-
|
|
2
|
-
import { t as Server } from "../server-
|
|
3
|
-
import "../index-
|
|
1
|
+
import "../defineRoute-FhAN4ivP.mjs";
|
|
2
|
+
import { t as Server } from "../server-8X5Hgu7h.mjs";
|
|
3
|
+
import "../index-XcXGKQqt.mjs";
|
|
4
4
|
|
|
5
5
|
//#region src/monitoring/native-monitor.d.ts
|
|
6
6
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as RouteSchema, i as ProcessedRoute } from "./defineRoute-
|
|
1
|
+
import { a as RouteSchema, i as ProcessedRoute } from "./defineRoute-FhAN4ivP.mjs";
|
|
2
2
|
import { n as Method } from "./types-B8Z3cMtZ.mjs";
|
|
3
3
|
|
|
4
4
|
//#region src/utils/route-registry.d.ts
|
|
@@ -187,4 +187,4 @@ declare function filterRoutes<K extends string>(field: K): (RouteMeta & Record<K
|
|
|
187
187
|
declare function getRoutesByMethod(method: string): RouteMeta[];
|
|
188
188
|
//#endregion
|
|
189
189
|
export { getAllRoutes as a, getRoutesByMethod as c, filterRoutes as i, setGlobalRegistry as l, RouteRegistry as n, getRoute as o, createRouteRegistry as r, getRouteRegistry as s, RouteMeta as t };
|
|
190
|
-
//# sourceMappingURL=route-registry-
|
|
190
|
+
//# sourceMappingURL=route-registry-YIco7opr.d.mts.map
|
package/dist/server/index.d.mts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import "../defineRoute-
|
|
1
|
+
import "../defineRoute-FhAN4ivP.mjs";
|
|
2
2
|
import { t as BaseServer } from "../base-server-Contwrlf.mjs";
|
|
3
|
-
import { t as Server } from "../server-
|
|
3
|
+
import { t as Server } from "../server-8X5Hgu7h.mjs";
|
|
4
4
|
import { t as ComponentServer } from "../component-server-fR4UV6Jq.mjs";
|
|
5
|
-
import { t as ServerFactory } from "../index-
|
|
5
|
+
import { t as ServerFactory } from "../index-XcXGKQqt.mjs";
|
|
6
6
|
export { BaseServer, ComponentServer, Server, ServerFactory };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import "../defineRoute-
|
|
2
|
-
import "../server-
|
|
3
|
-
import { t as ServerFactory } from "../index-
|
|
1
|
+
import "../defineRoute-FhAN4ivP.mjs";
|
|
2
|
+
import "../server-8X5Hgu7h.mjs";
|
|
3
|
+
import { t as ServerFactory } from "../index-XcXGKQqt.mjs";
|
|
4
4
|
export { ServerFactory };
|
package/dist/server/server.d.mts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import "../defineRoute-
|
|
2
|
-
import { t as Server } from "../server-
|
|
1
|
+
import "../defineRoute-FhAN4ivP.mjs";
|
|
2
|
+
import { t as Server } from "../server-8X5Hgu7h.mjs";
|
|
3
3
|
export { Server };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { i as ProcessedRoute } from "./defineRoute-
|
|
1
|
+
import { i as ProcessedRoute } from "./defineRoute-FhAN4ivP.mjs";
|
|
2
2
|
import { n as Method } from "./types-B8Z3cMtZ.mjs";
|
|
3
3
|
import { t as BaseServer } from "./base-server-Contwrlf.mjs";
|
|
4
4
|
|
|
@@ -45,4 +45,4 @@ declare class Server extends BaseServer {
|
|
|
45
45
|
}
|
|
46
46
|
//#endregion
|
|
47
47
|
export { Server as t };
|
|
48
|
-
//# sourceMappingURL=server-
|
|
48
|
+
//# sourceMappingURL=server-8X5Hgu7h.d.mts.map
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as RouteSchema, t as HandlerContext } from "./defineRoute-
|
|
1
|
+
import { a as RouteSchema, t as HandlerContext } from "./defineRoute-FhAN4ivP.mjs";
|
|
2
2
|
|
|
3
3
|
//#region src/utils/sse.d.ts
|
|
4
4
|
|
|
@@ -60,4 +60,4 @@ declare function createSSEHandler<const T extends RouteSchema>(schema: T, genera
|
|
|
60
60
|
declare function createSSEHandler(generator: SSEGenerator<RouteSchema>): SSEHandler<RouteSchema>;
|
|
61
61
|
//#endregion
|
|
62
62
|
export { createSSEHandler as a, SSEMarker as i, SSEGenerator as n, SSEHandler as r, SSEEvent as t };
|
|
63
|
-
//# sourceMappingURL=sse-
|
|
63
|
+
//# sourceMappingURL=sse-DyI21Jqk.d.mts.map
|
package/dist/utils/index.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import "../defineRoute-
|
|
1
|
+
import "../defineRoute-FhAN4ivP.mjs";
|
|
2
2
|
import { t as DependencyManager } from "../dependency-manager-DIN9X0Gj.mjs";
|
|
3
3
|
import { a as parseBody, c as parseCookiesFast, d as parseHeaders, f as parseQuery, i as getHeader, p as parseQueryFast, r as getCookie, s as parseCookies } from "../parsers-8hIAx0OV.mjs";
|
|
4
4
|
import { c as text, i as json, n as err, o as redirect, r as html, s as stream, t as empty } from "../response-BNLzz4Tq.mjs";
|
|
@@ -7,7 +7,7 @@ import { n as base64urlEncode, t as base64urlDecode } from "../base64url-DNUGwek
|
|
|
7
7
|
import { t as HtmlRenderer } from "../html-renderer-DhQxRuyi.mjs";
|
|
8
8
|
import { a as ValidationError, c as getValidatorCacheStats, d as validateFast, f as validateSchema, l as precompileSchemas, o as ValidationResult, p as validateSchemaOrThrow, s as createValidator, t as SchemaConfig, u as validateAllSchemas } from "../validators-BFC6S_fr.mjs";
|
|
9
9
|
import { i as registerFormats, n as hasFormat, r as registerFormat, t as Patterns } from "../formats-DDDSFWP0.mjs";
|
|
10
|
-
import { a as createSSEHandler, i as SSEMarker, r as SSEHandler, t as SSEEvent } from "../sse-
|
|
11
|
-
import { a as getAllRoutes, c as getRoutesByMethod, i as filterRoutes, n as RouteRegistry, o as getRoute, r as createRouteRegistry, s as getRouteRegistry, t as RouteMeta } from "../route-registry-
|
|
10
|
+
import { a as createSSEHandler, i as SSEMarker, r as SSEHandler, t as SSEEvent } from "../sse-DyI21Jqk.mjs";
|
|
11
|
+
import { a as getAllRoutes, c as getRoutesByMethod, i as filterRoutes, n as RouteRegistry, o as getRoute, r as createRouteRegistry, s as getRouteRegistry, t as RouteMeta } from "../route-registry-YIco7opr.mjs";
|
|
12
12
|
import { n as getApiSpec, t as generateAITools } from "../contract-BL3JflJ7.mjs";
|
|
13
13
|
export { DependencyManager, HtmlRenderer, Patterns, type RouteMeta, RouteRegistry, type SSEEvent, type SSEHandler, type SSEMarker, type SchemaConfig, type ValidationError, type ValidationResult, base64urlDecode, base64urlEncode, createRouteRegistry, createSSEHandler, createValidator, empty, err, filterRoutes, generateAITools, getAllRoutes, getApiSpec, getCookie, getHeader, getRoute, getRouteRegistry, getRoutesByMethod, getValidatorCacheStats, goAwait, hasFormat, html, json, parseBody, parseCookies, parseCookiesFast, parseHeaders, parseQuery, parseQueryFast, precompileSchemas, redirect, registerFormat, registerFormats, stream, text, validateAllSchemas, validateFast, validateSchema, validateSchemaOrThrow };
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import "../defineRoute-
|
|
2
|
-
import { a as getAllRoutes, c as getRoutesByMethod, i as filterRoutes, l as setGlobalRegistry, n as RouteRegistry, o as getRoute, r as createRouteRegistry, s as getRouteRegistry, t as RouteMeta } from "../route-registry-
|
|
1
|
+
import "../defineRoute-FhAN4ivP.mjs";
|
|
2
|
+
import { a as getAllRoutes, c as getRoutesByMethod, i as filterRoutes, l as setGlobalRegistry, n as RouteRegistry, o as getRoute, r as createRouteRegistry, s as getRouteRegistry, t as RouteMeta } from "../route-registry-YIco7opr.mjs";
|
|
3
3
|
export { RouteMeta, RouteRegistry, createRouteRegistry, filterRoutes, getAllRoutes, getRoute, getRouteRegistry, getRoutesByMethod, setGlobalRegistry };
|
package/dist/utils/sse.d.mts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import "../defineRoute-
|
|
2
|
-
import { a as createSSEHandler, i as SSEMarker, n as SSEGenerator, r as SSEHandler, t as SSEEvent } from "../sse-
|
|
1
|
+
import "../defineRoute-FhAN4ivP.mjs";
|
|
2
|
+
import { a as createSSEHandler, i as SSEMarker, n as SSEGenerator, r as SSEHandler, t as SSEEvent } from "../sse-DyI21Jqk.mjs";
|
|
3
3
|
export { SSEEvent, SSEGenerator, SSEHandler, SSEMarker, createSSEHandler };
|