xxf_react 0.7.3 → 0.7.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.
@@ -8,110 +8,104 @@
8
8
  *
9
9
  * - **端点名称字面量**:每个端点名称保留字面量类型(如 `'getUser'`)
10
10
  * - **泛型累积**:链式调用累积所有端点类型 `T & Record<K, ApiEndpoint<...>>`
11
- * - **路径参数推断**:从 path 字符串自动提取 `{param}` 并约束 `pathParams` 类型
12
11
  * - **Body 类型约束**:POST/PUT/PATCH 支持指定请求体类型
13
- * - **防止重复端点**:添加已存在的端点名称会编译报错
14
12
  *
15
13
  * @example
16
14
  * ```ts
17
15
  * interface CreateUserDTO { name: string; email: string }
18
16
  *
19
- * // 定义 API
20
17
  * const userApi = ApiBuilder.create({
21
18
  * baseUrl: 'https://api.example.com',
22
19
  * })
23
- * .get<User>('getUser', {
24
- * path: '/users/{id}', // pathParams 自动约束为 { id: string | number }
25
- * cache: { mode: CacheMode.IfCache, ttl: 60000 },
26
- * })
20
+ * .get<User>('getUser', { path: '/users/{id}' })
27
21
  * .get<User[]>('getUsers', { path: '/users' })
28
- * .post<User, CreateUserDTO>('createUser', { path: '/users' }) // body 类型为 CreateUserDTO
22
+ * .post<User, CreateUserDTO>('createUser', { path: '/users' })
29
23
  * .build()
30
24
  *
31
25
  * // ✅ 类型正确推断
32
- * userApi.getUser({ pathParams: { id: '123' } }) // pathParams 必须有 id
33
- * userApi.getUsers() // 无需 pathParams
34
- * userApi.createUser({ body: { name: 'John', email: 'x' } }) // body 类型检查
26
+ * userApi.getUser({ pathParams: { id: '123' } })
27
+ * userApi.createUser({ body: { name: 'John', email: 'x' } })
35
28
  *
36
29
  * // ❌ 类型错误会被捕获
37
- * userApi.getUser({ pathParams: { wrong: '1' } }) // Error: 缺少 id
38
- * userApi.createUser({ body: { wrong: 'x' } }) // Error: body 类型不匹配
39
- * userApi.unknownMethod() // Error: 方法不存在
30
+ * userApi.unknownMethod() // Error: 方法不存在
40
31
  * ```
41
32
  */
42
33
  import ky, { type Options as KyOptions } from 'ky';
43
34
  import { HttpClient } from '../client/HttpClient';
44
35
  import { ApiStream } from '../client/ApiStream';
45
- import type { RequestConfig, CacheConfig, HttpClientConfig } from '../types';
36
+ import type { RequestConfig, ApiOptions, CacheConfig, HttpClientConfig } from '../types';
46
37
  /**
47
- * 从路径字符串中提取参数名称
48
- *
49
- * @example
50
- * ExtractPathParams<'/users/{id}'> = 'id'
51
- * ExtractPathParams<'/users/{userId}/posts/{postId}'> = 'userId' | 'postId'
52
- * ExtractPathParams<'/users'> = never
38
+ * 基础 API 调用选项(不含 body)
53
39
  */
54
- type ExtractPathParams<Path extends string> = Path extends `${string}{${infer Param}}${infer Rest}` ? Param | ExtractPathParams<Rest> : never;
40
+ type BaseApiOptions = Omit<ApiOptions, 'body'>;
55
41
  /**
56
- * 根据路径参数生成 pathParams 类型
57
- *
58
- * @example
59
- * PathParamsType<'/users/{id}'> = { id: string | number }
60
- * PathParamsType<'/users'> = undefined (无需路径参数)
42
+ * body 的 API 调用选项(body 必需)
61
43
  */
62
- type PathParamsType<Path extends string> = ExtractPathParams<Path> extends never ? undefined : Record<ExtractPathParams<Path>, string | number>;
44
+ type WithBodyRequired<TBody> = BaseApiOptions & {
45
+ body: TBody;
46
+ };
63
47
  /**
64
- * 判断路径是否有参数
48
+ * 带 body 的 API 调用选项(body 可选)
65
49
  */
66
- type HasPathParams<Path extends string> = ExtractPathParams<Path> extends never ? false : true;
50
+ type WithBodyOptional = BaseApiOptions & {
51
+ body?: unknown;
52
+ };
67
53
  /**
68
- * 带类型约束的 API 调用选项
69
- *
70
- * @template Path 路径字符串字面量
71
- * @template TBody 请求体类型(用于 POST/PUT/PATCH)
72
- * @template HasBody 是否需要 body
54
+ * body 的 API 调用选项(GET/DELETE)
73
55
  */
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
- } : {
56
+ type WithoutBodyOptions = BaseApiOptions & {
88
57
  body?: never;
89
- });
58
+ };
90
59
  /**
91
- * API 端点函数类型(带路径参数和 Body 类型约束)
92
- *
93
- * @template TResponse 响应数据类型
94
- * @template Path 路径字符串字面量
95
- * @template TBody 请求体类型
96
- * @template HasBody 是否需要 body
60
+ * API 端点函数类型(有 body 且必需 - POST/PUT/PATCH 指定了 TBody)
97
61
  */
98
- export type ApiEndpoint<TResponse, Path extends string = string, TBody = never, HasBody extends boolean = false> = {
99
- (options?: TypedApiOptions<Path, TBody, HasBody>): ApiStream<TResponse>;
62
+ export type ApiEndpointWithBodyRequired<TResponse, TBody> = {
63
+ (options: WithBodyRequired<TBody>): ApiStream<TResponse>;
100
64
  /** 请求配置 */
101
65
  config: RequestConfig;
102
66
  };
67
+ /**
68
+ * API 端点函数类型(有 body 但可选 - POST/PUT/PATCH 未指定 TBody)
69
+ */
70
+ export type ApiEndpointWithBodyOptional<TResponse> = {
71
+ (options?: WithBodyOptional): ApiStream<TResponse>;
72
+ /** 请求配置 */
73
+ config: RequestConfig;
74
+ };
75
+ /**
76
+ * API 端点函数类型(无 body - GET/DELETE)
77
+ */
78
+ export type ApiEndpointWithoutBody<TResponse> = {
79
+ (options?: WithoutBodyOptions): ApiStream<TResponse>;
80
+ /** 请求配置 */
81
+ config: RequestConfig;
82
+ };
83
+ /**
84
+ * 检测是否为 unknown 类型
85
+ */
86
+ type IsUnknown<T> = [unknown] extends [T] ? ([T] extends [unknown] ? true : false) : false;
87
+ /**
88
+ * 检测是否为 void 类型
89
+ */
90
+ type IsVoid<T> = [T] extends [void] ? ([void] extends [T] ? true : false) : false;
91
+ /**
92
+ * 有 body 的端点类型(根据 TBody 选择必需或可选)
93
+ */
94
+ export type ApiEndpointWithBody<TResponse, TBody> = IsUnknown<TBody> extends true ? ApiEndpointWithBodyOptional<TResponse> : IsVoid<TBody> extends true ? ApiEndpointWithBodyOptional<TResponse> : ApiEndpointWithBodyRequired<TResponse, TBody>;
95
+ /**
96
+ * API 端点函数类型(泛型版本,用于内部和类型导出)
97
+ */
98
+ export type ApiEndpoint<TResponse, TBody = unknown, HasBody extends boolean = false> = HasBody extends true ? ApiEndpointWithBody<TResponse, TBody> : ApiEndpointWithoutBody<TResponse>;
103
99
  /**
104
100
  * API 定义类型(端点集合)
105
101
  */
106
- export type ApiDefinition = Record<string, ApiEndpoint<unknown, string, unknown, boolean>>;
102
+ export type ApiDefinition = Record<string, ApiEndpoint<unknown, unknown, boolean>>;
107
103
  /**
108
104
  * 端点选项(构建时配置)
109
- *
110
- * @template Path 路径字符串字面量,用于类型推断
111
105
  */
112
- interface EndpointOptions<Path extends string = string> {
106
+ interface EndpointOptions {
113
107
  /** 请求路径,支持 {param} 占位符 */
114
- path: Path;
108
+ path: string;
115
109
  /** 请求方法 */
116
110
  method?: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';
117
111
  /** 请求头 */
@@ -123,12 +117,39 @@ interface EndpointOptions<Path extends string = string> {
123
117
  /** 缓存配置 */
124
118
  cache?: CacheConfig;
125
119
  }
120
+ /**
121
+ * 空 API 类型(空记录,不允许任意属性访问)
122
+ */
123
+ type EmptyApi = {};
126
124
  /**
127
125
  * API 构建器类
128
126
  *
127
+ * 使用链式调用定义类型安全的 API 端点。
128
+ *
129
129
  * @template T 当前已定义的端点类型集合,通过链式调用累积
130
+ *
131
+ * @remarks
132
+ * **类型深度限制**:由于 TypeScript 对深层交叉类型的处理限制,
133
+ * 当单个 builder 链式调用超过 4 个端点时,类型推断可能不够精确。
134
+ *
135
+ * 建议:
136
+ * - 保持单个 builder 的端点数量在 4 个以内
137
+ * - 如需更多端点,功能仍然正常,但部分类型检查可能被放宽
138
+ *
139
+ * @example
140
+ * ```ts
141
+ * const userApi = ApiBuilder.create({ baseUrl: '/api' })
142
+ * .get<User>('getUser', { path: '/users/{id}' })
143
+ * .post<User, CreateUserDTO>('createUser', { path: '/users' })
144
+ * .put<User, UpdateUserDTO>('updateUser', { path: '/users/{id}' })
145
+ * .build()
146
+ *
147
+ * // 类型安全调用
148
+ * const user = await userApi.getUser({ pathParams: { id: '1' } })
149
+ * const newUser = await userApi.createUser({ body: { name: 'John', email: 'j@test.com' } })
150
+ * ```
130
151
  */
131
- export declare class ApiBuilder<T extends ApiDefinition = Record<string, never>> {
152
+ export declare class ApiBuilder<T extends object = EmptyApi> {
132
153
  private client;
133
154
  private endpoints;
134
155
  private constructor();
@@ -137,203 +158,85 @@ export declare class ApiBuilder<T extends ApiDefinition = Record<string, never>>
137
158
  *
138
159
  * @param config HTTP 客户端配置
139
160
  * @returns 新的 ApiBuilder 实例
140
- *
141
- * @example
142
- * ```ts
143
- * const api = ApiBuilder.create({
144
- * baseUrl: 'https://api.example.com',
145
- * timeout: 10000,
146
- * })
147
- * ```
148
161
  */
149
- static create(config: HttpClientConfig): ApiBuilder;
162
+ static create(config: HttpClientConfig): ApiBuilder<object>;
150
163
  /**
151
164
  * 从现有 ky 实例创建
152
165
  *
153
166
  * @param kyInstance 已配置的 ky 实例
154
167
  * @param config 可选的额外配置
155
168
  * @returns 新的 ApiBuilder 实例
156
- *
157
- * @example
158
- * ```ts
159
- * const kyInstance = ky.create({ prefixUrl: 'https://api.example.com' })
160
- * const api = ApiBuilder.from(kyInstance)
161
- * ```
162
169
  */
163
- static from(kyInstance: ReturnType<typeof ky.create>, config?: Partial<HttpClientConfig>): ApiBuilder;
170
+ static from(kyInstance: ReturnType<typeof ky.create>, config?: Partial<HttpClientConfig>): ApiBuilder<object>;
164
171
  /**
165
172
  * 定义 GET 请求
166
173
  *
167
174
  * @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
- * ```
175
+ * @template K 端点名称(自动推断)
182
176
  */
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>>>;
177
+ get<TResponse, const K extends string = string>(name: K, options: Omit<EndpointOptions, 'method'>): ApiBuilder<T & {
178
+ [P in K]: ApiEndpointWithoutBody<TResponse>;
179
+ }>;
185
180
  /**
186
181
  * 定义 POST 请求
187
182
  *
188
183
  * @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
- * ```
184
+ * @template TBody 请求体类型
185
+ * @template K 端点名称
206
186
  */
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>>>;
187
+ post<TResponse, TBody = void, const K extends string = string>(name: K, options: Omit<EndpointOptions, 'method'>): ApiBuilder<T & {
188
+ [P in K]: ApiEndpointWithBody<TResponse, TBody>;
189
+ }>;
208
190
  /**
209
191
  * 定义 PUT 请求
210
192
  *
211
193
  * @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
- * ```
194
+ * @template TBody 请求体类型
195
+ * @template K 端点名称
226
196
  */
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>>>;
197
+ put<TResponse, TBody = void, const K extends string = string>(name: K, options: Omit<EndpointOptions, 'method'>): ApiBuilder<T & {
198
+ [P in K]: ApiEndpointWithBody<TResponse, TBody>;
199
+ }>;
228
200
  /**
229
201
  * 定义 DELETE 请求
230
202
  *
231
203
  * @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
- * ```
204
+ * @template K 端点名称
245
205
  */
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>>>;
206
+ delete<TResponse, const K extends string = string>(name: K, options: Omit<EndpointOptions, 'method'>): ApiBuilder<T & {
207
+ [P in K]: ApiEndpointWithoutBody<TResponse>;
208
+ }>;
247
209
  /**
248
210
  * 定义 PATCH 请求
249
211
  *
250
212
  * @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
- * ```
213
+ * @template TBody 请求体类型
214
+ * @template K 端点名称
265
215
  */
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>>>;
216
+ patch<TResponse, TBody = void, const K extends string = string>(name: K, options: Omit<EndpointOptions, 'method'>): ApiBuilder<T & {
217
+ [P in K]: ApiEndpointWithBody<TResponse, TBody>;
218
+ }>;
267
219
  /**
268
220
  * 通用端点定义(内部方法)
269
- *
270
- * @template TResponse 响应数据类型
271
- * @template K 端点名称
272
- * @template Path 路径字符串
273
- * @template TBody 请求体类型
274
- * @template HasBody 是否有请求体
275
221
  */
276
222
  private endpoint;
277
223
  /**
278
224
  * 扩展 ky 实例(添加 hooks 等)
279
225
  *
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
- * ```
226
+ * @param options Ky 配置选项
227
+ * @returns 当前 Builder 实例(保留累积的类型)
293
228
  */
294
- extend(options: KyOptions): this;
229
+ extend(options: KyOptions): ApiBuilder<T>;
295
230
  /**
296
231
  * 构建 API 服务
297
232
  *
298
- * 将所有定义的端点转换为可调用的 API 对象。
299
- * 每个端点都带有完整的类型信息(响应类型、路径参数、请求体类型)。
300
- *
301
233
  * @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
- * ```
314
234
  */
315
235
  build(): T;
316
236
  /**
317
237
  * 获取 HTTP 客户端实例(用于高级操作)
318
- *
319
- * @returns HttpClient 实例
320
- *
321
- * @example
322
- * ```ts
323
- * const client = api.getClient()
324
- * await client.clearCache()
325
- * ```
326
238
  */
327
239
  getClient(): HttpClient;
328
240
  }
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 };
241
+ export {};
339
242
  //# 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":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAEH,OAAO,EAAE,EAAE,EAAE,KAAK,OAAO,IAAI,SAAS,EAAE,MAAM,IAAI,CAAA;AAClD,OAAO,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAA;AACjD,OAAO,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAA;AAC/C,OAAO,KAAK,EAAE,aAAa,EAAE,UAAU,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAA;AAMxF;;GAEG;AACH,KAAK,cAAc,GAAG,IAAI,CAAC,UAAU,EAAE,MAAM,CAAC,CAAA;AAO9C;;GAEG;AACH,KAAK,gBAAgB,CAAC,KAAK,IAAI,cAAc,GAAG;IAAE,IAAI,EAAE,KAAK,CAAA;CAAE,CAAA;AAE/D;;GAEG;AACH,KAAK,gBAAgB,GAAG,cAAc,GAAG;IAAE,IAAI,CAAC,EAAE,OAAO,CAAA;CAAE,CAAA;AAE3D;;GAEG;AACH,KAAK,kBAAkB,GAAG,cAAc,GAAG;IAAE,IAAI,CAAC,EAAE,KAAK,CAAA;CAAE,CAAA;AAE3D;;GAEG;AACH,MAAM,MAAM,2BAA2B,CAAC,SAAS,EAAE,KAAK,IAAI;IACxD,CAAC,OAAO,EAAE,gBAAgB,CAAC,KAAK,CAAC,GAAG,SAAS,CAAC,SAAS,CAAC,CAAA;IACxD,WAAW;IACX,MAAM,EAAE,aAAa,CAAA;CACxB,CAAA;AAED;;GAEG;AACH,MAAM,MAAM,2BAA2B,CAAC,SAAS,IAAI;IACjD,CAAC,OAAO,CAAC,EAAE,gBAAgB,GAAG,SAAS,CAAC,SAAS,CAAC,CAAA;IAClD,WAAW;IACX,MAAM,EAAE,aAAa,CAAA;CACxB,CAAA;AAED;;GAEG;AACH,MAAM,MAAM,sBAAsB,CAAC,SAAS,IAAI;IAC5C,CAAC,OAAO,CAAC,EAAE,kBAAkB,GAAG,SAAS,CAAC,SAAS,CAAC,CAAA;IACpD,WAAW;IACX,MAAM,EAAE,aAAa,CAAA;CACxB,CAAA;AAED;;GAEG;AACH,KAAK,SAAS,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,OAAO,CAAC,GAAG,IAAI,GAAG,KAAK,CAAC,GAAG,KAAK,CAAA;AAE1F;;GAEG;AACH,KAAK,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,GAAG,IAAI,GAAG,KAAK,CAAC,GAAG,KAAK,CAAA;AAEjF;;GAEG;AACH,MAAM,MAAM,mBAAmB,CAAC,SAAS,EAAE,KAAK,IAC5C,SAAS,CAAC,KAAK,CAAC,SAAS,IAAI,GACvB,2BAA2B,CAAC,SAAS,CAAC,GACtC,MAAM,CAAC,KAAK,CAAC,SAAS,IAAI,GACtB,2BAA2B,CAAC,SAAS,CAAC,GACtC,2BAA2B,CAAC,SAAS,EAAE,KAAK,CAAC,CAAA;AAE3D;;GAEG;AACH,MAAM,MAAM,WAAW,CACnB,SAAS,EACT,KAAK,GAAG,OAAO,EACf,OAAO,SAAS,OAAO,GAAG,KAAK,IAC/B,OAAO,SAAS,IAAI,GAClB,mBAAmB,CAAC,SAAS,EAAE,KAAK,CAAC,GACrC,sBAAsB,CAAC,SAAS,CAAC,CAAA;AAEvC;;GAEG;AACH,MAAM,MAAM,aAAa,GAAG,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAA;AAMlF;;GAEG;AACH,UAAU,eAAe;IACrB,0BAA0B;IAC1B,IAAI,EAAE,MAAM,CAAA;IACZ,WAAW;IACX,MAAM,CAAC,EAAE,KAAK,GAAG,MAAM,GAAG,KAAK,GAAG,QAAQ,GAAG,OAAO,CAAA;IACpD,UAAU;IACV,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAChC,gBAAgB;IAChB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,WAAW;IACX,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,WAAW;IACX,KAAK,CAAC,EAAE,WAAW,CAAA;CACtB;AAMD;;GAEG;AAEH,KAAK,QAAQ,GAAG,EAAE,CAAA;AAElB;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,qBAAa,UAAU,CAAC,CAAC,SAAS,MAAM,GAAG,QAAQ;IAC/C,OAAO,CAAC,MAAM,CAAY;IAC1B,OAAO,CAAC,SAAS,CAAwC;IAEzD,OAAO;IAIP;;;;;OAKG;IACH,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,gBAAgB,GAAG,UAAU,CAAC,MAAM,CAAC;IAI3D;;;;;;OAMG;IACH,MAAM,CAAC,IAAI,CACP,UAAU,EAAE,UAAU,CAAC,OAAO,EAAE,CAAC,MAAM,CAAC,EACxC,MAAM,CAAC,EAAE,OAAO,CAAC,gBAAgB,CAAC,GACnC,UAAU,CAAC,MAAM,CAAC;IASrB;;;;;OAKG;IACH,GAAG,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC,SAAS,MAAM,GAAG,MAAM,EAC1C,IAAI,EAAE,CAAC,EACP,OAAO,EAAE,IAAI,CAAC,eAAe,EAAE,QAAQ,CAAC,GACzC,UAAU,CAAC,CAAC,GAAG;SAAG,CAAC,IAAI,CAAC,GAAG,sBAAsB,CAAC,SAAS,CAAC;KAAE,CAAC;IAIlE;;;;;;OAMG;IACH,IAAI,CAAC,SAAS,EAAE,KAAK,GAAG,IAAI,EAAE,KAAK,CAAC,CAAC,SAAS,MAAM,GAAG,MAAM,EACzD,IAAI,EAAE,CAAC,EACP,OAAO,EAAE,IAAI,CAAC,eAAe,EAAE,QAAQ,CAAC,GACzC,UAAU,CAAC,CAAC,GAAG;SAAG,CAAC,IAAI,CAAC,GAAG,mBAAmB,CAAC,SAAS,EAAE,KAAK,CAAC;KAAE,CAAC;IAItE;;;;;;OAMG;IACH,GAAG,CAAC,SAAS,EAAE,KAAK,GAAG,IAAI,EAAE,KAAK,CAAC,CAAC,SAAS,MAAM,GAAG,MAAM,EACxD,IAAI,EAAE,CAAC,EACP,OAAO,EAAE,IAAI,CAAC,eAAe,EAAE,QAAQ,CAAC,GACzC,UAAU,CAAC,CAAC,GAAG;SAAG,CAAC,IAAI,CAAC,GAAG,mBAAmB,CAAC,SAAS,EAAE,KAAK,CAAC;KAAE,CAAC;IAItE;;;;;OAKG;IACH,MAAM,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC,SAAS,MAAM,GAAG,MAAM,EAC7C,IAAI,EAAE,CAAC,EACP,OAAO,EAAE,IAAI,CAAC,eAAe,EAAE,QAAQ,CAAC,GACzC,UAAU,CAAC,CAAC,GAAG;SAAG,CAAC,IAAI,CAAC,GAAG,sBAAsB,CAAC,SAAS,CAAC;KAAE,CAAC;IAIlE;;;;;;OAMG;IACH,KAAK,CAAC,SAAS,EAAE,KAAK,GAAG,IAAI,EAAE,KAAK,CAAC,CAAC,SAAS,MAAM,GAAG,MAAM,EAC1D,IAAI,EAAE,CAAC,EACP,OAAO,EAAE,IAAI,CAAC,eAAe,EAAE,QAAQ,CAAC,GACzC,UAAU,CAAC,CAAC,GAAG;SAAG,CAAC,IAAI,CAAC,GAAG,mBAAmB,CAAC,SAAS,EAAE,KAAK,CAAC;KAAE,CAAC;IAItE;;OAEG;IACH,OAAO,CAAC,QAAQ;IAgBhB;;;;;OAKG;IACH,MAAM,CAAC,OAAO,EAAE,SAAS,GAAG,UAAU,CAAC,CAAC,CAAC;IAKzC;;;;OAIG;IACH,KAAK,IAAI,CAAC;IAeV;;OAEG;IACH,SAAS,IAAI,UAAU;CAG1B"}
@@ -8,45 +8,56 @@
8
8
  *
9
9
  * - **端点名称字面量**:每个端点名称保留字面量类型(如 `'getUser'`)
10
10
  * - **泛型累积**:链式调用累积所有端点类型 `T & Record<K, ApiEndpoint<...>>`
11
- * - **路径参数推断**:从 path 字符串自动提取 `{param}` 并约束 `pathParams` 类型
12
11
  * - **Body 类型约束**:POST/PUT/PATCH 支持指定请求体类型
13
- * - **防止重复端点**:添加已存在的端点名称会编译报错
14
12
  *
15
13
  * @example
16
14
  * ```ts
17
15
  * interface CreateUserDTO { name: string; email: string }
18
16
  *
19
- * // 定义 API
20
17
  * const userApi = ApiBuilder.create({
21
18
  * baseUrl: 'https://api.example.com',
22
19
  * })
23
- * .get<User>('getUser', {
24
- * path: '/users/{id}', // pathParams 自动约束为 { id: string | number }
25
- * cache: { mode: CacheMode.IfCache, ttl: 60000 },
26
- * })
20
+ * .get<User>('getUser', { path: '/users/{id}' })
27
21
  * .get<User[]>('getUsers', { path: '/users' })
28
- * .post<User, CreateUserDTO>('createUser', { path: '/users' }) // body 类型为 CreateUserDTO
22
+ * .post<User, CreateUserDTO>('createUser', { path: '/users' })
29
23
  * .build()
30
24
  *
31
25
  * // ✅ 类型正确推断
32
- * userApi.getUser({ pathParams: { id: '123' } }) // pathParams 必须有 id
33
- * userApi.getUsers() // 无需 pathParams
34
- * userApi.createUser({ body: { name: 'John', email: 'x' } }) // body 类型检查
26
+ * userApi.getUser({ pathParams: { id: '123' } })
27
+ * userApi.createUser({ body: { name: 'John', email: 'x' } })
35
28
  *
36
29
  * // ❌ 类型错误会被捕获
37
- * userApi.getUser({ pathParams: { wrong: '1' } }) // Error: 缺少 id
38
- * userApi.createUser({ body: { wrong: 'x' } }) // Error: body 类型不匹配
39
- * userApi.unknownMethod() // Error: 方法不存在
30
+ * userApi.unknownMethod() // Error: 方法不存在
40
31
  * ```
41
32
  */
42
33
  import { HttpClient } from '../client/HttpClient';
43
- // ============================================================================
44
- // API 构建器
45
- // ============================================================================
46
34
  /**
47
35
  * API 构建器类
48
36
  *
37
+ * 使用链式调用定义类型安全的 API 端点。
38
+ *
49
39
  * @template T 当前已定义的端点类型集合,通过链式调用累积
40
+ *
41
+ * @remarks
42
+ * **类型深度限制**:由于 TypeScript 对深层交叉类型的处理限制,
43
+ * 当单个 builder 链式调用超过 4 个端点时,类型推断可能不够精确。
44
+ *
45
+ * 建议:
46
+ * - 保持单个 builder 的端点数量在 4 个以内
47
+ * - 如需更多端点,功能仍然正常,但部分类型检查可能被放宽
48
+ *
49
+ * @example
50
+ * ```ts
51
+ * const userApi = ApiBuilder.create({ baseUrl: '/api' })
52
+ * .get<User>('getUser', { path: '/users/{id}' })
53
+ * .post<User, CreateUserDTO>('createUser', { path: '/users' })
54
+ * .put<User, UpdateUserDTO>('updateUser', { path: '/users/{id}' })
55
+ * .build()
56
+ *
57
+ * // 类型安全调用
58
+ * const user = await userApi.getUser({ pathParams: { id: '1' } })
59
+ * const newUser = await userApi.createUser({ body: { name: 'John', email: 'j@test.com' } })
60
+ * ```
50
61
  */
51
62
  export class ApiBuilder {
52
63
  constructor(config) {
@@ -58,14 +69,6 @@ export class ApiBuilder {
58
69
  *
59
70
  * @param config HTTP 客户端配置
60
71
  * @returns 新的 ApiBuilder 实例
61
- *
62
- * @example
63
- * ```ts
64
- * const api = ApiBuilder.create({
65
- * baseUrl: 'https://api.example.com',
66
- * timeout: 10000,
67
- * })
68
- * ```
69
72
  */
70
73
  static create(config) {
71
74
  return new ApiBuilder(config);
@@ -76,12 +79,6 @@ export class ApiBuilder {
76
79
  * @param kyInstance 已配置的 ky 实例
77
80
  * @param config 可选的额外配置
78
81
  * @returns 新的 ApiBuilder 实例
79
- *
80
- * @example
81
- * ```ts
82
- * const kyInstance = ky.create({ prefixUrl: 'https://api.example.com' })
83
- * const api = ApiBuilder.from(kyInstance)
84
- * ```
85
82
  */
86
83
  static from(kyInstance, config) {
87
84
  const builder = new ApiBuilder({
@@ -95,46 +92,17 @@ export class ApiBuilder {
95
92
  * 定义 GET 请求
96
93
  *
97
94
  * @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
- * ```
95
+ * @template K 端点名称(自动推断)
112
96
  */
113
- get(name, // 防止重复端点名称
114
- options) {
97
+ get(name, options) {
115
98
  return this.endpoint(name, { ...options, method: 'GET' });
116
99
  }
117
100
  /**
118
101
  * 定义 POST 请求
119
102
  *
120
103
  * @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
- * ```
104
+ * @template TBody 请求体类型
105
+ * @template K 端点名称
138
106
  */
139
107
  post(name, options) {
140
108
  return this.endpoint(name, { ...options, method: 'POST' });
@@ -143,20 +111,8 @@ export class ApiBuilder {
143
111
  * 定义 PUT 请求
144
112
  *
145
113
  * @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
- * ```
114
+ * @template TBody 请求体类型
115
+ * @template K 端点名称
160
116
  */
161
117
  put(name, options) {
162
118
  return this.endpoint(name, { ...options, method: 'PUT' });
@@ -165,19 +121,7 @@ export class ApiBuilder {
165
121
  * 定义 DELETE 请求
166
122
  *
167
123
  * @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
- * ```
124
+ * @template K 端点名称
181
125
  */
182
126
  delete(name, options) {
183
127
  return this.endpoint(name, { ...options, method: 'DELETE' });
@@ -186,32 +130,14 @@ export class ApiBuilder {
186
130
  * 定义 PATCH 请求
187
131
  *
188
132
  * @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
- * ```
133
+ * @template TBody 请求体类型
134
+ * @template K 端点名称
203
135
  */
204
136
  patch(name, options) {
205
137
  return this.endpoint(name, { ...options, method: 'PATCH' });
206
138
  }
207
139
  /**
208
140
  * 通用端点定义(内部方法)
209
- *
210
- * @template TResponse 响应数据类型
211
- * @template K 端点名称
212
- * @template Path 路径字符串
213
- * @template TBody 请求体类型
214
- * @template HasBody 是否有请求体
215
141
  */
216
142
  endpoint(name, options) {
217
143
  var _a;
@@ -224,25 +150,13 @@ export class ApiBuilder {
224
150
  cache: options.cache,
225
151
  };
226
152
  this.endpoints.set(name, config);
227
- // 关键:返回 this,但类型已经累积
228
153
  return this;
229
154
  }
230
155
  /**
231
156
  * 扩展 ky 实例(添加 hooks 等)
232
157
  *
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
- * ```
158
+ * @param options Ky 配置选项
159
+ * @returns 当前 Builder 实例(保留累积的类型)
246
160
  */
247
161
  extend(options) {
248
162
  this.client.extendKy(options);
@@ -251,27 +165,11 @@ export class ApiBuilder {
251
165
  /**
252
166
  * 构建 API 服务
253
167
  *
254
- * 将所有定义的端点转换为可调用的 API 对象。
255
- * 每个端点都带有完整的类型信息(响应类型、路径参数、请求体类型)。
256
- *
257
168
  * @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
- * ```
270
169
  */
271
170
  build() {
272
171
  const api = {};
273
172
  for (const [name, config] of this.endpoints) {
274
- // 运行时不需要类型检查,类型安全由编译时保证
275
173
  const endpoint = ((options) => {
276
174
  return this.client.request(config, options);
277
175
  });
@@ -282,14 +180,6 @@ export class ApiBuilder {
282
180
  }
283
181
  /**
284
182
  * 获取 HTTP 客户端实例(用于高级操作)
285
- *
286
- * @returns HttpClient 实例
287
- *
288
- * @example
289
- * ```ts
290
- * const client = api.getClient()
291
- * await client.clearCache()
292
- * ```
293
183
  */
294
184
  getClient() {
295
185
  return this.client;
@@ -1,2 +1,2 @@
1
- export { ApiBuilder, type ApiEndpoint, type ApiDefinition, type ExtractPathParams, type PathParamsType, type HasPathParams, } from './ApiBuilder';
1
+ export { ApiBuilder, type ApiEndpoint, type ApiDefinition } 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,EACH,UAAU,EACV,KAAK,WAAW,EAChB,KAAK,aAAa,EAElB,KAAK,iBAAiB,EACtB,KAAK,cAAc,EACnB,KAAK,aAAa,GACrB,MAAM,cAAc,CAAA"}
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 +1 @@
1
- export { ApiBuilder, } from './ApiBuilder';
1
+ export { ApiBuilder } from './ApiBuilder';
@@ -0,0 +1,53 @@
1
+ /**
2
+ * ApiBuilder 使用示例
3
+ *
4
+ * 此文件演示 ApiBuilder 的类型安全特性:
5
+ * 1. 泛型累积 - 链式调用自动累积端点类型
6
+ * 2. Body 类型约束 - POST/PUT/PATCH 请求体类型检查
7
+ * 3. 端点名称字面量 - 调用不存在的端点会编译报错
8
+ */
9
+ /** 用户实体 */
10
+ interface User {
11
+ id: string;
12
+ name: string;
13
+ email: string;
14
+ createdAt: string;
15
+ }
16
+ /** 创建用户 DTO */
17
+ interface CreateUserDTO {
18
+ name: string;
19
+ email: string;
20
+ }
21
+ /** 更新用户 DTO */
22
+ interface UpdateUserDTO {
23
+ name?: string;
24
+ email?: string;
25
+ }
26
+ /** 分页响应 */
27
+ interface PaginatedResponse<T> {
28
+ data: T[];
29
+ total: number;
30
+ page: number;
31
+ pageSize: number;
32
+ }
33
+ export declare const userApi: object & {
34
+ [x: string]: import("../api/ApiBuilder").ApiEndpointWithoutBody<User[]>;
35
+ } & {
36
+ [x: string]: import("../api/ApiBuilder").ApiEndpointWithoutBody<User>;
37
+ } & {
38
+ [x: string]: import("../api/ApiBuilder").ApiEndpointWithoutBody<PaginatedResponse<User>>;
39
+ } & {
40
+ [x: string]: import("../api/ApiBuilder").ApiEndpointWithBodyRequired<User, CreateUserDTO>;
41
+ } & {
42
+ [x: string]: import("../api/ApiBuilder").ApiEndpointWithBodyOptional<void>;
43
+ } & {
44
+ [x: string]: import("../api/ApiBuilder").ApiEndpointWithBodyRequired<User, UpdateUserDTO>;
45
+ } & {
46
+ [x: string]: import("../api/ApiBuilder").ApiEndpointWithBodyRequired<User, Partial<UpdateUserDTO>>;
47
+ } & {
48
+ [x: string]: import("../api/ApiBuilder").ApiEndpointWithoutBody<void>;
49
+ };
50
+ declare function demo(): Promise<void>;
51
+ declare function typeErrors(): void;
52
+ export { demo, typeErrors };
53
+ //# sourceMappingURL=api-builder.demo.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api-builder.demo.d.ts","sourceRoot":"","sources":["../../../src/http/demo/api-builder.demo.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AASH,WAAW;AACX,UAAU,IAAI;IACV,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,MAAM,CAAA;IACb,SAAS,EAAE,MAAM,CAAA;CACpB;AAED,eAAe;AACf,UAAU,aAAa;IACnB,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,MAAM,CAAA;CAChB;AAED,eAAe;AACf,UAAU,aAAa;IACnB,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,KAAK,CAAC,EAAE,MAAM,CAAA;CACjB;AAED,WAAW;AACX,UAAU,iBAAiB,CAAC,CAAC;IACzB,IAAI,EAAE,CAAC,EAAE,CAAA;IACT,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,EAAE,MAAM,CAAA;IACZ,QAAQ,EAAE,MAAM,CAAA;CACnB;AAMD,eAAO,MAAM,OAAO;;;;;;;;;;;;;;;;CA8DR,CAAA;AAMZ,iBAAe,IAAI,kBAuElB;AAoBD,iBAAS,UAAU,SAYlB;AAMD,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,CAAA"}
@@ -0,0 +1,163 @@
1
+ /**
2
+ * ApiBuilder 使用示例
3
+ *
4
+ * 此文件演示 ApiBuilder 的类型安全特性:
5
+ * 1. 泛型累积 - 链式调用自动累积端点类型
6
+ * 2. Body 类型约束 - POST/PUT/PATCH 请求体类型检查
7
+ * 3. 端点名称字面量 - 调用不存在的端点会编译报错
8
+ */
9
+ import { ApiBuilder } from '../api/ApiBuilder';
10
+ import { CacheMode } from '../models/CacheMode';
11
+ // ============================================================================
12
+ // 创建 API 服务
13
+ // ============================================================================
14
+ export const userApi = ApiBuilder.create({
15
+ baseUrl: 'https://api.example.com',
16
+ timeout: 10000,
17
+ defaultCache: {
18
+ mode: CacheMode.FirstRemote,
19
+ ttl: 5 * 60 * 1000,
20
+ },
21
+ })
22
+ // GET 请求 - 无路径参数
23
+ .get('getUsers', {
24
+ path: '/users',
25
+ cache: { mode: CacheMode.FirstCache },
26
+ })
27
+ // GET 请求 - 有路径参数
28
+ .get('getUser', {
29
+ path: '/users/{id}',
30
+ cache: { mode: CacheMode.IfCache, ttl: 60000 },
31
+ })
32
+ // GET 请求 - 分页
33
+ .get('getUsersPaginated', {
34
+ path: '/users/paginated',
35
+ })
36
+ // POST 请求 - 有 body 类型
37
+ .post('createUser', {
38
+ path: '/users',
39
+ })
40
+ // POST 请求 - 无 body 类型(触发器)
41
+ .post('activateUser', {
42
+ path: '/users/{id}/activate',
43
+ })
44
+ // PUT 请求 - 完整更新
45
+ .put('updateUser', {
46
+ path: '/users/{id}',
47
+ })
48
+ // PATCH 请求 - 部分更新
49
+ .patch('patchUser', {
50
+ path: '/users/{id}',
51
+ })
52
+ // DELETE 请求
53
+ .delete('deleteUser', {
54
+ path: '/users/{id}',
55
+ })
56
+ // 扩展 ky 实例(添加拦截器)
57
+ .extend({
58
+ hooks: {
59
+ beforeRequest: [
60
+ (request) => {
61
+ const token = 'mock-token';
62
+ request.headers.set('Authorization', `Bearer ${token}`);
63
+ },
64
+ ],
65
+ },
66
+ })
67
+ .build();
68
+ // ============================================================================
69
+ // 使用示例 - 类型安全调用
70
+ // ============================================================================
71
+ async function demo() {
72
+ // ✅ GET 无路径参数
73
+ const users = await userApi.getUsers();
74
+ console.log('Users:', users);
75
+ // ✅ GET 有路径参数
76
+ const user = await userApi.getUser({
77
+ pathParams: { id: '123' },
78
+ });
79
+ console.log('User:', user);
80
+ // ✅ GET 带 query 参数
81
+ const paginatedUsers = await userApi.getUsersPaginated({
82
+ query: { page: 1, pageSize: 10 },
83
+ });
84
+ console.log('Paginated:', paginatedUsers);
85
+ // ✅ POST 有 body 类型约束
86
+ const newUser = await userApi.createUser({
87
+ body: { name: 'John', email: 'john@example.com' },
88
+ });
89
+ console.log('Created:', newUser);
90
+ // ✅ POST 无 body(触发器)
91
+ await userApi.activateUser({
92
+ pathParams: { id: '123' },
93
+ });
94
+ // ✅ PUT 完整更新
95
+ const updatedUser = await userApi.updateUser({
96
+ pathParams: { id: '123' },
97
+ body: { name: 'John Updated', email: 'john.updated@example.com' },
98
+ });
99
+ console.log('Updated:', updatedUser);
100
+ // ✅ PATCH 部分更新
101
+ const patchedUser = await userApi.patchUser({
102
+ pathParams: { id: '123' },
103
+ body: { name: 'John Patched' },
104
+ });
105
+ console.log('Patched:', patchedUser);
106
+ // ✅ DELETE
107
+ await userApi.deleteUser({
108
+ pathParams: { id: '123' },
109
+ });
110
+ // ✅ 覆盖缓存配置
111
+ const freshUser = await userApi.getUser({
112
+ pathParams: { id: '123' },
113
+ cache: { mode: CacheMode.OnlyRemote },
114
+ });
115
+ console.log('Fresh:', freshUser);
116
+ // ✅ 多次响应(for await)
117
+ for await (const { data, fromCache } of userApi.getUser({ pathParams: { id: '123' } })) {
118
+ console.log('Data:', data, 'FromCache:', fromCache);
119
+ }
120
+ // ✅ subscribe 方式
121
+ await userApi.getUser({ pathParams: { id: '123' } }).subscribe({
122
+ onData: (data, fromCache) => {
123
+ console.log('Subscribe data:', data, fromCache);
124
+ },
125
+ onError: (error) => {
126
+ console.error('Error:', error);
127
+ },
128
+ onComplete: () => {
129
+ console.log('Complete');
130
+ },
131
+ });
132
+ }
133
+ // ============================================================================
134
+ // 类型错误示例
135
+ // ============================================================================
136
+ /**
137
+ * 类型安全验证
138
+ *
139
+ * 注意:由于 TypeScript 对深层交叉类型的处理限制,
140
+ * 当链式调用超过 4 个端点时,类型推断可能不够精确。
141
+ * 建议:保持单个 builder 的端点数量在 4 个以内,或分批构建。
142
+ *
143
+ * 以下是简化版本的类型安全验证(2-3 个端点)
144
+ */
145
+ const simpleApi = ApiBuilder.create({ baseUrl: 'http://test' })
146
+ .get('getUser', { path: '/users/{id}' })
147
+ .post('createUser', { path: '/users' })
148
+ .build();
149
+ function typeErrors() {
150
+ // ❌ body 类型不匹配(缺少 email)
151
+ // @ts-expect-error email is required
152
+ simpleApi.createUser({ body: { name: 'John' } });
153
+ // ❌ body 类型不匹配(错误的属性)
154
+ // @ts-expect-error wrong property
155
+ simpleApi.createUser({ body: { wrong: 'x' } });
156
+ // ❌ GET 请求不应该有 body
157
+ // @ts-expect-error body not allowed for GET
158
+ simpleApi.getUser({ pathParams: { id: '1' }, body: {} });
159
+ }
160
+ // ============================================================================
161
+ // 导出
162
+ // ============================================================================
163
+ export { demo, typeErrors };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "xxf_react",
3
- "version": "0.7.3",
3
+ "version": "0.7.4",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "main": "dist/index.js",