xxf_react 0.7.3 → 0.7.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.
@@ -8,110 +8,111 @@
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
+ * 强制 TypeScript 展开交叉类型为单一对象类型
43
+ * 用于用户自定义接口类型时使用
61
44
  */
62
- type PathParamsType<Path extends string> = ExtractPathParams<Path> extends never ? undefined : Record<ExtractPathParams<Path>, string | number>;
45
+ export type Simplify<T> = {
46
+ [K in keyof T]: T[K];
47
+ } & {};
63
48
  /**
64
- * 判断路径是否有参数
49
+ * 带 body 的 API 调用选项(body 必需)
65
50
  */
66
- type HasPathParams<Path extends string> = ExtractPathParams<Path> extends never ? false : true;
51
+ type WithBodyRequired<TBody> = BaseApiOptions & {
52
+ body: TBody;
53
+ };
67
54
  /**
68
- * 带类型约束的 API 调用选项
69
- *
70
- * @template Path 路径字符串字面量
71
- * @template TBody 请求体类型(用于 POST/PUT/PATCH)
72
- * @template HasBody 是否需要 body
55
+ * body 的 API 调用选项(body 可选)
73
56
  */
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
- } : {
57
+ type WithBodyOptional = BaseApiOptions & {
58
+ body?: unknown;
59
+ };
60
+ /**
61
+ * 无 body 的 API 调用选项(GET/DELETE)
62
+ */
63
+ type WithoutBodyOptions = BaseApiOptions & {
88
64
  body?: never;
89
- });
65
+ };
90
66
  /**
91
- * API 端点函数类型(带路径参数和 Body 类型约束)
92
- *
93
- * @template TResponse 响应数据类型
94
- * @template Path 路径字符串字面量
95
- * @template TBody 请求体类型
96
- * @template HasBody 是否需要 body
67
+ * API 端点函数类型(有 body 且必需 - POST/PUT/PATCH 指定了 TBody)
68
+ */
69
+ export type ApiEndpointWithBodyRequired<TResponse, TBody> = {
70
+ (options: WithBodyRequired<TBody>): ApiStream<TResponse>;
71
+ /** 请求配置 */
72
+ config: RequestConfig;
73
+ };
74
+ /**
75
+ * API 端点函数类型(有 body 但可选 - POST/PUT/PATCH 未指定 TBody)
76
+ */
77
+ export type ApiEndpointWithBodyOptional<TResponse> = {
78
+ (options?: WithBodyOptional): ApiStream<TResponse>;
79
+ /** 请求配置 */
80
+ config: RequestConfig;
81
+ };
82
+ /**
83
+ * API 端点函数类型(无 body - GET/DELETE)
97
84
  */
98
- export type ApiEndpoint<TResponse, Path extends string = string, TBody = never, HasBody extends boolean = false> = {
99
- (options?: TypedApiOptions<Path, TBody, HasBody>): ApiStream<TResponse>;
85
+ export type ApiEndpointWithoutBody<TResponse> = {
86
+ (options?: WithoutBodyOptions): ApiStream<TResponse>;
100
87
  /** 请求配置 */
101
88
  config: RequestConfig;
102
89
  };
90
+ /**
91
+ * 检测是否为 unknown 类型
92
+ */
93
+ type IsUnknown<T> = [unknown] extends [T] ? ([T] extends [unknown] ? true : false) : false;
94
+ /**
95
+ * 检测是否为 void 类型
96
+ */
97
+ type IsVoid<T> = [T] extends [void] ? ([void] extends [T] ? true : false) : false;
98
+ /**
99
+ * 有 body 的端点类型(根据 TBody 选择必需或可选)
100
+ */
101
+ export type ApiEndpointWithBody<TResponse, TBody> = IsUnknown<TBody> extends true ? ApiEndpointWithBodyOptional<TResponse> : IsVoid<TBody> extends true ? ApiEndpointWithBodyOptional<TResponse> : ApiEndpointWithBodyRequired<TResponse, TBody>;
102
+ /**
103
+ * API 端点函数类型(泛型版本,用于内部和类型导出)
104
+ */
105
+ export type ApiEndpoint<TResponse, TBody = unknown, HasBody extends boolean = false> = HasBody extends true ? ApiEndpointWithBody<TResponse, TBody> : ApiEndpointWithoutBody<TResponse>;
103
106
  /**
104
107
  * API 定义类型(端点集合)
105
108
  */
106
- export type ApiDefinition = Record<string, ApiEndpoint<unknown, string, unknown, boolean>>;
109
+ export type ApiDefinition = Record<string, ApiEndpoint<unknown, unknown, boolean>>;
107
110
  /**
108
111
  * 端点选项(构建时配置)
109
- *
110
- * @template Path 路径字符串字面量,用于类型推断
111
112
  */
112
- interface EndpointOptions<Path extends string = string> {
113
+ interface EndpointOptions {
113
114
  /** 请求路径,支持 {param} 占位符 */
114
- path: Path;
115
+ path: string;
115
116
  /** 请求方法 */
116
117
  method?: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';
117
118
  /** 请求头 */
@@ -123,12 +124,41 @@ interface EndpointOptions<Path extends string = string> {
123
124
  /** 缓存配置 */
124
125
  cache?: CacheConfig;
125
126
  }
127
+ /**
128
+ * 空 API 类型(空记录,不允许任意属性访问)
129
+ */
130
+ type EmptyApi = {};
126
131
  /**
127
132
  * API 构建器类
128
133
  *
134
+ * 使用链式调用定义类型安全的 API 端点。
135
+ *
129
136
  * @template T 当前已定义的端点类型集合,通过链式调用累积
137
+ *
138
+ * @remarks
139
+ * **类型深度限制**:由于 TypeScript 对深层交叉类型的处理限制,
140
+ * 当单个 builder 链式调用超过 4 个端点时,类型推断可能不够精确。
141
+ *
142
+ * 建议:
143
+ * - 保持单个 builder 的端点数量在 4 个以内以获得最佳类型推断
144
+ * - 如需更多端点,功能仍然正常,但部分类型检查可能被放宽
145
+ * - 可以使用导出的 `ApiEndpointWithBody`、`ApiEndpointWithoutBody` 等类型
146
+ * 来定义自己的接口类型,获得更精确的类型检查
147
+ *
148
+ * @example
149
+ * ```ts
150
+ * const userApi = ApiBuilder.create({ baseUrl: '/api' })
151
+ * .get<User>('getUser', { path: '/users/{id}' })
152
+ * .post<User, CreateUserDTO>('createUser', { path: '/users' })
153
+ * .put<User, UpdateUserDTO>('updateUser', { path: '/users/{id}' })
154
+ * .build()
155
+ *
156
+ * // 类型安全调用
157
+ * const user = await userApi.getUser({ pathParams: { id: '1' } })
158
+ * const newUser = await userApi.createUser({ body: { name: 'John', email: 'j@test.com' } })
159
+ * ```
130
160
  */
131
- export declare class ApiBuilder<T extends ApiDefinition = Record<string, never>> {
161
+ export declare class ApiBuilder<T extends object = EmptyApi> {
132
162
  private client;
133
163
  private endpoints;
134
164
  private constructor();
@@ -137,203 +167,85 @@ export declare class ApiBuilder<T extends ApiDefinition = Record<string, never>>
137
167
  *
138
168
  * @param config HTTP 客户端配置
139
169
  * @returns 新的 ApiBuilder 实例
140
- *
141
- * @example
142
- * ```ts
143
- * const api = ApiBuilder.create({
144
- * baseUrl: 'https://api.example.com',
145
- * timeout: 10000,
146
- * })
147
- * ```
148
170
  */
149
- static create(config: HttpClientConfig): ApiBuilder;
171
+ static create(config: HttpClientConfig): ApiBuilder<object>;
150
172
  /**
151
173
  * 从现有 ky 实例创建
152
174
  *
153
175
  * @param kyInstance 已配置的 ky 实例
154
176
  * @param config 可选的额外配置
155
177
  * @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
178
  */
163
- static from(kyInstance: ReturnType<typeof ky.create>, config?: Partial<HttpClientConfig>): ApiBuilder;
179
+ static from(kyInstance: ReturnType<typeof ky.create>, config?: Partial<HttpClientConfig>): ApiBuilder<object>;
164
180
  /**
165
181
  * 定义 GET 请求
166
182
  *
167
183
  * @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
- * ```
184
+ * @template K 端点名称(自动推断)
182
185
  */
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>>>;
186
+ get<TResponse, const K extends string = string>(name: K, options: Omit<EndpointOptions, 'method'>): ApiBuilder<T & {
187
+ [P in K]: ApiEndpointWithoutBody<TResponse>;
188
+ }>;
185
189
  /**
186
190
  * 定义 POST 请求
187
191
  *
188
192
  * @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
- * ```
193
+ * @template TBody 请求体类型
194
+ * @template K 端点名称
206
195
  */
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>>>;
196
+ post<TResponse, TBody = void, const K extends string = string>(name: K, options: Omit<EndpointOptions, 'method'>): ApiBuilder<T & {
197
+ [P in K]: ApiEndpointWithBody<TResponse, TBody>;
198
+ }>;
208
199
  /**
209
200
  * 定义 PUT 请求
210
201
  *
211
202
  * @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
- * ```
203
+ * @template TBody 请求体类型
204
+ * @template K 端点名称
226
205
  */
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>>>;
206
+ put<TResponse, TBody = void, const K extends string = string>(name: K, options: Omit<EndpointOptions, 'method'>): ApiBuilder<T & {
207
+ [P in K]: ApiEndpointWithBody<TResponse, TBody>;
208
+ }>;
228
209
  /**
229
210
  * 定义 DELETE 请求
230
211
  *
231
212
  * @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
- * ```
213
+ * @template K 端点名称
245
214
  */
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>>>;
215
+ delete<TResponse, const K extends string = string>(name: K, options: Omit<EndpointOptions, 'method'>): ApiBuilder<T & {
216
+ [P in K]: ApiEndpointWithoutBody<TResponse>;
217
+ }>;
247
218
  /**
248
219
  * 定义 PATCH 请求
249
220
  *
250
221
  * @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
- * ```
222
+ * @template TBody 请求体类型
223
+ * @template K 端点名称
265
224
  */
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>>>;
225
+ patch<TResponse, TBody = void, const K extends string = string>(name: K, options: Omit<EndpointOptions, 'method'>): ApiBuilder<T & {
226
+ [P in K]: ApiEndpointWithBody<TResponse, TBody>;
227
+ }>;
267
228
  /**
268
229
  * 通用端点定义(内部方法)
269
- *
270
- * @template TResponse 响应数据类型
271
- * @template K 端点名称
272
- * @template Path 路径字符串
273
- * @template TBody 请求体类型
274
- * @template HasBody 是否有请求体
275
230
  */
276
231
  private endpoint;
277
232
  /**
278
233
  * 扩展 ky 实例(添加 hooks 等)
279
234
  *
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
- * ```
235
+ * @param options Ky 配置选项
236
+ * @returns 当前 Builder 实例(保留累积的类型)
293
237
  */
294
- extend(options: KyOptions): this;
238
+ extend(options: KyOptions): ApiBuilder<T>;
295
239
  /**
296
240
  * 构建 API 服务
297
241
  *
298
- * 将所有定义的端点转换为可调用的 API 对象。
299
- * 每个端点都带有完整的类型信息(响应类型、路径参数、请求体类型)。
300
- *
301
242
  * @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
243
  */
315
244
  build(): T;
316
245
  /**
317
246
  * 获取 HTTP 客户端实例(用于高级操作)
318
- *
319
- * @returns HttpClient 实例
320
- *
321
- * @example
322
- * ```ts
323
- * const client = api.getClient()
324
- * await client.clearCache()
325
- * ```
326
247
  */
327
248
  getClient(): HttpClient;
328
249
  }
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 };
250
+ export {};
339
251
  //# sourceMappingURL=ApiBuilder.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ApiBuilder.d.ts","sourceRoot":"","sources":["../../../src/http/api/ApiBuilder.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;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,WAAW,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAA;AAM5E;;;;;;;GAOG;AACH,KAAK,iBAAiB,CAAC,IAAI,SAAS,MAAM,IACtC,IAAI,SAAS,GAAG,MAAM,IAAI,MAAM,KAAK,IAAI,MAAM,IAAI,EAAE,GAC/C,KAAK,GAAG,iBAAiB,CAAC,IAAI,CAAC,GAC/B,KAAK,CAAA;AAEf;;;;;;GAMG;AACH,KAAK,cAAc,CAAC,IAAI,SAAS,MAAM,IACnC,iBAAiB,CAAC,IAAI,CAAC,SAAS,KAAK,GAC/B,SAAS,GACT,MAAM,CAAC,iBAAiB,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC,CAAA;AAE1D;;GAEG;AACH,KAAK,aAAa,CAAC,IAAI,SAAS,MAAM,IAClC,iBAAiB,CAAC,IAAI,CAAC,SAAS,KAAK,GAAG,KAAK,GAAG,IAAI,CAAA;AAMxD;;;;;;GAMG;AACH,KAAK,eAAe,CAChB,IAAI,SAAS,MAAM,EACnB,KAAK,GAAG,KAAK,EACb,OAAO,SAAS,OAAO,GAAG,KAAK,IAC/B;IACA,kCAAkC;IAClC,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,SAAS,CAAC,CAAA;IAC7D,oBAAoB;IACpB,KAAK,CAAC,EAAE,OAAO,CAAC,WAAW,CAAC,CAAA;IAC5B,YAAY;IACZ,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CACnC,GAAG,CAEA,aAAa,CAAC,IAAI,CAAC,SAAS,IAAI,GAC1B;IAAE,UAAU,EAAE,cAAc,CAAC,IAAI,CAAC,CAAA;CAAE,GACpC;IAAE,UAAU,CAAC,EAAE,SAAS,CAAA;CAAE,CACnC,GAAG,CAEA,OAAO,SAAS,IAAI,GACd;IAAE,IAAI,CAAC,EAAE,KAAK,CAAA;CAAE,GAChB;IAAE,IAAI,CAAC,EAAE,KAAK,CAAA;CAAE,CACzB,CAAA;AAMD;;;;;;;GAOG;AACH,MAAM,MAAM,WAAW,CACnB,SAAS,EACT,IAAI,SAAS,MAAM,GAAG,MAAM,EAC5B,KAAK,GAAG,KAAK,EACb,OAAO,SAAS,OAAO,GAAG,KAAK,IAC/B;IACA,CAAC,OAAO,CAAC,EAAE,eAAe,CAAC,IAAI,EAAE,KAAK,EAAE,OAAO,CAAC,GAAG,SAAS,CAAC,SAAS,CAAC,CAAA;IACvE,WAAW;IACX,MAAM,EAAE,aAAa,CAAA;CACxB,CAAA;AAED;;GAEG;AACH,MAAM,MAAM,aAAa,GAAG,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAA;AAM1F;;;;GAIG;AACH,UAAU,eAAe,CAAC,IAAI,SAAS,MAAM,GAAG,MAAM;IAClD,0BAA0B;IAC1B,IAAI,EAAE,IAAI,CAAA;IACV,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;;;;GAIG;AACH,qBAAa,UAAU,CAAC,CAAC,SAAS,aAAa,GAAG,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC;IACnE,OAAO,CAAC,MAAM,CAAY;IAC1B,OAAO,CAAC,SAAS,CAAwC;IAEzD,OAAO;IAIP;;;;;;;;;;;;;OAaG;IACH,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,gBAAgB,GAAG,UAAU;IAInD;;;;;;;;;;;;OAYG;IACH,MAAM,CAAC,IAAI,CAAC,UAAU,EAAE,UAAU,CAAC,OAAO,EAAE,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,EAAE,OAAO,CAAC,gBAAgB,CAAC,GAAG,UAAU;IASrG;;;;;;;;;;;;;;;;;;OAkBG;IACH,GAAG,CACC,SAAS,EACT,CAAC,SAAS,MAAM,GAAG,MAAM,EACzB,IAAI,SAAS,MAAM,GAAG,MAAM,EAE5B,IAAI,EAAE,CAAC,SAAS,MAAM,CAAC,GAAG,KAAK,GAAG,CAAC,EAAG,WAAW;IACjD,OAAO,EAAE,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,EAAE,QAAQ,CAAC,GAC/C,UAAU,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC,EAAE,WAAW,CAAC,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC;IAIxE;;;;;;;;;;;;;;;;;;;;;OAqBG;IACH,IAAI,CACA,SAAS,EACT,KAAK,GAAG,OAAO,EACf,CAAC,SAAS,MAAM,GAAG,MAAM,EACzB,IAAI,SAAS,MAAM,GAAG,MAAM,EAE5B,IAAI,EAAE,CAAC,SAAS,MAAM,CAAC,GAAG,KAAK,GAAG,CAAC,EACnC,OAAO,EAAE,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,EAAE,QAAQ,CAAC,GAC/C,UAAU,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC,EAAE,WAAW,CAAC,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,CAAC,CAAC,CAAC;IAIvE;;;;;;;;;;;;;;;;;;OAkBG;IACH,GAAG,CACC,SAAS,EACT,KAAK,GAAG,OAAO,EACf,CAAC,SAAS,MAAM,GAAG,MAAM,EACzB,IAAI,SAAS,MAAM,GAAG,MAAM,EAE5B,IAAI,EAAE,CAAC,SAAS,MAAM,CAAC,GAAG,KAAK,GAAG,CAAC,EACnC,OAAO,EAAE,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,EAAE,QAAQ,CAAC,GAC/C,UAAU,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC,EAAE,WAAW,CAAC,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,CAAC,CAAC,CAAC;IAIvE;;;;;;;;;;;;;;;;;OAiBG;IACH,MAAM,CACF,SAAS,EACT,CAAC,SAAS,MAAM,GAAG,MAAM,EACzB,IAAI,SAAS,MAAM,GAAG,MAAM,EAE5B,IAAI,EAAE,CAAC,SAAS,MAAM,CAAC,GAAG,KAAK,GAAG,CAAC,EACnC,OAAO,EAAE,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,EAAE,QAAQ,CAAC,GAC/C,UAAU,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC,EAAE,WAAW,CAAC,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC;IAIxE;;;;;;;;;;;;;;;;;;OAkBG;IACH,KAAK,CACD,SAAS,EACT,KAAK,GAAG,OAAO,EACf,CAAC,SAAS,MAAM,GAAG,MAAM,EACzB,IAAI,SAAS,MAAM,GAAG,MAAM,EAE5B,IAAI,EAAE,CAAC,SAAS,MAAM,CAAC,GAAG,KAAK,GAAG,CAAC,EACnC,OAAO,EAAE,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,EAAE,QAAQ,CAAC,GAC/C,UAAU,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC,EAAE,WAAW,CAAC,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,CAAC,CAAC,CAAC;IAIvE;;;;;;;;OAQG;IACH,OAAO,CAAC,QAAQ;IAuBhB;;;;;;;;;;;;;;;;OAgBG;IACH,MAAM,CAAC,OAAO,EAAE,SAAS,GAAG,IAAI;IAKhC;;;;;;;;;;;;;;;;;;;OAmBG;IACH,KAAK,IAAI,CAAC;IAgBV;;;;;;;;;;OAUG;IACH,SAAS,IAAI,UAAU;CAG1B;AAMD;;;;;;;;GAQG;AACH,YAAY,EAAE,iBAAiB,EAAE,cAAc,EAAE,aAAa,EAAE,CAAA"}
1
+ {"version":3,"file":"ApiBuilder.d.ts","sourceRoot":"","sources":["../../../src/http/api/ApiBuilder.ts"],"names":[],"mappings":"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"}
@@ -8,45 +8,58 @@
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
+ * - 可以使用导出的 `ApiEndpointWithBody`、`ApiEndpointWithoutBody` 等类型
49
+ * 来定义自己的接口类型,获得更精确的类型检查
50
+ *
51
+ * @example
52
+ * ```ts
53
+ * const userApi = ApiBuilder.create({ baseUrl: '/api' })
54
+ * .get<User>('getUser', { path: '/users/{id}' })
55
+ * .post<User, CreateUserDTO>('createUser', { path: '/users' })
56
+ * .put<User, UpdateUserDTO>('updateUser', { path: '/users/{id}' })
57
+ * .build()
58
+ *
59
+ * // 类型安全调用
60
+ * const user = await userApi.getUser({ pathParams: { id: '1' } })
61
+ * const newUser = await userApi.createUser({ body: { name: 'John', email: 'j@test.com' } })
62
+ * ```
50
63
  */
51
64
  export class ApiBuilder {
52
65
  constructor(config) {
@@ -58,14 +71,6 @@ export class ApiBuilder {
58
71
  *
59
72
  * @param config HTTP 客户端配置
60
73
  * @returns 新的 ApiBuilder 实例
61
- *
62
- * @example
63
- * ```ts
64
- * const api = ApiBuilder.create({
65
- * baseUrl: 'https://api.example.com',
66
- * timeout: 10000,
67
- * })
68
- * ```
69
74
  */
70
75
  static create(config) {
71
76
  return new ApiBuilder(config);
@@ -76,12 +81,6 @@ export class ApiBuilder {
76
81
  * @param kyInstance 已配置的 ky 实例
77
82
  * @param config 可选的额外配置
78
83
  * @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
84
  */
86
85
  static from(kyInstance, config) {
87
86
  const builder = new ApiBuilder({
@@ -95,46 +94,17 @@ export class ApiBuilder {
95
94
  * 定义 GET 请求
96
95
  *
97
96
  * @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
- * ```
97
+ * @template K 端点名称(自动推断)
112
98
  */
113
- get(name, // 防止重复端点名称
114
- options) {
99
+ get(name, options) {
115
100
  return this.endpoint(name, { ...options, method: 'GET' });
116
101
  }
117
102
  /**
118
103
  * 定义 POST 请求
119
104
  *
120
105
  * @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
- * ```
106
+ * @template TBody 请求体类型
107
+ * @template K 端点名称
138
108
  */
139
109
  post(name, options) {
140
110
  return this.endpoint(name, { ...options, method: 'POST' });
@@ -143,20 +113,8 @@ export class ApiBuilder {
143
113
  * 定义 PUT 请求
144
114
  *
145
115
  * @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
- * ```
116
+ * @template TBody 请求体类型
117
+ * @template K 端点名称
160
118
  */
161
119
  put(name, options) {
162
120
  return this.endpoint(name, { ...options, method: 'PUT' });
@@ -165,19 +123,7 @@ export class ApiBuilder {
165
123
  * 定义 DELETE 请求
166
124
  *
167
125
  * @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
- * ```
126
+ * @template K 端点名称
181
127
  */
182
128
  delete(name, options) {
183
129
  return this.endpoint(name, { ...options, method: 'DELETE' });
@@ -186,32 +132,14 @@ export class ApiBuilder {
186
132
  * 定义 PATCH 请求
187
133
  *
188
134
  * @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
- * ```
135
+ * @template TBody 请求体类型
136
+ * @template K 端点名称
203
137
  */
204
138
  patch(name, options) {
205
139
  return this.endpoint(name, { ...options, method: 'PATCH' });
206
140
  }
207
141
  /**
208
142
  * 通用端点定义(内部方法)
209
- *
210
- * @template TResponse 响应数据类型
211
- * @template K 端点名称
212
- * @template Path 路径字符串
213
- * @template TBody 请求体类型
214
- * @template HasBody 是否有请求体
215
143
  */
216
144
  endpoint(name, options) {
217
145
  var _a;
@@ -224,25 +152,13 @@ export class ApiBuilder {
224
152
  cache: options.cache,
225
153
  };
226
154
  this.endpoints.set(name, config);
227
- // 关键:返回 this,但类型已经累积
228
155
  return this;
229
156
  }
230
157
  /**
231
158
  * 扩展 ky 实例(添加 hooks 等)
232
159
  *
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
- * ```
160
+ * @param options Ky 配置选项
161
+ * @returns 当前 Builder 实例(保留累积的类型)
246
162
  */
247
163
  extend(options) {
248
164
  this.client.extendKy(options);
@@ -251,27 +167,11 @@ export class ApiBuilder {
251
167
  /**
252
168
  * 构建 API 服务
253
169
  *
254
- * 将所有定义的端点转换为可调用的 API 对象。
255
- * 每个端点都带有完整的类型信息(响应类型、路径参数、请求体类型)。
256
- *
257
170
  * @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
171
  */
271
172
  build() {
272
173
  const api = {};
273
174
  for (const [name, config] of this.endpoints) {
274
- // 运行时不需要类型检查,类型安全由编译时保证
275
175
  const endpoint = ((options) => {
276
176
  return this.client.request(config, options);
277
177
  });
@@ -282,14 +182,6 @@ export class ApiBuilder {
282
182
  }
283
183
  /**
284
184
  * 获取 HTTP 客户端实例(用于高级操作)
285
- *
286
- * @returns HttpClient 实例
287
- *
288
- * @example
289
- * ```ts
290
- * const client = api.getClient()
291
- * await client.clearCache()
292
- * ```
293
185
  */
294
186
  getClient() {
295
187
  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, type ApiEndpointWithBody, type ApiEndpointWithoutBody, type ApiEndpointWithBodyRequired, type ApiEndpointWithBodyOptional, type Simplify, } 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,EACH,UAAU,EACV,KAAK,WAAW,EAChB,KAAK,aAAa,EAClB,KAAK,mBAAmB,EACxB,KAAK,sBAAsB,EAC3B,KAAK,2BAA2B,EAChC,KAAK,2BAA2B,EAChC,KAAK,QAAQ,GAChB,MAAM,cAAc,CAAA"}
@@ -0,0 +1,102 @@
1
+ /**
2
+ * ApiBuilder 使用示例
3
+ *
4
+ * 此文件演示 ApiBuilder 的核心特性:
5
+ * 1. 泛型累积 - 链式调用自动累积端点类型
6
+ * 2. Body 类型约束 - POST/PUT/PATCH 请求体类型检查
7
+ * 3. 端点名称字面量 - 调用不存在的端点会编译报错
8
+ * 4. 缓存策略 - CacheMode 控制缓存行为
9
+ * 5. 多次响应 - 支持缓存优先场景下的两次数据返回
10
+ */
11
+ /** 用户实体 */
12
+ interface User {
13
+ id: string;
14
+ name: string;
15
+ email: string;
16
+ createdAt: string;
17
+ }
18
+ /** 创建用户 DTO */
19
+ interface CreateUserDTO {
20
+ name: string;
21
+ email: string;
22
+ }
23
+ /** 更新用户 DTO */
24
+ interface UpdateUserDTO {
25
+ name?: string;
26
+ email?: string;
27
+ }
28
+ /** 分页响应 */
29
+ interface PaginatedResponse<T> {
30
+ data: T[];
31
+ total: number;
32
+ page: number;
33
+ pageSize: number;
34
+ }
35
+ export declare const userApi: object & {
36
+ [x: string]: ApiEndpointWithoutBody<User[]>;
37
+ } & {
38
+ [x: string]: ApiEndpointWithoutBody<User>;
39
+ } & {
40
+ [x: string]: ApiEndpointWithoutBody<PaginatedResponse<User>>;
41
+ } & {
42
+ [x: string]: import("..").ApiEndpointWithBodyRequired<User, CreateUserDTO>;
43
+ } & {
44
+ [x: string]: import("..").ApiEndpointWithBodyOptional<void>;
45
+ } & {
46
+ [x: string]: import("..").ApiEndpointWithBodyRequired<User, UpdateUserDTO>;
47
+ } & {
48
+ [x: string]: import("..").ApiEndpointWithBodyRequired<User, Partial<UpdateUserDTO>>;
49
+ } & {
50
+ [x: string]: ApiEndpointWithoutBody<void>;
51
+ };
52
+ declare function demo(): Promise<void>;
53
+ /**
54
+ * CacheMode 枚举值说明:
55
+ *
56
+ * - OnlyRemote: 只请求网络,不使用缓存(适用于实时性要求高的数据)
57
+ * - OnlyCache: 只读取缓存,不请求网络(适用于离线场景)
58
+ * - FirstCache: 先返回缓存,再请求网络更新(可能返回两次数据)
59
+ * - FirstRemote: 先请求网络,失败时回退到缓存(网络优先)
60
+ * - IfCache: 有缓存则用缓存,无缓存则请求网络(只返回一次)
61
+ */
62
+ /**
63
+ * 缓存模式使用示例
64
+ */
65
+ declare function cacheModeDemo(): Promise<void>;
66
+ /**
67
+ * FirstCache 模式会返回两次数据:
68
+ * 1. 第一次:从缓存返回(如果有缓存),fromCache = true
69
+ * 2. 第二次:从网络返回最新数据,fromCache = false
70
+ *
71
+ * 这种模式非常适合:
72
+ * - 列表页快速展示 + 后台刷新
73
+ * - 详情页先显示旧数据,再更新为最新数据
74
+ * - 提升用户体验,减少白屏时间
75
+ */
76
+ /**
77
+ * 方式一:使用 for await 处理多次响应
78
+ * 推荐用于需要处理每次响应的场景
79
+ */
80
+ declare function multiResponseWithForAwait(): Promise<void>;
81
+ /**
82
+ * 方式二:使用 subscribe 处理多次响应
83
+ * 推荐用于 React/Vue 等框架中的响应式更新
84
+ */
85
+ declare function multiResponseWithSubscribe(): Promise<void>;
86
+ /**
87
+ * 方式三:只需要最终结果时使用 await
88
+ * 注意:await 只返回最后一次响应(网络数据)
89
+ */
90
+ declare function singleResponseWithAwait(): Promise<void>;
91
+ /**
92
+ * React 组件中的典型用法示例(伪代码)
93
+ */
94
+ declare function reactUsageExample(): void;
95
+ declare function typeErrors(): void;
96
+ import type { ApiEndpointWithoutBody } from '../api/ApiBuilder';
97
+ /**
98
+ * 类型兼容性验证示例
99
+ */
100
+ declare function verifyTypeCompatibility(): void;
101
+ export { demo, typeErrors, verifyTypeCompatibility, cacheModeDemo, multiResponseWithForAwait, multiResponseWithSubscribe, singleResponseWithAwait, reactUsageExample, };
102
+ //# 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;;;;;;;;;GASG;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,kBAqDlB;AAMD;;;;;;;;GAQG;AAEH;;GAEG;AACH,iBAAe,aAAa,kBAmC3B;AAMD;;;;;;;;;GASG;AAEH;;;GAGG;AACH,iBAAe,yBAAyB,kBAiBvC;AAED;;;GAGG;AACH,iBAAe,0BAA0B,kBA0BxC;AAED;;;GAGG;AACH,iBAAe,uBAAuB,kBAUrC;AAED;;GAEG;AACH,iBAAS,iBAAiB,SA4CzB;AAoBD,iBAAS,UAAU,SAYlB;AAMD,OAAO,KAAK,EAAuB,sBAAsB,EAAE,MAAM,mBAAmB,CAAA;AAcpF;;GAEG;AACH,iBAAS,uBAAuB,SAkB/B;AAMD,OAAO,EACH,IAAI,EACJ,UAAU,EACV,uBAAuB,EACvB,aAAa,EACb,yBAAyB,EACzB,0BAA0B,EAC1B,uBAAuB,EACvB,iBAAiB,GACpB,CAAA"}
@@ -0,0 +1,343 @@
1
+ /**
2
+ * ApiBuilder 使用示例
3
+ *
4
+ * 此文件演示 ApiBuilder 的核心特性:
5
+ * 1. 泛型累积 - 链式调用自动累积端点类型
6
+ * 2. Body 类型约束 - POST/PUT/PATCH 请求体类型检查
7
+ * 3. 端点名称字面量 - 调用不存在的端点会编译报错
8
+ * 4. 缓存策略 - CacheMode 控制缓存行为
9
+ * 5. 多次响应 - 支持缓存优先场景下的两次数据返回
10
+ */
11
+ import { ApiBuilder } from '../api/ApiBuilder';
12
+ import { CacheMode } from '../models/CacheMode';
13
+ // ============================================================================
14
+ // 创建 API 服务
15
+ // ============================================================================
16
+ export const userApi = ApiBuilder.create({
17
+ baseUrl: 'https://api.example.com',
18
+ timeout: 10000,
19
+ defaultCache: {
20
+ mode: CacheMode.FirstRemote,
21
+ ttl: 5 * 60 * 1000,
22
+ },
23
+ })
24
+ // GET 请求 - 无路径参数
25
+ .get('getUsers', {
26
+ path: '/users',
27
+ cache: { mode: CacheMode.FirstCache },
28
+ })
29
+ // GET 请求 - 有路径参数
30
+ .get('getUser', {
31
+ path: '/users/{id}',
32
+ cache: { mode: CacheMode.IfCache, ttl: 60000 },
33
+ })
34
+ // GET 请求 - 分页
35
+ .get('getUsersPaginated', {
36
+ path: '/users/paginated',
37
+ })
38
+ // POST 请求 - 有 body 类型
39
+ .post('createUser', {
40
+ path: '/users',
41
+ })
42
+ // POST 请求 - 无 body 类型(触发器)
43
+ .post('activateUser', {
44
+ path: '/users/{id}/activate',
45
+ })
46
+ // PUT 请求 - 完整更新
47
+ .put('updateUser', {
48
+ path: '/users/{id}',
49
+ })
50
+ // PATCH 请求 - 部分更新
51
+ .patch('patchUser', {
52
+ path: '/users/{id}',
53
+ })
54
+ // DELETE 请求
55
+ .delete('deleteUser', {
56
+ path: '/users/{id}',
57
+ })
58
+ // 扩展 ky 实例(添加拦截器)
59
+ .extend({
60
+ hooks: {
61
+ beforeRequest: [
62
+ (request) => {
63
+ const token = 'mock-token';
64
+ request.headers.set('Authorization', `Bearer ${token}`);
65
+ },
66
+ ],
67
+ },
68
+ })
69
+ .build();
70
+ // ============================================================================
71
+ // 使用示例 - 类型安全调用
72
+ // ============================================================================
73
+ async function demo() {
74
+ // ✅ GET 无路径参数
75
+ const users = await userApi.getUsers();
76
+ console.log('Users:', users);
77
+ // ✅ GET 有路径参数
78
+ const user = await userApi.getUser({
79
+ pathParams: { id: '123' },
80
+ });
81
+ console.log('User:', user);
82
+ // ✅ GET 带 query 参数
83
+ const paginatedUsers = await userApi.getUsersPaginated({
84
+ query: { page: 1, pageSize: 10 },
85
+ });
86
+ console.log('Paginated:', paginatedUsers);
87
+ // ✅ POST 有 body 类型约束
88
+ const newUser = await userApi.createUser({
89
+ body: { name: 'John', email: 'john@example.com' },
90
+ });
91
+ console.log('Created:', newUser);
92
+ // ✅ POST 无 body(触发器)
93
+ await userApi.activateUser({
94
+ pathParams: { id: '123' },
95
+ });
96
+ // ✅ PUT 完整更新
97
+ const updatedUser = await userApi.updateUser({
98
+ pathParams: { id: '123' },
99
+ body: { name: 'John Updated', email: 'john.updated@example.com' },
100
+ });
101
+ console.log('Updated:', updatedUser);
102
+ // ✅ PATCH 部分更新
103
+ const patchedUser = await userApi.patchUser({
104
+ pathParams: { id: '123' },
105
+ body: { name: 'John Patched' },
106
+ });
107
+ console.log('Patched:', patchedUser);
108
+ // ✅ DELETE
109
+ await userApi.deleteUser({
110
+ pathParams: { id: '123' },
111
+ });
112
+ // ✅ 覆盖缓存配置
113
+ const freshUser = await userApi.getUser({
114
+ pathParams: { id: '123' },
115
+ cache: { mode: CacheMode.OnlyRemote },
116
+ });
117
+ console.log('Fresh:', freshUser);
118
+ }
119
+ // ============================================================================
120
+ // CacheMode 缓存模式详解
121
+ // ============================================================================
122
+ /**
123
+ * CacheMode 枚举值说明:
124
+ *
125
+ * - OnlyRemote: 只请求网络,不使用缓存(适用于实时性要求高的数据)
126
+ * - OnlyCache: 只读取缓存,不请求网络(适用于离线场景)
127
+ * - FirstCache: 先返回缓存,再请求网络更新(可能返回两次数据)
128
+ * - FirstRemote: 先请求网络,失败时回退到缓存(网络优先)
129
+ * - IfCache: 有缓存则用缓存,无缓存则请求网络(只返回一次)
130
+ */
131
+ /**
132
+ * 缓存模式使用示例
133
+ */
134
+ async function cacheModeDemo() {
135
+ // 1. OnlyRemote - 始终请求网络,忽略缓存
136
+ // 适用场景:提交表单、实时数据
137
+ const realTimeData = await userApi.getUser({
138
+ pathParams: { id: '123' },
139
+ cache: { mode: CacheMode.OnlyRemote },
140
+ });
141
+ console.log('实时数据:', realTimeData);
142
+ // 2. OnlyCache - 只读缓存,不请求网络
143
+ // 适用场景:离线模式、快速展示历史数据
144
+ try {
145
+ const cachedData = await userApi.getUser({
146
+ pathParams: { id: '123' },
147
+ cache: { mode: CacheMode.OnlyCache },
148
+ });
149
+ console.log('缓存数据:', cachedData);
150
+ }
151
+ catch (error) {
152
+ console.log('无缓存数据');
153
+ }
154
+ // 3. IfCache - 有缓存用缓存,无缓存请求网络(只返回一次)
155
+ // 适用场景:静态资源、不常变化的配置
156
+ const configData = await userApi.getUsers({
157
+ cache: { mode: CacheMode.IfCache, ttl: 60 * 60 * 1000 }, // 1小时
158
+ });
159
+ console.log('配置数据:', configData);
160
+ // 4. FirstRemote - 优先网络,失败回退缓存(只返回一次)
161
+ // 适用场景:希望获取最新数据,但网络不稳定时有兜底
162
+ const freshData = await userApi.getUser({
163
+ pathParams: { id: '123' },
164
+ cache: { mode: CacheMode.FirstRemote },
165
+ });
166
+ console.log('最新数据:', freshData);
167
+ }
168
+ // ============================================================================
169
+ // 多次响应处理 - FirstCache 模式的核心特性
170
+ // ============================================================================
171
+ /**
172
+ * FirstCache 模式会返回两次数据:
173
+ * 1. 第一次:从缓存返回(如果有缓存),fromCache = true
174
+ * 2. 第二次:从网络返回最新数据,fromCache = false
175
+ *
176
+ * 这种模式非常适合:
177
+ * - 列表页快速展示 + 后台刷新
178
+ * - 详情页先显示旧数据,再更新为最新数据
179
+ * - 提升用户体验,减少白屏时间
180
+ */
181
+ /**
182
+ * 方式一:使用 for await 处理多次响应
183
+ * 推荐用于需要处理每次响应的场景
184
+ */
185
+ async function multiResponseWithForAwait() {
186
+ console.log('=== for await 方式 ===');
187
+ for await (const { data, fromCache } of userApi.getUser({
188
+ pathParams: { id: '123' },
189
+ cache: { mode: CacheMode.FirstCache },
190
+ })) {
191
+ if (fromCache) {
192
+ console.log('📦 缓存数据(快速展示):', data);
193
+ // 可以先用缓存数据渲染 UI
194
+ }
195
+ else {
196
+ console.log('🌐 网络数据(最新):', data);
197
+ // 用最新数据更新 UI
198
+ }
199
+ }
200
+ console.log('✅ 请求完成');
201
+ }
202
+ /**
203
+ * 方式二:使用 subscribe 处理多次响应
204
+ * 推荐用于 React/Vue 等框架中的响应式更新
205
+ */
206
+ async function multiResponseWithSubscribe() {
207
+ console.log('=== subscribe 方式 ===');
208
+ await userApi
209
+ .getUser({
210
+ pathParams: { id: '123' },
211
+ cache: { mode: CacheMode.FirstCache },
212
+ })
213
+ .subscribe({
214
+ onData: (data, fromCache) => {
215
+ if (fromCache) {
216
+ console.log('📦 缓存数据:', data);
217
+ // setState({ user: data, loading: false })
218
+ }
219
+ else {
220
+ console.log('🌐 网络数据:', data);
221
+ // setState({ user: data, loading: false, isStale: false })
222
+ }
223
+ },
224
+ onError: (error) => {
225
+ console.error('❌ 请求失败:', error);
226
+ // setState({ error, loading: false })
227
+ },
228
+ onComplete: () => {
229
+ console.log('✅ 请求完成');
230
+ },
231
+ });
232
+ }
233
+ /**
234
+ * 方式三:只需要最终结果时使用 await
235
+ * 注意:await 只返回最后一次响应(网络数据)
236
+ */
237
+ async function singleResponseWithAwait() {
238
+ console.log('=== await 方式(只获取最终结果)===');
239
+ // 直接 await 只会返回最终的网络数据
240
+ const user = await userApi.getUser({
241
+ pathParams: { id: '123' },
242
+ cache: { mode: CacheMode.FirstCache },
243
+ });
244
+ console.log('最终数据:', user);
245
+ }
246
+ /**
247
+ * React 组件中的典型用法示例(伪代码)
248
+ */
249
+ function reactUsageExample() {
250
+ /*
251
+ // React 组件示例
252
+ function UserProfile({ userId }: { userId: string }) {
253
+ const [user, setUser] = useState<User | null>(null)
254
+ const [isStale, setIsStale] = useState(false)
255
+ const [loading, setLoading] = useState(true)
256
+
257
+ useEffect(() => {
258
+ let cancelled = false
259
+
260
+ // 使用 subscribe 处理多次响应
261
+ userApi.getUser({
262
+ pathParams: { id: userId },
263
+ cache: { mode: CacheMode.FirstCache }
264
+ }).subscribe({
265
+ onData: (data, fromCache) => {
266
+ if (cancelled) return
267
+ setUser(data)
268
+ setIsStale(fromCache) // 标记是否为陈旧数据
269
+ setLoading(false)
270
+ },
271
+ onError: (error) => {
272
+ if (cancelled) return
273
+ console.error(error)
274
+ setLoading(false)
275
+ }
276
+ })
277
+
278
+ return () => { cancelled = true }
279
+ }, [userId])
280
+
281
+ if (loading) return <div>加载中...</div>
282
+ if (!user) return <div>用户不存在</div>
283
+
284
+ return (
285
+ <div>
286
+ {isStale && <span className="badge">数据更新中...</span>}
287
+ <h1>{user.name}</h1>
288
+ <p>{user.email}</p>
289
+ </div>
290
+ )
291
+ }
292
+ */
293
+ }
294
+ // ============================================================================
295
+ // 类型错误示例
296
+ // ============================================================================
297
+ /**
298
+ * 类型安全验证
299
+ *
300
+ * 注意:由于 TypeScript 对深层交叉类型的处理限制,
301
+ * 当链式调用超过 4 个端点时,类型推断可能不够精确。
302
+ * 建议:保持单个 builder 的端点数量在 4 个以内,或分批构建。
303
+ *
304
+ * 以下是简化版本的类型安全验证(2-3 个端点)
305
+ */
306
+ const simpleApi = ApiBuilder.create({ baseUrl: 'http://test' })
307
+ .get('getUser', { path: '/users/{id}' })
308
+ .post('createUser', { path: '/users' })
309
+ .build();
310
+ function typeErrors() {
311
+ // ❌ body 类型不匹配(缺少 email)
312
+ // @ts-expect-error email is required
313
+ simpleApi.createUser({ body: { name: 'John' } });
314
+ // ❌ body 类型不匹配(错误的属性)
315
+ // @ts-expect-error wrong property
316
+ simpleApi.createUser({ body: { wrong: 'x' } });
317
+ // ❌ GET 请求不应该有 body
318
+ // @ts-expect-error body not allowed for GET
319
+ simpleApi.getUser({ pathParams: { id: '1' }, body: {} });
320
+ }
321
+ /**
322
+ * 类型兼容性验证示例
323
+ */
324
+ function verifyTypeCompatibility() {
325
+ const api = ApiBuilder.create({ baseUrl: 'http://test' })
326
+ .get('getUser', { path: '/users/{id}' })
327
+ .get('getUsers', { path: '/users' })
328
+ .post('createUser', { path: '/users' })
329
+ .put('updateUser', { path: '/users/{id}' })
330
+ .delete('deleteUser', { path: '/users/{id}' })
331
+ .build();
332
+ // ✅ 使用类型断言确保 API 符合定义
333
+ // 注意:由于 TypeScript 深层交叉类型限制,需要通过 unknown 进行断言
334
+ const typedApi = api;
335
+ // 现在可以获得精确的类型检查
336
+ typedApi.createUser({ body: { name: 'John', email: 'john@test.com' } });
337
+ typedApi.getUser({ pathParams: { id: '1' } });
338
+ console.log('Typed API:', typedApi);
339
+ }
340
+ // ============================================================================
341
+ // 导出
342
+ // ============================================================================
343
+ export { demo, typeErrors, verifyTypeCompatibility, cacheModeDemo, multiResponseWithForAwait, multiResponseWithSubscribe, singleResponseWithAwait, reactUsageExample, };
@@ -38,5 +38,5 @@
38
38
  export { CacheMode, type CacheEntry, type ApiResult, type ApiCallback, type CacheConfig, type CacheKeyRequest, type HttpClientConfig, type RequestConfig, type ApiOptions, type DiskLruCacheConfig, type HttpCacheConfig, type CacheInterceptor, type CacheInterceptorContext, type CacheInterceptorResult, } from './types';
39
39
  export { DiskLruCache, HttpCache } from './cache';
40
40
  export { HttpClient, ApiStream } from './client';
41
- export { ApiBuilder, type ApiEndpoint, type ApiDefinition } from './api';
41
+ export { ApiBuilder, type ApiEndpoint, type ApiDefinition, type ApiEndpointWithBody, type ApiEndpointWithoutBody, type ApiEndpointWithBodyRequired, type ApiEndpointWithBodyOptional, type Simplify, } from './api';
42
42
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/http/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AAGH,OAAO,EACH,SAAS,EACT,KAAK,UAAU,EACf,KAAK,SAAS,EACd,KAAK,WAAW,EAChB,KAAK,WAAW,EAChB,KAAK,eAAe,EACpB,KAAK,gBAAgB,EACrB,KAAK,aAAa,EAClB,KAAK,UAAU,EACf,KAAK,kBAAkB,EACvB,KAAK,eAAe,EAEpB,KAAK,gBAAgB,EACrB,KAAK,uBAAuB,EAC5B,KAAK,sBAAsB,GAC9B,MAAM,SAAS,CAAA;AAGhB,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,SAAS,CAAA;AAGjD,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,UAAU,CAAA;AAGhD,OAAO,EAAE,UAAU,EAAE,KAAK,WAAW,EAAE,KAAK,aAAa,EAAE,MAAM,OAAO,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/http/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AAGH,OAAO,EACH,SAAS,EACT,KAAK,UAAU,EACf,KAAK,SAAS,EACd,KAAK,WAAW,EAChB,KAAK,WAAW,EAChB,KAAK,eAAe,EACpB,KAAK,gBAAgB,EACrB,KAAK,aAAa,EAClB,KAAK,UAAU,EACf,KAAK,kBAAkB,EACvB,KAAK,eAAe,EAEpB,KAAK,gBAAgB,EACrB,KAAK,uBAAuB,EAC5B,KAAK,sBAAsB,GAC9B,MAAM,SAAS,CAAA;AAGhB,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,SAAS,CAAA;AAGjD,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,UAAU,CAAA;AAGhD,OAAO,EACH,UAAU,EACV,KAAK,WAAW,EAChB,KAAK,aAAa,EAClB,KAAK,mBAAmB,EACxB,KAAK,sBAAsB,EAC3B,KAAK,2BAA2B,EAChC,KAAK,2BAA2B,EAChC,KAAK,QAAQ,GAChB,MAAM,OAAO,CAAA"}
@@ -42,4 +42,4 @@ export { DiskLruCache, HttpCache } from './cache';
42
42
  // Client
43
43
  export { HttpClient, ApiStream } from './client';
44
44
  // API
45
- export { ApiBuilder } from './api';
45
+ export { ApiBuilder, } from './api';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "xxf_react",
3
- "version": "0.7.3",
3
+ "version": "0.7.5",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "main": "dist/index.js",