@vafast/api-client 0.1.2 → 0.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,202 +0,0 @@
1
- /**
2
- * ✨ 自动从 vafast 路由推断契约
3
- *
4
- * 特性:
5
- * 1. 使用 route() 函数,无需 as const
6
- * 2. 支持 SSE 流式响应
7
- * 3. 完整的类型推断
8
- */
9
-
10
- import {
11
- defineRoutes,
12
- route,
13
- get,
14
- post,
15
- put,
16
- del,
17
- createHandler,
18
- createSSEHandler,
19
- Type
20
- } from 'vafast'
21
- import { eden, InferEden } from '../src'
22
-
23
- // ============= 业务类型定义 =============
24
-
25
- interface User {
26
- id: string
27
- name: string
28
- email: string
29
- }
30
-
31
- interface ChatMessage {
32
- text: string
33
- timestamp?: number
34
- }
35
-
36
- // ============= 服务端:定义路由 =============
37
-
38
- /**
39
- * ✨ 新方式:使用 route() 函数,无需 as const!
40
- */
41
- const routes = defineRoutes([
42
- // GET /users - 获取用户列表
43
- route('GET', '/users', createHandler(
44
- { query: Type.Object({
45
- page: Type.Optional(Type.Number({ default: 1 })),
46
- limit: Type.Optional(Type.Number({ default: 10 }))
47
- })},
48
- async ({ query }) => ({
49
- users: [] as User[],
50
- total: 0,
51
- page: query.page ?? 1,
52
- limit: query.limit ?? 10
53
- })
54
- )),
55
-
56
- // POST /users - 创建用户
57
- route('POST', '/users', createHandler(
58
- { body: Type.Object({ name: Type.String(), email: Type.String() }) },
59
- async ({ body }) => ({
60
- id: crypto.randomUUID(),
61
- name: body.name,
62
- email: body.email
63
- } as User)
64
- )),
65
-
66
- // GET /users/:id - 获取单个用户
67
- route('GET', '/users/:id', createHandler(
68
- { params: Type.Object({ id: Type.String() }) },
69
- async ({ params }) => ({
70
- id: params.id,
71
- name: 'User',
72
- email: 'user@example.com'
73
- } as User | null)
74
- )),
75
-
76
- // PUT /users/:id - 更新用户(使用快捷方法)
77
- put('/users/:id', createHandler(
78
- {
79
- params: Type.Object({ id: Type.String() }),
80
- body: Type.Object({
81
- name: Type.Optional(Type.String()),
82
- email: Type.Optional(Type.String())
83
- })
84
- },
85
- async ({ params, body }) => ({
86
- id: params.id,
87
- name: body?.name ?? 'User',
88
- email: body?.email ?? 'user@example.com'
89
- } as User)
90
- )),
91
-
92
- // DELETE /users/:id - 删除用户(使用快捷方法)
93
- del('/users/:id', createHandler(
94
- { params: Type.Object({ id: Type.String() }) },
95
- async () => ({ success: true, deletedAt: new Date().toISOString() })
96
- )),
97
-
98
- // 🌊 GET /chat/stream - SSE 流式响应
99
- route('GET', '/chat/stream', createSSEHandler(
100
- { query: Type.Object({ prompt: Type.String() }) },
101
- async function* ({ query }) {
102
- // 模拟 AI 流式响应
103
- yield { event: 'start', data: { message: 'Starting...' } }
104
-
105
- const words = `Hello! You said: "${query.prompt}"`.split(' ')
106
- for (const word of words) {
107
- yield { data: { text: word + ' ' } as ChatMessage }
108
- await new Promise(r => setTimeout(r, 100))
109
- }
110
-
111
- yield { event: 'end', data: { message: 'Done!' } }
112
- }
113
- ))
114
- ])
115
-
116
- // ============= 🎉 自动推断契约类型!=============
117
-
118
- /**
119
- * 从路由定义自动推断 API 契约
120
- * 无需手动定义任何接口!无需 as const!
121
- */
122
- type Api = InferEden<typeof routes>
123
-
124
- // ============= 客户端:完全类型安全的调用 =============
125
-
126
- const api = eden<Api>('http://localhost:3000', {
127
- headers: {
128
- 'Authorization': 'Bearer your-token-here'
129
- },
130
- timeout: 5000,
131
- onError: (error) => {
132
- console.error('API Error:', error.message)
133
- }
134
- })
135
-
136
- async function main() {
137
- console.log('=== 自动推断契约示例(无需 as const)===\n')
138
-
139
- // ✅ GET /users?page=1&limit=10
140
- const usersResult = await api.users.get({ page: 1, limit: 10 })
141
- if (usersResult.data) {
142
- console.log('📋 用户列表:', usersResult.data.users)
143
- console.log(' 总数:', usersResult.data.total)
144
- }
145
-
146
- // ✅ POST /users
147
- const newUserResult = await api.users.post({
148
- name: 'John Doe',
149
- email: 'john@example.com'
150
- })
151
- if (newUserResult.data) {
152
- console.log('\n✨ 新用户:', newUserResult.data.name)
153
- }
154
-
155
- // ✅ GET /users/:id
156
- const userResult = await api.users({ id: '123' }).get()
157
- if (userResult.data) {
158
- console.log('\n👤 用户详情:', userResult.data.name)
159
- }
160
-
161
- // ✅ PUT /users/:id
162
- const updateResult = await api.users({ id: '123' }).put({ name: 'Jane' })
163
- if (updateResult.data) {
164
- console.log('\n📝 更新后:', updateResult.data.name)
165
- }
166
-
167
- // ✅ DELETE /users/:id
168
- const deleteResult = await api.users({ id: '123' }).delete()
169
- if (deleteResult.data) {
170
- console.log('\n🗑️ 删除成功:', deleteResult.data.success)
171
- }
172
-
173
- // 🌊 SSE 流式响应
174
- console.log('\n=== SSE 流式响应 ===\n')
175
-
176
- // SSE 返回类型目前是 unknown,需要手动断言
177
- // 未来版本会改进 SSE 返回类型推断
178
- const subscription = api.chat.stream.subscribe(
179
- { prompt: 'Hello AI!' },
180
- {
181
- onOpen: () => console.log('📡 连接已建立'),
182
- onMessage: (data: unknown) => {
183
- console.log('收到消息:', data)
184
- },
185
- onError: (err) => console.error('❌ 错误:', err.message),
186
- onClose: () => console.log('📴 连接已关闭')
187
- }
188
- )
189
-
190
- // 5 秒后取消订阅
191
- setTimeout(() => {
192
- subscription.unsubscribe()
193
- console.log('\n\n=== 示例完成 ===')
194
- }, 5000)
195
- }
196
-
197
- main().catch(console.error)
198
-
199
- // ============= 导出 =============
200
-
201
- export { routes, api }
202
- export type { Api }
@@ -1,192 +0,0 @@
1
- /**
2
- * SSE 端到端测试
3
- * 测试功能:
4
- * 1. 请求取消 (AbortController)
5
- * 2. SSE 自动重连
6
- */
7
-
8
- import {
9
- defineRoutes,
10
- route,
11
- createHandler,
12
- createSSEHandler,
13
- Type,
14
- serve
15
- } from 'vafast'
16
- import { eden, InferEden } from '../src'
17
-
18
- // 定义路由
19
- const routes = defineRoutes([
20
- // 普通 GET 请求
21
- route('GET', '/hello', createHandler(
22
- { query: Type.Object({ name: Type.Optional(Type.String()) }) },
23
- async ({ query }) => ({ message: `Hello, ${query.name || 'World'}!` })
24
- )),
25
-
26
- // 慢请求(用于测试取消)
27
- route('GET', '/slow', createHandler(
28
- {},
29
- async () => {
30
- await new Promise(r => setTimeout(r, 5000))
31
- return { message: 'Slow response' }
32
- }
33
- )),
34
-
35
- // SSE 流式响应
36
- route('GET', '/stream', createSSEHandler(
37
- { query: Type.Object({ count: Type.Optional(Type.Number({ default: 5 })) }) },
38
- async function* ({ query }) {
39
- const count = query.count ?? 5
40
-
41
- yield { event: 'start', data: { message: '开始流式传输...' } }
42
-
43
- for (let i = 1; i <= count; i++) {
44
- yield { id: String(i), data: { index: i, text: `消息 ${i}/${count}` } }
45
- await new Promise(r => setTimeout(r, 200))
46
- }
47
-
48
- yield { event: 'end', data: { message: '传输完成!' } }
49
- }
50
- ))
51
- ])
52
-
53
- type Api = InferEden<typeof routes>
54
-
55
- async function main() {
56
- // 启动服务器
57
- console.log('🚀 启动服务器...')
58
- const server = serve({
59
- fetch: (req) => {
60
- const url = new URL(req.url)
61
-
62
- // 简单路由
63
- if (url.pathname === '/hello') {
64
- const name = url.searchParams.get('name') || 'World'
65
- return new Response(JSON.stringify({ message: `Hello, ${name}!` }), {
66
- headers: { 'Content-Type': 'application/json' }
67
- })
68
- }
69
-
70
- // 慢请求
71
- if (url.pathname === '/slow') {
72
- return new Promise(resolve => {
73
- setTimeout(() => {
74
- resolve(new Response(JSON.stringify({ message: 'Slow response' }), {
75
- headers: { 'Content-Type': 'application/json' }
76
- }))
77
- }, 5000)
78
- })
79
- }
80
-
81
- if (url.pathname === '/stream') {
82
- const count = parseInt(url.searchParams.get('count') || '5')
83
-
84
- const stream = new ReadableStream({
85
- async start(controller) {
86
- const encoder = new TextEncoder()
87
-
88
- controller.enqueue(encoder.encode(`event: start\ndata: ${JSON.stringify({ message: '开始流式传输...' })}\n\n`))
89
-
90
- for (let i = 1; i <= count; i++) {
91
- controller.enqueue(encoder.encode(`id: ${i}\ndata: ${JSON.stringify({ index: i, text: `消息 ${i}/${count}` })}\n\n`))
92
- await new Promise(r => setTimeout(r, 200))
93
- }
94
-
95
- controller.enqueue(encoder.encode(`event: end\ndata: ${JSON.stringify({ message: '传输完成!' })}\n\n`))
96
- controller.close()
97
- }
98
- })
99
-
100
- return new Response(stream, {
101
- headers: {
102
- 'Content-Type': 'text/event-stream',
103
- 'Cache-Control': 'no-cache',
104
- 'Connection': 'keep-alive'
105
- }
106
- })
107
- }
108
-
109
- return new Response('Not Found', { status: 404 })
110
- },
111
- port: 3456
112
- })
113
-
114
- console.log('✅ 服务器启动在 http://localhost:3456\n')
115
-
116
- // 等待服务器启动
117
- await new Promise(r => setTimeout(r, 500))
118
-
119
- // 创建客户端
120
- const api = eden<Api>('http://localhost:3456')
121
-
122
- // ============= 测试 1: 请求取消 =============
123
- console.log('🧪 测试 1: 请求取消')
124
-
125
- const controller = new AbortController()
126
-
127
- // 发起慢请求
128
- const slowPromise = api.slow.get(undefined, { signal: controller.signal })
129
-
130
- // 100ms 后取消
131
- setTimeout(() => {
132
- controller.abort()
133
- console.log(' ⏹️ 请求已取消')
134
- }, 100)
135
-
136
- const result = await slowPromise
137
- // 取消后 status 为 0,error 可能是 AbortError 或 "This operation was aborted"
138
- if (result.status === 0 && result.error) {
139
- console.log(' ✅ 请求取消成功 (error:', result.error.message || result.error.name, ')\n')
140
- } else {
141
- console.log(' ❌ 请求取消失败: status=', result.status, '\n')
142
- }
143
-
144
- // ============= 测试 2: 普通请求 =============
145
- console.log('🧪 测试 2: 普通请求')
146
- const helloResult = await api.hello.get({ name: 'TypeScript' })
147
- console.log(' 响应:', helloResult.data)
148
- console.log()
149
-
150
- // ============= 测试 3: SSE 流式响应 =============
151
- console.log('🧪 测试 3: SSE 流式响应')
152
-
153
- await new Promise<void>((resolve) => {
154
- const sub = api.stream.subscribe(
155
- { count: 3 },
156
- {
157
- onOpen: () => console.log(' 📡 连接已建立'),
158
- onMessage: (data: unknown) => {
159
- console.log(' 📨', data)
160
- },
161
- onError: (err) => console.log(' ❌ 错误:', err.message),
162
- onClose: () => {
163
- console.log(' 📴 连接已关闭')
164
- resolve()
165
- },
166
- onReconnect: (attempt, max) => {
167
- console.log(` 🔄 重连中 (${attempt}/${max})...`)
168
- },
169
- onMaxReconnects: () => {
170
- console.log(' ⚠️ 达到最大重连次数')
171
- }
172
- },
173
- {
174
- reconnectInterval: 1000,
175
- maxReconnects: 3
176
- }
177
- )
178
-
179
- // 5 秒超时
180
- setTimeout(() => {
181
- sub.unsubscribe()
182
- resolve()
183
- }, 5000)
184
- })
185
-
186
- console.log('\n✅ 所有测试完成!')
187
-
188
- // 关闭服务器
189
- server.stop()
190
- }
191
-
192
- main().catch(console.error)