@jintianxiayu/cache-decorator 0.1.2

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 (55) hide show
  1. package/README.md +133 -0
  2. package/dist/core/cache-provider-registry.d.ts +30 -0
  3. package/dist/core/cache-provider-registry.d.ts.map +1 -0
  4. package/dist/core/cache-provider-registry.js +52 -0
  5. package/dist/core/cache-provider-registry.js.map +1 -0
  6. package/dist/core/cache-provider.d.ts +34 -0
  7. package/dist/core/cache-provider.d.ts.map +1 -0
  8. package/dist/core/cache-provider.js +3 -0
  9. package/dist/core/cache-provider.js.map +1 -0
  10. package/dist/core/key-builder.d.ts +14 -0
  11. package/dist/core/key-builder.d.ts.map +1 -0
  12. package/dist/core/key-builder.js +38 -0
  13. package/dist/core/key-builder.js.map +1 -0
  14. package/dist/core/native-cache.d.ts +18 -0
  15. package/dist/core/native-cache.d.ts.map +1 -0
  16. package/dist/core/native-cache.js +46 -0
  17. package/dist/core/native-cache.js.map +1 -0
  18. package/dist/core/pending-cache.d.ts +12 -0
  19. package/dist/core/pending-cache.d.ts.map +1 -0
  20. package/dist/core/pending-cache.js +29 -0
  21. package/dist/core/pending-cache.js.map +1 -0
  22. package/dist/core/redis-cache.d.ts +20 -0
  23. package/dist/core/redis-cache.d.ts.map +1 -0
  24. package/dist/core/redis-cache.js +58 -0
  25. package/dist/core/redis-cache.js.map +1 -0
  26. package/dist/decorators/cache-evict.d.ts +23 -0
  27. package/dist/decorators/cache-evict.d.ts.map +1 -0
  28. package/dist/decorators/cache-evict.js +54 -0
  29. package/dist/decorators/cache-evict.js.map +1 -0
  30. package/dist/decorators/cache.d.ts +37 -0
  31. package/dist/decorators/cache.d.ts.map +1 -0
  32. package/dist/decorators/cache.js +74 -0
  33. package/dist/decorators/cache.js.map +1 -0
  34. package/dist/index.d.ts +9 -0
  35. package/dist/index.d.ts.map +1 -0
  36. package/dist/index.js +25 -0
  37. package/dist/index.js.map +1 -0
  38. package/jest.config.js +14 -0
  39. package/package.json +30 -0
  40. package/src/core/cache-provider-registry.ts +53 -0
  41. package/src/core/cache-provider.ts +37 -0
  42. package/src/core/key-builder.ts +31 -0
  43. package/src/core/native-cache.ts +53 -0
  44. package/src/core/pending-cache.ts +26 -0
  45. package/src/core/redis-cache.ts +62 -0
  46. package/src/decorators/cache-evict.ts +73 -0
  47. package/src/decorators/cache.ts +129 -0
  48. package/src/index.ts +9 -0
  49. package/test/cache-evict.test.ts +178 -0
  50. package/test/cache-provider-registry.test.ts +36 -0
  51. package/test/cache.test.ts +256 -0
  52. package/test/key-builder.test.ts +37 -0
  53. package/test/native-cache.test.ts +71 -0
  54. package/test/pending-cache.test.ts +56 -0
  55. package/tsconfig.json +8 -0
package/README.md ADDED
@@ -0,0 +1,133 @@
1
+ # @jintianxiayu/cache-decorator
2
+
3
+ 方法缓存装饰器,为 TypeScript 方法提供声明式缓存功能。
4
+
5
+ ## 安装
6
+
7
+ ```bash
8
+ npm install @jintianxiayu/cache-decorator reflect-metadata
9
+ ```
10
+
11
+ ## 特性
12
+
13
+ - `@Cache` 装饰器:方法缓存,支持 TTL 过期
14
+ - `@CacheEvict` 装饰器:缓存清除
15
+ - 可插拔的缓存后端(Memory / Redis / 自定义)
16
+ - 请求合并:并发场景下返回同一个 Promise
17
+ - 错误结果缓存:防止缓存穿透
18
+
19
+ ## 重要提示
20
+
21
+ > ⚠️ **@Cache 和 @CacheEvict 装饰器仅适用于返回 Promise 的方法**
22
+ >
23
+ > 这两个装饰器都会将被装饰的方法替换为异步函数。如果装饰返回非 Promise 的同步方法,调用方会收到 Promise 对象而非期望的返回值,导致难以排查的 bug。
24
+ >
25
+ > ```typescript
26
+ > // ✅ 正确:async 方法或返回 Promise 的方法
27
+ > @Cache('data')
28
+ > async getData() { ... }
29
+ >
30
+ > @CacheEvict('data')
31
+ > async clearData() { ... }
32
+ >
33
+ > // ❌ 错误:返回非 Promise 的同步方法不要使用这些装饰器
34
+ > @Cache('data')
35
+ > getData() { return 'result'; } // 调用方得到 Promise,不是 'result'
36
+ > ```
37
+
38
+ ## 使用示例
39
+
40
+ ```typescript
41
+ import 'reflect-metadata';
42
+ import { Cache, CacheEvict, CacheProviderRegistry, MemoryCacheProvider } from '@jintianxiayu/cache-decorator';
43
+
44
+ // 注册默认 Provider
45
+ CacheProviderRegistry.register('memory', new MemoryCacheProvider());
46
+ CacheProviderRegistry.setDefault('memory');
47
+
48
+ class UserService {
49
+ @Cache('user-cache', { ttl: 60000 })
50
+ async getUser(id: number) {
51
+ return { id, name: 'test' };
52
+ }
53
+
54
+ @CacheEvict('user-cache')
55
+ async updateUser(id: number) {
56
+ return { id, updated: true };
57
+ }
58
+ }
59
+ ```
60
+
61
+ ### 自定义缓存 Key
62
+
63
+ 通过 `key` 选项自定义缓存 key 生成逻辑:
64
+
65
+ ```typescript
66
+ class UserService {
67
+ // 使用字符串作为 key,所有调用共享同一缓存
68
+ @Cache('config', { key: 'global-config' })
69
+ async getConfig() {
70
+ return { ... };
71
+ }
72
+
73
+ // 使用函数基于参数生成 key
74
+ @Cache('user', { key: (...args) => String(args[0]) })
75
+ async getUser(id: number) {
76
+ return { id };
77
+ }
78
+
79
+ // 清除时使用相同的 key 函数
80
+ @CacheEvict('user', { key: (...args) => String(args[0]) })
81
+ async deleteUser(id: number) {
82
+ return { id, deleted: true };
83
+ }
84
+ }
85
+ ```
86
+
87
+
88
+
89
+ ## API
90
+
91
+ ### @Cache(cacheName, options?)
92
+
93
+ 缓存装饰器。**必须用于 async 方法**。
94
+
95
+ - `cacheName`: 缓存名称
96
+ - `options.ttl`: 过期时间(秒)
97
+ - `options.providerName`: 指定 CacheProvider
98
+ - `options.key`: 自定义缓存 key,支持以下形式:
99
+ - `undefined/null`: 使用默认逻辑,基于方法参数生成 key
100
+ - `string`: 直接作为 key 值,格式为 `cacheName:keyValue`
101
+ - `function`: 接收方法参数数组,返回自定义字符串
102
+
103
+ ### @CacheEvict(cacheName, options?)
104
+
105
+ 缓存清除装饰器。**必须用于返回 Promise 的方法**。
106
+
107
+ - `cacheName`: 缓存名称
108
+ - `options.allEntries`: 清除该缓存名称下的所有条目(而非所有缓存),默认 false
109
+ - `options.providerName`: 指定 CacheProvider
110
+ - `options.key`: 自定义缓存 key,与 `@Cache` 行为一致。当 `allEntries: true` 时忽略
111
+
112
+ ### CacheProviderRegistry
113
+
114
+ 全局缓存提供者注册表。
115
+
116
+ ```typescript
117
+ CacheProviderRegistry.register('memory', new MemoryCacheProvider());
118
+ CacheProviderRegistry.setDefault('memory');
119
+ CacheProviderRegistry.get('memory');
120
+ ```
121
+
122
+ ### MemoryCacheProvider
123
+
124
+ 内存 Map 实现,适用于单机应用。
125
+
126
+ ### RedisCacheProvider
127
+
128
+ Redis 实现,适用于分布式场景。
129
+
130
+ ```typescript
131
+ import Redis from 'ioredis';
132
+ const redisProvider = new RedisCacheProvider(new Redis());
133
+ ```
@@ -0,0 +1,30 @@
1
+ import { CacheProvider } from './cache-provider';
2
+ /**
3
+ * 全局缓存提供者注册表
4
+ * 用于注册和获取不同类型的 CacheProvider
5
+ */
6
+ export declare class CacheProviderRegistry {
7
+ /**
8
+ * 注册缓存提供者
9
+ * @param name 提供者名称
10
+ * @param provider 缓存提供者实例
11
+ */
12
+ static register(name: string, provider: CacheProvider): void;
13
+ /**
14
+ * 获取缓存提供者
15
+ * @param name 提供者名称,不指定则使用默认提供者
16
+ * @returns 缓存提供者实例
17
+ * @throws 未注册且无默认提供者时抛出错误
18
+ */
19
+ static get(name?: string): CacheProvider;
20
+ /**
21
+ * 设置默认缓存提供者
22
+ * @param name 提供者名称
23
+ */
24
+ static setDefault(name: string): void;
25
+ /**
26
+ * 清空注册表
27
+ */
28
+ static clear(): void;
29
+ }
30
+ //# sourceMappingURL=cache-provider-registry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cache-provider-registry.d.ts","sourceRoot":"","sources":["../../src/core/cache-provider-registry.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAKjD;;;GAGG;AACH,qBAAa,qBAAqB;IAChC;;;;OAIG;IACH,MAAM,CAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,aAAa,GAAG,IAAI;IAI5D;;;;;OAKG;IACH,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,aAAa;IAYxC;;;OAGG;IACH,MAAM,CAAC,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAIrC;;OAEG;IACH,MAAM,CAAC,KAAK,IAAI,IAAI;CAIrB"}
@@ -0,0 +1,52 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.CacheProviderRegistry = void 0;
4
+ const providers = new Map();
5
+ let defaultName;
6
+ /**
7
+ * 全局缓存提供者注册表
8
+ * 用于注册和获取不同类型的 CacheProvider
9
+ */
10
+ class CacheProviderRegistry {
11
+ /**
12
+ * 注册缓存提供者
13
+ * @param name 提供者名称
14
+ * @param provider 缓存提供者实例
15
+ */
16
+ static register(name, provider) {
17
+ providers.set(name, provider);
18
+ }
19
+ /**
20
+ * 获取缓存提供者
21
+ * @param name 提供者名称,不指定则使用默认提供者
22
+ * @returns 缓存提供者实例
23
+ * @throws 未注册且无默认提供者时抛出错误
24
+ */
25
+ static get(name) {
26
+ const providerName = name || defaultName;
27
+ if (!providerName) {
28
+ throw new Error('No CacheProvider registered and no default set');
29
+ }
30
+ const provider = providers.get(providerName);
31
+ if (!provider) {
32
+ throw new Error(`CacheProvider "${providerName}" not found`);
33
+ }
34
+ return provider;
35
+ }
36
+ /**
37
+ * 设置默认缓存提供者
38
+ * @param name 提供者名称
39
+ */
40
+ static setDefault(name) {
41
+ defaultName = name;
42
+ }
43
+ /**
44
+ * 清空注册表
45
+ */
46
+ static clear() {
47
+ providers.clear();
48
+ defaultName = undefined;
49
+ }
50
+ }
51
+ exports.CacheProviderRegistry = CacheProviderRegistry;
52
+ //# sourceMappingURL=cache-provider-registry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cache-provider-registry.js","sourceRoot":"","sources":["../../src/core/cache-provider-registry.ts"],"names":[],"mappings":";;;AAEA,MAAM,SAAS,GAAG,IAAI,GAAG,EAAyB,CAAC;AACnD,IAAI,WAA+B,CAAC;AAEpC;;;GAGG;AACH,MAAa,qBAAqB;IAChC;;;;OAIG;IACH,MAAM,CAAC,QAAQ,CAAC,IAAY,EAAE,QAAuB;QACnD,SAAS,CAAC,GAAG,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;IAChC,CAAC;IAED;;;;;OAKG;IACH,MAAM,CAAC,GAAG,CAAC,IAAa;QACtB,MAAM,YAAY,GAAG,IAAI,IAAI,WAAW,CAAC;QACzC,IAAI,CAAC,YAAY,EAAE,CAAC;YAClB,MAAM,IAAI,KAAK,CAAC,gDAAgD,CAAC,CAAC;QACpE,CAAC;QACD,MAAM,QAAQ,GAAG,SAAS,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;QAC7C,IAAI,CAAC,QAAQ,EAAE,CAAC;YACd,MAAM,IAAI,KAAK,CAAC,kBAAkB,YAAY,aAAa,CAAC,CAAC;QAC/D,CAAC;QACD,OAAO,QAAQ,CAAC;IAClB,CAAC;IAED;;;OAGG;IACH,MAAM,CAAC,UAAU,CAAC,IAAY;QAC5B,WAAW,GAAG,IAAI,CAAC;IACrB,CAAC;IAED;;OAEG;IACH,MAAM,CAAC,KAAK;QACV,SAAS,CAAC,KAAK,EAAE,CAAC;QAClB,WAAW,GAAG,SAAS,CAAC;IAC1B,CAAC;CACF;AA3CD,sDA2CC"}
@@ -0,0 +1,34 @@
1
+ /**
2
+ * 缓存提供者接口,定义缓存存储后端的统一契约
3
+ * 支持可插拔的缓存实现(Memory、Redis、自定义)
4
+ */
5
+ export interface CacheProvider {
6
+ /**
7
+ * 获取缓存值
8
+ * @param key 缓存键
9
+ * @returns 缓存值,不存在或已过期返回 undefined
10
+ */
11
+ get<T>(key: string): T | undefined | Promise<T | undefined>;
12
+ /**
13
+ * 设置缓存值
14
+ * @param key 缓存键
15
+ * @param value 缓存值
16
+ * @param ttl 过期时间(秒),不设置则永不过期
17
+ */
18
+ set<T>(key: string, value: T, ttl?: number): void | Promise<void>;
19
+ /**
20
+ * 删除指定缓存
21
+ * @param key 缓存键
22
+ */
23
+ delete(key: string): void | Promise<void>;
24
+ /**
25
+ * 清除所有缓存
26
+ */
27
+ clear(): void | Promise<void>;
28
+ /**
29
+ * 根据模式删除匹配的缓存键
30
+ * @param pattern glob模式字符串(如 user:*),末尾的 * 作为前缀匹配
31
+ */
32
+ deleteByPattern(pattern: string): void | Promise<void>;
33
+ }
34
+ //# sourceMappingURL=cache-provider.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cache-provider.d.ts","sourceRoot":"","sources":["../../src/core/cache-provider.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,WAAW,aAAa;IAC5B;;;;OAIG;IACH,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,GAAG,CAAC,GAAG,SAAS,GAAG,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC,CAAC;IAE5D;;;;;OAKG;IACH,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAElE;;;OAGG;IACH,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAE1C;;OAEG;IACH,KAAK,IAAI,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAE9B;;;OAGG;IACH,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACxD"}
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=cache-provider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cache-provider.js","sourceRoot":"","sources":["../../src/core/cache-provider.ts"],"names":[],"mappings":""}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * 缓存 Key 构建器
3
+ * 负责将缓存名称和参数转换为统一的 key 字符串
4
+ */
5
+ export declare class KeyBuilder {
6
+ /**
7
+ * 构建缓存 key
8
+ * @param cacheName 缓存名称
9
+ * @param args 方法参数列表
10
+ * @returns 格式为 {cacheName}:{serializedArgs} 的 key 字符串
11
+ */
12
+ static build(cacheName: string, args: unknown[]): string;
13
+ }
14
+ //# sourceMappingURL=key-builder.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"key-builder.d.ts","sourceRoot":"","sources":["../../src/core/key-builder.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,qBAAa,UAAU;IACrB;;;;;OAKG;IACH,MAAM,CAAC,KAAK,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,MAAM;CAmBzD"}
@@ -0,0 +1,38 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.KeyBuilder = void 0;
4
+ /**
5
+ * 缓存 Key 构建器
6
+ * 负责将缓存名称和参数转换为统一的 key 字符串
7
+ */
8
+ class KeyBuilder {
9
+ /**
10
+ * 构建缓存 key
11
+ * @param cacheName 缓存名称
12
+ * @param args 方法参数列表
13
+ * @returns 格式为 {cacheName}:{serializedArgs} 的 key 字符串
14
+ */
15
+ static build(cacheName, args) {
16
+ if (args.length === 0) {
17
+ return cacheName;
18
+ }
19
+ const serializedArgs = args
20
+ .map(arg => {
21
+ if (arg === null || arg === undefined)
22
+ return 'null';
23
+ if (typeof arg === 'object') {
24
+ try {
25
+ return JSON.stringify(arg);
26
+ }
27
+ catch {
28
+ return String(arg);
29
+ }
30
+ }
31
+ return String(arg);
32
+ })
33
+ .join('&');
34
+ return `${cacheName}:${serializedArgs}`;
35
+ }
36
+ }
37
+ exports.KeyBuilder = KeyBuilder;
38
+ //# sourceMappingURL=key-builder.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"key-builder.js","sourceRoot":"","sources":["../../src/core/key-builder.ts"],"names":[],"mappings":";;;AAAA;;;GAGG;AACH,MAAa,UAAU;IACrB;;;;;OAKG;IACH,MAAM,CAAC,KAAK,CAAC,SAAiB,EAAE,IAAe;QAC7C,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACtB,OAAO,SAAS,CAAC;QACnB,CAAC;QACD,MAAM,cAAc,GAAG,IAAI;aACxB,GAAG,CAAC,GAAG,CAAC,EAAE;YACT,IAAI,GAAG,KAAK,IAAI,IAAI,GAAG,KAAK,SAAS;gBAAE,OAAO,MAAM,CAAC;YACrD,IAAI,OAAO,GAAG,KAAK,QAAQ,EAAE,CAAC;gBAC5B,IAAI,CAAC;oBACH,OAAO,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;gBAC7B,CAAC;gBAAC,MAAM,CAAC;oBACP,OAAO,MAAM,CAAC,GAAG,CAAC,CAAC;gBACrB,CAAC;YACH,CAAC;YACD,OAAO,MAAM,CAAC,GAAG,CAAC,CAAC;QACrB,CAAC,CAAC;aACD,IAAI,CAAC,GAAG,CAAC,CAAC;QACb,OAAO,GAAG,SAAS,IAAI,cAAc,EAAE,CAAC;IAC1C,CAAC;CACF;AA1BD,gCA0BC"}
@@ -0,0 +1,18 @@
1
+ import { CacheProvider } from './cache-provider';
2
+ /**
3
+ * 内存缓存提供者,基于 Map 实现
4
+ * 适用于单机应用,缓存随进程重启清除
5
+ */
6
+ export declare class MemoryCacheProvider implements CacheProvider {
7
+ private cache;
8
+ get<T>(key: string): T | undefined;
9
+ set<T>(key: string, value: T, ttl?: number): void;
10
+ delete(key: string): void;
11
+ clear(): void;
12
+ /**
13
+ * 根据模式删除匹配的缓存键
14
+ * @param pattern glob模式字符串(如 user:*),末尾的 * 作为前缀匹配
15
+ */
16
+ deleteByPattern(pattern: string): void;
17
+ }
18
+ //# sourceMappingURL=native-cache.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"native-cache.d.ts","sourceRoot":"","sources":["../../src/core/native-cache.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAUjD;;;GAGG;AACH,qBAAa,mBAAoB,YAAW,aAAa;IACvD,OAAO,CAAC,KAAK,CAA0C;IAEvD,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,GAAG,CAAC,GAAG,SAAS;IAUlC,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,IAAI;IAKjD,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI;IAIzB,KAAK,IAAI,IAAI;IAIb;;;OAGG;IACH,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;CAQvC"}
@@ -0,0 +1,46 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.MemoryCacheProvider = void 0;
4
+ /**
5
+ * 内存缓存提供者,基于 Map 实现
6
+ * 适用于单机应用,缓存随进程重启清除
7
+ */
8
+ class MemoryCacheProvider {
9
+ constructor() {
10
+ this.cache = new Map();
11
+ }
12
+ get(key) {
13
+ const entry = this.cache.get(key);
14
+ if (!entry)
15
+ return undefined;
16
+ if (entry.expiresAt !== null && Date.now() > entry.expiresAt) {
17
+ this.cache.delete(key);
18
+ return undefined;
19
+ }
20
+ return entry.value;
21
+ }
22
+ set(key, value, ttl) {
23
+ const expiresAt = ttl ? Date.now() + ttl * 1000 : null;
24
+ this.cache.set(key, { value, expiresAt });
25
+ }
26
+ delete(key) {
27
+ this.cache.delete(key);
28
+ }
29
+ clear() {
30
+ this.cache.clear();
31
+ }
32
+ /**
33
+ * 根据模式删除匹配的缓存键
34
+ * @param pattern glob模式字符串(如 user:*),末尾的 * 作为前缀匹配
35
+ */
36
+ deleteByPattern(pattern) {
37
+ const prefix = pattern.replace(/\*$/, '');
38
+ for (const key of this.cache.keys()) {
39
+ if (key.startsWith(prefix)) {
40
+ this.cache.delete(key);
41
+ }
42
+ }
43
+ }
44
+ }
45
+ exports.MemoryCacheProvider = MemoryCacheProvider;
46
+ //# sourceMappingURL=native-cache.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"native-cache.js","sourceRoot":"","sources":["../../src/core/native-cache.ts"],"names":[],"mappings":";;;AAUA;;;GAGG;AACH,MAAa,mBAAmB;IAAhC;QACU,UAAK,GAAG,IAAI,GAAG,EAA+B,CAAC;IAqCzD,CAAC;IAnCC,GAAG,CAAI,GAAW;QAChB,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAClC,IAAI,CAAC,KAAK;YAAE,OAAO,SAAS,CAAC;QAC7B,IAAI,KAAK,CAAC,SAAS,KAAK,IAAI,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC,SAAS,EAAE,CAAC;YAC7D,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YACvB,OAAO,SAAS,CAAC;QACnB,CAAC;QACD,OAAO,KAAK,CAAC,KAAU,CAAC;IAC1B,CAAC;IAED,GAAG,CAAI,GAAW,EAAE,KAAQ,EAAE,GAAY;QACxC,MAAM,SAAS,GAAG,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,GAAG,GAAG,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC;QACvD,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC,CAAC;IAC5C,CAAC;IAED,MAAM,CAAC,GAAW;QAChB,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IACzB,CAAC;IAED,KAAK;QACH,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;IACrB,CAAC;IAED;;;OAGG;IACH,eAAe,CAAC,OAAe;QAC7B,MAAM,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;QAC1C,KAAK,MAAM,GAAG,IAAI,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,EAAE,CAAC;YACpC,IAAI,GAAG,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC;gBAC3B,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YACzB,CAAC;QACH,CAAC;IACH,CAAC;CACF;AAtCD,kDAsCC"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * 请求合并缓存
3
+ * 用于在并发场景下返回同一个 Promise,避免重复执行
4
+ */
5
+ export declare class PendingCache {
6
+ private pending;
7
+ get<T>(key: string): Promise<T> | undefined;
8
+ set<T>(key: string, promise: Promise<T>): void;
9
+ delete(key: string): void;
10
+ clear(): void;
11
+ }
12
+ //# sourceMappingURL=pending-cache.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pending-cache.d.ts","sourceRoot":"","sources":["../../src/core/pending-cache.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,qBAAa,YAAY;IACvB,OAAO,CAAC,OAAO,CAAuC;IAEtD,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC,GAAG,SAAS;IAI3C,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,GAAG,IAAI;IAO9C,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI;IAIzB,KAAK,IAAI,IAAI;CAGd"}
@@ -0,0 +1,29 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PendingCache = void 0;
4
+ /**
5
+ * 请求合并缓存
6
+ * 用于在并发场景下返回同一个 Promise,避免重复执行
7
+ */
8
+ class PendingCache {
9
+ constructor() {
10
+ this.pending = new Map();
11
+ }
12
+ get(key) {
13
+ return this.pending.get(key);
14
+ }
15
+ set(key, promise) {
16
+ this.pending.set(key, promise);
17
+ promise.finally(() => {
18
+ this.pending.delete(key);
19
+ });
20
+ }
21
+ delete(key) {
22
+ this.pending.delete(key);
23
+ }
24
+ clear() {
25
+ this.pending.clear();
26
+ }
27
+ }
28
+ exports.PendingCache = PendingCache;
29
+ //# sourceMappingURL=pending-cache.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pending-cache.js","sourceRoot":"","sources":["../../src/core/pending-cache.ts"],"names":[],"mappings":";;;AAAA;;;GAGG;AACH,MAAa,YAAY;IAAzB;QACU,YAAO,GAAG,IAAI,GAAG,EAA4B,CAAC;IAoBxD,CAAC;IAlBC,GAAG,CAAI,GAAW;QAChB,OAAO,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAA2B,CAAC;IACzD,CAAC;IAED,GAAG,CAAI,GAAW,EAAE,OAAmB;QACrC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;QAC/B,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE;YACnB,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QAC3B,CAAC,CAAC,CAAC;IACL,CAAC;IAED,MAAM,CAAC,GAAW;QAChB,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IAC3B,CAAC;IAED,KAAK;QACH,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;IACvB,CAAC;CACF;AArBD,oCAqBC"}
@@ -0,0 +1,20 @@
1
+ import { CacheProvider } from './cache-provider';
2
+ import Redis from 'ioredis';
3
+ /**
4
+ * Redis 缓存提供者,基于 ioredis 实现
5
+ * 适用于分布式场景,支持跨进程共享缓存
6
+ */
7
+ export declare class RedisCacheProvider implements CacheProvider {
8
+ private redis;
9
+ constructor(redis?: Redis);
10
+ get<T>(key: string): Promise<T | undefined>;
11
+ set<T>(key: string, value: T, ttl?: number): Promise<void>;
12
+ delete(key: string): Promise<void>;
13
+ clear(): Promise<void>;
14
+ /**
15
+ * 根据模式删除匹配的缓存键
16
+ * @param pattern glob模式字符串(如 user:*),使用 SCAN 游标迭代避免阻塞 Redis
17
+ */
18
+ deleteByPattern(pattern: string): Promise<void>;
19
+ }
20
+ //# sourceMappingURL=redis-cache.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"redis-cache.d.ts","sourceRoot":"","sources":["../../src/core/redis-cache.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACjD,OAAO,KAAK,MAAM,SAAS,CAAC;AAE5B;;;GAGG;AACH,qBAAa,kBAAmB,YAAW,aAAa;IACtD,OAAO,CAAC,KAAK,CAAQ;gBAET,KAAK,CAAC,EAAE,KAAK;IAInB,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC;IAU3C,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAS1D,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIlC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAI5B;;;OAGG;IACG,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAgBtD"}
@@ -0,0 +1,58 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.RedisCacheProvider = void 0;
7
+ const ioredis_1 = __importDefault(require("ioredis"));
8
+ /**
9
+ * Redis 缓存提供者,基于 ioredis 实现
10
+ * 适用于分布式场景,支持跨进程共享缓存
11
+ */
12
+ class RedisCacheProvider {
13
+ constructor(redis) {
14
+ this.redis = redis ?? new ioredis_1.default();
15
+ }
16
+ async get(key) {
17
+ const value = await this.redis.get(key);
18
+ if (value === null)
19
+ return undefined;
20
+ try {
21
+ return JSON.parse(value);
22
+ }
23
+ catch {
24
+ return value;
25
+ }
26
+ }
27
+ async set(key, value, ttl) {
28
+ const serialized = typeof value === 'string' ? value : JSON.stringify(value);
29
+ if (ttl) {
30
+ await this.redis.setex(key, ttl, serialized);
31
+ }
32
+ else {
33
+ await this.redis.set(key, serialized);
34
+ }
35
+ }
36
+ async delete(key) {
37
+ await this.redis.del(key);
38
+ }
39
+ async clear() {
40
+ await this.redis.flushdb();
41
+ }
42
+ /**
43
+ * 根据模式删除匹配的缓存键
44
+ * @param pattern glob模式字符串(如 user:*),使用 SCAN 游标迭代避免阻塞 Redis
45
+ */
46
+ async deleteByPattern(pattern) {
47
+ let cursor = '0';
48
+ do {
49
+ const [nextCursor, keys] = await this.redis.scan(cursor, 'MATCH', pattern, 'COUNT', 100);
50
+ cursor = nextCursor;
51
+ if (keys.length > 0) {
52
+ await this.redis.del(...keys);
53
+ }
54
+ } while (cursor !== '0');
55
+ }
56
+ }
57
+ exports.RedisCacheProvider = RedisCacheProvider;
58
+ //# sourceMappingURL=redis-cache.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"redis-cache.js","sourceRoot":"","sources":["../../src/core/redis-cache.ts"],"names":[],"mappings":";;;;;;AACA,sDAA4B;AAE5B;;;GAGG;AACH,MAAa,kBAAkB;IAG7B,YAAY,KAAa;QACvB,IAAI,CAAC,KAAK,GAAG,KAAK,IAAI,IAAI,iBAAK,EAAE,CAAC;IACpC,CAAC;IAED,KAAK,CAAC,GAAG,CAAI,GAAW;QACtB,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACxC,IAAI,KAAK,KAAK,IAAI;YAAE,OAAO,SAAS,CAAC;QACrC,IAAI,CAAC;YACH,OAAO,IAAI,CAAC,KAAK,CAAC,KAAK,CAAM,CAAC;QAChC,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,KAAqB,CAAC;QAC/B,CAAC;IACH,CAAC;IAED,KAAK,CAAC,GAAG,CAAI,GAAW,EAAE,KAAQ,EAAE,GAAY;QAC9C,MAAM,UAAU,GAAG,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;QAC7E,IAAI,GAAG,EAAE,CAAC;YACR,MAAM,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,EAAE,GAAG,EAAE,UAAU,CAAC,CAAC;QAC/C,CAAC;aAAM,CAAC;YACN,MAAM,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,UAAU,CAAC,CAAC;QACxC,CAAC;IACH,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,GAAW;QACtB,MAAM,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAC5B,CAAC;IAED,KAAK,CAAC,KAAK;QACT,MAAM,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC;IAC7B,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,eAAe,CAAC,OAAe;QACnC,IAAI,MAAM,GAAG,GAAG,CAAC;QACjB,GAAG,CAAC;YACF,MAAM,CAAC,UAAU,EAAE,IAAI,CAAC,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,IAAI,CAC9C,MAAM,EACN,OAAO,EACP,OAAO,EACP,OAAO,EACP,GAAG,CACJ,CAAC;YACF,MAAM,GAAG,UAAU,CAAC;YACpB,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBACpB,MAAM,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,CAAC;YAChC,CAAC;QACH,CAAC,QAAQ,MAAM,KAAK,GAAG,EAAE;IAC3B,CAAC;CACF;AAtDD,gDAsDC"}
@@ -0,0 +1,23 @@
1
+ import { CacheOptions } from './cache';
2
+ /**
3
+ * @CacheEvict 装饰器配置项
4
+ */
5
+ export interface CacheEvictOptions extends Pick<CacheOptions, 'key'> {
6
+ /**
7
+ * 是否清除所有条目,默认为 false
8
+ */
9
+ allEntries?: boolean;
10
+ /**
11
+ * 指定 CacheProvider 名称
12
+ */
13
+ providerName?: string;
14
+ }
15
+ /**
16
+ * 缓存清除装饰器
17
+ * 在方法执行后清除对应的缓存条目
18
+ * @param cacheName 缓存名称
19
+ * @param options 配置项
20
+ */
21
+ export declare function CacheEvict(cacheName: string, options?: CacheEvictOptions): (_target: object, _propertyKey: string, descriptor: PropertyDescriptor) => void;
22
+ export { CacheProviderRegistry } from '../core/cache-provider-registry';
23
+ //# sourceMappingURL=cache-evict.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cache-evict.d.ts","sourceRoot":"","sources":["../../src/decorators/cache-evict.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,YAAY,EAAoB,MAAM,SAAS,CAAC;AAEzD;;GAEG;AACH,MAAM,WAAW,iBAAkB,SAAQ,IAAI,CAAC,YAAY,EAAE,KAAK,CAAC;IAClE;;OAEG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IAErB;;OAEG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAuBD;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,iBAAiB,IAErE,SAAS,MAAM,EACf,cAAc,MAAM,EACpB,YAAY,kBAAkB,UAoBjC;AAED,OAAO,EAAE,qBAAqB,EAAE,MAAM,iCAAiC,CAAC"}
@@ -0,0 +1,54 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.CacheProviderRegistry = void 0;
4
+ exports.CacheEvict = CacheEvict;
5
+ const cache_provider_registry_1 = require("../core/cache-provider-registry");
6
+ const key_builder_1 = require("../core/key-builder");
7
+ /**
8
+ * 解析缓存 key
9
+ * @param cacheName 缓存名称
10
+ * @param keyResolver key 解析器
11
+ * @param args 方法参数数组
12
+ * @returns 解析后的缓存 key
13
+ */
14
+ function resolveCacheKey(cacheName, keyResolver, args) {
15
+ if (keyResolver === undefined || keyResolver === null) {
16
+ return key_builder_1.KeyBuilder.build(cacheName, args);
17
+ }
18
+ if (typeof keyResolver === 'string') {
19
+ return key_builder_1.KeyBuilder.build(cacheName, [keyResolver]);
20
+ }
21
+ try {
22
+ return key_builder_1.KeyBuilder.build(cacheName, [keyResolver(...args)]);
23
+ }
24
+ catch {
25
+ return key_builder_1.KeyBuilder.build(cacheName, args);
26
+ }
27
+ }
28
+ /**
29
+ * 缓存清除装饰器
30
+ * 在方法执行后清除对应的缓存条目
31
+ * @param cacheName 缓存名称
32
+ * @param options 配置项
33
+ */
34
+ function CacheEvict(cacheName, options) {
35
+ return function (_target, _propertyKey, descriptor) {
36
+ const originalMethod = descriptor.value;
37
+ descriptor.value = async function (...args) {
38
+ const result = await originalMethod.apply(this, args);
39
+ const providerName = options?.providerName;
40
+ const provider = cache_provider_registry_1.CacheProviderRegistry.get(providerName);
41
+ if (options?.allEntries) {
42
+ await provider.deleteByPattern(cacheName + '*');
43
+ }
44
+ else {
45
+ const cacheKey = resolveCacheKey(cacheName, options?.key, args);
46
+ provider.delete(cacheKey);
47
+ }
48
+ return result;
49
+ };
50
+ };
51
+ }
52
+ var cache_provider_registry_2 = require("../core/cache-provider-registry");
53
+ Object.defineProperty(exports, "CacheProviderRegistry", { enumerable: true, get: function () { return cache_provider_registry_2.CacheProviderRegistry; } });
54
+ //# sourceMappingURL=cache-evict.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cache-evict.js","sourceRoot":"","sources":["../../src/decorators/cache-evict.ts"],"names":[],"mappings":";;;AA8CA,gCAwBC;AAtED,6EAAwE;AACxE,qDAAiD;AAkBjD;;;;;;GAMG;AACH,SAAS,eAAe,CAAC,SAAiB,EAAE,WAAyC,EAAE,IAAe;IACpG,IAAI,WAAW,KAAK,SAAS,IAAI,WAAW,KAAK,IAAI,EAAE,CAAC;QACtD,OAAO,wBAAU,CAAC,KAAK,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;IAC3C,CAAC;IACD,IAAI,OAAO,WAAW,KAAK,QAAQ,EAAE,CAAC;QACpC,OAAO,wBAAU,CAAC,KAAK,CAAC,SAAS,EAAE,CAAC,WAAW,CAAC,CAAC,CAAC;IACpD,CAAC;IACD,IAAI,CAAC;QACH,OAAO,wBAAU,CAAC,KAAK,CAAC,SAAS,EAAE,CAAC,WAAW,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;IAC7D,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,wBAAU,CAAC,KAAK,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;IAC3C,CAAC;AACH,CAAC;AAED;;;;;GAKG;AACH,SAAgB,UAAU,CAAC,SAAiB,EAAE,OAA2B;IACvE,OAAO,UACL,OAAe,EACf,YAAoB,EACpB,UAA8B;QAE9B,MAAM,cAAc,GAAG,UAAU,CAAC,KAAK,CAAC;QAExC,UAAU,CAAC,KAAK,GAAG,KAAK,WAAW,GAAG,IAAe;YACnD,MAAM,MAAM,GAAG,MAAM,cAAc,CAAC,KAAK,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;YAEtD,MAAM,YAAY,GAAG,OAAO,EAAE,YAAY,CAAC;YAC3C,MAAM,QAAQ,GAAG,+CAAqB,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;YAEzD,IAAI,OAAO,EAAE,UAAU,EAAE,CAAC;gBACxB,MAAM,QAAQ,CAAC,eAAe,CAAC,SAAS,GAAG,GAAG,CAAC,CAAC;YAClD,CAAC;iBAAM,CAAC;gBACN,MAAM,QAAQ,GAAG,eAAe,CAAC,SAAS,EAAE,OAAO,EAAE,GAAG,EAAE,IAAI,CAAC,CAAC;gBAChE,QAAQ,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;YAC5B,CAAC;YAED,OAAO,MAAM,CAAC;QAChB,CAAC,CAAC;IACJ,CAAC,CAAC;AACJ,CAAC;AAED,2EAAwE;AAA/D,gIAAA,qBAAqB,OAAA"}