xxf_react 0.6.7 → 0.6.9

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.
Files changed (74) hide show
  1. package/README.md +18 -13
  2. package/dist/foundation/Copy.d.ts +8 -0
  3. package/dist/foundation/Copy.d.ts.map +1 -0
  4. package/dist/foundation/Copy.js +21 -0
  5. package/dist/foundation/index.d.ts +2 -0
  6. package/dist/foundation/index.d.ts.map +1 -1
  7. package/dist/foundation/index.js +2 -0
  8. package/dist/http/api/ApiBuilder.d.ts +115 -0
  9. package/dist/http/api/ApiBuilder.d.ts.map +1 -0
  10. package/dist/http/api/ApiBuilder.js +128 -0
  11. package/dist/http/api/index.d.ts +2 -0
  12. package/dist/http/api/index.d.ts.map +1 -0
  13. package/dist/http/api/index.js +1 -0
  14. package/dist/http/cache/DiskLruCache.d.ts +66 -0
  15. package/dist/http/cache/DiskLruCache.d.ts.map +1 -0
  16. package/dist/http/cache/DiskLruCache.js +254 -0
  17. package/dist/http/cache/HttpCache.d.ts +59 -0
  18. package/dist/http/cache/HttpCache.d.ts.map +1 -0
  19. package/dist/http/cache/HttpCache.js +215 -0
  20. package/dist/http/cache/index.d.ts +3 -0
  21. package/dist/http/cache/index.d.ts.map +1 -0
  22. package/dist/http/cache/index.js +2 -0
  23. package/dist/http/client/ApiStream.d.ts +80 -0
  24. package/dist/http/client/ApiStream.d.ts.map +1 -0
  25. package/dist/http/client/ApiStream.js +190 -0
  26. package/dist/http/client/HttpClient.d.ts +88 -0
  27. package/dist/http/client/HttpClient.d.ts.map +1 -0
  28. package/dist/http/client/HttpClient.js +381 -0
  29. package/dist/http/client/index.d.ts +3 -0
  30. package/dist/http/client/index.d.ts.map +1 -0
  31. package/dist/http/client/index.js +2 -0
  32. package/dist/http/index.d.ts +42 -0
  33. package/dist/http/index.d.ts.map +1 -0
  34. package/dist/http/index.js +45 -0
  35. package/dist/http/models/ApiTypes.d.ts +54 -0
  36. package/dist/http/models/ApiTypes.d.ts.map +1 -0
  37. package/dist/http/models/ApiTypes.js +4 -0
  38. package/dist/http/models/CacheConfig.d.ts +58 -0
  39. package/dist/http/models/CacheConfig.d.ts.map +1 -0
  40. package/dist/http/models/CacheConfig.js +4 -0
  41. package/dist/http/models/CacheEntry.d.ts +28 -0
  42. package/dist/http/models/CacheEntry.d.ts.map +1 -0
  43. package/dist/http/models/CacheEntry.js +1 -0
  44. package/dist/http/models/CacheInterceptor.d.ts +112 -0
  45. package/dist/http/models/CacheInterceptor.d.ts.map +1 -0
  46. package/dist/http/models/CacheInterceptor.js +6 -0
  47. package/dist/http/models/CacheMode.d.ts +45 -0
  48. package/dist/http/models/CacheMode.d.ts.map +1 -0
  49. package/dist/http/models/CacheMode.js +45 -0
  50. package/dist/http/models/DiskCacheConfig.d.ts +56 -0
  51. package/dist/http/models/DiskCacheConfig.d.ts.map +1 -0
  52. package/dist/http/models/DiskCacheConfig.js +4 -0
  53. package/dist/http/models/HttpClientConfig.d.ts +77 -0
  54. package/dist/http/models/HttpClientConfig.d.ts.map +1 -0
  55. package/dist/http/models/HttpClientConfig.js +4 -0
  56. package/dist/http/models/LruMetadata.d.ts +28 -0
  57. package/dist/http/models/LruMetadata.d.ts.map +1 -0
  58. package/dist/http/models/LruMetadata.js +6 -0
  59. package/dist/http/models/RequestConfig.d.ts +67 -0
  60. package/dist/http/models/RequestConfig.d.ts.map +1 -0
  61. package/dist/http/models/RequestConfig.js +4 -0
  62. package/dist/http/models/index.d.ts +24 -0
  63. package/dist/http/models/index.d.ts.map +1 -0
  64. package/dist/http/models/index.js +16 -0
  65. package/dist/http/types.d.ts +13 -0
  66. package/dist/http/types.d.ts.map +1 -0
  67. package/dist/http/types.js +14 -0
  68. package/dist/index.d.ts +1 -0
  69. package/dist/index.d.ts.map +1 -1
  70. package/dist/index.js +1 -0
  71. package/dist/layout/hover/XHover.d.ts +1 -1
  72. package/dist/layout/hover/XHover.d.ts.map +1 -1
  73. package/dist/layout/hover/XHover.js +4 -4
  74. package/package.json +14 -4
@@ -0,0 +1,381 @@
1
+ /**
2
+ * HTTP 客户端
3
+ *
4
+ * 基于 ky 封装,支持:
5
+ * - 多种缓存模式
6
+ * - 自动重试
7
+ * - 超时处理
8
+ */
9
+ import ky from 'ky';
10
+ import { HttpCache } from '../cache/HttpCache';
11
+ import { ApiStream } from './ApiStream';
12
+ import { CacheMode, } from '../types';
13
+ /**
14
+ * 对象 key 排序(用于生成稳定的缓存 key)
15
+ * 支持嵌套对象递归排序
16
+ */
17
+ function sortKeys(obj) {
18
+ const sorted = {};
19
+ for (const key of Object.keys(obj).sort()) {
20
+ const value = obj[key];
21
+ // 递归处理嵌套对象(排除 null 和数组)
22
+ if (value !== null && typeof value === 'object' && !Array.isArray(value)) {
23
+ sorted[key] = sortKeys(value);
24
+ }
25
+ else {
26
+ sorted[key] = value;
27
+ }
28
+ }
29
+ return sorted;
30
+ }
31
+ /**
32
+ * 简单 hash 函数
33
+ */
34
+ function simpleHash(str) {
35
+ let hash = 0;
36
+ for (let i = 0; i < str.length; i++) {
37
+ const char = str.charCodeAt(i);
38
+ hash = (hash << 5) - hash + char;
39
+ hash = hash & hash; // Convert to 32bit integer
40
+ }
41
+ return Math.abs(hash).toString(36);
42
+ }
43
+ /**
44
+ * 过滤对象中的 undefined 和 null 值
45
+ */
46
+ function filterUndefined(obj) {
47
+ if (!obj)
48
+ return undefined;
49
+ const result = {};
50
+ for (const [key, value] of Object.entries(obj)) {
51
+ if (value !== undefined && value !== null) {
52
+ result[key] = String(value);
53
+ }
54
+ }
55
+ return Object.keys(result).length > 0 ? result : undefined;
56
+ }
57
+ /**
58
+ * 默认缓存 key 生成
59
+ */
60
+ function defaultGenerateCacheKey(request, keyHeaders) {
61
+ const parts = [request.method, request.path];
62
+ if (request.query && Object.keys(request.query).length > 0) {
63
+ parts.push(JSON.stringify(sortKeys(request.query)));
64
+ }
65
+ if (request.body) {
66
+ parts.push(simpleHash(JSON.stringify(request.body)));
67
+ }
68
+ if ((keyHeaders === null || keyHeaders === void 0 ? void 0 : keyHeaders.length) && request.headers) {
69
+ const headerParts = keyHeaders
70
+ .filter((k) => request.headers[k])
71
+ .map((k) => `${k}:${request.headers[k]}`)
72
+ .join('|');
73
+ if (headerParts) {
74
+ parts.push(headerParts);
75
+ }
76
+ }
77
+ return parts.join(':');
78
+ }
79
+ export class HttpClient {
80
+ constructor(config) {
81
+ this.config = {
82
+ headers: {},
83
+ timeout: 30000,
84
+ retry: 2,
85
+ memoryCacheMaxSize: 100,
86
+ diskCacheMaxSize: 500,
87
+ dbName: 'xxf-http-cache',
88
+ defaultKeyHeaders: ['Authorization'],
89
+ defaultCache: undefined, // 默认不缓存 (OnlyRemote)
90
+ cacheInterceptor: undefined, // 默认无拦截器
91
+ ...config,
92
+ };
93
+ // 初始化 ky 实例
94
+ this.client = ky.create({
95
+ prefixUrl: this.config.baseUrl,
96
+ timeout: this.config.timeout,
97
+ retry: this.config.retry,
98
+ headers: this.config.headers,
99
+ });
100
+ // 初始化双层缓存
101
+ this.cache = new HttpCache({
102
+ memoryMaxSize: this.config.memoryCacheMaxSize,
103
+ diskMaxSize: this.config.diskCacheMaxSize,
104
+ dbName: this.config.dbName,
105
+ });
106
+ }
107
+ /**
108
+ * 获取 ky 实例(用于扩展 hooks 等)
109
+ */
110
+ getKyInstance() {
111
+ return this.client;
112
+ }
113
+ /**
114
+ * 设置 ky 实例
115
+ */
116
+ setKyInstance(instance) {
117
+ this.client = instance;
118
+ }
119
+ /**
120
+ * 扩展 ky 实例
121
+ */
122
+ extendKy(options) {
123
+ this.client = this.client.extend(options);
124
+ }
125
+ /**
126
+ * 执行请求,返回 ApiStream
127
+ */
128
+ request(requestConfig, options = {}) {
129
+ const self = this;
130
+ return new ApiStream(async function* () {
131
+ const { method, path, headers: configHeaders, timeout, cache: cacheConfig, retry } = requestConfig;
132
+ const { pathParams, query, body, cache: overrideCache, headers: optionHeaders } = options;
133
+ // 替换路径参数(自动 URL 编码)
134
+ let url = path;
135
+ if (pathParams) {
136
+ for (const [key, value] of Object.entries(pathParams)) {
137
+ url = url.replace(`{${key}}`, encodeURIComponent(String(value)));
138
+ }
139
+ }
140
+ // 合并 headers
141
+ const mergedHeaders = {
142
+ ...self.config.headers,
143
+ ...configHeaders,
144
+ ...optionHeaders,
145
+ };
146
+ // 合并缓存配置(优先级:调用时覆盖 > 接口配置 > 全局默认 > 硬编码默认)
147
+ const finalCacheConfig = {
148
+ mode: CacheMode.OnlyRemote,
149
+ ttl: 5 * 60 * 1000, // 默认 5 分钟
150
+ keyHeaders: self.config.defaultKeyHeaders,
151
+ ...self.config.defaultCache, // 全局默认缓存配置
152
+ ...cacheConfig, // 接口级配置
153
+ ...overrideCache, // 调用时覆盖
154
+ };
155
+ // 生成缓存 key
156
+ const cacheKeyRequest = {
157
+ method,
158
+ path: url,
159
+ query: query,
160
+ body,
161
+ headers: mergedHeaders,
162
+ pathParams,
163
+ };
164
+ const cacheKey = finalCacheConfig.cacheKey
165
+ ? finalCacheConfig.cacheKey(cacheKeyRequest)
166
+ : defaultGenerateCacheKey(cacheKeyRequest, finalCacheConfig.keyHeaders);
167
+ // 过滤 query 参数中的 undefined/null
168
+ const filteredQuery = filterUndefined(query);
169
+ // 请求函数(返回完整响应信息,供缓存拦截器使用)
170
+ const doRequest = async () => {
171
+ const kyOptions = {
172
+ method: method,
173
+ headers: mergedHeaders,
174
+ timeout,
175
+ retry,
176
+ searchParams: filteredQuery,
177
+ };
178
+ if (body && ['POST', 'PUT', 'PATCH'].includes(method)) {
179
+ kyOptions.json = body;
180
+ }
181
+ const response = await self.client(url, kyOptions);
182
+ const data = await response.json();
183
+ return {
184
+ data,
185
+ status: response.status,
186
+ headers: response.headers,
187
+ };
188
+ };
189
+ // 根据缓存模式执行
190
+ yield* self.executeWithCacheStrategy(finalCacheConfig.mode, cacheKey, finalCacheConfig.ttl, doRequest, cacheKeyRequest);
191
+ });
192
+ }
193
+ /**
194
+ * 根据缓存策略执行请求
195
+ */
196
+ async *executeWithCacheStrategy(mode, cacheKey, ttl, fetchFn, request) {
197
+ switch (mode) {
198
+ case CacheMode.OnlyRemote:
199
+ yield* this.strategyOnlyRemote(cacheKey, ttl, fetchFn, request);
200
+ break;
201
+ case CacheMode.OnlyCache:
202
+ yield* this.strategyOnlyCache(cacheKey);
203
+ break;
204
+ case CacheMode.IfCache:
205
+ yield* this.strategyIfCache(cacheKey, ttl, fetchFn, request);
206
+ break;
207
+ case CacheMode.FirstRemote:
208
+ yield* this.strategyFirstRemote(cacheKey, ttl, fetchFn, request);
209
+ break;
210
+ case CacheMode.FirstCache:
211
+ yield* this.strategyFirstCache(cacheKey, ttl, fetchFn, request);
212
+ break;
213
+ case CacheMode.LastCache:
214
+ yield* this.strategyLastCache(cacheKey, ttl, fetchFn, request);
215
+ break;
216
+ default:
217
+ yield* this.strategyOnlyRemote(cacheKey, ttl, fetchFn, request);
218
+ }
219
+ }
220
+ /**
221
+ * 判断响应是否应该缓存
222
+ *
223
+ * 调用全局缓存拦截器,根据响应内容决定是否缓存。
224
+ * 如果没有配置拦截器,默认允许缓存。
225
+ *
226
+ * @param result 请求响应结果
227
+ * @param cacheKey 缓存 key
228
+ * @param request 请求信息
229
+ * @param defaultTtl 默认 TTL
230
+ * @returns 是否缓存及最终 TTL
231
+ */
232
+ async shouldCacheResponse(result, cacheKey, request, defaultTtl) {
233
+ var _a;
234
+ // 没有配置拦截器,默认允许缓存
235
+ if (!this.config.cacheInterceptor) {
236
+ return { shouldCache: true, ttl: defaultTtl };
237
+ }
238
+ // 构建拦截器上下文
239
+ const context = {
240
+ data: result.data,
241
+ status: result.status,
242
+ headers: result.headers,
243
+ request,
244
+ cacheKey,
245
+ };
246
+ try {
247
+ // 调用拦截器(支持同步和异步)
248
+ const interceptorResult = await this.config.cacheInterceptor(context);
249
+ // 处理返回结果
250
+ if (typeof interceptorResult === 'boolean') {
251
+ return { shouldCache: interceptorResult, ttl: defaultTtl };
252
+ }
253
+ return {
254
+ shouldCache: interceptorResult.shouldCache,
255
+ ttl: (_a = interceptorResult.ttl) !== null && _a !== void 0 ? _a : defaultTtl,
256
+ };
257
+ }
258
+ catch (error) {
259
+ // 拦截器执行出错,保守起见不缓存,但不影响正常响应
260
+ console.warn('[HttpClient] cacheInterceptor error:', error);
261
+ return { shouldCache: false, ttl: defaultTtl };
262
+ }
263
+ }
264
+ /**
265
+ * 条件缓存:根据拦截器结果决定是否写入缓存
266
+ */
267
+ async conditionalCache(result, cacheKey, request, defaultTtl) {
268
+ const { shouldCache, ttl } = await this.shouldCacheResponse(result, cacheKey, request, defaultTtl);
269
+ if (shouldCache) {
270
+ await this.cache.set(cacheKey, result.data, ttl);
271
+ }
272
+ }
273
+ /**
274
+ * OnlyRemote: 只从服务器拿
275
+ */
276
+ async *strategyOnlyRemote(cacheKey, ttl, fetchFn, request) {
277
+ const result = await fetchFn();
278
+ await this.conditionalCache(result, cacheKey, request, ttl);
279
+ yield { data: result.data, fromCache: false };
280
+ }
281
+ /**
282
+ * OnlyCache: 只从缓存拿
283
+ */
284
+ async *strategyOnlyCache(cacheKey) {
285
+ const cached = await this.cache.get(cacheKey);
286
+ if (cached) {
287
+ yield { data: cached.data, fromCache: true };
288
+ }
289
+ // 没有缓存就不 yield(类似 Observable.empty())
290
+ }
291
+ /**
292
+ * IfCache: 有缓存返回缓存,否则请求网络
293
+ */
294
+ async *strategyIfCache(cacheKey, ttl, fetchFn, request) {
295
+ const cached = await this.cache.get(cacheKey);
296
+ if (cached) {
297
+ yield { data: cached.data, fromCache: true };
298
+ return;
299
+ }
300
+ const result = await fetchFn();
301
+ await this.conditionalCache(result, cacheKey, request, ttl);
302
+ yield { data: result.data, fromCache: false };
303
+ }
304
+ /**
305
+ * FirstRemote: 先从服务器获取,没网络则读缓存
306
+ */
307
+ async *strategyFirstRemote(cacheKey, ttl, fetchFn, request) {
308
+ try {
309
+ const result = await fetchFn();
310
+ await this.conditionalCache(result, cacheKey, request, ttl);
311
+ yield { data: result.data, fromCache: false };
312
+ }
313
+ catch (error) {
314
+ // 网络错误时尝试读取缓存
315
+ const cached = await this.cache.get(cacheKey);
316
+ if (cached) {
317
+ yield { data: cached.data, fromCache: true };
318
+ }
319
+ else {
320
+ throw error;
321
+ }
322
+ }
323
+ }
324
+ /**
325
+ * FirstCache: 先从本地缓存拿,然后从服务器拿,可能 yield 两次
326
+ */
327
+ async *strategyFirstCache(cacheKey, ttl, fetchFn, request) {
328
+ // 先尝试读取缓存
329
+ const cached = await this.cache.get(cacheKey);
330
+ if (cached) {
331
+ yield { data: cached.data, fromCache: true };
332
+ }
333
+ // 请求网络
334
+ try {
335
+ const result = await fetchFn();
336
+ await this.conditionalCache(result, cacheKey, request, ttl);
337
+ yield { data: result.data, fromCache: false };
338
+ }
339
+ catch (error) {
340
+ // 如果已有缓存,网络错误可以忽略
341
+ if (!cached) {
342
+ throw error;
343
+ }
344
+ // 有缓存时,网络错误静默处理
345
+ }
346
+ }
347
+ /**
348
+ * LastCache: 读上次缓存,没有则返回网络并同步缓存;有缓存也会后台同步网络但不 yield
349
+ */
350
+ async *strategyLastCache(cacheKey, ttl, fetchFn, request) {
351
+ const cached = await this.cache.get(cacheKey);
352
+ if (cached) {
353
+ // 有缓存,返回缓存,后台同步网络(不 yield)
354
+ yield { data: cached.data, fromCache: true };
355
+ // 后台静默更新(也会经过拦截器判断)
356
+ fetchFn()
357
+ .then((result) => this.conditionalCache(result, cacheKey, request, ttl))
358
+ .catch(() => {
359
+ /* 静默忽略错误 */
360
+ });
361
+ }
362
+ else {
363
+ // 无缓存,从网络获取
364
+ const result = await fetchFn();
365
+ await this.conditionalCache(result, cacheKey, request, ttl);
366
+ yield { data: result.data, fromCache: false };
367
+ }
368
+ }
369
+ /**
370
+ * 获取缓存实例(用于高级操作)
371
+ */
372
+ getCache() {
373
+ return this.cache;
374
+ }
375
+ /**
376
+ * 清空所有缓存
377
+ */
378
+ async clearCache() {
379
+ await this.cache.clear();
380
+ }
381
+ }
@@ -0,0 +1,3 @@
1
+ export { HttpClient } from './HttpClient';
2
+ export { ApiStream } from './ApiStream';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/http/client/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AACzC,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAA"}
@@ -0,0 +1,2 @@
1
+ export { HttpClient } from './HttpClient';
2
+ export { ApiStream } from './ApiStream';
@@ -0,0 +1,42 @@
1
+ /**
2
+ * HTTP 网络框架
3
+ *
4
+ * 基于 ky + idb-keyval,支持:
5
+ * - 内存 + 磁盘双层缓存
6
+ * - 6 种缓存模式
7
+ * - AsyncGenerator 流式响应
8
+ * - 类 Retrofit 的 API 定义方式
9
+ *
10
+ * @example
11
+ * ```ts
12
+ * import { ApiBuilder, CacheMode } from 'xxf_react/http'
13
+ *
14
+ * const userApi = ApiBuilder.create({
15
+ * baseUrl: 'https://api.example.com',
16
+ * })
17
+ * .get<User>('getUser', {
18
+ * path: '/users/{id}',
19
+ * cache: { mode: CacheMode.IfCache, ttl: 60000 },
20
+ * })
21
+ * .build()
22
+ *
23
+ * // 方式1: 简单 await
24
+ * const user = await userApi.getUser({ pathParams: { id: '123' } })
25
+ *
26
+ * // 方式2: for await 多次响应
27
+ * for await (const { data, fromCache } of userApi.getUser({ pathParams: { id: '123' } })) {
28
+ * console.log(data, fromCache)
29
+ * }
30
+ *
31
+ * // 方式3: subscribe 安全消费
32
+ * await userApi.getUser({ pathParams: { id: '123' } }).subscribe({
33
+ * onData: (data, fromCache) => console.log(data),
34
+ * onError: (e) => console.error(e),
35
+ * })
36
+ * ```
37
+ */
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
+ export { DiskLruCache, HttpCache } from './cache';
40
+ export { HttpClient, ApiStream } from './client';
41
+ export { ApiBuilder, type ApiEndpoint, type ApiDefinition } from './api';
42
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +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"}
@@ -0,0 +1,45 @@
1
+ /**
2
+ * HTTP 网络框架
3
+ *
4
+ * 基于 ky + idb-keyval,支持:
5
+ * - 内存 + 磁盘双层缓存
6
+ * - 6 种缓存模式
7
+ * - AsyncGenerator 流式响应
8
+ * - 类 Retrofit 的 API 定义方式
9
+ *
10
+ * @example
11
+ * ```ts
12
+ * import { ApiBuilder, CacheMode } from 'xxf_react/http'
13
+ *
14
+ * const userApi = ApiBuilder.create({
15
+ * baseUrl: 'https://api.example.com',
16
+ * })
17
+ * .get<User>('getUser', {
18
+ * path: '/users/{id}',
19
+ * cache: { mode: CacheMode.IfCache, ttl: 60000 },
20
+ * })
21
+ * .build()
22
+ *
23
+ * // 方式1: 简单 await
24
+ * const user = await userApi.getUser({ pathParams: { id: '123' } })
25
+ *
26
+ * // 方式2: for await 多次响应
27
+ * for await (const { data, fromCache } of userApi.getUser({ pathParams: { id: '123' } })) {
28
+ * console.log(data, fromCache)
29
+ * }
30
+ *
31
+ * // 方式3: subscribe 安全消费
32
+ * await userApi.getUser({ pathParams: { id: '123' } }).subscribe({
33
+ * onData: (data, fromCache) => console.log(data),
34
+ * onError: (e) => console.error(e),
35
+ * })
36
+ * ```
37
+ */
38
+ // Types
39
+ export { CacheMode, } from './types';
40
+ // Cache
41
+ export { DiskLruCache, HttpCache } from './cache';
42
+ // Client
43
+ export { HttpClient, ApiStream } from './client';
44
+ // API
45
+ export { ApiBuilder } from './api';
@@ -0,0 +1,54 @@
1
+ /**
2
+ * API 响应相关类型
3
+ */
4
+ /**
5
+ * API 响应结果
6
+ *
7
+ * 包含响应数据和来源标识
8
+ *
9
+ * @template T 响应数据类型
10
+ *
11
+ * @example
12
+ * ```ts
13
+ * for await (const result of api.getUser({ pathParams: { id: '1' } })) {
14
+ * if (result.fromCache) {
15
+ * showSkeleton(result.data)
16
+ * } else {
17
+ * renderFull(result.data)
18
+ * }
19
+ * }
20
+ * ```
21
+ */
22
+ export interface ApiResult<T> {
23
+ /** 响应数据 */
24
+ data: T;
25
+ /** 是否来自缓存 */
26
+ fromCache: boolean;
27
+ }
28
+ /**
29
+ * API 回调配置
30
+ *
31
+ * 用于 subscribe 安全消费模式,确保回调错误不影响后续响应
32
+ *
33
+ * @template T 响应数据类型
34
+ *
35
+ * @example
36
+ * ```ts
37
+ * await api.getUser({ pathParams: { id: '1' } }).subscribe({
38
+ * onData: (data, fromCache) => {
39
+ * renderUI(data) // 即使这里抛错,网络数据仍会返回
40
+ * },
41
+ * onError: (e) => console.error(e),
42
+ * onComplete: () => console.log('done'),
43
+ * })
44
+ * ```
45
+ */
46
+ export interface ApiCallback<T> {
47
+ /** 数据回调 (可能多次调用) */
48
+ onData: (data: T, fromCache: boolean) => void;
49
+ /** 错误回调 */
50
+ onError?: (error: Error) => void;
51
+ /** 完成回调 */
52
+ onComplete?: () => void;
53
+ }
54
+ //# sourceMappingURL=ApiTypes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ApiTypes.d.ts","sourceRoot":"","sources":["../../../src/http/models/ApiTypes.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,WAAW,SAAS,CAAC,CAAC;IACxB,WAAW;IACX,IAAI,EAAE,CAAC,CAAA;IACP,aAAa;IACb,SAAS,EAAE,OAAO,CAAA;CACrB;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,WAAW,WAAW,CAAC,CAAC;IAC1B,oBAAoB;IACpB,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,EAAE,SAAS,EAAE,OAAO,KAAK,IAAI,CAAA;IAC7C,WAAW;IACX,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAA;IAChC,WAAW;IACX,UAAU,CAAC,EAAE,MAAM,IAAI,CAAA;CAC1B"}
@@ -0,0 +1,4 @@
1
+ /**
2
+ * API 响应相关类型
3
+ */
4
+ export {};
@@ -0,0 +1,58 @@
1
+ /**
2
+ * 缓存配置相关类型
3
+ */
4
+ import { CacheMode } from './CacheMode';
5
+ /**
6
+ * 缓存 key 计算请求参数
7
+ *
8
+ * 用于自定义缓存 key 生成函数
9
+ *
10
+ * @example
11
+ * ```ts
12
+ * cache: {
13
+ * cacheKey: (req: CacheKeyRequest) => `user:${req.pathParams?.id}`,
14
+ * }
15
+ * ```
16
+ */
17
+ export interface CacheKeyRequest {
18
+ /** 请求方法 */
19
+ method: string;
20
+ /** 请求路径 */
21
+ path: string;
22
+ /** 查询参数 */
23
+ query?: Record<string, unknown>;
24
+ /** 请求体 */
25
+ body?: unknown;
26
+ /** 请求头 */
27
+ headers?: Record<string, string>;
28
+ /** 路径参数 */
29
+ pathParams?: Record<string, string | number>;
30
+ }
31
+ /**
32
+ * 缓存配置
33
+ *
34
+ * 用于配置单个 API 的缓存行为
35
+ *
36
+ * @example
37
+ * ```ts
38
+ * .get<User>('getUser', {
39
+ * path: '/users/{id}',
40
+ * cache: {
41
+ * mode: CacheMode.FirstCache,
42
+ * ttl: 5 * 60 * 1000, // 5 分钟
43
+ * keyHeaders: ['Authorization'],
44
+ * },
45
+ * })
46
+ * ```
47
+ */
48
+ export interface CacheConfig {
49
+ /** 缓存模式 */
50
+ mode: CacheMode;
51
+ /** 缓存时间 (ms),默认 5 分钟 */
52
+ ttl?: number;
53
+ /** 参与缓存 key 计算的 header 名称 */
54
+ keyHeaders?: string[];
55
+ /** 自定义缓存 key 生成函数 */
56
+ cacheKey?: (request: CacheKeyRequest) => string;
57
+ }
58
+ //# sourceMappingURL=CacheConfig.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CacheConfig.d.ts","sourceRoot":"","sources":["../../../src/http/models/CacheConfig.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAA;AAEvC;;;;;;;;;;;GAWG;AACH,MAAM,WAAW,eAAe;IAC5B,WAAW;IACX,MAAM,EAAE,MAAM,CAAA;IACd,WAAW;IACX,IAAI,EAAE,MAAM,CAAA;IACZ,WAAW;IACX,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAC/B,UAAU;IACV,IAAI,CAAC,EAAE,OAAO,CAAA;IACd,UAAU;IACV,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAChC,WAAW;IACX,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,CAAA;CAC/C;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,WAAW,WAAW;IACxB,WAAW;IACX,IAAI,EAAE,SAAS,CAAA;IACf,wBAAwB;IACxB,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,6BAA6B;IAC7B,UAAU,CAAC,EAAE,MAAM,EAAE,CAAA;IACrB,qBAAqB;IACrB,QAAQ,CAAC,EAAE,CAAC,OAAO,EAAE,eAAe,KAAK,MAAM,CAAA;CAClD"}
@@ -0,0 +1,4 @@
1
+ /**
2
+ * 缓存配置相关类型
3
+ */
4
+ export {};
@@ -0,0 +1,28 @@
1
+ /**
2
+ * 缓存条目
3
+ *
4
+ * 存储在内存缓存和磁盘缓存中的数据结构
5
+ *
6
+ * @template T 缓存数据类型
7
+ *
8
+ * @example
9
+ * ```ts
10
+ * const entry: CacheEntry<User> = {
11
+ * data: { id: '1', name: 'John' },
12
+ * timestamp: Date.now(),
13
+ * ttl: 5 * 60 * 1000, // 5 分钟
14
+ * key: 'GET:/users/1',
15
+ * }
16
+ * ```
17
+ */
18
+ export interface CacheEntry<T = unknown> {
19
+ /** 缓存数据 */
20
+ data: T;
21
+ /** 缓存时间戳 (ms) */
22
+ timestamp: number;
23
+ /** 过期时间 (ms),0 表示永不过期 */
24
+ ttl: number;
25
+ /** 缓存 key */
26
+ key: string;
27
+ }
28
+ //# sourceMappingURL=CacheEntry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CacheEntry.d.ts","sourceRoot":"","sources":["../../../src/http/models/CacheEntry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,WAAW,UAAU,CAAC,CAAC,GAAG,OAAO;IACnC,WAAW;IACX,IAAI,EAAE,CAAC,CAAA;IACP,iBAAiB;IACjB,SAAS,EAAE,MAAM,CAAA;IACjB,yBAAyB;IACzB,GAAG,EAAE,MAAM,CAAA;IACX,aAAa;IACb,GAAG,EAAE,MAAM,CAAA;CACd"}
@@ -0,0 +1 @@
1
+ export {};