@vafast/cli 0.1.4 → 0.1.5

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 CHANGED
@@ -32,6 +32,18 @@ npx vafast sync --url http://localhost:3000 --endpoint /api/contract
32
32
  | `--url <url>` | 服务端地址(必填) | - |
33
33
  | `--out <path>` | 输出文件路径 | `src/api.generated.ts` |
34
34
  | `--endpoint <path>` | 契约接口路径 | `/__contract__` |
35
+ | `--strip-prefix <prefix>` | 去掉路径前缀 | - |
36
+
37
+ **示例:**
38
+
39
+ ```bash
40
+ # 去掉 /restfulApi 前缀
41
+ npx vafast sync \
42
+ --url http://localhost:9002 \
43
+ --endpoint /restfulApi/api-spec \
44
+ --out src/types/api/ones.generated.ts \
45
+ --strip-prefix /restfulApi
46
+ ```
35
47
 
36
48
  ## 工作流程
37
49
 
@@ -66,13 +78,23 @@ npx vafast sync --url http://localhost:3000
66
78
  ### 3. 使用生成的类型
67
79
 
68
80
  ```typescript
69
- import { eden } from '@vafast/api-client'
70
- import type { Api } from './api.generated'
81
+ import { createClient } from '@vafast/api-client'
82
+ import { createApiClient } from './api.generated'
71
83
 
72
- const api = eden<Api>('http://localhost:3000')
84
+ // 创建底层客户端
85
+ const client = createClient({
86
+ baseURL: 'http://localhost:3000',
87
+ timeout: 30000
88
+ })
73
89
 
74
- // 类型安全的调用
90
+ // 创建类型安全的 API 客户端
91
+ const api = createApiClient(client)
92
+
93
+ // 类型安全的调用(错误路径会被 TypeScript 检测)
75
94
  const { data, error } = await api.users.get({ page: 1 })
95
+
96
+ // ❌ TypeScript 会报错
97
+ // api.nonExistent.get() // Error: Property 'nonExistent' does not exist
76
98
  ```
77
99
 
78
100
  ## 自动化
@@ -82,9 +104,12 @@ const { data, error } = await api.users.get({ page: 1 })
82
104
  ```json
83
105
  {
84
106
  "scripts": {
85
- "sync": "vafast sync --url $API_URL",
86
- "dev": "npm run sync && vite",
87
- "build": "npm run sync && vite build"
107
+ "sync:auth": "vafast sync --url http://localhost:9003 --endpoint /authRestfulApi/api-spec --out src/types/api/auth.generated.ts --strip-prefix /authRestfulApi",
108
+ "sync:ones": "vafast sync --url http://localhost:9002 --endpoint /restfulApi/api-spec --out src/types/api/ones.generated.ts --strip-prefix /restfulApi",
109
+ "sync:billing": "vafast sync --url http://localhost:9004 --endpoint /billingRestfulApi/api-spec --out src/types/api/billing.generated.ts --strip-prefix /billingRestfulApi",
110
+ "sync:types": "npm run sync:auth && npm run sync:billing && npm run sync:ones",
111
+ "dev": "vite",
112
+ "build": "npm run sync:types && vite build"
88
113
  }
89
114
  }
90
115
  ```
@@ -113,28 +138,57 @@ const { data, error } = await api.users.get({ page: 1 })
113
138
  生成的类型:
114
139
 
115
140
  ```typescript
141
+ import type { ApiResponse, RequestConfig, Client, EdenClient } from '@vafast/api-client'
142
+ import { eden } from '@vafast/api-client'
143
+
144
+ /** API 契约类型 */
116
145
  export type Api = {
117
146
  users: {
118
147
  get: {
119
148
  query: { page?: number }
120
- return: unknown
149
+ return: any
121
150
  }
122
151
  post: {
123
152
  body: { name?: string }
124
- return: unknown
153
+ return: any
125
154
  }
126
155
  }
127
156
  }
157
+
158
+ /** API 客户端类型别名 */
159
+ export type ApiClientType = EdenClient<Api>
160
+
161
+ /**
162
+ * 创建类型安全的 API 客户端
163
+ */
164
+ export function createApiClient(client: Client): EdenClient<Api> {
165
+ return eden<Api>(client)
166
+ }
167
+ ```
168
+
169
+ **使用方式:**
170
+
171
+ ```typescript
172
+ import { createClient } from '@vafast/api-client'
173
+ import { createApiClient } from './api.generated'
174
+
175
+ const client = createClient({ baseURL: '/api', timeout: 30000 })
176
+ const api = createApiClient(client)
177
+
178
+ // 完整的类型安全
179
+ const { data, error } = await api.users.post({ name: 'John' })
128
180
  ```
129
181
 
130
182
  ## 注意事项
131
183
 
132
- 1. **返回类型**:当前契约不包含返回类型信息,生成的类型中返回值为 `unknown`。如需完整类型推断,建议使用 monorepo 共享路由定义。
184
+ 1. **返回类型**:如果后端未定义 `response` schema,生成的返回类型为 `any`(渐进式类型安全)。建议后端添加 `response` schema 获得完整类型检查。
133
185
 
134
186
  2. **服务器必须运行**:执行 `sync` 命令时,服务端必须在运行并暴露契约接口。
135
187
 
136
188
  3. **不要手动修改**:生成的文件会被覆盖,请勿手动修改。
137
189
 
190
+ 4. **类型安全**:生成的 `createApiClient` 返回 `EdenClient<Api>`,TypeScript 会检测错误的 API 路径。
191
+
138
192
  ## License
139
193
 
140
194
  MIT
package/bin/vafast.js CHANGED
File without changes
package/dist/cli.js CHANGED
@@ -1,4 +1,4 @@
1
- import { syncTypes } from "./sync-Bq9q17kE.js";
1
+ import { syncTypes } from "./sync-CS1-nDMt.js";
2
2
  import { cac } from "cac";
3
3
 
4
4
  //#region src/cli.ts
package/dist/index.js CHANGED
@@ -1,3 +1,3 @@
1
- import { schemaToType, syncTypes } from "./sync-Bq9q17kE.js";
1
+ import { schemaToType, syncTypes } from "./sync-CS1-nDMt.js";
2
2
 
3
3
  export { schemaToType, syncTypes };
@@ -113,14 +113,87 @@ function generateTypeDefinition(contract, stripPrefix) {
113
113
  lines.push(" * ⚠️ 请勿手动修改此文件,使用 `vafast sync` 重新生成");
114
114
  lines.push(" */");
115
115
  lines.push("");
116
+ lines.push("import type { ApiResponse, RequestConfig, Client, EdenClient } from '@vafast/api-client'");
117
+ lines.push("import { eden } from '@vafast/api-client'");
118
+ lines.push("");
116
119
  const routeTree = buildRouteTree(contract.routes, stripPrefix);
120
+ lines.push("/** API 契约类型 */");
117
121
  lines.push("export type Api = {");
118
122
  lines.push(generateRouteTreeType(routeTree, 1));
119
123
  lines.push("}");
120
124
  lines.push("");
125
+ lines.push("/** API 客户端类型(提供完整的 IDE 智能提示) */");
126
+ lines.push("export interface ApiClient {");
127
+ lines.push(generateClientType(routeTree, 1));
128
+ lines.push("}");
129
+ lines.push("");
130
+ lines.push("/** API 客户端类型别名(基于 EdenClient 推断,提供完整类型检查) */");
131
+ lines.push("export type ApiClientType = EdenClient<Api>");
132
+ lines.push("");
133
+ lines.push("/**");
134
+ lines.push(" * 创建类型安全的 API 客户端");
135
+ lines.push(" * ");
136
+ lines.push(" * @example");
137
+ lines.push(" * ```typescript");
138
+ lines.push(" * import { createClient } from '@vafast/api-client'");
139
+ lines.push(" * import { createApiClient } from './api.generated'");
140
+ lines.push(" * ");
141
+ lines.push(" * const client = createClient('/api').use(authMiddleware)");
142
+ lines.push(" * const api = createApiClient(client)");
143
+ lines.push(" * ");
144
+ lines.push(" * // 完整的 IDE 智能提示和类型检查");
145
+ lines.push(" * const { data, error } = await api.users.find.post({ current: 1, pageSize: 10 })");
146
+ lines.push(" * // ❌ 错误路径会被 TypeScript 检测到");
147
+ lines.push(" * // api.nonExistent.post() // Error: Property 'nonExistent' does not exist");
148
+ lines.push(" * ```");
149
+ lines.push(" */");
150
+ lines.push("export function createApiClient(client: Client): EdenClient<Api> {");
151
+ lines.push(" return eden<Api>(client)");
152
+ lines.push("}");
153
+ lines.push("");
154
+ return lines.join("\n");
155
+ }
156
+ /**
157
+ * 生成客户端接口类型(带完整方法签名,IDE 友好)
158
+ */
159
+ function generateClientType(tree, indent) {
160
+ const lines = [];
161
+ const pad = " ".repeat(indent);
162
+ for (const [key, node] of tree) {
163
+ const needsQuotes = /[^a-zA-Z0-9_$]/.test(key) || /^\d/.test(key);
164
+ const propName = needsQuotes ? `'${key}'` : key;
165
+ lines.push(`${pad}${propName}: {`);
166
+ for (const [method, route] of node.methods) {
167
+ if (route.description) lines.push(`${pad} /** ${route.description} */`);
168
+ const methodSig = generateMethodSignature(route, method);
169
+ lines.push(`${pad} ${method}: ${methodSig}`);
170
+ }
171
+ if (node.children.size > 0) {
172
+ const childContent = generateClientType(node.children, indent + 1);
173
+ if (childContent) lines.push(childContent);
174
+ }
175
+ lines.push(`${pad}}`);
176
+ }
121
177
  return lines.join("\n");
122
178
  }
123
179
  /**
180
+ * 生成方法签名(函数类型)
181
+ */
182
+ function generateMethodSignature(route, method) {
183
+ const params = [];
184
+ if (route.schema?.body) {
185
+ const bodyType = schemaToType(route.schema.body);
186
+ params.push(`body: ${bodyType}`);
187
+ }
188
+ if (route.schema?.query) {
189
+ const queryType = schemaToType(route.schema.query);
190
+ params.push(`query?: ${queryType}`);
191
+ }
192
+ params.push("config?: RequestConfig");
193
+ const returnType = route.schema?.response ? schemaToType(route.schema.response) : "any";
194
+ return `(${params.join(", ")}) => Promise<ApiResponse<${returnType}>>`;
195
+ }
196
+ /**
124
197
  * 构建路由树
125
198
  */
126
199
  function buildRouteTree(routes, stripPrefix) {
@@ -188,7 +261,10 @@ function generateMethodType(route) {
188
261
  const paramsType = schemaToType(route.schema.params);
189
262
  parts.push(`params: ${paramsType}`);
190
263
  }
191
- parts.push("return: unknown");
264
+ if (route.schema?.response) {
265
+ const responseType = schemaToType(route.schema.response);
266
+ parts.push(`return: ${responseType}`);
267
+ } else parts.push("return: any");
192
268
  if (parts.length === 1) return `{ ${parts[0]} }`;
193
269
  return `{\n ${parts.join("\n ")}\n }`;
194
270
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vafast/cli",
3
- "version": "0.1.4",
3
+ "version": "0.1.5",
4
4
  "description": "Vafast CLI - 类型同步和开发工具",
5
5
  "type": "module",
6
6
  "bin": {