@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.
- package/README.md +43 -34
- package/dist/index.d.mts +296 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +226 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +13 -9
- package/TODO.md +0 -83
- package/build.ts +0 -37
- package/bun.lock +0 -569
- package/example/auto-infer.ts +0 -202
- package/example/test-sse.ts +0 -192
- package/src/core/eden.ts +0 -697
- package/src/index.ts +0 -42
- package/src/types/index.ts +0 -34
- package/test/eden.test.ts +0 -425
- package/tsconfig.dts.json +0 -20
- package/tsconfig.json +0 -20
- package/tsup.config.ts +0 -23
- package/vitest.config.ts +0 -8
package/src/index.ts
DELETED
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Vafast API Client
|
|
3
|
-
*
|
|
4
|
-
* 类型安全的 Eden 风格 API 客户端
|
|
5
|
-
*
|
|
6
|
-
* @example
|
|
7
|
-
* ```typescript
|
|
8
|
-
* import { eden, InferEden } from '@vafast/api-client'
|
|
9
|
-
* import { defineRoutes, route, createHandler, Type } from 'vafast'
|
|
10
|
-
*
|
|
11
|
-
* // 定义路由(无需 as const)
|
|
12
|
-
* const routes = defineRoutes([
|
|
13
|
-
* route('GET', '/users', createHandler(...)),
|
|
14
|
-
* route('POST', '/users', createHandler(...))
|
|
15
|
-
* ])
|
|
16
|
-
*
|
|
17
|
-
* // 自动推断类型
|
|
18
|
-
* type Api = InferEden<typeof routes>
|
|
19
|
-
* const api = eden<Api>('http://localhost:3000')
|
|
20
|
-
*
|
|
21
|
-
* // 类型安全的调用
|
|
22
|
-
* const { data } = await api.users.get({ page: 1 })
|
|
23
|
-
* ```
|
|
24
|
-
*/
|
|
25
|
-
|
|
26
|
-
// Eden 客户端(核心)
|
|
27
|
-
export {
|
|
28
|
-
eden,
|
|
29
|
-
type EdenConfig,
|
|
30
|
-
type EdenClient,
|
|
31
|
-
type InferEden,
|
|
32
|
-
type SSEEvent,
|
|
33
|
-
type SSESubscribeOptions,
|
|
34
|
-
type SSESubscription,
|
|
35
|
-
} from './core/eden'
|
|
36
|
-
|
|
37
|
-
// 类型定义
|
|
38
|
-
export type {
|
|
39
|
-
HTTPMethod,
|
|
40
|
-
RequestConfig,
|
|
41
|
-
ApiResponse,
|
|
42
|
-
} from './types'
|
package/src/types/index.ts
DELETED
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 类型定义
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
/** HTTP 方法 */
|
|
6
|
-
export type HTTPMethod = 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH' | 'HEAD' | 'OPTIONS'
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* 请求配置
|
|
10
|
-
*/
|
|
11
|
-
export interface RequestConfig {
|
|
12
|
-
/** 请求头 */
|
|
13
|
-
headers?: Record<string, string>
|
|
14
|
-
/** 超时时间(毫秒) */
|
|
15
|
-
timeout?: number
|
|
16
|
-
/** 取消信号 */
|
|
17
|
-
signal?: AbortSignal
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* API 响应
|
|
22
|
-
*/
|
|
23
|
-
export interface ApiResponse<T = unknown> {
|
|
24
|
-
/** 响应数据 */
|
|
25
|
-
data: T | null
|
|
26
|
-
/** 错误信息 */
|
|
27
|
-
error: Error | null
|
|
28
|
-
/** HTTP 状态码 */
|
|
29
|
-
status: number
|
|
30
|
-
/** 响应头 */
|
|
31
|
-
headers: Headers
|
|
32
|
-
/** 原始响应对象 */
|
|
33
|
-
response: Response
|
|
34
|
-
}
|
package/test/eden.test.ts
DELETED
|
@@ -1,425 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it, beforeEach, vi, afterEach } from 'vitest'
|
|
2
|
-
import { eden } from '../src'
|
|
3
|
-
|
|
4
|
-
// Mock fetch
|
|
5
|
-
const mockFetch = vi.fn()
|
|
6
|
-
globalThis.fetch = mockFetch
|
|
7
|
-
|
|
8
|
-
describe('Eden Client', () => {
|
|
9
|
-
beforeEach(() => {
|
|
10
|
-
mockFetch.mockReset()
|
|
11
|
-
mockFetch.mockResolvedValue(
|
|
12
|
-
new Response(JSON.stringify({ id: '1', name: 'John' }), {
|
|
13
|
-
status: 200,
|
|
14
|
-
headers: { 'Content-Type': 'application/json' }
|
|
15
|
-
})
|
|
16
|
-
)
|
|
17
|
-
})
|
|
18
|
-
|
|
19
|
-
// ============= 类型定义 =============
|
|
20
|
-
|
|
21
|
-
interface TestContract {
|
|
22
|
-
users: {
|
|
23
|
-
get: { query: { page?: number }; return: { id: string }[] }
|
|
24
|
-
post: { body: { name: string }; return: { id: string } }
|
|
25
|
-
':id': {
|
|
26
|
-
get: { return: { id: string; name: string } }
|
|
27
|
-
put: { body: { name: string }; return: { id: string; name: string } }
|
|
28
|
-
delete: { return: { success: boolean } }
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
chat: {
|
|
32
|
-
stream: {
|
|
33
|
-
get: { query: { prompt: string }; return: unknown; sse: true }
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
// ============= 基础请求测试 =============
|
|
39
|
-
|
|
40
|
-
describe('基础请求', () => {
|
|
41
|
-
it('应该发送 GET 请求', async () => {
|
|
42
|
-
const api = eden<TestContract>('http://localhost:3000')
|
|
43
|
-
|
|
44
|
-
const result = await api.users.get({ page: 1 })
|
|
45
|
-
|
|
46
|
-
expect(mockFetch).toHaveBeenCalled()
|
|
47
|
-
const req = mockFetch.mock.calls[0][0] as Request
|
|
48
|
-
expect(req.url).toContain('/users')
|
|
49
|
-
expect(req.url).toContain('page=1')
|
|
50
|
-
expect(req.method).toBe('GET')
|
|
51
|
-
expect(result.status).toBe(200)
|
|
52
|
-
})
|
|
53
|
-
|
|
54
|
-
it('应该发送 POST 请求', async () => {
|
|
55
|
-
const api = eden<TestContract>('http://localhost:3000')
|
|
56
|
-
|
|
57
|
-
await api.users.post({ name: 'John' })
|
|
58
|
-
|
|
59
|
-
const req = mockFetch.mock.calls[0][0] as Request
|
|
60
|
-
expect(req.method).toBe('POST')
|
|
61
|
-
expect(req.url).toContain('/users')
|
|
62
|
-
})
|
|
63
|
-
|
|
64
|
-
it('应该正确设置请求头', async () => {
|
|
65
|
-
const api = eden<TestContract>('http://localhost:3000', {
|
|
66
|
-
headers: { 'Authorization': 'Bearer token123' }
|
|
67
|
-
})
|
|
68
|
-
|
|
69
|
-
await api.users.get()
|
|
70
|
-
|
|
71
|
-
const req = mockFetch.mock.calls[0][0] as Request
|
|
72
|
-
expect(req.headers.get('Authorization')).toBe('Bearer token123')
|
|
73
|
-
expect(req.headers.get('Content-Type')).toBe('application/json')
|
|
74
|
-
})
|
|
75
|
-
})
|
|
76
|
-
|
|
77
|
-
// ============= 参数化路由测试 =============
|
|
78
|
-
|
|
79
|
-
describe('参数化路由', () => {
|
|
80
|
-
it('应该通过函数调用处理路径参数', async () => {
|
|
81
|
-
const api = eden<TestContract>('http://localhost:3000')
|
|
82
|
-
|
|
83
|
-
await api.users({ id: '123' }).get()
|
|
84
|
-
|
|
85
|
-
const req = mockFetch.mock.calls[0][0] as Request
|
|
86
|
-
expect(req.url).toBe('http://localhost:3000/users/123')
|
|
87
|
-
expect(req.method).toBe('GET')
|
|
88
|
-
})
|
|
89
|
-
|
|
90
|
-
it('应该处理 PUT 请求和路径参数', async () => {
|
|
91
|
-
const api = eden<TestContract>('http://localhost:3000')
|
|
92
|
-
|
|
93
|
-
await api.users({ id: '123' }).put({ name: 'Jane' })
|
|
94
|
-
|
|
95
|
-
const req = mockFetch.mock.calls[0][0] as Request
|
|
96
|
-
expect(req.url).toBe('http://localhost:3000/users/123')
|
|
97
|
-
expect(req.method).toBe('PUT')
|
|
98
|
-
})
|
|
99
|
-
|
|
100
|
-
it('应该处理 DELETE 请求和路径参数', async () => {
|
|
101
|
-
const api = eden<TestContract>('http://localhost:3000')
|
|
102
|
-
|
|
103
|
-
await api.users({ id: '456' }).delete()
|
|
104
|
-
|
|
105
|
-
const req = mockFetch.mock.calls[0][0] as Request
|
|
106
|
-
expect(req.url).toBe('http://localhost:3000/users/456')
|
|
107
|
-
expect(req.method).toBe('DELETE')
|
|
108
|
-
})
|
|
109
|
-
|
|
110
|
-
it('应该对路径参数进行 URL 编码', async () => {
|
|
111
|
-
const api = eden<TestContract>('http://localhost:3000')
|
|
112
|
-
|
|
113
|
-
await api.users({ id: 'user/123' }).get()
|
|
114
|
-
|
|
115
|
-
const req = mockFetch.mock.calls[0][0] as Request
|
|
116
|
-
expect(req.url).toBe('http://localhost:3000/users/user%2F123')
|
|
117
|
-
})
|
|
118
|
-
})
|
|
119
|
-
|
|
120
|
-
// ============= 嵌套路由测试 =============
|
|
121
|
-
|
|
122
|
-
describe('嵌套路由', () => {
|
|
123
|
-
interface NestedContract {
|
|
124
|
-
users: {
|
|
125
|
-
':id': {
|
|
126
|
-
posts: {
|
|
127
|
-
get: { return: { id: string }[] }
|
|
128
|
-
':id': {
|
|
129
|
-
get: { return: { id: string; title: string } }
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
it('应该处理嵌套路径', async () => {
|
|
137
|
-
const api = eden<NestedContract>('http://localhost:3000')
|
|
138
|
-
|
|
139
|
-
await api.users({ id: '123' }).posts.get()
|
|
140
|
-
|
|
141
|
-
const req = mockFetch.mock.calls[0][0] as Request
|
|
142
|
-
expect(req.url).toBe('http://localhost:3000/users/123/posts')
|
|
143
|
-
})
|
|
144
|
-
|
|
145
|
-
it('应该处理多层嵌套路径参数', async () => {
|
|
146
|
-
const api = eden<NestedContract>('http://localhost:3000')
|
|
147
|
-
|
|
148
|
-
// 动态参数统一使用 :id
|
|
149
|
-
await api.users({ id: '123' }).posts({ id: '456' }).get()
|
|
150
|
-
|
|
151
|
-
const req = mockFetch.mock.calls[0][0] as Request
|
|
152
|
-
expect(req.url).toBe('http://localhost:3000/users/123/posts/456')
|
|
153
|
-
})
|
|
154
|
-
})
|
|
155
|
-
|
|
156
|
-
// ============= 请求取消测试 =============
|
|
157
|
-
|
|
158
|
-
describe('请求取消', () => {
|
|
159
|
-
it('应该支持通过 AbortController 取消请求', async () => {
|
|
160
|
-
const controller = new AbortController()
|
|
161
|
-
|
|
162
|
-
mockFetch.mockImplementation(() => {
|
|
163
|
-
return new Promise((_, reject) => {
|
|
164
|
-
controller.signal.addEventListener('abort', () => {
|
|
165
|
-
const error = new Error('This operation was aborted')
|
|
166
|
-
error.name = 'AbortError'
|
|
167
|
-
reject(error)
|
|
168
|
-
})
|
|
169
|
-
})
|
|
170
|
-
})
|
|
171
|
-
|
|
172
|
-
const api = eden<TestContract>('http://localhost:3000')
|
|
173
|
-
|
|
174
|
-
const promise = api.users.get(undefined, { signal: controller.signal })
|
|
175
|
-
controller.abort()
|
|
176
|
-
|
|
177
|
-
const result = await promise
|
|
178
|
-
expect(result.error).toBeTruthy()
|
|
179
|
-
expect(result.status).toBe(0)
|
|
180
|
-
})
|
|
181
|
-
|
|
182
|
-
it('应该在超时后自动取消请求', async () => {
|
|
183
|
-
mockFetch.mockImplementation((input: Request | string) => {
|
|
184
|
-
return new Promise((resolve, reject) => {
|
|
185
|
-
const timeoutId = setTimeout(() => {
|
|
186
|
-
resolve(new Response(JSON.stringify({}), { status: 200 }))
|
|
187
|
-
}, 5000)
|
|
188
|
-
|
|
189
|
-
// 从 Request 对象获取 signal
|
|
190
|
-
const signal = input instanceof Request ? input.signal : undefined
|
|
191
|
-
signal?.addEventListener('abort', () => {
|
|
192
|
-
clearTimeout(timeoutId)
|
|
193
|
-
const error = new Error('This operation was aborted')
|
|
194
|
-
error.name = 'AbortError'
|
|
195
|
-
reject(error)
|
|
196
|
-
})
|
|
197
|
-
})
|
|
198
|
-
})
|
|
199
|
-
|
|
200
|
-
const api = eden<TestContract>('http://localhost:3000', { timeout: 100 })
|
|
201
|
-
|
|
202
|
-
const result = await api.users.get()
|
|
203
|
-
|
|
204
|
-
expect(result.error).toBeTruthy()
|
|
205
|
-
expect(result.status).toBe(0)
|
|
206
|
-
})
|
|
207
|
-
})
|
|
208
|
-
|
|
209
|
-
// ============= 拦截器测试 =============
|
|
210
|
-
|
|
211
|
-
describe('拦截器', () => {
|
|
212
|
-
it('应该执行 onRequest 拦截器', async () => {
|
|
213
|
-
const onRequest = vi.fn((req: Request) => {
|
|
214
|
-
return new Request(req.url, {
|
|
215
|
-
...req,
|
|
216
|
-
headers: { ...Object.fromEntries(req.headers), 'X-Custom': 'value' }
|
|
217
|
-
})
|
|
218
|
-
})
|
|
219
|
-
|
|
220
|
-
const api = eden<TestContract>('http://localhost:3000', { onRequest })
|
|
221
|
-
|
|
222
|
-
await api.users.get()
|
|
223
|
-
|
|
224
|
-
expect(onRequest).toHaveBeenCalled()
|
|
225
|
-
})
|
|
226
|
-
|
|
227
|
-
it('应该执行 onResponse 拦截器', async () => {
|
|
228
|
-
const onResponse = vi.fn((response) => response)
|
|
229
|
-
|
|
230
|
-
const api = eden<TestContract>('http://localhost:3000', { onResponse })
|
|
231
|
-
|
|
232
|
-
await api.users.get()
|
|
233
|
-
|
|
234
|
-
expect(onResponse).toHaveBeenCalled()
|
|
235
|
-
})
|
|
236
|
-
|
|
237
|
-
it('应该在错误时执行 onError 回调', async () => {
|
|
238
|
-
mockFetch.mockResolvedValue(
|
|
239
|
-
new Response(JSON.stringify({ error: 'Not found' }), {
|
|
240
|
-
status: 404,
|
|
241
|
-
headers: { 'Content-Type': 'application/json' }
|
|
242
|
-
})
|
|
243
|
-
)
|
|
244
|
-
|
|
245
|
-
const onError = vi.fn()
|
|
246
|
-
const api = eden<TestContract>('http://localhost:3000', { onError })
|
|
247
|
-
|
|
248
|
-
await api.users.get()
|
|
249
|
-
|
|
250
|
-
expect(onError).toHaveBeenCalled()
|
|
251
|
-
})
|
|
252
|
-
})
|
|
253
|
-
|
|
254
|
-
// ============= 响应处理测试 =============
|
|
255
|
-
|
|
256
|
-
describe('响应处理', () => {
|
|
257
|
-
it('应该正确解析 JSON 响应', async () => {
|
|
258
|
-
mockFetch.mockResolvedValue(
|
|
259
|
-
new Response(JSON.stringify({ users: [{ id: '1' }], total: 10 }), {
|
|
260
|
-
status: 200,
|
|
261
|
-
headers: { 'Content-Type': 'application/json' }
|
|
262
|
-
})
|
|
263
|
-
)
|
|
264
|
-
|
|
265
|
-
const api = eden<TestContract>('http://localhost:3000')
|
|
266
|
-
const result = await api.users.get()
|
|
267
|
-
|
|
268
|
-
expect(result.data).toEqual({ users: [{ id: '1' }], total: 10 })
|
|
269
|
-
expect(result.status).toBe(200)
|
|
270
|
-
expect(result.error).toBeNull()
|
|
271
|
-
})
|
|
272
|
-
|
|
273
|
-
it('应该正确解析文本响应', async () => {
|
|
274
|
-
mockFetch.mockResolvedValue(
|
|
275
|
-
new Response('Hello World', {
|
|
276
|
-
status: 200,
|
|
277
|
-
headers: { 'Content-Type': 'text/plain' }
|
|
278
|
-
})
|
|
279
|
-
)
|
|
280
|
-
|
|
281
|
-
const api = eden<TestContract>('http://localhost:3000')
|
|
282
|
-
const result = await api.users.get()
|
|
283
|
-
|
|
284
|
-
expect(result.data).toBe('Hello World')
|
|
285
|
-
})
|
|
286
|
-
|
|
287
|
-
it('应该处理 HTTP 错误状态', async () => {
|
|
288
|
-
mockFetch.mockResolvedValue(
|
|
289
|
-
new Response(JSON.stringify({ message: 'Unauthorized' }), {
|
|
290
|
-
status: 401,
|
|
291
|
-
headers: { 'Content-Type': 'application/json' }
|
|
292
|
-
})
|
|
293
|
-
)
|
|
294
|
-
|
|
295
|
-
const api = eden<TestContract>('http://localhost:3000')
|
|
296
|
-
const result = await api.users.get()
|
|
297
|
-
|
|
298
|
-
expect(result.status).toBe(401)
|
|
299
|
-
expect(result.error).toBeTruthy()
|
|
300
|
-
expect(result.error?.message).toContain('401')
|
|
301
|
-
})
|
|
302
|
-
|
|
303
|
-
it('应该处理网络错误', async () => {
|
|
304
|
-
mockFetch.mockRejectedValue(new Error('Network error'))
|
|
305
|
-
|
|
306
|
-
const api = eden<TestContract>('http://localhost:3000')
|
|
307
|
-
const result = await api.users.get()
|
|
308
|
-
|
|
309
|
-
expect(result.error?.message).toBe('Network error')
|
|
310
|
-
expect(result.status).toBe(0)
|
|
311
|
-
expect(result.data).toBeNull()
|
|
312
|
-
})
|
|
313
|
-
})
|
|
314
|
-
|
|
315
|
-
// ============= Query 参数测试 =============
|
|
316
|
-
|
|
317
|
-
describe('Query 参数', () => {
|
|
318
|
-
it('应该正确构建查询字符串', async () => {
|
|
319
|
-
const api = eden<TestContract>('http://localhost:3000')
|
|
320
|
-
|
|
321
|
-
await api.users.get({ page: 2 })
|
|
322
|
-
|
|
323
|
-
const req = mockFetch.mock.calls[0][0] as Request
|
|
324
|
-
const url = new URL(req.url)
|
|
325
|
-
expect(url.searchParams.get('page')).toBe('2')
|
|
326
|
-
})
|
|
327
|
-
|
|
328
|
-
it('应该忽略 undefined 和 null 值', async () => {
|
|
329
|
-
// 直接使用 TestContract,测试 users.get
|
|
330
|
-
const api = eden<TestContract>('http://localhost:3000')
|
|
331
|
-
|
|
332
|
-
await api.users.get({ page: undefined })
|
|
333
|
-
|
|
334
|
-
const req = mockFetch.mock.calls[0][0] as Request
|
|
335
|
-
const url = new URL(req.url)
|
|
336
|
-
// undefined 值不应该出现在查询字符串中
|
|
337
|
-
expect(url.searchParams.has('page')).toBe(false)
|
|
338
|
-
})
|
|
339
|
-
})
|
|
340
|
-
})
|
|
341
|
-
|
|
342
|
-
// ============= SSE 测试 =============
|
|
343
|
-
|
|
344
|
-
describe('SSE 订阅', () => {
|
|
345
|
-
beforeEach(() => {
|
|
346
|
-
mockFetch.mockReset()
|
|
347
|
-
})
|
|
348
|
-
|
|
349
|
-
it('应该调用 subscribe 方法', async () => {
|
|
350
|
-
// 创建模拟 SSE 流
|
|
351
|
-
const encoder = new TextEncoder()
|
|
352
|
-
const stream = new ReadableStream({
|
|
353
|
-
start(controller) {
|
|
354
|
-
controller.enqueue(encoder.encode('data: {"message":"hello"}\n\n'))
|
|
355
|
-
controller.close()
|
|
356
|
-
}
|
|
357
|
-
})
|
|
358
|
-
|
|
359
|
-
mockFetch.mockResolvedValue(
|
|
360
|
-
new Response(stream, {
|
|
361
|
-
status: 200,
|
|
362
|
-
headers: { 'Content-Type': 'text/event-stream' }
|
|
363
|
-
})
|
|
364
|
-
)
|
|
365
|
-
|
|
366
|
-
interface SSEContract {
|
|
367
|
-
events: {
|
|
368
|
-
get: { query: { channel: string }; return: unknown; sse: { readonly __brand: 'SSE' } }
|
|
369
|
-
}
|
|
370
|
-
}
|
|
371
|
-
|
|
372
|
-
const api = eden<SSEContract>('http://localhost:3000')
|
|
373
|
-
const onMessage = vi.fn()
|
|
374
|
-
const onClose = vi.fn()
|
|
375
|
-
|
|
376
|
-
await new Promise<void>((resolve) => {
|
|
377
|
-
api.events.subscribe(
|
|
378
|
-
{ channel: 'test' },
|
|
379
|
-
{
|
|
380
|
-
onMessage,
|
|
381
|
-
onClose: () => {
|
|
382
|
-
onClose()
|
|
383
|
-
resolve()
|
|
384
|
-
}
|
|
385
|
-
}
|
|
386
|
-
)
|
|
387
|
-
})
|
|
388
|
-
|
|
389
|
-
expect(mockFetch).toHaveBeenCalled()
|
|
390
|
-
expect(onMessage).toHaveBeenCalledWith({ message: 'hello' })
|
|
391
|
-
expect(onClose).toHaveBeenCalled()
|
|
392
|
-
})
|
|
393
|
-
|
|
394
|
-
it('应该支持取消订阅', async () => {
|
|
395
|
-
const stream = new ReadableStream({
|
|
396
|
-
start(controller) {
|
|
397
|
-
// 永不关闭的流
|
|
398
|
-
}
|
|
399
|
-
})
|
|
400
|
-
|
|
401
|
-
mockFetch.mockResolvedValue(
|
|
402
|
-
new Response(stream, {
|
|
403
|
-
status: 200,
|
|
404
|
-
headers: { 'Content-Type': 'text/event-stream' }
|
|
405
|
-
})
|
|
406
|
-
)
|
|
407
|
-
|
|
408
|
-
interface SSEContract {
|
|
409
|
-
events: {
|
|
410
|
-
get: { return: unknown; sse: { readonly __brand: 'SSE' } }
|
|
411
|
-
}
|
|
412
|
-
}
|
|
413
|
-
|
|
414
|
-
const api = eden<SSEContract>('http://localhost:3000')
|
|
415
|
-
|
|
416
|
-
const sub = api.events.subscribe({
|
|
417
|
-
onMessage: () => {}
|
|
418
|
-
})
|
|
419
|
-
|
|
420
|
-
expect(sub.connected).toBe(false) // 还在连接中
|
|
421
|
-
|
|
422
|
-
sub.unsubscribe()
|
|
423
|
-
expect(sub.connected).toBe(false)
|
|
424
|
-
})
|
|
425
|
-
})
|
package/tsconfig.dts.json
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"compilerOptions": {
|
|
3
|
-
"preserveSymlinks": true,
|
|
4
|
-
"target": "ES2021",
|
|
5
|
-
"lib": ["ESNext"],
|
|
6
|
-
"module": "ES2022",
|
|
7
|
-
"rootDir": "./src",
|
|
8
|
-
"moduleResolution": "node",
|
|
9
|
-
"resolveJsonModule": true,
|
|
10
|
-
"declaration": true,
|
|
11
|
-
"emitDeclarationOnly": true,
|
|
12
|
-
"outDir": "./dist",
|
|
13
|
-
"allowSyntheticDefaultImports": true,
|
|
14
|
-
"esModuleInterop": true,
|
|
15
|
-
"forceConsistentCasingInFileNames": true,
|
|
16
|
-
"strict": true,
|
|
17
|
-
"skipLibCheck": true
|
|
18
|
-
},
|
|
19
|
-
"exclude": ["node_modules", "test", "example", "dist", "build.ts"]
|
|
20
|
-
}
|
package/tsconfig.json
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"compilerOptions": {
|
|
3
|
-
"target": "ES2022",
|
|
4
|
-
"module": "ESNext",
|
|
5
|
-
"moduleResolution": "bundler",
|
|
6
|
-
"allowSyntheticDefaultImports": true,
|
|
7
|
-
"esModuleInterop": true,
|
|
8
|
-
"forceConsistentCasingInFileNames": true,
|
|
9
|
-
"strict": true,
|
|
10
|
-
"skipLibCheck": true,
|
|
11
|
-
"declaration": true,
|
|
12
|
-
"declarationMap": true,
|
|
13
|
-
"sourceMap": true,
|
|
14
|
-
"outDir": "./dist",
|
|
15
|
-
"rootDir": "./src",
|
|
16
|
-
"types": []
|
|
17
|
-
},
|
|
18
|
-
"include": ["src/**/*"],
|
|
19
|
-
"exclude": ["node_modules", "dist", "test"]
|
|
20
|
-
}
|
package/tsup.config.ts
DELETED
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import { defineConfig } from 'tsup'
|
|
2
|
-
|
|
3
|
-
export default defineConfig([
|
|
4
|
-
{
|
|
5
|
-
entry: ['src/index.ts'],
|
|
6
|
-
outDir: 'dist',
|
|
7
|
-
format: ['esm'],
|
|
8
|
-
target: 'node18',
|
|
9
|
-
dts: true,
|
|
10
|
-
clean: true,
|
|
11
|
-
sourcemap: true,
|
|
12
|
-
outExtension: () => ({ js: '.mjs' }),
|
|
13
|
-
},
|
|
14
|
-
{
|
|
15
|
-
entry: ['src/index.ts'],
|
|
16
|
-
outDir: 'dist/cjs',
|
|
17
|
-
format: ['cjs'],
|
|
18
|
-
target: 'node18',
|
|
19
|
-
dts: false,
|
|
20
|
-
clean: false,
|
|
21
|
-
},
|
|
22
|
-
])
|
|
23
|
-
|