vafast 0.4.24 → 0.5.1
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 +132 -1
- package/dist/{base-server-B7MYJNsl.mjs → base-server-CE0mfqPY.mjs} +11 -23
- package/dist/base-server-CE0mfqPY.mjs.map +1 -0
- package/dist/{base-server-DLxtulAO.d.mts → base-server-Contwrlf.d.mts} +5 -6
- package/dist/{base64url-C2zopQdH.mjs → base64url-Cc77f1ms.mjs} +1 -1
- package/dist/{base64url-C2zopQdH.mjs.map → base64url-Cc77f1ms.mjs.map} +1 -1
- package/dist/{base64url-Dwi2Afhc.d.mts → base64url-DNUGwekK.d.mts} +1 -1
- package/dist/{component-route-nrrO0iSI.d.mts → component-route-Di7R40-2.d.mts} +2 -2
- package/dist/{component-router-RwPL20vN.mjs → component-router-B6M5XDnp.mjs} +1 -1
- package/dist/{component-router-RwPL20vN.mjs.map → component-router-B6M5XDnp.mjs.map} +1 -1
- package/dist/{component-server-DomPJ_7S.mjs → component-server-Cya46YN3.mjs} +7 -7
- package/dist/component-server-Cya46YN3.mjs.map +1 -0
- package/dist/{component-server-JqpDC7wy.d.mts → component-server-fR4UV6Jq.d.mts} +4 -4
- package/dist/contract-BKqc9fFH.mjs +114 -0
- package/dist/contract-BKqc9fFH.mjs.map +1 -0
- package/dist/contract-BL3JflJ7.d.mts +71 -0
- package/dist/defineRoute-BjLBWeSj.d.mts +152 -0
- package/dist/defineRoute.d.mts +2 -60
- package/dist/defineRoute.mjs +135 -26
- package/dist/defineRoute.mjs.map +1 -1
- package/dist/{dependency-manager-DCmh7xFc.mjs → dependency-manager-CGMZJTer.mjs} +1 -1
- package/dist/{dependency-manager-DCmh7xFc.mjs.map → dependency-manager-CGMZJTer.mjs.map} +1 -1
- package/dist/{dependency-manager-C3_7ic4h.d.mts → dependency-manager-DIN9X0Gj.d.mts} +1 -1
- package/dist/{formats-Dk-DSBY4.d.mts → formats-DDDSFWP0.d.mts} +1 -1
- package/dist/{go-await-C4ZdEUwY.mjs → go-await-B1U27PgB.mjs} +1 -1
- package/dist/{go-await-C4ZdEUwY.mjs.map → go-await-B1U27PgB.mjs.map} +1 -1
- package/dist/{go-await-DL1A_-4X.d.mts → go-await-DPtVBug4.d.mts} +1 -1
- package/dist/{html-renderer-DTtJ_Yic.mjs → html-renderer-D1zzDVQM.mjs} +1 -1
- package/dist/{html-renderer-DTtJ_Yic.mjs.map → html-renderer-D1zzDVQM.mjs.map} +1 -1
- package/dist/{html-renderer-CfKK2BrP.d.mts → html-renderer-DhQxRuyi.d.mts} +1 -1
- package/dist/{index-Bj3SWrMU.d.mts → index-DCloGU_g.d.mts} +6 -6
- package/dist/index.d.mts +22 -27
- package/dist/index.mjs +19 -22
- package/dist/index.mjs.map +1 -1
- package/dist/middleware/component-router.d.mts +1 -1
- package/dist/middleware/component-router.mjs +1 -1
- package/dist/{middleware-KXEoefLX.d.mts → middleware-BR-R4p0M.d.mts} +2 -2
- package/dist/middleware.d.mts +1 -3
- package/dist/middleware.mjs +1 -1
- package/dist/monitoring/index.d.mts +3 -6
- package/dist/monitoring/native-monitor.d.mts +3 -6
- package/dist/node-server/index.d.mts +1 -1
- package/dist/node-server/index.mjs +3 -3
- package/dist/node-server/request.mjs +1 -1
- package/dist/node-server/response.mjs +1 -1
- package/dist/node-server/serve.d.mts +1 -1
- package/dist/node-server/serve.mjs +2 -2
- package/dist/{parsers-BerGr2_q.d.mts → parsers-8hIAx0OV.d.mts} +1 -1
- package/dist/{parsers-DpH_mD0H.mjs → parsers-BrG_mRLq.mjs} +1 -1
- package/dist/{parsers-DpH_mD0H.mjs.map → parsers-BrG_mRLq.mjs.map} +1 -1
- package/dist/{path-matcher-CGczAIl_.mjs → path-matcher-Dp-DRUV9.mjs} +1 -1
- package/dist/{path-matcher-CGczAIl_.mjs.map → path-matcher-Dp-DRUV9.mjs.map} +1 -1
- package/dist/{radix-tree-qqSjnVXF.mjs → radix-tree-CccjvyTP.mjs} +1 -1
- package/dist/{radix-tree-qqSjnVXF.mjs.map → radix-tree-CccjvyTP.mjs.map} +1 -1
- package/dist/{request-B-Nct5f7.mjs → request-CppAFyqX.mjs} +1 -1
- package/dist/{request-B-Nct5f7.mjs.map → request-CppAFyqX.mjs.map} +1 -1
- package/dist/{response-BMfdEcTm.d.mts → response-BNLzz4Tq.d.mts} +2 -2
- package/dist/{middleware-CewKbtb4.mjs → response-CQ1IgWei.mjs} +55 -55
- package/dist/response-CQ1IgWei.mjs.map +1 -0
- package/dist/{response-BnkYA4pj.mjs → response-DNdvtn-K.mjs} +1 -1
- package/dist/{response-BnkYA4pj.mjs.map → response-DNdvtn-K.mjs.map} +1 -1
- package/dist/{route-registry-emTmRrWQ.mjs → route-registry-BzExlM2t.mjs} +23 -9
- package/dist/route-registry-BzExlM2t.mjs.map +1 -0
- package/dist/{route-registry-CmABJA2V.d.mts → route-registry-CDBB0GI1.d.mts} +21 -7
- package/dist/router/index.d.mts +2 -4
- package/dist/router/index.mjs +3 -3
- package/dist/router/radix-tree.d.mts +1 -3
- package/dist/router/radix-tree.mjs +1 -1
- package/dist/router.d.mts +4 -7
- package/dist/router.mjs +27 -2
- package/dist/router.mjs.map +1 -0
- package/dist/{serve-48LEIkPa.mjs → serve-Df3HzwZM.mjs} +3 -3
- package/dist/{serve-48LEIkPa.mjs.map → serve-Df3HzwZM.mjs.map} +1 -1
- package/dist/{serve-AG80VaIr.d.mts → serve-DlzxgOhz.d.mts} +1 -1
- package/dist/serve.d.mts +1 -1
- package/dist/serve.mjs +2 -2
- package/dist/server/base-server.d.mts +1 -3
- package/dist/server/base-server.mjs +1 -1
- package/dist/server/component-server.d.mts +1 -4
- package/dist/server/component-server.mjs +2 -2
- package/dist/server/index.d.mts +5 -6
- package/dist/server/index.mjs +6 -6
- package/dist/server/server-factory.d.mts +3 -6
- package/dist/server/server-factory.mjs +5 -5
- package/dist/server/server.d.mts +2 -4
- package/dist/server/server.mjs +2 -2
- package/dist/{server-OUUug5DR.mjs → server-Bm0BGm01.mjs} +19 -30
- package/dist/server-Bm0BGm01.mjs.map +1 -0
- package/dist/server-CW1cdqGD.d.mts +48 -0
- package/dist/{server-BUom6ISO.mjs → server-CZLmrJSk.mjs} +3 -3
- package/dist/server-CZLmrJSk.mjs.map +1 -0
- package/dist/{sse-BgLhEo43.d.mts → sse-5HykEmSm.d.mts} +2 -4
- package/dist/{sse-CBl-szg1.mjs → sse-CWNz0ky7.mjs} +3 -4
- package/dist/sse-CWNz0ky7.mjs.map +1 -0
- package/dist/types/component-route.d.mts +1 -1
- package/dist/types/index.d.mts +3 -6
- package/dist/types/index.mjs +1 -3
- package/dist/types/types.d.mts +2 -2
- package/dist/types-B8Z3cMtZ.d.mts +12 -0
- package/dist/utils/base64url.d.mts +1 -1
- package/dist/utils/base64url.mjs +1 -1
- package/dist/utils/contract.d.mts +2 -0
- package/dist/utils/contract.mjs +3 -0
- package/dist/utils/dependency-manager.d.mts +1 -1
- package/dist/utils/dependency-manager.mjs +1 -1
- package/dist/utils/formats.d.mts +1 -1
- package/dist/utils/go-await.d.mts +1 -1
- package/dist/utils/go-await.mjs +1 -1
- package/dist/utils/handle.d.mts +13 -2
- package/dist/utils/handle.mjs +19 -3
- package/dist/utils/handle.mjs.map +1 -0
- package/dist/utils/html-renderer.d.mts +1 -1
- package/dist/utils/html-renderer.mjs +1 -1
- package/dist/utils/index.d.mts +13 -17
- package/dist/utils/index.mjs +11 -13
- package/dist/utils/parsers.d.mts +1 -1
- package/dist/utils/parsers.mjs +1 -1
- package/dist/utils/path-matcher.mjs +1 -1
- package/dist/utils/response.d.mts +1 -4
- package/dist/utils/response.mjs +1 -1
- package/dist/utils/route-registry.d.mts +3 -4
- package/dist/utils/route-registry.mjs +2 -2
- package/dist/utils/sse.d.mts +2 -2
- package/dist/utils/sse.mjs +3 -3
- package/dist/utils/validators/validators.d.mts +1 -1
- package/dist/utils/validators/validators.mjs +1 -1
- package/dist/{validators-Ch71zkT8.d.mts → validators-BFC6S_fr.d.mts} +1 -1
- package/dist/{validators-DBkyw6BG.mjs → validators-CkfvNBbK.mjs} +1 -1
- package/dist/{validators-DBkyw6BG.mjs.map → validators-CkfvNBbK.mjs.map} +1 -1
- package/package.json +1 -1
- package/dist/base-server-B7MYJNsl.mjs.map +0 -1
- package/dist/component-server-DomPJ_7S.mjs.map +0 -1
- package/dist/create-handler-DKw-sQOV.d.mts +0 -87
- package/dist/create-handler-RconAcAB.mjs +0 -165
- package/dist/create-handler-RconAcAB.mjs.map +0 -1
- package/dist/handle-BhR3oyky.d.mts +0 -15
- package/dist/handle-BxJwSvV0.mjs +0 -30
- package/dist/handle-BxJwSvV0.mjs.map +0 -1
- package/dist/middleware-CewKbtb4.mjs.map +0 -1
- package/dist/request-validator-Bz9X48FX.mjs +0 -77
- package/dist/request-validator-Bz9X48FX.mjs.map +0 -1
- package/dist/request-validator-Coo8dI-p.d.mts +0 -67
- package/dist/route-6A7umH7b.d.mts +0 -44
- package/dist/route-Ds53PR4M.mjs +0 -11
- package/dist/route-Ds53PR4M.mjs.map +0 -1
- package/dist/route-registry-emTmRrWQ.mjs.map +0 -1
- package/dist/router-CgYfGB4J.mjs +0 -98
- package/dist/router-CgYfGB4J.mjs.map +0 -1
- package/dist/schema-1fwiv7cm.mjs +0 -1
- package/dist/schema-B6DFN5c2.d.mts +0 -81
- package/dist/server-BUom6ISO.mjs.map +0 -1
- package/dist/server-C2RPKSvC.d.mts +0 -51
- package/dist/server-OUUug5DR.mjs.map +0 -1
- package/dist/sse-CBl-szg1.mjs.map +0 -1
- package/dist/types/route.d.mts +0 -2
- package/dist/types/route.mjs +0 -3
- package/dist/types/schema.d.mts +0 -2
- package/dist/types/schema.mjs +0 -1
- package/dist/types-D1PUFkda.d.mts +0 -45
- package/dist/utils/create-handler.d.mts +0 -3
- package/dist/utils/create-handler.mjs +0 -6
- package/dist/utils/request-validator.d.mts +0 -3
- package/dist/utils/request-validator.mjs +0 -5
- /package/dist/{component-route-DNgAj6VC.mjs → component-route-BiUHBq7a.mjs} +0 -0
- /package/dist/{index-CREkvfw9.d.mts → index-Dflz2i1X.d.mts} +0 -0
package/README.md
CHANGED
|
@@ -567,7 +567,7 @@ const permissionRoutes = registry.filter('permission');
|
|
|
567
567
|
const categories = registry.getCategories(); // ['auth', 'users']
|
|
568
568
|
```
|
|
569
569
|
|
|
570
|
-
|
|
570
|
+
**Registry 实例方法:**
|
|
571
571
|
|
|
572
572
|
| 方法 | 说明 |
|
|
573
573
|
|------|------|
|
|
@@ -582,6 +582,137 @@ const categories = registry.getCategories(); // ['auth', 'users']
|
|
|
582
582
|
| `map(callback)` | 映射所有路由 |
|
|
583
583
|
| `size` | 路由数量 |
|
|
584
584
|
|
|
585
|
+
**全局便捷函数:**
|
|
586
|
+
|
|
587
|
+
```typescript
|
|
588
|
+
import {
|
|
589
|
+
getRoute,
|
|
590
|
+
getAllRoutes,
|
|
591
|
+
filterRoutes,
|
|
592
|
+
getRoutesByMethod,
|
|
593
|
+
} from 'vafast'
|
|
594
|
+
|
|
595
|
+
// 获取单个路由
|
|
596
|
+
const route = getRoute('POST', '/users')
|
|
597
|
+
|
|
598
|
+
// 获取所有路由
|
|
599
|
+
const allRoutes = getAllRoutes()
|
|
600
|
+
|
|
601
|
+
// 按字段筛选
|
|
602
|
+
const webhookRoutes = filterRoutes('webhook')
|
|
603
|
+
|
|
604
|
+
// 按 HTTP 方法获取
|
|
605
|
+
const getRoutes = getRoutesByMethod('GET')
|
|
606
|
+
const postRoutes = getRoutesByMethod('POST')
|
|
607
|
+
|
|
608
|
+
// 按路径前缀筛选(自己 filter)
|
|
609
|
+
const authRoutes = getAllRoutes().filter(r => r.path.startsWith('/auth'))
|
|
610
|
+
```
|
|
611
|
+
|
|
612
|
+
### API Spec 生成
|
|
613
|
+
|
|
614
|
+
Vafast 提供 `getApiSpec` 用于生成 API 规范,支持跨仓库类型同步和 AI 工具函数生成:
|
|
615
|
+
|
|
616
|
+
```typescript
|
|
617
|
+
import { Server, defineRoutes, getApiSpec } from 'vafast';
|
|
618
|
+
|
|
619
|
+
const routes = defineRoutes([
|
|
620
|
+
{ method: 'GET', path: '/users', handler: getUsers },
|
|
621
|
+
{ method: 'POST', path: '/users', handler: createUser },
|
|
622
|
+
]);
|
|
623
|
+
|
|
624
|
+
// 添加 API Spec 接口
|
|
625
|
+
const allRoutes = [
|
|
626
|
+
...routes,
|
|
627
|
+
{ method: 'GET', path: '/api-spec', handler: getApiSpec } // 直接作为 handler
|
|
628
|
+
];
|
|
629
|
+
|
|
630
|
+
const server = new Server(allRoutes);
|
|
631
|
+
```
|
|
632
|
+
|
|
633
|
+
**三种使用方式:**
|
|
634
|
+
|
|
635
|
+
```typescript
|
|
636
|
+
// 方式 1:直接作为 handler(推荐,最简洁)
|
|
637
|
+
{ method: 'GET', path: '/api-spec', handler: getApiSpec }
|
|
638
|
+
|
|
639
|
+
// 方式 2:显式传参(只暴露公开 API)
|
|
640
|
+
{ handler: () => getApiSpec(publicRoutes) }
|
|
641
|
+
|
|
642
|
+
// 方式 3:本地使用(CLI、测试)
|
|
643
|
+
const spec = getApiSpec()
|
|
644
|
+
```
|
|
645
|
+
|
|
646
|
+
**返回格式:**
|
|
647
|
+
|
|
648
|
+
```json
|
|
649
|
+
{
|
|
650
|
+
"version": "1.0.0",
|
|
651
|
+
"generatedAt": "2024-01-01T00:00:00.000Z",
|
|
652
|
+
"routes": [
|
|
653
|
+
{
|
|
654
|
+
"method": "GET",
|
|
655
|
+
"path": "/users",
|
|
656
|
+
"name": "get_users",
|
|
657
|
+
"description": "获取用户列表",
|
|
658
|
+
"schema": { "query": { "type": "object", ... } }
|
|
659
|
+
}
|
|
660
|
+
]
|
|
661
|
+
}
|
|
662
|
+
```
|
|
663
|
+
|
|
664
|
+
### AI 工具函数生成
|
|
665
|
+
|
|
666
|
+
`generateAITools` 可将路由转换为 OpenAI Function Calling / Claude Tools 格式:
|
|
667
|
+
|
|
668
|
+
```typescript
|
|
669
|
+
import { generateAITools } from 'vafast';
|
|
670
|
+
|
|
671
|
+
const tools = generateAITools(routes);
|
|
672
|
+
// [
|
|
673
|
+
// { name: 'get_users', description: '获取用户列表', parameters: {...} },
|
|
674
|
+
// { name: 'create_user', description: '创建用户', parameters: {...} }
|
|
675
|
+
// ]
|
|
676
|
+
|
|
677
|
+
// 直接用于 AI 调用
|
|
678
|
+
const response = await openai.chat.completions.create({
|
|
679
|
+
model: 'gpt-4',
|
|
680
|
+
messages: [...],
|
|
681
|
+
tools: tools.map(t => ({ type: 'function', function: t }))
|
|
682
|
+
});
|
|
683
|
+
```
|
|
684
|
+
|
|
685
|
+
### 跨仓库类型同步 (CLI)
|
|
686
|
+
|
|
687
|
+
对于多仓库项目,使用 `@vafast/cli` 从远程服务同步类型:
|
|
688
|
+
|
|
689
|
+
```bash
|
|
690
|
+
# 安装 CLI
|
|
691
|
+
npm install -g @vafast/cli
|
|
692
|
+
|
|
693
|
+
# 从服务端同步类型
|
|
694
|
+
vafast sync --url http://api.example.com --out src/api.generated.ts
|
|
695
|
+
```
|
|
696
|
+
|
|
697
|
+
**生成的类型文件:**
|
|
698
|
+
|
|
699
|
+
```typescript
|
|
700
|
+
// src/api.generated.ts
|
|
701
|
+
export interface Api {
|
|
702
|
+
users: {
|
|
703
|
+
get: { query: { page?: number } }
|
|
704
|
+
post: { body: { name: string; email: string } }
|
|
705
|
+
}
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
// 使用
|
|
709
|
+
import { eden } from '@vafast/api-client'
|
|
710
|
+
import type { Api } from './api.generated'
|
|
711
|
+
|
|
712
|
+
const api = eden<Api>('http://api.example.com')
|
|
713
|
+
const { data } = await api.users.get({ query: { page: 1 } })
|
|
714
|
+
```
|
|
715
|
+
|
|
585
716
|
## 📊 内置监控
|
|
586
717
|
|
|
587
718
|
零依赖的性能监控,一行代码启用:
|
|
@@ -9,26 +9,25 @@ var BaseServer = class {
|
|
|
9
9
|
this.globalMiddleware.push(mw);
|
|
10
10
|
}
|
|
11
11
|
/**
|
|
12
|
-
*
|
|
12
|
+
* 打印路由信息,用于调试
|
|
13
13
|
*/
|
|
14
|
-
|
|
15
|
-
console.log(`🚀
|
|
14
|
+
logRoutes(routes, type = "路由") {
|
|
15
|
+
console.log(`🚀 注册${type}:`);
|
|
16
16
|
for (const route of routes) {
|
|
17
17
|
const method = route.method || "GET";
|
|
18
|
-
const path = route.
|
|
18
|
+
const path = route.path;
|
|
19
19
|
console.log(` ${method} ${path}`);
|
|
20
|
-
if (route.
|
|
20
|
+
if (route.middleware && route.middleware.length > 0) console.log(` 中间件: ${route.middleware.length} 个`);
|
|
21
21
|
}
|
|
22
22
|
console.log("");
|
|
23
23
|
}
|
|
24
24
|
/**
|
|
25
25
|
* 检测路由冲突
|
|
26
|
-
* 检查是否有路径相同但方法不同的路由,以及潜在的路径冲突
|
|
27
26
|
*/
|
|
28
27
|
detectRouteConflicts(routes) {
|
|
29
28
|
const pathGroups = /* @__PURE__ */ new Map();
|
|
30
29
|
for (const route of routes) {
|
|
31
|
-
const path = route.
|
|
30
|
+
const path = route.path;
|
|
32
31
|
const method = route.method || "GET";
|
|
33
32
|
if (!pathGroups.has(path)) pathGroups.set(path, []);
|
|
34
33
|
pathGroups.get(path).push({
|
|
@@ -39,32 +38,21 @@ var BaseServer = class {
|
|
|
39
38
|
for (const [path, routeList] of pathGroups) if (routeList.length > 1) {
|
|
40
39
|
const methods = routeList.map((r) => r.method);
|
|
41
40
|
const uniqueMethods = [...new Set(methods)];
|
|
42
|
-
if (uniqueMethods.length === 1) {
|
|
43
|
-
console.warn(`⚠️ 路由冲突: ${uniqueMethods[0]} ${path} 定义了 ${routeList.length} 次`);
|
|
44
|
-
routeList.forEach((route, index) => {
|
|
45
|
-
console.warn(` ${index + 1}. ${route.method} ${path}`);
|
|
46
|
-
});
|
|
47
|
-
} else console.log(`ℹ️ 路径 ${path} 支持方法: ${uniqueMethods.join(", ")}`);
|
|
41
|
+
if (uniqueMethods.length === 1) console.warn(`⚠️ 路由冲突: ${uniqueMethods[0]} ${path} 定义了 ${routeList.length} 次`);
|
|
48
42
|
}
|
|
49
43
|
this.detectDynamicRouteConflicts(routes);
|
|
50
44
|
}
|
|
51
45
|
/**
|
|
52
|
-
*
|
|
46
|
+
* 检测动态路由冲突
|
|
53
47
|
*/
|
|
54
48
|
detectDynamicRouteConflicts(routes) {
|
|
55
49
|
const dynamicRoutes = routes.filter((r) => {
|
|
56
|
-
|
|
57
|
-
return path.includes(":") || path.includes("*");
|
|
50
|
+
return r.path.includes(":") || r.path.includes("*");
|
|
58
51
|
});
|
|
59
52
|
for (let i = 0; i < dynamicRoutes.length; i++) for (let j = i + 1; j < dynamicRoutes.length; j++) {
|
|
60
53
|
const route1 = dynamicRoutes[i];
|
|
61
54
|
const route2 = dynamicRoutes[j];
|
|
62
|
-
|
|
63
|
-
if (method1 === (route2.method || "GET")) {
|
|
64
|
-
const path1 = route1.fullPath || route1.path;
|
|
65
|
-
const path2 = route2.fullPath || route2.path;
|
|
66
|
-
if (this.pathsMayConflict(path1, path2)) console.warn(`⚠️ 潜在路由冲突: ${method1} ${path1} 可能与 ${path2} 冲突`);
|
|
67
|
-
}
|
|
55
|
+
if (route1.method === route2.method && this.pathsMayConflict(route1.path, route2.path)) console.warn(`⚠️ 潜在路由冲突: ${route1.method} ${route1.path} 与 ${route2.path}`);
|
|
68
56
|
}
|
|
69
57
|
}
|
|
70
58
|
/**
|
|
@@ -109,4 +97,4 @@ var BaseServer = class {
|
|
|
109
97
|
|
|
110
98
|
//#endregion
|
|
111
99
|
export { BaseServer as t };
|
|
112
|
-
//# sourceMappingURL=base-server-
|
|
100
|
+
//# sourceMappingURL=base-server-CE0mfqPY.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"base-server-CE0mfqPY.mjs","names":[],"sources":["../src/server/base-server.ts"],"sourcesContent":["import type { Middleware } from \"../types\";\n\n/**\n * 服务器基类\n * 包含所有服务器类型的公共逻辑\n */\nexport abstract class BaseServer {\n protected globalMiddleware: Middleware[] = [];\n\n use(mw: Middleware) {\n this.globalMiddleware.push(mw);\n }\n\n /**\n * 打印路由信息,用于调试\n */\n protected logRoutes(routes: any[], type: string = \"路由\"): void {\n console.log(`🚀 注册${type}:`);\n for (const route of routes) {\n const method = route.method || \"GET\";\n const path = route.path;\n console.log(` ${method} ${path}`);\n if (route.middleware && route.middleware.length > 0) {\n console.log(` 中间件: ${route.middleware.length} 个`);\n }\n }\n console.log(\"\");\n }\n\n /**\n * 检测路由冲突\n */\n protected detectRouteConflicts(routes: any[]): void {\n const pathGroups = new Map<string, any[]>();\n\n // 按路径分组\n for (const route of routes) {\n const path = route.path;\n const method = route.method || \"GET\";\n if (!pathGroups.has(path)) {\n pathGroups.set(path, []);\n }\n pathGroups.get(path)!.push({ ...route, method });\n }\n\n // 检查冲突\n for (const [path, routeList] of pathGroups) {\n if (routeList.length > 1) {\n const methods = routeList.map((r: { method: string }) => r.method);\n const uniqueMethods = [...new Set(methods)];\n\n if (uniqueMethods.length === 1) {\n console.warn(\n `⚠️ 路由冲突: ${uniqueMethods[0]} ${path} 定义了 ${routeList.length} 次`,\n );\n }\n }\n }\n\n // 检查动态路由冲突\n this.detectDynamicRouteConflicts(routes);\n }\n\n /**\n * 检测动态路由冲突\n */\n private detectDynamicRouteConflicts(routes: any[]): void {\n const dynamicRoutes = routes.filter((r) => {\n return r.path.includes(\":\") || r.path.includes(\"*\");\n });\n\n for (let i = 0; i < dynamicRoutes.length; i++) {\n for (let j = i + 1; j < dynamicRoutes.length; j++) {\n const route1 = dynamicRoutes[i];\n const route2 = dynamicRoutes[j];\n if (route1.method === route2.method && this.pathsMayConflict(route1.path, route2.path)) {\n console.warn(`⚠️ 潜在路由冲突: ${route1.method} ${route1.path} 与 ${route2.path}`);\n }\n }\n }\n }\n\n /**\n * 判断两个路径是否可能冲突\n */\n private pathsMayConflict(path1: string, path2: string): boolean {\n const parts1 = path1.split(\"/\").filter(Boolean);\n const parts2 = path2.split(\"/\").filter(Boolean);\n\n if (parts1.length !== parts2.length) return false;\n\n for (let i = 0; i < parts1.length; i++) {\n const p1 = parts1[i];\n const p2 = parts2[i];\n\n // 如果两个部分都是静态的且不同,则不会冲突\n if (\n !p1.startsWith(\":\") &&\n !p1.startsWith(\"*\") &&\n !p2.startsWith(\":\") &&\n !p2.startsWith(\"*\") &&\n p1 !== p2\n ) {\n return false;\n }\n\n // 如果一个是通配符,另一个是动态参数,可能冲突\n if (\n (p1 === \"*\" && p2.startsWith(\":\")) ||\n (p2 === \"*\" && p1.startsWith(\":\"))\n ) {\n return true;\n }\n }\n\n return false;\n }\n\n /**\n * 路径匹配\n */\n protected matchPath(pattern: string, path: string): boolean {\n const patternParts = pattern.split(\"/\").filter(Boolean);\n const pathParts = path.split(\"/\").filter(Boolean);\n\n if (patternParts.length !== pathParts.length) {\n return false;\n }\n\n for (let i = 0; i < patternParts.length; i++) {\n if (\n patternParts[i] !== pathParts[i] &&\n !patternParts[i].startsWith(\":\")\n ) {\n return false;\n }\n }\n\n return true;\n }\n\n /**\n * 提取路径参数\n */\n protected extractParams(\n pattern: string,\n path: string,\n ): Record<string, string> {\n const params: Record<string, string> = {};\n const patternParts = pattern.split(\"/\").filter(Boolean);\n const pathParts = path.split(\"/\").filter(Boolean);\n\n for (let i = 0; i < patternParts.length; i++) {\n if (patternParts[i].startsWith(\":\")) {\n const paramName = patternParts[i].slice(1);\n params[paramName] = pathParts[i];\n }\n }\n\n return params;\n }\n}\n"],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { r as Middleware } from "./types-B8Z3cMtZ.mjs";
|
|
2
2
|
|
|
3
3
|
//#region src/server/base-server.d.ts
|
|
4
4
|
|
|
@@ -10,16 +10,15 @@ declare abstract class BaseServer {
|
|
|
10
10
|
protected globalMiddleware: Middleware[];
|
|
11
11
|
use(mw: Middleware): void;
|
|
12
12
|
/**
|
|
13
|
-
*
|
|
13
|
+
* 打印路由信息,用于调试
|
|
14
14
|
*/
|
|
15
|
-
protected
|
|
15
|
+
protected logRoutes(routes: any[], type?: string): void;
|
|
16
16
|
/**
|
|
17
17
|
* 检测路由冲突
|
|
18
|
-
* 检查是否有路径相同但方法不同的路由,以及潜在的路径冲突
|
|
19
18
|
*/
|
|
20
19
|
protected detectRouteConflicts(routes: any[]): void;
|
|
21
20
|
/**
|
|
22
|
-
*
|
|
21
|
+
* 检测动态路由冲突
|
|
23
22
|
*/
|
|
24
23
|
private detectDynamicRouteConflicts;
|
|
25
24
|
/**
|
|
@@ -37,4 +36,4 @@ declare abstract class BaseServer {
|
|
|
37
36
|
}
|
|
38
37
|
//#endregion
|
|
39
38
|
export { BaseServer as t };
|
|
40
|
-
//# sourceMappingURL=base-server-
|
|
39
|
+
//# sourceMappingURL=base-server-Contwrlf.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"base64url-
|
|
1
|
+
{"version":3,"file":"base64url-Cc77f1ms.mjs","names":[],"sources":["../src/utils/base64url.ts"],"sourcesContent":["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"],"mappings":";AAAA,SAAgB,gBAAgB,KAAqB;AACnD,QAAO,KAAK,IAAI,CACb,QAAQ,MAAM,GAAG,CACjB,QAAQ,OAAO,IAAI,CACnB,QAAQ,OAAO,IAAI;;AAGxB,SAAgB,gBAAgB,KAAqB;CACnD,MAAM,MAAM,IAAI,SAAS,MAAM,IAAI,KAAK,IAAI,OAAO,IAAK,IAAI,SAAS,EAAG;CACxE,MAAM,SAAS,IAAI,QAAQ,MAAM,IAAI,CAAC,QAAQ,MAAM,IAAI,GAAG;AAC3D,QAAO,KAAK,OAAO"}
|
|
@@ -3,4 +3,4 @@ declare function base64urlEncode(str: string): string;
|
|
|
3
3
|
declare function base64urlDecode(str: string): string;
|
|
4
4
|
//#endregion
|
|
5
5
|
export { base64urlEncode as n, base64urlDecode as t };
|
|
6
|
-
//# sourceMappingURL=base64url-
|
|
6
|
+
//# sourceMappingURL=base64url-DNUGwekK.d.mts.map
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { r as Middleware } from "./types-B8Z3cMtZ.mjs";
|
|
2
2
|
|
|
3
3
|
//#region src/types/component-route.d.ts
|
|
4
4
|
|
|
@@ -28,4 +28,4 @@ interface FlattenedComponentRoute extends ComponentRoute {
|
|
|
28
28
|
}
|
|
29
29
|
//#endregion
|
|
30
30
|
export { FlattenedComponentRoute as n, NestedComponentRoute as r, ComponentRoute as t };
|
|
31
|
-
//# sourceMappingURL=component-route-
|
|
31
|
+
//# sourceMappingURL=component-route-Di7R40-2.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"component-router-
|
|
1
|
+
{"version":3,"file":"component-router-B6M5XDnp.mjs","names":[],"sources":["../src/middleware/component-router.ts"],"sourcesContent":["import type {\n ComponentRoute,\n NestedComponentRoute,\n FlattenedComponentRoute,\n} from \"../types/component-route\";\nimport { vueRenderer, reactRenderer } from \"./component-renderer\";\n\n/**\n * 扁平化嵌套组件路由\n */\nexport function flattenComponentRoutes(\n routes: (ComponentRoute | NestedComponentRoute)[],\n): FlattenedComponentRoute[] {\n const flattened: FlattenedComponentRoute[] = [];\n\n function processRoute(\n route: ComponentRoute | NestedComponentRoute,\n parentPath: string = \"\",\n parentMiddleware: any[] = [],\n ) {\n const currentPath = parentPath + route.path;\n const currentMiddleware = [\n ...parentMiddleware,\n ...(route.middleware || []),\n ];\n\n if (\"component\" in route) {\n // 这是一个组件路由\n flattened.push({\n ...route,\n fullPath: currentPath,\n middlewareChain: currentMiddleware,\n });\n } else if (\"children\" in route && route.children) {\n // 这是一个嵌套路由\n for (const child of route.children) {\n processRoute(child, currentPath, currentMiddleware);\n }\n }\n }\n\n for (const route of routes) {\n processRoute(route);\n }\n\n return flattened;\n}\n\n/**\n * 组件路由处理器中间件\n * 自动检测组件类型并应用相应的渲染器\n */\nexport const componentRouter = () => {\n return async (req: Request, next: () => Promise<Response>) => {\n // 这里可以添加组件路由的自动处理逻辑\n // 比如自动检测组件类型,应用相应的渲染器\n return next();\n };\n};\n"],"mappings":";;;;AAUA,SAAgB,uBACd,QAC2B;CAC3B,MAAM,YAAuC,EAAE;CAE/C,SAAS,aACP,OACA,aAAqB,IACrB,mBAA0B,EAAE,EAC5B;EACA,MAAM,cAAc,aAAa,MAAM;EACvC,MAAM,oBAAoB,CACxB,GAAG,kBACH,GAAI,MAAM,cAAc,EAAE,CAC3B;AAED,MAAI,eAAe,MAEjB,WAAU,KAAK;GACb,GAAG;GACH,UAAU;GACV,iBAAiB;GAClB,CAAC;WACO,cAAc,SAAS,MAAM,SAEtC,MAAK,MAAM,SAAS,MAAM,SACxB,cAAa,OAAO,aAAa,kBAAkB;;AAKzD,MAAK,MAAM,SAAS,OAClB,cAAa,MAAM;AAGrB,QAAO;;;;;;AAOT,MAAa,wBAAwB;AACnC,QAAO,OAAO,KAAc,SAAkC;AAG5D,SAAO,MAAM"}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { t as BaseServer } from "./base-server-
|
|
2
|
-
import { n as flattenComponentRoutes } from "./component-router-
|
|
3
|
-
import { t as PathMatcher } from "./path-matcher-
|
|
4
|
-
import { t as HtmlRenderer } from "./html-renderer-
|
|
5
|
-
import { t as DependencyManager } from "./dependency-manager-
|
|
1
|
+
import { t as BaseServer } from "./base-server-CE0mfqPY.mjs";
|
|
2
|
+
import { n as flattenComponentRoutes } from "./component-router-B6M5XDnp.mjs";
|
|
3
|
+
import { t as PathMatcher } from "./path-matcher-Dp-DRUV9.mjs";
|
|
4
|
+
import { t as HtmlRenderer } from "./html-renderer-D1zzDVQM.mjs";
|
|
5
|
+
import { t as DependencyManager } from "./dependency-manager-CGMZJTer.mjs";
|
|
6
6
|
|
|
7
7
|
//#region src/server/component-server.ts
|
|
8
8
|
/**
|
|
@@ -17,7 +17,7 @@ var ComponentServer = class extends BaseServer {
|
|
|
17
17
|
this.routes = flattenComponentRoutes(routes);
|
|
18
18
|
this.dependencyManager = new DependencyManager();
|
|
19
19
|
this.detectRouteConflicts(this.routes);
|
|
20
|
-
this.
|
|
20
|
+
this.logRoutes(this.routes, "组件路由");
|
|
21
21
|
console.log("🚀 依赖按需加载,服务器启动完成");
|
|
22
22
|
}
|
|
23
23
|
/**
|
|
@@ -116,4 +116,4 @@ var ComponentServer = class extends BaseServer {
|
|
|
116
116
|
|
|
117
117
|
//#endregion
|
|
118
118
|
export { ComponentServer as t };
|
|
119
|
-
//# sourceMappingURL=component-server-
|
|
119
|
+
//# sourceMappingURL=component-server-Cya46YN3.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"component-server-Cya46YN3.mjs","names":[],"sources":["../src/server/component-server.ts"],"sourcesContent":["import type {\n ComponentRoute,\n NestedComponentRoute,\n FlattenedComponentRoute,\n} from \"../types/component-route\";\nimport { flattenComponentRoutes } from \"../middleware/component-router\";\nimport { BaseServer } from \"./base-server\";\nimport { PathMatcher } from \"../utils/path-matcher\";\nimport { HtmlRenderer } from \"../utils/html-renderer\";\nimport { DependencyManager } from \"../utils/dependency-manager\";\n\n/**\n * 组件路由服务器\n * 专门处理声明式组件路由\n */\nexport class ComponentServer extends BaseServer {\n private routes: FlattenedComponentRoute[];\n private dependencyManager: DependencyManager;\n\n constructor(routes: (ComponentRoute | NestedComponentRoute)[]) {\n super();\n this.routes = flattenComponentRoutes(routes);\n this.dependencyManager = new DependencyManager();\n\n // 检测路由冲突\n this.detectRouteConflicts(this.routes);\n this.logRoutes(this.routes, \"组件路由\");\n console.log(\"🚀 依赖按需加载,服务器启动完成\");\n }\n\n /**\n * 处理请求\n */\n async fetch(req: Request): Promise<Response> {\n const url = new URL(req.url);\n const pathname = url.pathname;\n const method = req.method;\n\n // 只支持 GET 请求\n if (method !== \"GET\") {\n return new Response(\"Method Not Allowed\", { status: 405 });\n }\n\n // 查找匹配的路由\n let matchedRoute: FlattenedComponentRoute | null = null;\n for (const route of this.routes) {\n if (PathMatcher.matchPath(route.fullPath, pathname)) {\n matchedRoute = route;\n break;\n }\n }\n\n if (!matchedRoute) {\n return new Response(\"Not Found\", { status: 404 });\n }\n\n try {\n // 创建中间件上下文\n const context = {\n req,\n params: PathMatcher.extractParams(matchedRoute.fullPath, pathname),\n query: Object.fromEntries(url.searchParams),\n pathname,\n };\n\n // 执行中间件链,中间件会处理组件渲染\n return await this.executeMiddlewareChain(\n matchedRoute.middlewareChain,\n context,\n matchedRoute.component,\n );\n } catch (error) {\n console.error(\"组件渲染失败:\", error);\n return new Response(\"Internal Server Error\", { status: 500 });\n }\n }\n\n /**\n * 执行中间件链\n */\n private async executeMiddlewareChain(\n middlewareChain: any[],\n context: any,\n componentImport: () => Promise<any>,\n ): Promise<Response> {\n // 创建最终的渲染函数\n const renderComponent = async () => {\n const componentModule = await componentImport();\n const component = componentModule.default || componentModule;\n\n // 自动检测组件类型\n const componentType =\n this.dependencyManager.detectComponentType(component);\n\n // 按需加载依赖\n const deps = await this.dependencyManager.getFrameworkDeps(componentType);\n\n // 根据组件类型渲染\n if (componentType === \"vue\") {\n return await this.renderVueComponent(component, context, deps);\n } else if (componentType === \"react\") {\n return await this.renderReactComponent(component, context, deps);\n } else {\n throw new Error(`不支持的组件类型: ${componentType}`);\n }\n };\n\n // 执行中间件链\n let index = 0;\n const next = async (): Promise<Response> => {\n if (index >= middlewareChain.length) {\n return await renderComponent();\n }\n\n const middleware = middlewareChain[index++];\n return await middleware(context.req, next);\n };\n\n return await next();\n }\n\n /**\n * 渲染 Vue 组件\n */\n private async renderVueComponent(\n component: any,\n context: any,\n deps: any,\n ): Promise<Response> {\n try {\n const [vue, renderer] = deps;\n const app = vue.createSSRApp(component);\n\n // 提供路由信息\n app.provide(\"routeInfo\", {\n params: context.params || {},\n query: context.query || {},\n pathname: context.pathname,\n });\n\n const html = await renderer.renderToString(app);\n const fullHtml = HtmlRenderer.generateVueHtml(html, context);\n\n return new Response(fullHtml, {\n headers: { \"Content-Type\": \"text/html; charset=utf-8\" },\n });\n } catch (error) {\n console.error(\"Vue 组件渲染失败:\", error);\n return new Response(\"Vue Component Render Error\", { status: 500 });\n }\n }\n\n /**\n * 渲染 React 组件\n */\n private async renderReactComponent(\n component: any,\n context: any,\n deps: any,\n ): Promise<Response> {\n try {\n const [react, renderer] = deps;\n const content = react.createElement(component, {\n req: context.req,\n params: context.params || {},\n query: context.query || {},\n });\n\n const html = renderer.renderToString(content);\n const fullHtml = HtmlRenderer.generateReactHtml(html, context);\n\n return new Response(fullHtml, {\n headers: { \"Content-Type\": \"text/html; charset=utf-8\" },\n });\n } catch (error) {\n console.error(\"React 组件渲染失败:\", error);\n return new Response(\"React Component Render Error\", { status: 500 });\n }\n }\n\n /**\n * 获取依赖管理器(用于外部访问)\n */\n getDependencyManager(): DependencyManager {\n return this.dependencyManager;\n }\n}\n"],"mappings":"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"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { r as NestedComponentRoute, t as ComponentRoute } from "./component-route-
|
|
2
|
-
import { t as BaseServer } from "./base-server-
|
|
3
|
-
import { t as DependencyManager } from "./dependency-manager-
|
|
1
|
+
import { r as NestedComponentRoute, t as ComponentRoute } from "./component-route-Di7R40-2.mjs";
|
|
2
|
+
import { t as BaseServer } from "./base-server-Contwrlf.mjs";
|
|
3
|
+
import { t as DependencyManager } from "./dependency-manager-DIN9X0Gj.mjs";
|
|
4
4
|
|
|
5
5
|
//#region src/server/component-server.d.ts
|
|
6
6
|
|
|
@@ -35,4 +35,4 @@ declare class ComponentServer extends BaseServer {
|
|
|
35
35
|
}
|
|
36
36
|
//#endregion
|
|
37
37
|
export { ComponentServer as t };
|
|
38
|
-
//# sourceMappingURL=component-server-
|
|
38
|
+
//# sourceMappingURL=component-server-fR4UV6Jq.d.mts.map
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { o as getRouteRegistry } from "./route-registry-BzExlM2t.mjs";
|
|
2
|
+
|
|
3
|
+
//#region src/utils/contract.ts
|
|
4
|
+
/**
|
|
5
|
+
* 从路由数组生成 API 规范
|
|
6
|
+
*/
|
|
7
|
+
function generateSpec(routes) {
|
|
8
|
+
const routeSpecs = [];
|
|
9
|
+
for (const route of routes) {
|
|
10
|
+
const r = route;
|
|
11
|
+
if (!r.method || !r.path) continue;
|
|
12
|
+
if (r.path === "/api-spec" || r.path === "/__spec__") continue;
|
|
13
|
+
const spec = {
|
|
14
|
+
method: r.method,
|
|
15
|
+
path: r.path
|
|
16
|
+
};
|
|
17
|
+
if (r.name) spec.name = r.name;
|
|
18
|
+
if (r.description) spec.description = r.description;
|
|
19
|
+
if (r.schema) {
|
|
20
|
+
const schema = r.schema;
|
|
21
|
+
if (schema.body || schema.query || schema.params || schema.headers || schema.cookies) {
|
|
22
|
+
spec.schema = {};
|
|
23
|
+
if (schema.body) spec.schema.body = schema.body;
|
|
24
|
+
if (schema.query) spec.schema.query = schema.query;
|
|
25
|
+
if (schema.params) spec.schema.params = schema.params;
|
|
26
|
+
if (schema.headers) spec.schema.headers = schema.headers;
|
|
27
|
+
if (schema.cookies) spec.schema.cookies = schema.cookies;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
routeSpecs.push(spec);
|
|
31
|
+
}
|
|
32
|
+
return {
|
|
33
|
+
version: "1.0.0",
|
|
34
|
+
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
35
|
+
routes: routeSpecs
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* 从路由数组生成 AI 工具函数
|
|
40
|
+
*
|
|
41
|
+
* 可直接用于 OpenAI Function Calling / Claude Tools
|
|
42
|
+
*
|
|
43
|
+
* @example
|
|
44
|
+
* ```typescript
|
|
45
|
+
* const tools = generateAITools(routes)
|
|
46
|
+
* // [{ name: 'get_users', description: '获取用户列表', parameters: {...} }]
|
|
47
|
+
* ```
|
|
48
|
+
*/
|
|
49
|
+
function generateAITools(routes) {
|
|
50
|
+
const tools = [];
|
|
51
|
+
for (const route of routes) {
|
|
52
|
+
const r = route;
|
|
53
|
+
if (!r.method || !r.path) continue;
|
|
54
|
+
if (r.path === "/api-spec" || r.path === "/__spec__") continue;
|
|
55
|
+
const tool = { name: r.name || pathToToolName(r.method, r.path) };
|
|
56
|
+
if (r.description) tool.description = r.description;
|
|
57
|
+
if (r.schema) {
|
|
58
|
+
if (r.method === "GET" && r.schema.query) tool.parameters = r.schema.query;
|
|
59
|
+
else if (r.schema.body) tool.parameters = r.schema.body;
|
|
60
|
+
}
|
|
61
|
+
tools.push(tool);
|
|
62
|
+
}
|
|
63
|
+
return tools;
|
|
64
|
+
}
|
|
65
|
+
/** 从路径生成工具名 */
|
|
66
|
+
function pathToToolName(method, path) {
|
|
67
|
+
const cleanSegments = path.split("/").filter(Boolean).map((s) => s.startsWith(":") ? "" : s).filter(Boolean);
|
|
68
|
+
const prefix = method.toLowerCase();
|
|
69
|
+
const suffix = cleanSegments.join("_");
|
|
70
|
+
return suffix ? `${prefix}_${suffix}` : prefix;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* 获取 API 规范
|
|
74
|
+
*
|
|
75
|
+
* 支持多种调用方式:
|
|
76
|
+
* 1. 直接作为 handler:自动从全局 Registry 获取(推荐)
|
|
77
|
+
* 2. 无参调用:同上,用于 CLI/测试
|
|
78
|
+
* 3. 有参调用:显式传递路由数组(灵活场景)
|
|
79
|
+
*
|
|
80
|
+
* @param routesOrContext - 可选,路由数组或 handler context。不传则从全局 Registry 获取
|
|
81
|
+
* @returns ApiSpec 对象
|
|
82
|
+
*
|
|
83
|
+
* @example
|
|
84
|
+
* ```typescript
|
|
85
|
+
* import { getApiSpec } from 'vafast'
|
|
86
|
+
*
|
|
87
|
+
* // 方式 1:直接作为 handler(推荐,最简洁)
|
|
88
|
+
* { method: 'GET', path: '/api-spec', handler: getApiSpec }
|
|
89
|
+
*
|
|
90
|
+
* // 方式 2:显式传参(只暴露公开 API)
|
|
91
|
+
* { handler: () => getApiSpec(publicRoutes) }
|
|
92
|
+
*
|
|
93
|
+
* // 方式 3:本地使用(CLI、测试)
|
|
94
|
+
* const spec = getApiSpec()
|
|
95
|
+
* ```
|
|
96
|
+
*/
|
|
97
|
+
function getApiSpec(routesOrContext) {
|
|
98
|
+
return generateSpec(Array.isArray(routesOrContext) && routesOrContext.length > 0 && typeof routesOrContext[0]?.method === "string" ? routesOrContext : getRoutesFromRegistry());
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* 从全局 Registry 获取路由列表
|
|
102
|
+
* 包含 schema 信息(Registry 存储完整路由)
|
|
103
|
+
*/
|
|
104
|
+
function getRoutesFromRegistry() {
|
|
105
|
+
try {
|
|
106
|
+
return getRouteRegistry().getAll();
|
|
107
|
+
} catch {
|
|
108
|
+
return [];
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
//#endregion
|
|
113
|
+
export { getApiSpec as n, generateAITools as t };
|
|
114
|
+
//# sourceMappingURL=contract-BKqc9fFH.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"contract-BKqc9fFH.mjs","names":[],"sources":["../src/utils/contract.ts"],"sourcesContent":["/**\n * API Spec 生成器\n *\n * 用于生成 API 规范,支持:\n * - 跨仓库类型同步\n * - AI 工具函数生成\n * - Swagger/OpenAPI 文档生成\n *\n * @example\n * ```typescript\n * import { getApiSpec } from 'vafast'\n *\n * // 方式 1:直接作为 handler(推荐,自动从全局 Registry 获取)\n * const allRoutes = [\n * ...routes,\n * { method: 'GET', path: '/api-spec', handler: getApiSpec }\n * ]\n *\n * // 方式 2:显式传参(灵活场景,如只暴露公开 API)\n * { handler: () => getApiSpec(publicRoutes) }\n * ```\n */\n\nimport type { TSchema } from '@sinclair/typebox'\nimport type { RouteSchema } from '../defineRoute'\nimport { getRouteRegistry } from './route-registry'\n\n/** 路由规范信息 */\ninterface RouteSpec {\n method: string\n path: string\n name?: string\n description?: string\n schema?: {\n body?: TSchema\n query?: TSchema\n params?: TSchema\n headers?: TSchema\n cookies?: TSchema\n }\n}\n\n/** API 规范 */\ninterface ApiSpec {\n version: string\n generatedAt: string\n routes: RouteSpec[]\n}\n\n/** AI 工具函数格式 */\ninterface AITool {\n name: string\n description?: string\n parameters?: TSchema\n}\n\n/**\n * 从路由数组生成 API 规范\n */\nfunction generateSpec(routes: readonly unknown[]): ApiSpec {\n const routeSpecs: RouteSpec[] = []\n\n for (const route of routes) {\n const r = route as {\n method?: string\n path?: string\n name?: string\n description?: string\n schema?: RouteSchema\n }\n\n if (!r.method || !r.path) continue\n\n // 跳过 spec 接口本身\n if (r.path === '/api-spec' || r.path === '/__spec__') continue\n\n const spec: RouteSpec = {\n method: r.method,\n path: r.path,\n }\n\n // 直接从路由获取 name 和 description\n if (r.name) spec.name = r.name\n if (r.description) spec.description = r.description\n\n // 直接从路由获取 schema\n if (r.schema) {\n const schema = r.schema\n if (schema.body || schema.query || schema.params || schema.headers || schema.cookies) {\n spec.schema = {}\n if (schema.body) spec.schema.body = schema.body\n if (schema.query) spec.schema.query = schema.query\n if (schema.params) spec.schema.params = schema.params\n if (schema.headers) spec.schema.headers = schema.headers\n if (schema.cookies) spec.schema.cookies = schema.cookies\n }\n }\n\n routeSpecs.push(spec)\n }\n\n return {\n version: '1.0.0',\n generatedAt: new Date().toISOString(),\n routes: routeSpecs,\n }\n}\n\n/**\n * 从路由数组生成 AI 工具函数\n *\n * 可直接用于 OpenAI Function Calling / Claude Tools\n *\n * @example\n * ```typescript\n * const tools = generateAITools(routes)\n * // [{ name: 'get_users', description: '获取用户列表', parameters: {...} }]\n * ```\n */\nexport function generateAITools(routes: readonly unknown[]): AITool[] {\n const tools: AITool[] = []\n\n for (const route of routes) {\n const r = route as {\n method?: string\n path?: string\n name?: string\n description?: string\n schema?: RouteSchema\n }\n\n if (!r.method || !r.path) continue\n if (r.path === '/api-spec' || r.path === '/__spec__') continue\n\n // 使用 name 或从 path 生成\n const name = r.name || pathToToolName(r.method, r.path)\n\n const tool: AITool = { name }\n if (r.description) tool.description = r.description\n\n // GET 用 query,其他用 body\n if (r.schema) {\n if (r.method === 'GET' && r.schema.query) {\n tool.parameters = r.schema.query\n } else if (r.schema.body) {\n tool.parameters = r.schema.body\n }\n }\n\n tools.push(tool)\n }\n\n return tools\n}\n\n/** 从路径生成工具名 */\nfunction pathToToolName(method: string, path: string): string {\n const segments = path.split('/').filter(Boolean)\n const cleanSegments = segments\n .map(s => s.startsWith(':') ? '' : s)\n .filter(Boolean)\n\n const prefix = method.toLowerCase()\n const suffix = cleanSegments.join('_')\n\n return suffix ? `${prefix}_${suffix}` : prefix\n}\n\n/**\n * 获取 API 规范\n *\n * 支持多种调用方式:\n * 1. 直接作为 handler:自动从全局 Registry 获取(推荐)\n * 2. 无参调用:同上,用于 CLI/测试\n * 3. 有参调用:显式传递路由数组(灵活场景)\n *\n * @param routesOrContext - 可选,路由数组或 handler context。不传则从全局 Registry 获取\n * @returns ApiSpec 对象\n *\n * @example\n * ```typescript\n * import { getApiSpec } from 'vafast'\n *\n * // 方式 1:直接作为 handler(推荐,最简洁)\n * { method: 'GET', path: '/api-spec', handler: getApiSpec }\n *\n * // 方式 2:显式传参(只暴露公开 API)\n * { handler: () => getApiSpec(publicRoutes) }\n *\n * // 方式 3:本地使用(CLI、测试)\n * const spec = getApiSpec()\n * ```\n */\nexport function getApiSpec(routesOrContext?: readonly unknown[] | Record<string, unknown>): ApiSpec {\n // 智能检测:是路由数组还是 handler context\n // 路由数组:Array && 第一个元素有 method 属性\n const isRoutesArray = Array.isArray(routesOrContext) &&\n routesOrContext.length > 0 &&\n typeof (routesOrContext[0] as Record<string, unknown>)?.method === 'string'\n\n const routeList = isRoutesArray\n ? routesOrContext\n : getRoutesFromRegistry()\n\n return generateSpec(routeList)\n}\n\n/**\n * 从全局 Registry 获取路由列表\n * 包含 schema 信息(Registry 存储完整路由)\n */\nfunction getRoutesFromRegistry(): readonly unknown[] {\n try {\n const registry = getRouteRegistry()\n return registry.getAll()\n } catch {\n // Registry 未初始化时返回空数组\n return []\n }\n}\n"],"mappings":";;;;;;AA2DA,SAAS,aAAa,QAAqC;CACzD,MAAM,aAA0B,EAAE;AAElC,MAAK,MAAM,SAAS,QAAQ;EAC1B,MAAM,IAAI;AAQV,MAAI,CAAC,EAAE,UAAU,CAAC,EAAE,KAAM;AAG1B,MAAI,EAAE,SAAS,eAAe,EAAE,SAAS,YAAa;EAEtD,MAAM,OAAkB;GACtB,QAAQ,EAAE;GACV,MAAM,EAAE;GACT;AAGD,MAAI,EAAE,KAAM,MAAK,OAAO,EAAE;AAC1B,MAAI,EAAE,YAAa,MAAK,cAAc,EAAE;AAGxC,MAAI,EAAE,QAAQ;GACZ,MAAM,SAAS,EAAE;AACjB,OAAI,OAAO,QAAQ,OAAO,SAAS,OAAO,UAAU,OAAO,WAAW,OAAO,SAAS;AACpF,SAAK,SAAS,EAAE;AAChB,QAAI,OAAO,KAAM,MAAK,OAAO,OAAO,OAAO;AAC3C,QAAI,OAAO,MAAO,MAAK,OAAO,QAAQ,OAAO;AAC7C,QAAI,OAAO,OAAQ,MAAK,OAAO,SAAS,OAAO;AAC/C,QAAI,OAAO,QAAS,MAAK,OAAO,UAAU,OAAO;AACjD,QAAI,OAAO,QAAS,MAAK,OAAO,UAAU,OAAO;;;AAIrD,aAAW,KAAK,KAAK;;AAGvB,QAAO;EACL,SAAS;EACT,8BAAa,IAAI,MAAM,EAAC,aAAa;EACrC,QAAQ;EACT;;;;;;;;;;;;;AAcH,SAAgB,gBAAgB,QAAsC;CACpE,MAAM,QAAkB,EAAE;AAE1B,MAAK,MAAM,SAAS,QAAQ;EAC1B,MAAM,IAAI;AAQV,MAAI,CAAC,EAAE,UAAU,CAAC,EAAE,KAAM;AAC1B,MAAI,EAAE,SAAS,eAAe,EAAE,SAAS,YAAa;EAKtD,MAAM,OAAe,EAAE,MAFV,EAAE,QAAQ,eAAe,EAAE,QAAQ,EAAE,KAAK,EAE1B;AAC7B,MAAI,EAAE,YAAa,MAAK,cAAc,EAAE;AAGxC,MAAI,EAAE,QACJ;OAAI,EAAE,WAAW,SAAS,EAAE,OAAO,MACjC,MAAK,aAAa,EAAE,OAAO;YAClB,EAAE,OAAO,KAClB,MAAK,aAAa,EAAE,OAAO;;AAI/B,QAAM,KAAK,KAAK;;AAGlB,QAAO;;;AAIT,SAAS,eAAe,QAAgB,MAAsB;CAE5D,MAAM,gBADW,KAAK,MAAM,IAAI,CAAC,OAAO,QAAQ,CAE7C,KAAI,MAAK,EAAE,WAAW,IAAI,GAAG,KAAK,EAAE,CACpC,OAAO,QAAQ;CAElB,MAAM,SAAS,OAAO,aAAa;CACnC,MAAM,SAAS,cAAc,KAAK,IAAI;AAEtC,QAAO,SAAS,GAAG,OAAO,GAAG,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;AA4B1C,SAAgB,WAAW,iBAAyE;AAWlG,QAAO,aARe,MAAM,QAAQ,gBAAgB,IAClD,gBAAgB,SAAS,KACzB,OAAQ,gBAAgB,IAAgC,WAAW,WAGjE,kBACA,uBAAuB,CAEG;;;;;;AAOhC,SAAS,wBAA4C;AACnD,KAAI;AAEF,SADiB,kBAAkB,CACnB,QAAQ;SAClB;AAEN,SAAO,EAAE"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { TSchema } from "@sinclair/typebox";
|
|
2
|
+
|
|
3
|
+
//#region src/utils/contract.d.ts
|
|
4
|
+
|
|
5
|
+
/** 路由规范信息 */
|
|
6
|
+
interface RouteSpec {
|
|
7
|
+
method: string;
|
|
8
|
+
path: string;
|
|
9
|
+
name?: string;
|
|
10
|
+
description?: string;
|
|
11
|
+
schema?: {
|
|
12
|
+
body?: TSchema;
|
|
13
|
+
query?: TSchema;
|
|
14
|
+
params?: TSchema;
|
|
15
|
+
headers?: TSchema;
|
|
16
|
+
cookies?: TSchema;
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
/** API 规范 */
|
|
20
|
+
interface ApiSpec {
|
|
21
|
+
version: string;
|
|
22
|
+
generatedAt: string;
|
|
23
|
+
routes: RouteSpec[];
|
|
24
|
+
}
|
|
25
|
+
/** AI 工具函数格式 */
|
|
26
|
+
interface AITool {
|
|
27
|
+
name: string;
|
|
28
|
+
description?: string;
|
|
29
|
+
parameters?: TSchema;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* 从路由数组生成 AI 工具函数
|
|
33
|
+
*
|
|
34
|
+
* 可直接用于 OpenAI Function Calling / Claude Tools
|
|
35
|
+
*
|
|
36
|
+
* @example
|
|
37
|
+
* ```typescript
|
|
38
|
+
* const tools = generateAITools(routes)
|
|
39
|
+
* // [{ name: 'get_users', description: '获取用户列表', parameters: {...} }]
|
|
40
|
+
* ```
|
|
41
|
+
*/
|
|
42
|
+
declare function generateAITools(routes: readonly unknown[]): AITool[];
|
|
43
|
+
/**
|
|
44
|
+
* 获取 API 规范
|
|
45
|
+
*
|
|
46
|
+
* 支持多种调用方式:
|
|
47
|
+
* 1. 直接作为 handler:自动从全局 Registry 获取(推荐)
|
|
48
|
+
* 2. 无参调用:同上,用于 CLI/测试
|
|
49
|
+
* 3. 有参调用:显式传递路由数组(灵活场景)
|
|
50
|
+
*
|
|
51
|
+
* @param routesOrContext - 可选,路由数组或 handler context。不传则从全局 Registry 获取
|
|
52
|
+
* @returns ApiSpec 对象
|
|
53
|
+
*
|
|
54
|
+
* @example
|
|
55
|
+
* ```typescript
|
|
56
|
+
* import { getApiSpec } from 'vafast'
|
|
57
|
+
*
|
|
58
|
+
* // 方式 1:直接作为 handler(推荐,最简洁)
|
|
59
|
+
* { method: 'GET', path: '/api-spec', handler: getApiSpec }
|
|
60
|
+
*
|
|
61
|
+
* // 方式 2:显式传参(只暴露公开 API)
|
|
62
|
+
* { handler: () => getApiSpec(publicRoutes) }
|
|
63
|
+
*
|
|
64
|
+
* // 方式 3:本地使用(CLI、测试)
|
|
65
|
+
* const spec = getApiSpec()
|
|
66
|
+
* ```
|
|
67
|
+
*/
|
|
68
|
+
declare function getApiSpec(routesOrContext?: readonly unknown[] | Record<string, unknown>): ApiSpec;
|
|
69
|
+
//#endregion
|
|
70
|
+
export { getApiSpec as n, generateAITools as t };
|
|
71
|
+
//# sourceMappingURL=contract-BL3JflJ7.d.mts.map
|