xxf_react 0.7.1 → 0.7.3

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.
@@ -4,63 +4,114 @@
4
4
  * 使用 Builder 模式定义 API,类似 Android Retrofit。
5
5
  * 采用泛型累积模式,每次 .get() / .post() 调用都返回带有累积类型的新 Builder。
6
6
  *
7
- * ## 类型安全
7
+ * ## 类型安全特性
8
8
  *
9
- * - 每个端点名称保留字面量类型(如 `'getUser'`)
10
- * - 链式调用累积所有端点类型:`T & Record<K, ApiEndpoint<TResponse>>`
11
- * - `build()` 后返回完整类型安全的 API 对象
12
- * - 调用不存在的端点会产生编译错误
9
+ * - **端点名称字面量**:每个端点名称保留字面量类型(如 `'getUser'`)
10
+ * - **泛型累积**:链式调用累积所有端点类型 `T & Record<K, ApiEndpoint<...>>`
11
+ * - **路径参数推断**:从 path 字符串自动提取 `{param}` 并约束 `pathParams` 类型
12
+ * - **Body 类型约束**:POST/PUT/PATCH 支持指定请求体类型
13
+ * - **防止重复端点**:添加已存在的端点名称会编译报错
13
14
  *
14
15
  * @example
15
16
  * ```ts
17
+ * interface CreateUserDTO { name: string; email: string }
18
+ *
16
19
  * // 定义 API
17
20
  * const userApi = ApiBuilder.create({
18
21
  * baseUrl: 'https://api.example.com',
19
22
  * })
20
23
  * .get<User>('getUser', {
21
- * path: '/users/{id}',
24
+ * path: '/users/{id}', // pathParams 自动约束为 { id: string | number }
22
25
  * cache: { mode: CacheMode.IfCache, ttl: 60000 },
23
26
  * })
24
27
  * .get<User[]>('getUsers', { path: '/users' })
25
- * .post<User>('createUser', { path: '/users' })
28
+ * .post<User, CreateUserDTO>('createUser', { path: '/users' }) // body 类型为 CreateUserDTO
26
29
  * .build()
27
30
  *
28
31
  * // ✅ 类型正确推断
29
- * userApi.getUser({ pathParams: { id: '123' } }) // ApiStream<User>
30
- * userApi.getUsers() // ApiStream<User[]>
31
- * userApi.createUser({ body: { name: 'John' } }) // ApiStream<User>
32
+ * userApi.getUser({ pathParams: { id: '123' } }) // pathParams 必须有 id
33
+ * userApi.getUsers() // 无需 pathParams
34
+ * userApi.createUser({ body: { name: 'John', email: 'x' } }) // body 类型检查
32
35
  *
33
36
  * // ❌ 类型错误会被捕获
34
- * userApi.unknownMethod() // Error: Property 'unknownMethod' does not exist
35
- *
36
- * // 多次响应
37
- * for await (const { data, fromCache } of userApi.getUser({ pathParams: { id: '123' } })) {
38
- * console.log(data, fromCache)
39
- * }
37
+ * userApi.getUser({ pathParams: { wrong: '1' } }) // Error: 缺少 id
38
+ * userApi.createUser({ body: { wrong: 'x' } }) // Error: body 类型不匹配
39
+ * userApi.unknownMethod() // Error: 方法不存在
40
40
  * ```
41
41
  */
42
42
  import ky, { type Options as KyOptions } from 'ky';
43
43
  import { HttpClient } from '../client/HttpClient';
44
44
  import { ApiStream } from '../client/ApiStream';
45
- import type { RequestConfig, ApiOptions, CacheConfig, HttpClientConfig } from '../types';
45
+ import type { RequestConfig, CacheConfig, HttpClientConfig } from '../types';
46
+ /**
47
+ * 从路径字符串中提取参数名称
48
+ *
49
+ * @example
50
+ * ExtractPathParams<'/users/{id}'> = 'id'
51
+ * ExtractPathParams<'/users/{userId}/posts/{postId}'> = 'userId' | 'postId'
52
+ * ExtractPathParams<'/users'> = never
53
+ */
54
+ type ExtractPathParams<Path extends string> = Path extends `${string}{${infer Param}}${infer Rest}` ? Param | ExtractPathParams<Rest> : never;
55
+ /**
56
+ * 根据路径参数生成 pathParams 类型
57
+ *
58
+ * @example
59
+ * PathParamsType<'/users/{id}'> = { id: string | number }
60
+ * PathParamsType<'/users'> = undefined (无需路径参数)
61
+ */
62
+ type PathParamsType<Path extends string> = ExtractPathParams<Path> extends never ? undefined : Record<ExtractPathParams<Path>, string | number>;
63
+ /**
64
+ * 判断路径是否有参数
65
+ */
66
+ type HasPathParams<Path extends string> = ExtractPathParams<Path> extends never ? false : true;
67
+ /**
68
+ * 带类型约束的 API 调用选项
69
+ *
70
+ * @template Path 路径字符串字面量
71
+ * @template TBody 请求体类型(用于 POST/PUT/PATCH)
72
+ * @template HasBody 是否需要 body
73
+ */
74
+ type TypedApiOptions<Path extends string, TBody = never, HasBody extends boolean = false> = {
75
+ /** 查询参数,会自动过滤 undefined 和 null */
76
+ query?: Record<string, string | number | boolean | undefined>;
77
+ /** 覆盖缓存配置(优先级最高) */
78
+ cache?: Partial<CacheConfig>;
79
+ /** 覆盖请求头 */
80
+ headers?: Record<string, string>;
81
+ } & (HasPathParams<Path> extends true ? {
82
+ pathParams: PathParamsType<Path>;
83
+ } : {
84
+ pathParams?: undefined;
85
+ }) & (HasBody extends true ? {
86
+ body?: TBody;
87
+ } : {
88
+ body?: never;
89
+ });
46
90
  /**
47
- * API 端点函数类型
91
+ * API 端点函数类型(带路径参数和 Body 类型约束)
92
+ *
93
+ * @template TResponse 响应数据类型
94
+ * @template Path 路径字符串字面量
95
+ * @template TBody 请求体类型
96
+ * @template HasBody 是否需要 body
48
97
  */
49
- export type ApiEndpoint<TResponse> = {
50
- (options?: ApiOptions): ApiStream<TResponse>;
98
+ export type ApiEndpoint<TResponse, Path extends string = string, TBody = never, HasBody extends boolean = false> = {
99
+ (options?: TypedApiOptions<Path, TBody, HasBody>): ApiStream<TResponse>;
51
100
  /** 请求配置 */
52
101
  config: RequestConfig;
53
102
  };
54
103
  /**
55
- * API 定义类型
104
+ * API 定义类型(端点集合)
56
105
  */
57
- export type ApiDefinition = Record<string, ApiEndpoint<unknown>>;
106
+ export type ApiDefinition = Record<string, ApiEndpoint<unknown, string, unknown, boolean>>;
58
107
  /**
59
- * 端点选项
108
+ * 端点选项(构建时配置)
109
+ *
110
+ * @template Path 路径字符串字面量,用于类型推断
60
111
  */
61
- interface EndpointOptions {
112
+ interface EndpointOptions<Path extends string = string> {
62
113
  /** 请求路径,支持 {param} 占位符 */
63
- path: string;
114
+ path: Path;
64
115
  /** 请求方法 */
65
116
  method?: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';
66
117
  /** 请求头 */
@@ -73,7 +124,9 @@ interface EndpointOptions {
73
124
  cache?: CacheConfig;
74
125
  }
75
126
  /**
76
- * API 构建器
127
+ * API 构建器类
128
+ *
129
+ * @template T 当前已定义的端点类型集合,通过链式调用累积
77
130
  */
78
131
  export declare class ApiBuilder<T extends ApiDefinition = Record<string, never>> {
79
132
  private client;
@@ -81,54 +134,206 @@ export declare class ApiBuilder<T extends ApiDefinition = Record<string, never>>
81
134
  private constructor();
82
135
  /**
83
136
  * 创建 API 构建器
137
+ *
138
+ * @param config HTTP 客户端配置
139
+ * @returns 新的 ApiBuilder 实例
140
+ *
141
+ * @example
142
+ * ```ts
143
+ * const api = ApiBuilder.create({
144
+ * baseUrl: 'https://api.example.com',
145
+ * timeout: 10000,
146
+ * })
147
+ * ```
84
148
  */
85
149
  static create(config: HttpClientConfig): ApiBuilder;
86
150
  /**
87
151
  * 从现有 ky 实例创建
152
+ *
153
+ * @param kyInstance 已配置的 ky 实例
154
+ * @param config 可选的额外配置
155
+ * @returns 新的 ApiBuilder 实例
156
+ *
157
+ * @example
158
+ * ```ts
159
+ * const kyInstance = ky.create({ prefixUrl: 'https://api.example.com' })
160
+ * const api = ApiBuilder.from(kyInstance)
161
+ * ```
88
162
  */
89
163
  static from(kyInstance: ReturnType<typeof ky.create>, config?: Partial<HttpClientConfig>): ApiBuilder;
90
164
  /**
91
165
  * 定义 GET 请求
92
- * @param name 端点名称(使用字面量类型实现类型累积)
166
+ *
167
+ * @template TResponse 响应数据类型
168
+ * @template K 端点名称(自动推断,不能与已有端点重复)
169
+ * @template Path 路径字符串(自动推断,用于提取路径参数)
170
+ *
171
+ * @param name 端点名称
172
+ * @param options 端点配置
173
+ * @returns 累积了新端点类型的 ApiBuilder
174
+ *
175
+ * @example
176
+ * ```ts
177
+ * .get<User>('getUser', {
178
+ * path: '/users/{id}', // pathParams 自动约束为 { id: string | number }
179
+ * cache: { mode: CacheMode.IfCache },
180
+ * })
181
+ * ```
93
182
  */
94
- get<TResponse, K extends string>(name: K, options: Omit<EndpointOptions, 'method'>): ApiBuilder<T & Record<K, ApiEndpoint<TResponse>>>;
183
+ get<TResponse, K extends string = string, Path extends string = string>(name: K extends keyof T ? never : K, // 防止重复端点名称
184
+ options: Omit<EndpointOptions<Path>, 'method'>): ApiBuilder<T & Record<K, ApiEndpoint<TResponse, Path, never, false>>>;
95
185
  /**
96
186
  * 定义 POST 请求
97
- * @param name 端点名称(使用字面量类型实现类型累积)
187
+ *
188
+ * @template TResponse 响应数据类型
189
+ * @template TBody 请求体类型(可选,默认 unknown)
190
+ * @template K 端点名称(自动推断)
191
+ * @template Path 路径字符串(自动推断)
192
+ *
193
+ * @param name 端点名称
194
+ * @param options 端点配置
195
+ * @returns 累积了新端点类型的 ApiBuilder
196
+ *
197
+ * @example
198
+ * ```ts
199
+ * interface CreateUserDTO { name: string; email: string }
200
+ *
201
+ * .post<User, CreateUserDTO>('createUser', {
202
+ * path: '/users',
203
+ * })
204
+ * // body 类型约束为 CreateUserDTO
205
+ * ```
98
206
  */
99
- post<TResponse, K extends string>(name: K, options: Omit<EndpointOptions, 'method'>): ApiBuilder<T & Record<K, ApiEndpoint<TResponse>>>;
207
+ post<TResponse, TBody = unknown, K extends string = string, Path extends string = string>(name: K extends keyof T ? never : K, options: Omit<EndpointOptions<Path>, 'method'>): ApiBuilder<T & Record<K, ApiEndpoint<TResponse, Path, TBody, true>>>;
100
208
  /**
101
209
  * 定义 PUT 请求
102
- * @param name 端点名称(使用字面量类型实现类型累积)
210
+ *
211
+ * @template TResponse 响应数据类型
212
+ * @template TBody 请求体类型(可选,默认 unknown)
213
+ * @template K 端点名称(自动推断)
214
+ * @template Path 路径字符串(自动推断)
215
+ *
216
+ * @param name 端点名称
217
+ * @param options 端点配置
218
+ * @returns 累积了新端点类型的 ApiBuilder
219
+ *
220
+ * @example
221
+ * ```ts
222
+ * .put<User, UpdateUserDTO>('updateUser', {
223
+ * path: '/users/{id}',
224
+ * })
225
+ * ```
103
226
  */
104
- put<TResponse, K extends string>(name: K, options: Omit<EndpointOptions, 'method'>): ApiBuilder<T & Record<K, ApiEndpoint<TResponse>>>;
227
+ put<TResponse, TBody = unknown, K extends string = string, Path extends string = string>(name: K extends keyof T ? never : K, options: Omit<EndpointOptions<Path>, 'method'>): ApiBuilder<T & Record<K, ApiEndpoint<TResponse, Path, TBody, true>>>;
105
228
  /**
106
229
  * 定义 DELETE 请求
107
- * @param name 端点名称(使用字面量类型实现类型累积)
230
+ *
231
+ * @template TResponse 响应数据类型
232
+ * @template K 端点名称(自动推断)
233
+ * @template Path 路径字符串(自动推断)
234
+ *
235
+ * @param name 端点名称
236
+ * @param options 端点配置
237
+ * @returns 累积了新端点类型的 ApiBuilder
238
+ *
239
+ * @example
240
+ * ```ts
241
+ * .delete<void>('deleteUser', {
242
+ * path: '/users/{id}',
243
+ * })
244
+ * ```
108
245
  */
109
- delete<TResponse, K extends string>(name: K, options: Omit<EndpointOptions, 'method'>): ApiBuilder<T & Record<K, ApiEndpoint<TResponse>>>;
246
+ delete<TResponse, K extends string = string, Path extends string = string>(name: K extends keyof T ? never : K, options: Omit<EndpointOptions<Path>, 'method'>): ApiBuilder<T & Record<K, ApiEndpoint<TResponse, Path, never, false>>>;
110
247
  /**
111
248
  * 定义 PATCH 请求
112
- * @param name 端点名称(使用字面量类型实现类型累积)
249
+ *
250
+ * @template TResponse 响应数据类型
251
+ * @template TBody 请求体类型(可选,默认 unknown)
252
+ * @template K 端点名称(自动推断)
253
+ * @template Path 路径字符串(自动推断)
254
+ *
255
+ * @param name 端点名称
256
+ * @param options 端点配置
257
+ * @returns 累积了新端点类型的 ApiBuilder
258
+ *
259
+ * @example
260
+ * ```ts
261
+ * .patch<User, Partial<UpdateUserDTO>>('patchUser', {
262
+ * path: '/users/{id}',
263
+ * })
264
+ * ```
113
265
  */
114
- patch<TResponse, K extends string>(name: K, options: Omit<EndpointOptions, 'method'>): ApiBuilder<T & Record<K, ApiEndpoint<TResponse>>>;
266
+ patch<TResponse, TBody = unknown, K extends string = string, Path extends string = string>(name: K extends keyof T ? never : K, options: Omit<EndpointOptions<Path>, 'method'>): ApiBuilder<T & Record<K, ApiEndpoint<TResponse, Path, TBody, true>>>;
115
267
  /**
116
- * 通用端点定义
117
- * 使用 `as unknown as` 类型断言绕过 TS 限制,实现泛型累积
268
+ * 通用端点定义(内部方法)
269
+ *
270
+ * @template TResponse 响应数据类型
271
+ * @template K 端点名称
272
+ * @template Path 路径字符串
273
+ * @template TBody 请求体类型
274
+ * @template HasBody 是否有请求体
118
275
  */
119
276
  private endpoint;
120
277
  /**
121
278
  * 扩展 ky 实例(添加 hooks 等)
279
+ *
280
+ * @param options ky 配置选项
281
+ * @returns this(支持链式调用)
282
+ *
283
+ * @example
284
+ * ```ts
285
+ * .extend({
286
+ * hooks: {
287
+ * beforeRequest: [(req) => {
288
+ * req.headers.set('Authorization', `Bearer ${token}`)
289
+ * }],
290
+ * },
291
+ * })
292
+ * ```
122
293
  */
123
294
  extend(options: KyOptions): this;
124
295
  /**
125
296
  * 构建 API 服务
297
+ *
298
+ * 将所有定义的端点转换为可调用的 API 对象。
299
+ * 每个端点都带有完整的类型信息(响应类型、路径参数、请求体类型)。
300
+ *
301
+ * @returns 类型安全的 API 对象
302
+ *
303
+ * @example
304
+ * ```ts
305
+ * const api = ApiBuilder.create({ baseUrl: '...' })
306
+ * .get<User>('getUser', { path: '/users/{id}' })
307
+ * .post<User, CreateUserDTO>('createUser', { path: '/users' })
308
+ * .build()
309
+ *
310
+ * // 类型安全调用
311
+ * await api.getUser({ pathParams: { id: '1' } })
312
+ * await api.createUser({ body: { name: 'John', email: 'x@x.com' } })
313
+ * ```
126
314
  */
127
315
  build(): T;
128
316
  /**
129
317
  * 获取 HTTP 客户端实例(用于高级操作)
318
+ *
319
+ * @returns HttpClient 实例
320
+ *
321
+ * @example
322
+ * ```ts
323
+ * const client = api.getClient()
324
+ * await client.clearCache()
325
+ * ```
130
326
  */
131
327
  getClient(): HttpClient;
132
328
  }
133
- export {};
329
+ /**
330
+ * 从路径字符串中提取参数名称
331
+ *
332
+ * @example
333
+ * ```ts
334
+ * type Params = ExtractPathParams<'/users/{userId}/posts/{postId}'>
335
+ * // Params = 'userId' | 'postId'
336
+ * ```
337
+ */
338
+ export type { ExtractPathParams, PathParamsType, HasPathParams };
134
339
  //# sourceMappingURL=ApiBuilder.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ApiBuilder.d.ts","sourceRoot":"","sources":["../../../src/http/api/ApiBuilder.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ApiBuilder.d.ts","sourceRoot":"","sources":["../../../src/http/api/ApiBuilder.ts"],"names":[],"mappings":"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"}
@@ -4,44 +4,49 @@
4
4
  * 使用 Builder 模式定义 API,类似 Android Retrofit。
5
5
  * 采用泛型累积模式,每次 .get() / .post() 调用都返回带有累积类型的新 Builder。
6
6
  *
7
- * ## 类型安全
7
+ * ## 类型安全特性
8
8
  *
9
- * - 每个端点名称保留字面量类型(如 `'getUser'`)
10
- * - 链式调用累积所有端点类型:`T & Record<K, ApiEndpoint<TResponse>>`
11
- * - `build()` 后返回完整类型安全的 API 对象
12
- * - 调用不存在的端点会产生编译错误
9
+ * - **端点名称字面量**:每个端点名称保留字面量类型(如 `'getUser'`)
10
+ * - **泛型累积**:链式调用累积所有端点类型 `T & Record<K, ApiEndpoint<...>>`
11
+ * - **路径参数推断**:从 path 字符串自动提取 `{param}` 并约束 `pathParams` 类型
12
+ * - **Body 类型约束**:POST/PUT/PATCH 支持指定请求体类型
13
+ * - **防止重复端点**:添加已存在的端点名称会编译报错
13
14
  *
14
15
  * @example
15
16
  * ```ts
17
+ * interface CreateUserDTO { name: string; email: string }
18
+ *
16
19
  * // 定义 API
17
20
  * const userApi = ApiBuilder.create({
18
21
  * baseUrl: 'https://api.example.com',
19
22
  * })
20
23
  * .get<User>('getUser', {
21
- * path: '/users/{id}',
24
+ * path: '/users/{id}', // pathParams 自动约束为 { id: string | number }
22
25
  * cache: { mode: CacheMode.IfCache, ttl: 60000 },
23
26
  * })
24
27
  * .get<User[]>('getUsers', { path: '/users' })
25
- * .post<User>('createUser', { path: '/users' })
28
+ * .post<User, CreateUserDTO>('createUser', { path: '/users' }) // body 类型为 CreateUserDTO
26
29
  * .build()
27
30
  *
28
31
  * // ✅ 类型正确推断
29
- * userApi.getUser({ pathParams: { id: '123' } }) // ApiStream<User>
30
- * userApi.getUsers() // ApiStream<User[]>
31
- * userApi.createUser({ body: { name: 'John' } }) // ApiStream<User>
32
+ * userApi.getUser({ pathParams: { id: '123' } }) // pathParams 必须有 id
33
+ * userApi.getUsers() // 无需 pathParams
34
+ * userApi.createUser({ body: { name: 'John', email: 'x' } }) // body 类型检查
32
35
  *
33
36
  * // ❌ 类型错误会被捕获
34
- * userApi.unknownMethod() // Error: Property 'unknownMethod' does not exist
35
- *
36
- * // 多次响应
37
- * for await (const { data, fromCache } of userApi.getUser({ pathParams: { id: '123' } })) {
38
- * console.log(data, fromCache)
39
- * }
37
+ * userApi.getUser({ pathParams: { wrong: '1' } }) // Error: 缺少 id
38
+ * userApi.createUser({ body: { wrong: 'x' } }) // Error: body 类型不匹配
39
+ * userApi.unknownMethod() // Error: 方法不存在
40
40
  * ```
41
41
  */
42
42
  import { HttpClient } from '../client/HttpClient';
43
+ // ============================================================================
44
+ // API 构建器
45
+ // ============================================================================
43
46
  /**
44
- * API 构建器
47
+ * API 构建器类
48
+ *
49
+ * @template T 当前已定义的端点类型集合,通过链式调用累积
45
50
  */
46
51
  export class ApiBuilder {
47
52
  constructor(config) {
@@ -50,12 +55,33 @@ export class ApiBuilder {
50
55
  }
51
56
  /**
52
57
  * 创建 API 构建器
58
+ *
59
+ * @param config HTTP 客户端配置
60
+ * @returns 新的 ApiBuilder 实例
61
+ *
62
+ * @example
63
+ * ```ts
64
+ * const api = ApiBuilder.create({
65
+ * baseUrl: 'https://api.example.com',
66
+ * timeout: 10000,
67
+ * })
68
+ * ```
53
69
  */
54
70
  static create(config) {
55
71
  return new ApiBuilder(config);
56
72
  }
57
73
  /**
58
74
  * 从现有 ky 实例创建
75
+ *
76
+ * @param kyInstance 已配置的 ky 实例
77
+ * @param config 可选的额外配置
78
+ * @returns 新的 ApiBuilder 实例
79
+ *
80
+ * @example
81
+ * ```ts
82
+ * const kyInstance = ky.create({ prefixUrl: 'https://api.example.com' })
83
+ * const api = ApiBuilder.from(kyInstance)
84
+ * ```
59
85
  */
60
86
  static from(kyInstance, config) {
61
87
  const builder = new ApiBuilder({
@@ -67,42 +93,125 @@ export class ApiBuilder {
67
93
  }
68
94
  /**
69
95
  * 定义 GET 请求
70
- * @param name 端点名称(使用字面量类型实现类型累积)
96
+ *
97
+ * @template TResponse 响应数据类型
98
+ * @template K 端点名称(自动推断,不能与已有端点重复)
99
+ * @template Path 路径字符串(自动推断,用于提取路径参数)
100
+ *
101
+ * @param name 端点名称
102
+ * @param options 端点配置
103
+ * @returns 累积了新端点类型的 ApiBuilder
104
+ *
105
+ * @example
106
+ * ```ts
107
+ * .get<User>('getUser', {
108
+ * path: '/users/{id}', // pathParams 自动约束为 { id: string | number }
109
+ * cache: { mode: CacheMode.IfCache },
110
+ * })
111
+ * ```
71
112
  */
72
- get(name, options) {
113
+ get(name, // 防止重复端点名称
114
+ options) {
73
115
  return this.endpoint(name, { ...options, method: 'GET' });
74
116
  }
75
117
  /**
76
118
  * 定义 POST 请求
77
- * @param name 端点名称(使用字面量类型实现类型累积)
119
+ *
120
+ * @template TResponse 响应数据类型
121
+ * @template TBody 请求体类型(可选,默认 unknown)
122
+ * @template K 端点名称(自动推断)
123
+ * @template Path 路径字符串(自动推断)
124
+ *
125
+ * @param name 端点名称
126
+ * @param options 端点配置
127
+ * @returns 累积了新端点类型的 ApiBuilder
128
+ *
129
+ * @example
130
+ * ```ts
131
+ * interface CreateUserDTO { name: string; email: string }
132
+ *
133
+ * .post<User, CreateUserDTO>('createUser', {
134
+ * path: '/users',
135
+ * })
136
+ * // body 类型约束为 CreateUserDTO
137
+ * ```
78
138
  */
79
139
  post(name, options) {
80
140
  return this.endpoint(name, { ...options, method: 'POST' });
81
141
  }
82
142
  /**
83
143
  * 定义 PUT 请求
84
- * @param name 端点名称(使用字面量类型实现类型累积)
144
+ *
145
+ * @template TResponse 响应数据类型
146
+ * @template TBody 请求体类型(可选,默认 unknown)
147
+ * @template K 端点名称(自动推断)
148
+ * @template Path 路径字符串(自动推断)
149
+ *
150
+ * @param name 端点名称
151
+ * @param options 端点配置
152
+ * @returns 累积了新端点类型的 ApiBuilder
153
+ *
154
+ * @example
155
+ * ```ts
156
+ * .put<User, UpdateUserDTO>('updateUser', {
157
+ * path: '/users/{id}',
158
+ * })
159
+ * ```
85
160
  */
86
161
  put(name, options) {
87
162
  return this.endpoint(name, { ...options, method: 'PUT' });
88
163
  }
89
164
  /**
90
165
  * 定义 DELETE 请求
91
- * @param name 端点名称(使用字面量类型实现类型累积)
166
+ *
167
+ * @template TResponse 响应数据类型
168
+ * @template K 端点名称(自动推断)
169
+ * @template Path 路径字符串(自动推断)
170
+ *
171
+ * @param name 端点名称
172
+ * @param options 端点配置
173
+ * @returns 累积了新端点类型的 ApiBuilder
174
+ *
175
+ * @example
176
+ * ```ts
177
+ * .delete<void>('deleteUser', {
178
+ * path: '/users/{id}',
179
+ * })
180
+ * ```
92
181
  */
93
182
  delete(name, options) {
94
183
  return this.endpoint(name, { ...options, method: 'DELETE' });
95
184
  }
96
185
  /**
97
186
  * 定义 PATCH 请求
98
- * @param name 端点名称(使用字面量类型实现类型累积)
187
+ *
188
+ * @template TResponse 响应数据类型
189
+ * @template TBody 请求体类型(可选,默认 unknown)
190
+ * @template K 端点名称(自动推断)
191
+ * @template Path 路径字符串(自动推断)
192
+ *
193
+ * @param name 端点名称
194
+ * @param options 端点配置
195
+ * @returns 累积了新端点类型的 ApiBuilder
196
+ *
197
+ * @example
198
+ * ```ts
199
+ * .patch<User, Partial<UpdateUserDTO>>('patchUser', {
200
+ * path: '/users/{id}',
201
+ * })
202
+ * ```
99
203
  */
100
204
  patch(name, options) {
101
205
  return this.endpoint(name, { ...options, method: 'PATCH' });
102
206
  }
103
207
  /**
104
- * 通用端点定义
105
- * 使用 `as unknown as` 类型断言绕过 TS 限制,实现泛型累积
208
+ * 通用端点定义(内部方法)
209
+ *
210
+ * @template TResponse 响应数据类型
211
+ * @template K 端点名称
212
+ * @template Path 路径字符串
213
+ * @template TBody 请求体类型
214
+ * @template HasBody 是否有请求体
106
215
  */
107
216
  endpoint(name, options) {
108
217
  var _a;
@@ -120,6 +229,20 @@ export class ApiBuilder {
120
229
  }
121
230
  /**
122
231
  * 扩展 ky 实例(添加 hooks 等)
232
+ *
233
+ * @param options ky 配置选项
234
+ * @returns this(支持链式调用)
235
+ *
236
+ * @example
237
+ * ```ts
238
+ * .extend({
239
+ * hooks: {
240
+ * beforeRequest: [(req) => {
241
+ * req.headers.set('Authorization', `Bearer ${token}`)
242
+ * }],
243
+ * },
244
+ * })
245
+ * ```
123
246
  */
124
247
  extend(options) {
125
248
  this.client.extendKy(options);
@@ -127,10 +250,28 @@ export class ApiBuilder {
127
250
  }
128
251
  /**
129
252
  * 构建 API 服务
253
+ *
254
+ * 将所有定义的端点转换为可调用的 API 对象。
255
+ * 每个端点都带有完整的类型信息(响应类型、路径参数、请求体类型)。
256
+ *
257
+ * @returns 类型安全的 API 对象
258
+ *
259
+ * @example
260
+ * ```ts
261
+ * const api = ApiBuilder.create({ baseUrl: '...' })
262
+ * .get<User>('getUser', { path: '/users/{id}' })
263
+ * .post<User, CreateUserDTO>('createUser', { path: '/users' })
264
+ * .build()
265
+ *
266
+ * // 类型安全调用
267
+ * await api.getUser({ pathParams: { id: '1' } })
268
+ * await api.createUser({ body: { name: 'John', email: 'x@x.com' } })
269
+ * ```
130
270
  */
131
271
  build() {
132
272
  const api = {};
133
273
  for (const [name, config] of this.endpoints) {
274
+ // 运行时不需要类型检查,类型安全由编译时保证
134
275
  const endpoint = ((options) => {
135
276
  return this.client.request(config, options);
136
277
  });
@@ -141,6 +282,14 @@ export class ApiBuilder {
141
282
  }
142
283
  /**
143
284
  * 获取 HTTP 客户端实例(用于高级操作)
285
+ *
286
+ * @returns HttpClient 实例
287
+ *
288
+ * @example
289
+ * ```ts
290
+ * const client = api.getClient()
291
+ * await client.clearCache()
292
+ * ```
144
293
  */
145
294
  getClient() {
146
295
  return this.client;
@@ -1,2 +1,2 @@
1
- export { ApiBuilder, type ApiEndpoint, type ApiDefinition } from './ApiBuilder';
1
+ export { ApiBuilder, type ApiEndpoint, type ApiDefinition, type ExtractPathParams, type PathParamsType, type HasPathParams, } from './ApiBuilder';
2
2
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/http/api/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,KAAK,WAAW,EAAE,KAAK,aAAa,EAAE,MAAM,cAAc,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/http/api/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACH,UAAU,EACV,KAAK,WAAW,EAChB,KAAK,aAAa,EAElB,KAAK,iBAAiB,EACtB,KAAK,cAAc,EACnB,KAAK,aAAa,GACrB,MAAM,cAAc,CAAA"}
@@ -1 +1 @@
1
- export { ApiBuilder } from './ApiBuilder';
1
+ export { ApiBuilder, } from './ApiBuilder';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "xxf_react",
3
- "version": "0.7.1",
3
+ "version": "0.7.3",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "main": "dist/index.js",