xxf_react 0.6.7 → 0.6.8

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 (71) hide show
  1. package/README.md +14 -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/package.json +14 -4
package/README.md CHANGED
@@ -18,19 +18,20 @@ This project was created using `bun init` in bun v1.3.2. [Bun](https://bun.com)
18
18
 
19
19
  ## 模块文档
20
20
 
21
- | 模块 | 说明 |
22
- |------|------|
23
- | [event-bus](./src/event-bus/README.md) | 事件总线,支持跨 Tab 通信 |
24
- | [fetch](./src/fetch/README.md) | 带超时控制的 fetch 封装 |
25
- | [flow](./src/flow/README.md) | Promise 扩展,全局错误处理 |
26
- | [foundation](./src/foundation/README.md) | 基础工具,性能监控 |
27
- | [layout](./src/layout/README.md) | 布局组件,悬浮交互 |
28
- | [media](./src/media/README.md) | 媒体组件,视频播放 |
29
- | [models](./src/models/README.md) | 数据模型,API 响应类型 |
30
- | [refresh](./src/refresh/README.md) | 刷新加载,分页加载更多 |
31
- | [responsive](./src/responsive/README.md) | 响应式布局,设备检测 |
32
- | [sse](./src/sse/README.md) | Server-Sent Events 连接管理 |
33
- | [utils](./src/utils/README.md) | 工具函数,滚动定位、组件刷新 |
21
+ | 模块 | 说明 |
22
+ |------------------------------------------|-------------------------|
23
+ | [event-bus](./src/event-bus/README.md) | 事件总线,支持跨 Tab 通信 |
24
+ | [fetch](./src/fetch/README.md) | 带超时控制的 fetch 封装 |
25
+ | [http](./src/http/README.md) | 超强网络框架设计 |
26
+ | [flow](./src/flow/README.md) | Promise 扩展,全局错误处理 |
27
+ | [foundation](./src/foundation/README.md) | 基础工具,性能监控 |
28
+ | [layout](./src/layout/README.md) | 布局组件,悬浮交互 |
29
+ | [media](./src/media/README.md) | 媒体组件,视频播放 |
30
+ | [models](./src/models/README.md) | 数据模型,API 响应类型 |
31
+ | [refresh](./src/refresh/README.md) | 刷新加载,分页加载更多 |
32
+ | [responsive](./src/responsive/README.md) | 响应式布局,设备检测 |
33
+ | [sse](./src/sse/README.md) | Server-Sent Events 连接管理 |
34
+ | [utils](./src/utils/README.md) | 工具函数,滚动定位、组件刷新 |
34
35
 
35
36
  ## 详细文档
36
37
 
@@ -0,0 +1,8 @@
1
+ /**
2
+ * 深拷贝
3
+ *
4
+ * 业务层只调用 copy()
5
+ * 不感知具体实现
6
+ */
7
+ export declare function copy<T>(value: T): T;
8
+ //# sourceMappingURL=Copy.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Copy.d.ts","sourceRoot":"","sources":["../../src/foundation/Copy.ts"],"names":[],"mappings":"AAcA;;;;;GAKG;AACH,wBAAgB,IAAI,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,GAAG,CAAC,CAEnC"}
@@ -0,0 +1,21 @@
1
+ // utils/copy.ts
2
+ import rfdc from 'rfdc';
3
+ /**
4
+ * 内部深拷贝实现
5
+ *
6
+ * proto: false -> 不拷贝原型链(更安全)
7
+ * circles: true -> 支持循环引用
8
+ */
9
+ const deepCloneImpl = rfdc({
10
+ proto: false,
11
+ circles: true,
12
+ });
13
+ /**
14
+ * 深拷贝
15
+ *
16
+ * 业务层只调用 copy()
17
+ * 不感知具体实现
18
+ */
19
+ export function copy(value) {
20
+ return deepCloneImpl(value);
21
+ }
@@ -1,3 +1,5 @@
1
1
  export * from './Debug';
2
2
  export * from './PerformanceMonitor';
3
+ export * from './LayoutSize';
4
+ export * from './Copy';
3
5
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/foundation/index.ts"],"names":[],"mappings":"AAAA,cAAc,SAAS,CAAA;AACvB,cAAc,sBAAsB,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/foundation/index.ts"],"names":[],"mappings":"AAAA,cAAc,SAAS,CAAA;AACvB,cAAc,sBAAsB,CAAA;AACpC,cAAc,cAAc,CAAA;AAC5B,cAAc,QAAQ,CAAA"}
@@ -1,2 +1,4 @@
1
1
  export * from './Debug';
2
2
  export * from './PerformanceMonitor';
3
+ export * from './LayoutSize';
4
+ export * from './Copy';
@@ -0,0 +1,115 @@
1
+ /**
2
+ * API 构建器
3
+ *
4
+ * 使用 Builder 模式定义 API,类似 Android Retrofit
5
+ *
6
+ * @example
7
+ * ```ts
8
+ * const userApi = ApiBuilder.create({
9
+ * baseUrl: 'https://api.example.com',
10
+ * })
11
+ * .get<User>('getUser', {
12
+ * path: '/users/{id}',
13
+ * cache: { mode: CacheMode.IfCache, ttl: 60000 },
14
+ * })
15
+ * .post<User, CreateUserDTO>('createUser', {
16
+ * path: '/users',
17
+ * })
18
+ * .build()
19
+ *
20
+ * // 使用 API
21
+ * const user = await userApi.getUser({ pathParams: { id: '123' } })
22
+ *
23
+ * // 多次响应
24
+ * for await (const { data, fromCache } of userApi.getUserList()) {
25
+ * console.log(data, fromCache)
26
+ * }
27
+ * ```
28
+ */
29
+ import ky, { type Options as KyOptions } from 'ky';
30
+ import { HttpClient } from '../client/HttpClient';
31
+ import { ApiStream } from '../client/ApiStream';
32
+ import type { RequestConfig, ApiOptions, CacheConfig, HttpClientConfig } from '../types';
33
+ /**
34
+ * API 端点函数类型
35
+ */
36
+ export type ApiEndpoint<TResponse> = {
37
+ (options?: ApiOptions): ApiStream<TResponse>;
38
+ /** 请求配置 */
39
+ config: RequestConfig;
40
+ };
41
+ /**
42
+ * API 定义类型
43
+ */
44
+ export type ApiDefinition = Record<string, ApiEndpoint<unknown>>;
45
+ /**
46
+ * 端点选项
47
+ */
48
+ interface EndpointOptions {
49
+ /** 请求路径,支持 {param} 占位符 */
50
+ path: string;
51
+ /** 请求方法 */
52
+ method?: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';
53
+ /** 请求头 */
54
+ headers?: Record<string, string>;
55
+ /** 超时时间 (ms) */
56
+ timeout?: number;
57
+ /** 重试次数 */
58
+ retry?: number;
59
+ /** 缓存配置 */
60
+ cache?: CacheConfig;
61
+ }
62
+ /**
63
+ * API 构建器
64
+ */
65
+ export declare class ApiBuilder<T extends ApiDefinition = Record<string, never>> {
66
+ private client;
67
+ private endpoints;
68
+ private constructor();
69
+ /**
70
+ * 创建 API 构建器
71
+ */
72
+ static create(config: HttpClientConfig): ApiBuilder;
73
+ /**
74
+ * 从现有 ky 实例创建
75
+ */
76
+ static from(kyInstance: ReturnType<typeof ky.create>, config?: Partial<HttpClientConfig>): ApiBuilder;
77
+ /**
78
+ * 定义 GET 请求
79
+ */
80
+ get<TResponse>(name: string, options: Omit<EndpointOptions, 'method'>): ApiBuilder<T & Record<typeof name, ApiEndpoint<TResponse>>>;
81
+ /**
82
+ * 定义 POST 请求
83
+ */
84
+ post<TResponse>(name: string, options: Omit<EndpointOptions, 'method'>): ApiBuilder<T & Record<typeof name, ApiEndpoint<TResponse>>>;
85
+ /**
86
+ * 定义 PUT 请求
87
+ */
88
+ put<TResponse>(name: string, options: Omit<EndpointOptions, 'method'>): ApiBuilder<T & Record<typeof name, ApiEndpoint<TResponse>>>;
89
+ /**
90
+ * 定义 DELETE 请求
91
+ */
92
+ delete<TResponse>(name: string, options: Omit<EndpointOptions, 'method'>): ApiBuilder<T & Record<typeof name, ApiEndpoint<TResponse>>>;
93
+ /**
94
+ * 定义 PATCH 请求
95
+ */
96
+ patch<TResponse>(name: string, options: Omit<EndpointOptions, 'method'>): ApiBuilder<T & Record<typeof name, ApiEndpoint<TResponse>>>;
97
+ /**
98
+ * 通用端点定义
99
+ */
100
+ private endpoint;
101
+ /**
102
+ * 扩展 ky 实例(添加 hooks 等)
103
+ */
104
+ extend(options: KyOptions): this;
105
+ /**
106
+ * 构建 API 服务
107
+ */
108
+ build(): T;
109
+ /**
110
+ * 获取 HTTP 客户端实例(用于高级操作)
111
+ */
112
+ getClient(): HttpClient;
113
+ }
114
+ export {};
115
+ //# sourceMappingURL=ApiBuilder.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ApiBuilder.d.ts","sourceRoot":"","sources":["../../../src/http/api/ApiBuilder.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;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;AAExF;;GAEG;AACH,MAAM,MAAM,WAAW,CAAC,SAAS,IAAI;IACjC,CAAC,OAAO,CAAC,EAAE,UAAU,GAAG,SAAS,CAAC,SAAS,CAAC,CAAA;IAC5C,WAAW;IACX,MAAM,EAAE,aAAa,CAAA;CACxB,CAAA;AAED;;GAEG;AACH,MAAM,MAAM,aAAa,GAAG,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,OAAO,CAAC,CAAC,CAAA;AAEhE;;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;AAED;;GAEG;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;;OAEG;IACH,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,gBAAgB,GAAG,UAAU;IAInD;;OAEG;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;;OAEG;IACH,GAAG,CAAC,SAAS,EACT,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,IAAI,CAAC,eAAe,EAAE,QAAQ,CAAC,GACzC,UAAU,CAAC,CAAC,GAAG,MAAM,CAAC,OAAO,IAAI,EAAE,WAAW,CAAC,SAAS,CAAC,CAAC,CAAC;IAI9D;;OAEG;IACH,IAAI,CAAC,SAAS,EACV,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,IAAI,CAAC,eAAe,EAAE,QAAQ,CAAC,GACzC,UAAU,CAAC,CAAC,GAAG,MAAM,CAAC,OAAO,IAAI,EAAE,WAAW,CAAC,SAAS,CAAC,CAAC,CAAC;IAI9D;;OAEG;IACH,GAAG,CAAC,SAAS,EACT,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,IAAI,CAAC,eAAe,EAAE,QAAQ,CAAC,GACzC,UAAU,CAAC,CAAC,GAAG,MAAM,CAAC,OAAO,IAAI,EAAE,WAAW,CAAC,SAAS,CAAC,CAAC,CAAC;IAI9D;;OAEG;IACH,MAAM,CAAC,SAAS,EACZ,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,IAAI,CAAC,eAAe,EAAE,QAAQ,CAAC,GACzC,UAAU,CAAC,CAAC,GAAG,MAAM,CAAC,OAAO,IAAI,EAAE,WAAW,CAAC,SAAS,CAAC,CAAC,CAAC;IAI9D;;OAEG;IACH,KAAK,CAAC,SAAS,EACX,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,IAAI,CAAC,eAAe,EAAE,QAAQ,CAAC,GACzC,UAAU,CAAC,CAAC,GAAG,MAAM,CAAC,OAAO,IAAI,EAAE,WAAW,CAAC,SAAS,CAAC,CAAC,CAAC;IAI9D;;OAEG;IACH,OAAO,CAAC,QAAQ;IAgBhB;;OAEG;IACH,MAAM,CAAC,OAAO,EAAE,SAAS,GAAG,IAAI;IAKhC;;OAEG;IACH,KAAK,IAAI,CAAC;IAeV;;OAEG;IACH,SAAS,IAAI,UAAU;CAG1B"}
@@ -0,0 +1,128 @@
1
+ /**
2
+ * API 构建器
3
+ *
4
+ * 使用 Builder 模式定义 API,类似 Android Retrofit
5
+ *
6
+ * @example
7
+ * ```ts
8
+ * const userApi = ApiBuilder.create({
9
+ * baseUrl: 'https://api.example.com',
10
+ * })
11
+ * .get<User>('getUser', {
12
+ * path: '/users/{id}',
13
+ * cache: { mode: CacheMode.IfCache, ttl: 60000 },
14
+ * })
15
+ * .post<User, CreateUserDTO>('createUser', {
16
+ * path: '/users',
17
+ * })
18
+ * .build()
19
+ *
20
+ * // 使用 API
21
+ * const user = await userApi.getUser({ pathParams: { id: '123' } })
22
+ *
23
+ * // 多次响应
24
+ * for await (const { data, fromCache } of userApi.getUserList()) {
25
+ * console.log(data, fromCache)
26
+ * }
27
+ * ```
28
+ */
29
+ import { HttpClient } from '../client/HttpClient';
30
+ /**
31
+ * API 构建器
32
+ */
33
+ export class ApiBuilder {
34
+ constructor(config) {
35
+ this.endpoints = new Map();
36
+ this.client = new HttpClient(config);
37
+ }
38
+ /**
39
+ * 创建 API 构建器
40
+ */
41
+ static create(config) {
42
+ return new ApiBuilder(config);
43
+ }
44
+ /**
45
+ * 从现有 ky 实例创建
46
+ */
47
+ static from(kyInstance, config) {
48
+ const builder = new ApiBuilder({
49
+ baseUrl: '',
50
+ ...config,
51
+ });
52
+ builder.client.setKyInstance(kyInstance);
53
+ return builder;
54
+ }
55
+ /**
56
+ * 定义 GET 请求
57
+ */
58
+ get(name, options) {
59
+ return this.endpoint(name, { ...options, method: 'GET' });
60
+ }
61
+ /**
62
+ * 定义 POST 请求
63
+ */
64
+ post(name, options) {
65
+ return this.endpoint(name, { ...options, method: 'POST' });
66
+ }
67
+ /**
68
+ * 定义 PUT 请求
69
+ */
70
+ put(name, options) {
71
+ return this.endpoint(name, { ...options, method: 'PUT' });
72
+ }
73
+ /**
74
+ * 定义 DELETE 请求
75
+ */
76
+ delete(name, options) {
77
+ return this.endpoint(name, { ...options, method: 'DELETE' });
78
+ }
79
+ /**
80
+ * 定义 PATCH 请求
81
+ */
82
+ patch(name, options) {
83
+ return this.endpoint(name, { ...options, method: 'PATCH' });
84
+ }
85
+ /**
86
+ * 通用端点定义
87
+ */
88
+ endpoint(name, options) {
89
+ var _a;
90
+ const config = {
91
+ method: (_a = options.method) !== null && _a !== void 0 ? _a : 'GET',
92
+ path: options.path,
93
+ headers: options.headers,
94
+ timeout: options.timeout,
95
+ retry: options.retry,
96
+ cache: options.cache,
97
+ };
98
+ this.endpoints.set(name, config);
99
+ return this;
100
+ }
101
+ /**
102
+ * 扩展 ky 实例(添加 hooks 等)
103
+ */
104
+ extend(options) {
105
+ this.client.extendKy(options);
106
+ return this;
107
+ }
108
+ /**
109
+ * 构建 API 服务
110
+ */
111
+ build() {
112
+ const api = {};
113
+ for (const [name, config] of this.endpoints) {
114
+ const endpoint = ((options) => {
115
+ return this.client.request(config, options);
116
+ });
117
+ endpoint.config = config;
118
+ api[name] = endpoint;
119
+ }
120
+ return api;
121
+ }
122
+ /**
123
+ * 获取 HTTP 客户端实例(用于高级操作)
124
+ */
125
+ getClient() {
126
+ return this.client;
127
+ }
128
+ }
@@ -0,0 +1,2 @@
1
+ export { ApiBuilder, type ApiEndpoint, type ApiDefinition } from './ApiBuilder';
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/http/api/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,KAAK,WAAW,EAAE,KAAK,aAAa,EAAE,MAAM,cAAc,CAAA"}
@@ -0,0 +1 @@
1
+ export { ApiBuilder } from './ApiBuilder';
@@ -0,0 +1,66 @@
1
+ /**
2
+ * 基于 IndexedDB 的 LRU 磁盘缓存
3
+ *
4
+ * 特点:
5
+ * - 使用 idb-keyval 简化 IndexedDB 操作
6
+ * - 维护 LRU 元数据实现淘汰策略
7
+ * - 支持 TTL 过期
8
+ * - 批量清理优化性能
9
+ * - 防污染:返回数据的深拷贝
10
+ */
11
+ import type { CacheEntry, DiskLruCacheConfig } from '../types';
12
+ export declare class DiskLruCache {
13
+ private store;
14
+ private config;
15
+ private metadata;
16
+ private metadataDirty;
17
+ private flushTimer;
18
+ private evicting;
19
+ constructor(config: DiskLruCacheConfig);
20
+ /**
21
+ * 加载或初始化 LRU 元数据(失败返回空元数据)
22
+ */
23
+ private loadMetadata;
24
+ /**
25
+ * 保存元数据到磁盘 (批量优化,延迟写入)
26
+ */
27
+ private scheduleFlushMetadata;
28
+ /**
29
+ * 立即刷新元数据到磁盘(失败静默忽略)
30
+ */
31
+ flush(): Promise<void>;
32
+ /**
33
+ * 获取缓存(失败返回 null,不影响业务)
34
+ * @returns 缓存条目的深拷贝
35
+ */
36
+ get<T>(key: string): Promise<CacheEntry<T> | null>;
37
+ /**
38
+ * 设置缓存(失败静默忽略,不影响业务)
39
+ */
40
+ set<T>(key: string, data: T, ttl?: number): Promise<void>;
41
+ /**
42
+ * 删除缓存(失败静默忽略)
43
+ */
44
+ delete(key: string): Promise<void>;
45
+ /**
46
+ * 检查缓存是否存在(不更新访问时间,失败返回 false)
47
+ */
48
+ has(key: string): Promise<boolean>;
49
+ /**
50
+ * 淘汰最旧的缓存条目(失败静默忽略)
51
+ */
52
+ private evict;
53
+ /**
54
+ * 清空所有缓存(失败静默忽略)
55
+ */
56
+ clear(): Promise<void>;
57
+ /**
58
+ * 获取缓存数量(失败返回 0)
59
+ */
60
+ size(): Promise<number>;
61
+ /**
62
+ * 获取所有缓存 key(失败返回空数组)
63
+ */
64
+ keys(): Promise<string[]>;
65
+ }
66
+ //# sourceMappingURL=DiskLruCache.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DiskLruCache.d.ts","sourceRoot":"","sources":["../../../src/http/cache/DiskLruCache.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAGH,OAAO,KAAK,EAAE,UAAU,EAAE,kBAAkB,EAAe,MAAM,UAAU,CAAA;AAK3E,qBAAa,YAAY;IACrB,OAAO,CAAC,KAAK,CAAU;IACvB,OAAO,CAAC,MAAM,CAA8B;IAC5C,OAAO,CAAC,QAAQ,CAA2B;IAC3C,OAAO,CAAC,aAAa,CAAQ;IAC7B,OAAO,CAAC,UAAU,CAA6C;IAC/D,OAAO,CAAC,QAAQ,CAAQ;gBAEZ,MAAM,EAAE,kBAAkB;IAQtC;;OAEG;YACW,YAAY;IAiB1B;;OAEG;IACH,OAAO,CAAC,qBAAqB;IAkB7B;;OAEG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAe5B;;;OAGG;IACG,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;IAmCxD;;OAEG;IACG,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,EAAE,GAAG,GAAE,MAAU,GAAG,OAAO,CAAC,IAAI,CAAC;IA6BlE;;OAEG;IACG,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAexC;;OAEG;IACG,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IASxC;;OAEG;YACW,KAAK;IAsCnB;;OAEG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAmB5B;;OAEG;IACG,IAAI,IAAI,OAAO,CAAC,MAAM,CAAC;IAS7B;;OAEG;IACG,IAAI,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;CAQlC"}
@@ -0,0 +1,254 @@
1
+ /**
2
+ * 基于 IndexedDB 的 LRU 磁盘缓存
3
+ *
4
+ * 特点:
5
+ * - 使用 idb-keyval 简化 IndexedDB 操作
6
+ * - 维护 LRU 元数据实现淘汰策略
7
+ * - 支持 TTL 过期
8
+ * - 批量清理优化性能
9
+ * - 防污染:返回数据的深拷贝
10
+ */
11
+ import { get, set, del, clear as clearStore, createStore } from 'idb-keyval';
12
+ const META_KEY = '__lru_metadata__';
13
+ export class DiskLruCache {
14
+ constructor(config) {
15
+ this.metadata = null;
16
+ this.metadataDirty = false;
17
+ this.flushTimer = null;
18
+ this.evicting = false; // 防止并发淘汰
19
+ this.config = {
20
+ cleanupThreshold: 0.1,
21
+ ...config,
22
+ };
23
+ this.store = createStore(config.dbName, config.storeName);
24
+ }
25
+ /**
26
+ * 加载或初始化 LRU 元数据(失败返回空元数据)
27
+ */
28
+ async loadMetadata() {
29
+ if (this.metadata)
30
+ return this.metadata;
31
+ try {
32
+ const stored = await get(META_KEY, this.store);
33
+ if (stored) {
34
+ this.metadata = stored;
35
+ }
36
+ else {
37
+ this.metadata = { entries: {}, count: 0 };
38
+ }
39
+ }
40
+ catch {
41
+ // 加载失败,使用空元数据
42
+ this.metadata = { entries: {}, count: 0 };
43
+ }
44
+ return this.metadata;
45
+ }
46
+ /**
47
+ * 保存元数据到磁盘 (批量优化,延迟写入)
48
+ */
49
+ scheduleFlushMetadata() {
50
+ if (this.flushTimer)
51
+ return;
52
+ this.metadataDirty = true;
53
+ this.flushTimer = setTimeout(() => {
54
+ this.flushTimer = null;
55
+ if (this.metadataDirty && this.metadata) {
56
+ set(META_KEY, this.metadata, this.store)
57
+ .then(() => {
58
+ this.metadataDirty = false;
59
+ })
60
+ .catch(() => {
61
+ // 静默处理写入错误,下次会重试
62
+ });
63
+ }
64
+ }, 1000); // 1 秒后批量写入
65
+ }
66
+ /**
67
+ * 立即刷新元数据到磁盘(失败静默忽略)
68
+ */
69
+ async flush() {
70
+ if (this.flushTimer) {
71
+ clearTimeout(this.flushTimer);
72
+ this.flushTimer = null;
73
+ }
74
+ if (this.metadataDirty && this.metadata) {
75
+ try {
76
+ await set(META_KEY, this.metadata, this.store);
77
+ this.metadataDirty = false;
78
+ }
79
+ catch {
80
+ // 刷新失败静默忽略,下次会重试
81
+ }
82
+ }
83
+ }
84
+ /**
85
+ * 获取缓存(失败返回 null,不影响业务)
86
+ * @returns 缓存条目的深拷贝
87
+ */
88
+ async get(key) {
89
+ try {
90
+ const meta = await this.loadMetadata();
91
+ // 检查是否存在
92
+ if (!meta.entries[key]) {
93
+ return null;
94
+ }
95
+ const entry = await get(key, this.store);
96
+ if (!entry) {
97
+ // 数据丢失,清理元数据
98
+ delete meta.entries[key];
99
+ meta.count = Math.max(0, meta.count - 1);
100
+ this.scheduleFlushMetadata();
101
+ return null;
102
+ }
103
+ // 检查是否过期
104
+ if (entry.ttl > 0 && Date.now() - entry.timestamp > entry.ttl) {
105
+ await this.delete(key);
106
+ return null;
107
+ }
108
+ // 更新访问时间
109
+ meta.entries[key] = { accessTime: Date.now() };
110
+ this.scheduleFlushMetadata();
111
+ return entry;
112
+ }
113
+ catch {
114
+ // IndexedDB 操作失败,返回 null 不影响业务
115
+ return null;
116
+ }
117
+ }
118
+ /**
119
+ * 设置缓存(失败静默忽略,不影响业务)
120
+ */
121
+ async set(key, data, ttl = 0) {
122
+ try {
123
+ const meta = await this.loadMetadata();
124
+ const entry = {
125
+ data,
126
+ timestamp: Date.now(),
127
+ ttl,
128
+ key,
129
+ };
130
+ // 检查是否需要淘汰
131
+ if (!meta.entries[key] && meta.count >= this.config.maxSize) {
132
+ await this.evict();
133
+ }
134
+ await set(key, entry, this.store);
135
+ // 更新元数据
136
+ if (!meta.entries[key]) {
137
+ meta.count++;
138
+ }
139
+ meta.entries[key] = { accessTime: Date.now() };
140
+ this.scheduleFlushMetadata();
141
+ }
142
+ catch {
143
+ // IndexedDB 操作失败,静默忽略不影响业务
144
+ }
145
+ }
146
+ /**
147
+ * 删除缓存(失败静默忽略)
148
+ */
149
+ async delete(key) {
150
+ try {
151
+ const meta = await this.loadMetadata();
152
+ if (meta.entries[key]) {
153
+ await del(key, this.store);
154
+ delete meta.entries[key];
155
+ meta.count = Math.max(0, meta.count - 1);
156
+ this.scheduleFlushMetadata();
157
+ }
158
+ }
159
+ catch {
160
+ // 删除失败静默忽略
161
+ }
162
+ }
163
+ /**
164
+ * 检查缓存是否存在(不更新访问时间,失败返回 false)
165
+ */
166
+ async has(key) {
167
+ try {
168
+ const meta = await this.loadMetadata();
169
+ return !!meta.entries[key];
170
+ }
171
+ catch {
172
+ return false;
173
+ }
174
+ }
175
+ /**
176
+ * 淘汰最旧的缓存条目(失败静默忽略)
177
+ */
178
+ async evict() {
179
+ // 防止并发淘汰
180
+ if (this.evicting)
181
+ return;
182
+ this.evicting = true;
183
+ try {
184
+ const meta = await this.loadMetadata();
185
+ // 计算需要淘汰的数量
186
+ const evictCount = Math.ceil(this.config.maxSize * this.config.cleanupThreshold);
187
+ // 按访问时间排序
188
+ const sorted = Object.entries(meta.entries).sort((a, b) => a[1].accessTime - b[1].accessTime);
189
+ // 淘汰最旧的
190
+ const toEvict = sorted.slice(0, evictCount);
191
+ for (const [key] of toEvict) {
192
+ try {
193
+ await del(key, this.store);
194
+ }
195
+ catch {
196
+ // 单个删除失败继续处理其他
197
+ }
198
+ delete meta.entries[key];
199
+ meta.count = Math.max(0, meta.count - 1);
200
+ }
201
+ this.scheduleFlushMetadata();
202
+ }
203
+ catch {
204
+ // 淘汰失败静默忽略
205
+ }
206
+ finally {
207
+ this.evicting = false;
208
+ }
209
+ }
210
+ /**
211
+ * 清空所有缓存(失败静默忽略)
212
+ */
213
+ async clear() {
214
+ // 取消待处理的元数据写入
215
+ if (this.flushTimer) {
216
+ clearTimeout(this.flushTimer);
217
+ this.flushTimer = null;
218
+ }
219
+ try {
220
+ // 使用 idb-keyval 的 clear 方法一次性清空整个 store
221
+ await clearStore(this.store);
222
+ }
223
+ catch {
224
+ // 清空失败静默忽略
225
+ }
226
+ // 重置内存中的元数据
227
+ this.metadata = { entries: {}, count: 0 };
228
+ this.metadataDirty = false;
229
+ }
230
+ /**
231
+ * 获取缓存数量(失败返回 0)
232
+ */
233
+ async size() {
234
+ try {
235
+ const meta = await this.loadMetadata();
236
+ return meta.count;
237
+ }
238
+ catch {
239
+ return 0;
240
+ }
241
+ }
242
+ /**
243
+ * 获取所有缓存 key(失败返回空数组)
244
+ */
245
+ async keys() {
246
+ try {
247
+ const meta = await this.loadMetadata();
248
+ return Object.keys(meta.entries);
249
+ }
250
+ catch {
251
+ return [];
252
+ }
253
+ }
254
+ }