@jintianxiayu/cache-decorator 0.1.2 → 1.0.0

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 (91) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/README.md +270 -133
  3. package/dist/adapters/ioredis-cache-client.d.ts +9 -0
  4. package/dist/adapters/ioredis-cache-client.d.ts.map +1 -0
  5. package/dist/adapters/ioredis-cache-client.js +73 -0
  6. package/dist/adapters/ioredis-cache-client.js.map +1 -0
  7. package/dist/adapters/node-redis-cache-client.d.ts +10 -0
  8. package/dist/adapters/node-redis-cache-client.d.ts.map +1 -0
  9. package/dist/adapters/node-redis-cache-client.js +81 -0
  10. package/dist/adapters/node-redis-cache-client.js.map +1 -0
  11. package/dist/adapters/redis-key-prefix.d.ts +22 -0
  12. package/dist/adapters/redis-key-prefix.d.ts.map +1 -0
  13. package/dist/adapters/redis-key-prefix.js +41 -0
  14. package/dist/adapters/redis-key-prefix.js.map +1 -0
  15. package/dist/core/cache-logger.d.ts +28 -0
  16. package/dist/core/cache-logger.d.ts.map +1 -0
  17. package/dist/core/cache-logger.js +70 -0
  18. package/dist/core/cache-logger.js.map +1 -0
  19. package/dist/core/cache-provider-registry.d.ts.map +1 -1
  20. package/dist/core/cache-provider-registry.js.map +1 -1
  21. package/dist/core/cache-provider.d.ts.map +1 -1
  22. package/dist/core/key-builder.d.ts.map +1 -1
  23. package/dist/core/key-builder.js +3 -2
  24. package/dist/core/key-builder.js.map +1 -1
  25. package/dist/core/native-cache.d.ts.map +1 -1
  26. package/dist/core/native-cache.js +2 -1
  27. package/dist/core/native-cache.js.map +1 -1
  28. package/dist/core/pending-cache.d.ts.map +1 -1
  29. package/dist/core/pending-cache.js +6 -3
  30. package/dist/core/pending-cache.js.map +1 -1
  31. package/dist/core/redis-cache-client.d.ts +156 -0
  32. package/dist/core/redis-cache-client.d.ts.map +1 -0
  33. package/dist/core/redis-cache-client.js +3 -0
  34. package/dist/core/redis-cache-client.js.map +1 -0
  35. package/dist/core/redis-cache.d.ts +39 -7
  36. package/dist/core/redis-cache.d.ts.map +1 -1
  37. package/dist/core/redis-cache.js +63 -22
  38. package/dist/core/redis-cache.js.map +1 -1
  39. package/dist/decorators/cache-evict.d.ts +1 -1
  40. package/dist/decorators/cache-evict.d.ts.map +1 -1
  41. package/dist/decorators/cache-evict.js +68 -14
  42. package/dist/decorators/cache-evict.js.map +1 -1
  43. package/dist/decorators/cache.d.ts +1 -1
  44. package/dist/decorators/cache.d.ts.map +1 -1
  45. package/dist/decorators/cache.js +85 -23
  46. package/dist/decorators/cache.js.map +1 -1
  47. package/dist/index.d.ts +3 -0
  48. package/dist/index.d.ts.map +1 -1
  49. package/dist/index.js +6 -0
  50. package/dist/index.js.map +1 -1
  51. package/jest.config.js +11 -14
  52. package/package.json +26 -29
  53. package/src/adapters/ioredis-cache-client.ts +89 -0
  54. package/src/adapters/node-redis-cache-client.ts +95 -0
  55. package/src/adapters/redis-key-prefix.ts +38 -0
  56. package/src/core/cache-logger.ts +105 -0
  57. package/src/core/cache-provider-registry.ts +39 -39
  58. package/src/core/cache-provider.ts +28 -28
  59. package/src/core/key-builder.ts +33 -31
  60. package/src/core/native-cache.ts +55 -53
  61. package/src/core/pending-cache.ts +29 -26
  62. package/src/core/redis-cache-client.ts +160 -0
  63. package/src/core/redis-cache.ts +104 -62
  64. package/src/decorators/cache-evict.ts +129 -73
  65. package/src/decorators/cache.ts +203 -129
  66. package/src/index.ts +11 -9
  67. package/test/cache-evict-logging.test.ts +362 -0
  68. package/test/cache-evict.test.ts +165 -165
  69. package/test/cache-logger.integration.test.ts +129 -0
  70. package/test/cache-logger.test.ts +153 -0
  71. package/test/cache-logging.test.ts +544 -0
  72. package/test/cache-provider-registry.test.ts +24 -24
  73. package/test/cache.test.ts +255 -256
  74. package/test/fixtures/cache-logger-child.mjs +108 -0
  75. package/test/helpers/legacy-redis-cache.ts +22 -0
  76. package/test/helpers/package-consumer.ts +231 -0
  77. package/test/helpers/redis-fixture.ts +142 -0
  78. package/test/ioredis-cache-client.test.ts +143 -0
  79. package/test/key-builder.test.ts +28 -28
  80. package/test/native-cache.test.ts +77 -71
  81. package/test/node-redis-cache-client.test.ts +149 -0
  82. package/test/pending-cache.test.ts +69 -56
  83. package/test/redis-cache-client-lifecycle.test.ts +112 -0
  84. package/test/redis-cache-client-types.test.ts +184 -0
  85. package/test/redis-cache-client.integration.test.ts +327 -0
  86. package/test/redis-cache-decorator.test.ts +201 -0
  87. package/test/redis-cache-package.integration.test.ts +328 -0
  88. package/test/redis-cache-provider.test.ts +269 -0
  89. package/test/type-contract/contract.ts +64 -0
  90. package/test/type-contract/tsconfig.json +12 -0
  91. package/tsconfig.json +8 -8
@@ -0,0 +1,41 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.escapeRedisGlobLiteral = escapeRedisGlobLiteral;
4
+ exports.createRedisScanPattern = createRedisScanPattern;
5
+ exports.stripRedisKeyPrefix = stripRedisKeyPrefix;
6
+ const REDIS_GLOB_META_CHARACTERS = new Set(['\\', '*', '?', '[', ']']);
7
+ /**
8
+ * 将普通字符串转换为 Redis glob pattern 中的字面量片段。
9
+ * @param value 仅作为字面前缀使用的字符串。
10
+ * @returns 对反斜杠、星号、问号和方括号增加反斜杠转义后的片段。
11
+ */
12
+ function escapeRedisGlobLiteral(value) {
13
+ let escaped = '';
14
+ for (const character of value) {
15
+ escaped += REDIS_GLOB_META_CHARACTERS.has(character) ? `\\${character}` : character;
16
+ }
17
+ return escaped;
18
+ }
19
+ /**
20
+ * 为逻辑 glob pattern 增加按字面量解释的 Redis key 前缀。
21
+ * @param keyPrefix 调用方配置的物理 key 前缀。
22
+ * @param pattern Provider 传入且需要保留 glob 语义的逻辑 pattern。
23
+ * @returns 可直接交给 Redis SCAN MATCH 的物理 pattern。
24
+ */
25
+ function createRedisScanPattern(keyPrefix, pattern) {
26
+ return `${escapeRedisGlobLiteral(keyPrefix)}${pattern}`;
27
+ }
28
+ /**
29
+ * 验证扫描结果属于指定物理命名空间并恢复逻辑 key。
30
+ * @param keyPrefix 调用方配置的物理 key 前缀。
31
+ * @param physicalKey Redis SCAN 返回的物理 key。
32
+ * @returns 只剥离一次字面前缀后的逻辑 key。
33
+ * @throws 扫描结果不属于指定前缀时抛出 TypeError,避免扩大后续删除范围。
34
+ */
35
+ function stripRedisKeyPrefix(keyPrefix, physicalKey) {
36
+ if (!physicalKey.startsWith(keyPrefix)) {
37
+ throw new TypeError('Redis SCAN returned a key outside the configured key prefix');
38
+ }
39
+ return physicalKey.slice(keyPrefix.length);
40
+ }
41
+ //# sourceMappingURL=redis-key-prefix.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"redis-key-prefix.js","sourceRoot":"","sources":["../../src/adapters/redis-key-prefix.ts"],"names":[],"mappings":";;AAOA,wDAMC;AAQD,wDAEC;AASD,kDAKC;AArCD,MAAM,0BAA0B,GAAG,IAAI,GAAG,CAAC,CAAC,IAAI,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC;AAEvE;;;;GAIG;AACH,SAAgB,sBAAsB,CAAC,KAAa;IAChD,IAAI,OAAO,GAAG,EAAE,CAAC;IACjB,KAAK,MAAM,SAAS,IAAI,KAAK,EAAE,CAAC;QAC5B,OAAO,IAAI,0BAA0B,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,KAAK,SAAS,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;IACxF,CAAC;IACD,OAAO,OAAO,CAAC;AACnB,CAAC;AAED;;;;;GAKG;AACH,SAAgB,sBAAsB,CAAC,SAAiB,EAAE,OAAe;IACrE,OAAO,GAAG,sBAAsB,CAAC,SAAS,CAAC,GAAG,OAAO,EAAE,CAAC;AAC5D,CAAC;AAED;;;;;;GAMG;AACH,SAAgB,mBAAmB,CAAC,SAAiB,EAAE,WAAmB;IACtE,IAAI,CAAC,WAAW,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;QACrC,MAAM,IAAI,SAAS,CAAC,6DAA6D,CAAC,CAAC;IACvF,CAAC;IACD,OAAO,WAAW,CAAC,KAAK,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;AAC/C,CAAC"}
@@ -0,0 +1,28 @@
1
+ export type CacheLogEvent = 'cache.pending_hit' | 'cache.hit' | 'cache.miss' | 'cache.write_dispatched' | 'cache.evict_dispatched' | 'cache.evict_completed' | 'cache.key_fallback' | 'cache.evict_skipped' | 'cache.operation_failed';
2
+ /** 缓存日志只接受与决策相关的稳定字段,避免业务数据和 cache key 被意外带入输出。 */
3
+ export interface CacheLogContext {
4
+ readonly cacheName: string;
5
+ readonly methodName: string;
6
+ readonly providerName: string;
7
+ readonly entryType?: 'value' | 'error';
8
+ readonly scope?: 'key' | 'allEntries';
9
+ readonly reason?: 'resolver_error' | 'business_error';
10
+ readonly operation?: 'provider_resolution' | 'read' | 'write' | 'evict';
11
+ readonly error?: unknown;
12
+ }
13
+ /**
14
+ * 提交一条不会影响缓存或业务结果的结构化日志。
15
+ * @param event 决定固定 message 与 level 的缓存事件。
16
+ * @param context 不包含参数、值或 key 的缓存决策上下文。
17
+ * @returns 无返回值;Logger 获取或同步写入失败时静默结束。
18
+ * @throws 不主动抛出异常。
19
+ */
20
+ export declare function logCacheEvent(event: CacheLogEvent, context: CacheLogContext): void;
21
+ /**
22
+ * 将 decorator 配置中的 Provider 名称转换为稳定日志标签。
23
+ * @param providerName 用户显式配置的 Provider 名称。
24
+ * @returns 非空显式名称;缺省或空字符串返回 `default`。
25
+ * @throws 不主动抛出异常。
26
+ */
27
+ export declare function cacheProviderLabel(providerName: string | undefined): string;
28
+ //# sourceMappingURL=cache-logger.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cache-logger.d.ts","sourceRoot":"","sources":["../../src/core/cache-logger.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,aAAa,GACnB,mBAAmB,GACnB,WAAW,GACX,YAAY,GACZ,wBAAwB,GACxB,wBAAwB,GACxB,uBAAuB,GACvB,oBAAoB,GACpB,qBAAqB,GACrB,wBAAwB,CAAC;AAS/B,mDAAmD;AACnD,MAAM,WAAW,eAAe;IAC5B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,SAAS,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC;IACvC,QAAQ,CAAC,KAAK,CAAC,EAAE,KAAK,GAAG,YAAY,CAAC;IACtC,QAAQ,CAAC,MAAM,CAAC,EAAE,gBAAgB,GAAG,gBAAgB,CAAC;IACtD,QAAQ,CAAC,SAAS,CAAC,EAAE,qBAAqB,GAAG,MAAM,GAAG,OAAO,GAAG,OAAO,CAAC;IACxE,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC;CAC5B;AA8CD;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,aAAa,EAAE,OAAO,EAAE,eAAe,GAAG,IAAI,CAMlF;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,YAAY,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAK3E"}
@@ -0,0 +1,70 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.logCacheEvent = logCacheEvent;
4
+ exports.cacheProviderLabel = cacheProviderLabel;
5
+ const logger_1 = require("@jintianxiayu/logger");
6
+ const CACHE_LOGGER_NAME = '@jintianxiayu/cache-decorator';
7
+ /** 日志定义映射中 K 为稳定事件名,V 为固定的日志级别与无业务数据消息。 */
8
+ const CACHE_LOG_DEFINITIONS = {
9
+ 'cache.pending_hit': { level: 'debug', message: 'Cache pending request reused' },
10
+ 'cache.hit': { level: 'debug', message: 'Cache entry hit' },
11
+ 'cache.miss': { level: 'debug', message: 'Cache entry missed' },
12
+ 'cache.write_dispatched': { level: 'debug', message: 'Cache write dispatched' },
13
+ 'cache.evict_dispatched': { level: 'debug', message: 'Cache eviction dispatched' },
14
+ 'cache.evict_completed': { level: 'debug', message: 'Cache eviction completed' },
15
+ 'cache.key_fallback': { level: 'warn', message: 'Cache key resolver failed; using default key' },
16
+ 'cache.evict_skipped': { level: 'warn', message: 'Cache eviction skipped after business failure' },
17
+ 'cache.operation_failed': { level: 'error', message: 'Cache operation failed' },
18
+ };
19
+ let cacheLogger;
20
+ function getCacheLogger() {
21
+ cacheLogger ??= logger_1.LoggerFactory.getLogger(CACHE_LOGGER_NAME);
22
+ return cacheLogger;
23
+ }
24
+ /**
25
+ * 按稳定事件定义调用对应 Logger level,不提供 info 分支以防高频缓存事件误入 info。
26
+ * @param logger 应用共享的 cache 命名 Logger。
27
+ * @param definition 当前事件固定的 level 与 message。
28
+ * @param metadata 只包含缓存决策白名单字段的结构化元数据。
29
+ * @returns 无返回值。
30
+ * @throws Logger 同步写入失败时透传给外层安全边界处理。
31
+ */
32
+ function writeCacheLog(logger, definition, metadata) {
33
+ if (definition.level === 'debug') {
34
+ logger.debug(definition.message, metadata);
35
+ return;
36
+ }
37
+ if (definition.level === 'warn') {
38
+ logger.warn(definition.message, metadata);
39
+ return;
40
+ }
41
+ logger.error(definition.message, metadata);
42
+ }
43
+ /**
44
+ * 提交一条不会影响缓存或业务结果的结构化日志。
45
+ * @param event 决定固定 message 与 level 的缓存事件。
46
+ * @param context 不包含参数、值或 key 的缓存决策上下文。
47
+ * @returns 无返回值;Logger 获取或同步写入失败时静默结束。
48
+ * @throws 不主动抛出异常。
49
+ */
50
+ function logCacheEvent(event, context) {
51
+ try {
52
+ writeCacheLog(getCacheLogger(), CACHE_LOG_DEFINITIONS[event], { event, ...context });
53
+ }
54
+ catch (_error) {
55
+ return;
56
+ }
57
+ }
58
+ /**
59
+ * 将 decorator 配置中的 Provider 名称转换为稳定日志标签。
60
+ * @param providerName 用户显式配置的 Provider 名称。
61
+ * @returns 非空显式名称;缺省或空字符串返回 `default`。
62
+ * @throws 不主动抛出异常。
63
+ */
64
+ function cacheProviderLabel(providerName) {
65
+ if (providerName === undefined || providerName.length === 0) {
66
+ return 'default';
67
+ }
68
+ return providerName;
69
+ }
70
+ //# sourceMappingURL=cache-logger.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cache-logger.js","sourceRoot":"","sources":["../../src/core/cache-logger.ts"],"names":[],"mappings":";;AAqFA,sCAMC;AAQD,gDAKC;AAxGD,iDAA2E;AAE3E,MAAM,iBAAiB,GAAG,+BAA+B,CAAC;AAgC1D,2CAA2C;AAC3C,MAAM,qBAAqB,GAAwD;IAC/E,mBAAmB,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,8BAA8B,EAAE;IAChF,WAAW,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,iBAAiB,EAAE;IAC3D,YAAY,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,oBAAoB,EAAE;IAC/D,wBAAwB,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,wBAAwB,EAAE;IAC/E,wBAAwB,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,2BAA2B,EAAE;IAClF,uBAAuB,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,0BAA0B,EAAE;IAChF,oBAAoB,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,8CAA8C,EAAE;IAChG,qBAAqB,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,+CAA+C,EAAE;IAClG,wBAAwB,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,wBAAwB,EAAE;CAClF,CAAC;AAEF,IAAI,WAAwC,CAAC;AAE7C,SAAS,cAAc;IACnB,WAAW,KAAK,sBAAa,CAAC,SAAS,CAAC,iBAAiB,CAAC,CAAC;IAC3D,OAAO,WAAW,CAAC;AACvB,CAAC;AAED;;;;;;;GAOG;AACH,SAAS,aAAa,CAClB,MAAuB,EACvB,UAA8B,EAC9B,QAA6D;IAE7D,IAAI,UAAU,CAAC,KAAK,KAAK,OAAO,EAAE,CAAC;QAC/B,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;QAC3C,OAAO;IACX,CAAC;IACD,IAAI,UAAU,CAAC,KAAK,KAAK,MAAM,EAAE,CAAC;QAC9B,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;QAC1C,OAAO;IACX,CAAC;IACD,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;AAC/C,CAAC;AAED;;;;;;GAMG;AACH,SAAgB,aAAa,CAAC,KAAoB,EAAE,OAAwB;IACxE,IAAI,CAAC;QACD,aAAa,CAAC,cAAc,EAAE,EAAE,qBAAqB,CAAC,KAAK,CAAC,EAAE,EAAE,KAAK,EAAE,GAAG,OAAO,EAAE,CAAC,CAAC;IACzF,CAAC;IAAC,OAAO,MAAM,EAAE,CAAC;QACd,OAAO;IACX,CAAC;AACL,CAAC;AAED;;;;;GAKG;AACH,SAAgB,kBAAkB,CAAC,YAAgC;IAC/D,IAAI,YAAY,KAAK,SAAS,IAAI,YAAY,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC1D,OAAO,SAAS,CAAC;IACrB,CAAC;IACD,OAAO,YAAY,CAAC;AACxB,CAAC"}
@@ -1 +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"}
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;IAC9B;;;;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;CAIvB"}
@@ -1 +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"}
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;IAC9B;;;;OAIG;IACH,MAAM,CAAC,QAAQ,CAAC,IAAY,EAAE,QAAuB;QACjD,SAAS,CAAC,GAAG,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;IAClC,CAAC;IAED;;;;;OAKG;IACH,MAAM,CAAC,GAAG,CAAC,IAAa;QACpB,MAAM,YAAY,GAAG,IAAI,IAAI,WAAW,CAAC;QACzC,IAAI,CAAC,YAAY,EAAE,CAAC;YAChB,MAAM,IAAI,KAAK,CAAC,gDAAgD,CAAC,CAAC;QACtE,CAAC;QACD,MAAM,QAAQ,GAAG,SAAS,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;QAC7C,IAAI,CAAC,QAAQ,EAAE,CAAC;YACZ,MAAM,IAAI,KAAK,CAAC,kBAAkB,YAAY,aAAa,CAAC,CAAC;QACjE,CAAC;QACD,OAAO,QAAQ,CAAC;IACpB,CAAC;IAED;;;OAGG;IACH,MAAM,CAAC,UAAU,CAAC,IAAY;QAC1B,WAAW,GAAG,IAAI,CAAC;IACvB,CAAC;IAED;;OAEG;IACH,MAAM,CAAC,KAAK;QACR,SAAS,CAAC,KAAK,EAAE,CAAC;QAClB,WAAW,GAAG,SAAS,CAAC;IAC5B,CAAC;CACJ;AA3CD,sDA2CC"}
@@ -1 +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"}
1
+ {"version":3,"file":"cache-provider.d.ts","sourceRoot":"","sources":["../../src/core/cache-provider.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,WAAW,aAAa;IAC1B;;;;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;CAC1D"}
@@ -1 +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"}
1
+ {"version":3,"file":"key-builder.d.ts","sourceRoot":"","sources":["../../src/core/key-builder.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,qBAAa,UAAU;IACnB;;;;;OAKG;IACH,MAAM,CAAC,KAAK,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,MAAM;CAqB3D"}
@@ -17,9 +17,10 @@ class KeyBuilder {
17
17
  return cacheName;
18
18
  }
19
19
  const serializedArgs = args
20
- .map(arg => {
21
- if (arg === null || arg === undefined)
20
+ .map((arg) => {
21
+ if (arg === null || arg === undefined) {
22
22
  return 'null';
23
+ }
23
24
  if (typeof arg === 'object') {
24
25
  try {
25
26
  return JSON.stringify(arg);
@@ -1 +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"}
1
+ {"version":3,"file":"key-builder.js","sourceRoot":"","sources":["../../src/core/key-builder.ts"],"names":[],"mappings":";;;AAAA;;;GAGG;AACH,MAAa,UAAU;IACnB;;;;;OAKG;IACH,MAAM,CAAC,KAAK,CAAC,SAAiB,EAAE,IAAe;QAC3C,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACpB,OAAO,SAAS,CAAC;QACrB,CAAC;QACD,MAAM,cAAc,GAAG,IAAI;aACtB,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE;YACT,IAAI,GAAG,KAAK,IAAI,IAAI,GAAG,KAAK,SAAS,EAAE,CAAC;gBACpC,OAAO,MAAM,CAAC;YAClB,CAAC;YACD,IAAI,OAAO,GAAG,KAAK,QAAQ,EAAE,CAAC;gBAC1B,IAAI,CAAC;oBACD,OAAO,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;gBAC/B,CAAC;gBAAC,MAAM,CAAC;oBACL,OAAO,MAAM,CAAC,GAAG,CAAC,CAAC;gBACvB,CAAC;YACL,CAAC;YACD,OAAO,MAAM,CAAC,GAAG,CAAC,CAAC;QACvB,CAAC,CAAC;aACD,IAAI,CAAC,GAAG,CAAC,CAAC;QACf,OAAO,GAAG,SAAS,IAAI,cAAc,EAAE,CAAC;IAC5C,CAAC;CACJ;AA5BD,gCA4BC"}
@@ -1 +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"}
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;IACrD,OAAO,CAAC,KAAK,CAA0C;IAEvD,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,GAAG,CAAC,GAAG,SAAS;IAYlC,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;CAQzC"}
@@ -11,8 +11,9 @@ class MemoryCacheProvider {
11
11
  }
12
12
  get(key) {
13
13
  const entry = this.cache.get(key);
14
- if (!entry)
14
+ if (!entry) {
15
15
  return undefined;
16
+ }
16
17
  if (entry.expiresAt !== null && Date.now() > entry.expiresAt) {
17
18
  this.cache.delete(key);
18
19
  return undefined;
@@ -1 +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"}
1
+ {"version":3,"file":"native-cache.js","sourceRoot":"","sources":["../../src/core/native-cache.ts"],"names":[],"mappings":";;;AAUA;;;GAGG;AACH,MAAa,mBAAmB;IAAhC;QACY,UAAK,GAAG,IAAI,GAAG,EAA+B,CAAC;IAuC3D,CAAC;IArCG,GAAG,CAAI,GAAW;QACd,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAClC,IAAI,CAAC,KAAK,EAAE,CAAC;YACT,OAAO,SAAS,CAAC;QACrB,CAAC;QACD,IAAI,KAAK,CAAC,SAAS,KAAK,IAAI,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC,SAAS,EAAE,CAAC;YAC3D,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YACvB,OAAO,SAAS,CAAC;QACrB,CAAC;QACD,OAAO,KAAK,CAAC,KAAU,CAAC;IAC5B,CAAC;IAED,GAAG,CAAI,GAAW,EAAE,KAAQ,EAAE,GAAY;QACtC,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;IAC9C,CAAC;IAED,MAAM,CAAC,GAAW;QACd,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IAC3B,CAAC;IAED,KAAK;QACD,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;IACvB,CAAC;IAED;;;OAGG;IACH,eAAe,CAAC,OAAe;QAC3B,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;YAClC,IAAI,GAAG,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC;gBACzB,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YAC3B,CAAC;QACL,CAAC;IACL,CAAC;CACJ;AAxCD,kDAwCC"}
@@ -1 +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"}
1
+ {"version":3,"file":"pending-cache.d.ts","sourceRoot":"","sources":["../../src/core/pending-cache.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,qBAAa,YAAY;IACrB,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;IAU9C,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI;IAIzB,KAAK,IAAI,IAAI;CAGhB"}
@@ -14,9 +14,12 @@ class PendingCache {
14
14
  }
15
15
  set(key, promise) {
16
16
  this.pending.set(key, promise);
17
- promise.finally(() => {
18
- this.pending.delete(key);
19
- });
17
+ const deleteSettledPromise = () => {
18
+ if (this.pending.get(key) === promise) {
19
+ this.pending.delete(key);
20
+ }
21
+ };
22
+ void promise.then(deleteSettledPromise, deleteSettledPromise);
20
23
  }
21
24
  delete(key) {
22
25
  this.pending.delete(key);
@@ -1 +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"}
1
+ {"version":3,"file":"pending-cache.js","sourceRoot":"","sources":["../../src/core/pending-cache.ts"],"names":[],"mappings":";;;AAAA;;;GAGG;AACH,MAAa,YAAY;IAAzB;QACY,YAAO,GAAG,IAAI,GAAG,EAA4B,CAAC;IAuB1D,CAAC;IArBG,GAAG,CAAI,GAAW;QACd,OAAO,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAA2B,CAAC;IAC3D,CAAC;IAED,GAAG,CAAI,GAAW,EAAE,OAAmB;QACnC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;QAC/B,MAAM,oBAAoB,GAAG,GAAS,EAAE;YACpC,IAAI,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,OAAO,EAAE,CAAC;gBACpC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YAC7B,CAAC;QACL,CAAC,CAAC;QACF,KAAK,OAAO,CAAC,IAAI,CAAC,oBAAoB,EAAE,oBAAoB,CAAC,CAAC;IAClE,CAAC;IAED,MAAM,CAAC,GAAW;QACd,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IAC7B,CAAC;IAED,KAAK;QACD,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;IACzB,CAAC;CACJ;AAxBD,oCAwBC"}
@@ -0,0 +1,156 @@
1
+ /** Redis 缓存使用的最小字符串命令边界,连接及其生命周期由调用方持有。 */
2
+ export interface RedisCacheClient {
3
+ /**
4
+ * 读取逻辑缓存键对应的字符串值。
5
+ * @param key 未经 Provider 改写的逻辑缓存键。
6
+ * @returns 缓存字符串;键不存在时返回 null。
7
+ * @throws 客户端命令失败时传播原始错误。
8
+ */
9
+ get(key: string): Promise<string | null>;
10
+ /**
11
+ * 写入已经由 Provider 序列化的缓存字符串。
12
+ * @param request 逻辑缓存键、字符串值及可选的秒级 TTL。
13
+ * @returns 命令成功后完成。
14
+ * @throws 客户端命令失败时传播原始错误。
15
+ */
16
+ set(request: {
17
+ readonly key: string;
18
+ readonly value: string;
19
+ readonly ttlSeconds?: number;
20
+ }): Promise<void>;
21
+ /**
22
+ * 批量删除逻辑缓存键;空数组不得发送无参数 DEL。
23
+ * @param keys 保持调用顺序的逻辑缓存键集合。
24
+ * @returns 删除命令成功后完成。
25
+ * @throws 客户端命令失败时传播原始错误。
26
+ */
27
+ deleteMany(keys: readonly string[]): Promise<void>;
28
+ /**
29
+ * 按逻辑 glob pattern 扫描一页缓存键。
30
+ * @param request 当前游标、逻辑 pattern 和本页数量提示。
31
+ * @returns 下一游标及已经恢复为逻辑形式的缓存键。
32
+ * @throws 客户端命令失败或响应不受支持时传播错误。
33
+ */
34
+ scan(request: {
35
+ readonly cursor: string;
36
+ readonly pattern: string;
37
+ readonly count: number;
38
+ }): Promise<{
39
+ readonly cursor: string;
40
+ readonly keys: readonly string[];
41
+ }>;
42
+ /**
43
+ * 清空客户端当前选择的整个 Redis 数据库。
44
+ * @returns 清库命令成功后完成。
45
+ * @throws 客户端命令失败时传播原始错误。
46
+ */
47
+ flushDatabase(): Promise<void>;
48
+ }
49
+ /** ioredis 适配器实际使用的命令形状,避免公共声明依赖完整客户端类型。 */
50
+ export interface IoredisCacheClientSource {
51
+ /** 调用方连接的现有选项;适配器只读取 keyPrefix。 */
52
+ readonly options: {
53
+ readonly keyPrefix?: string;
54
+ };
55
+ /**
56
+ * 读取由 ioredis 自行应用 keyPrefix 的逻辑键。
57
+ * @param key 逻辑缓存键。
58
+ * @returns ioredis 原始 GET 响应。
59
+ * @throws 客户端命令错误。
60
+ */
61
+ get(key: string): Promise<unknown>;
62
+ /**
63
+ * 不带过期时间写入由 ioredis 自行应用 keyPrefix 的逻辑键。
64
+ * @param key 逻辑缓存键。
65
+ * @param value 已序列化的缓存字符串。
66
+ * @returns ioredis 原始 SET 响应。
67
+ * @throws 客户端命令错误。
68
+ */
69
+ set(key: string, value: string): Promise<unknown>;
70
+ /**
71
+ * 使用秒级 TTL 写入由 ioredis 自行应用 keyPrefix 的逻辑键。
72
+ * @param key 逻辑缓存键。
73
+ * @param ttlSeconds 正整数秒级 TTL。
74
+ * @param value 已序列化的缓存字符串。
75
+ * @returns ioredis 原始 SETEX 响应。
76
+ * @throws 客户端命令错误。
77
+ */
78
+ setex(key: string, ttlSeconds: number, value: string): Promise<unknown>;
79
+ /**
80
+ * 删除由 ioredis 自行应用 keyPrefix 的逻辑键。
81
+ * @param keys 保持调用顺序的逻辑缓存键。
82
+ * @returns ioredis 原始 DEL 响应。
83
+ * @throws 客户端命令错误。
84
+ */
85
+ del(...keys: string[]): Promise<unknown>;
86
+ /**
87
+ * 使用 ioredis 的五段 SCAN 参数形状扫描物理键。
88
+ * @param args 游标、MATCH、物理 pattern、COUNT 和数量提示。
89
+ * @returns ioredis 原始 tuple SCAN 响应。
90
+ * @throws 客户端命令错误。
91
+ */
92
+ scan(...args: [cursor: string, match: 'MATCH', pattern: string, count: 'COUNT', limit: number]): Promise<unknown>;
93
+ /**
94
+ * 清空连接当前选择的 Redis 数据库。
95
+ * @returns ioredis 原始 FLUSHDB 响应。
96
+ * @throws 客户端命令错误。
97
+ */
98
+ flushdb(): Promise<unknown>;
99
+ }
100
+ /** node-redis 适配器实际使用的命令形状,避免公共声明依赖完整客户端类型。 */
101
+ export interface NodeRedisCacheClientSource {
102
+ /**
103
+ * 读取已经应用适配器前缀的物理键。
104
+ * @param key 物理缓存键。
105
+ * @returns node-redis 原始 GET 响应。
106
+ * @throws 客户端命令错误。
107
+ */
108
+ get(key: string): Promise<unknown>;
109
+ /**
110
+ * 不带过期时间写入已经应用适配器前缀的物理键。
111
+ * @param key 物理缓存键。
112
+ * @param value 已序列化的缓存字符串。
113
+ * @returns node-redis 原始 SET 响应。
114
+ * @throws 客户端命令错误。
115
+ */
116
+ set(key: string, value: string): Promise<unknown>;
117
+ /**
118
+ * 使用秒级 TTL 写入已经应用适配器前缀的物理键。
119
+ * @param key 物理缓存键。
120
+ * @param ttlSeconds 正整数秒级 TTL。
121
+ * @param value 已序列化的缓存字符串。
122
+ * @returns node-redis 原始 SETEX 响应。
123
+ * @throws 客户端命令错误。
124
+ */
125
+ setEx(key: string, ttlSeconds: number, value: string): Promise<unknown>;
126
+ /**
127
+ * 删除一个或多个已经应用适配器前缀的物理键。
128
+ * @param keys 单个物理键或保持调用顺序的物理键数组。
129
+ * @returns node-redis 原始 DEL 响应。
130
+ * @throws 客户端命令错误。
131
+ */
132
+ del(keys: string | string[]): Promise<unknown>;
133
+ /**
134
+ * 使用 node-redis 的对象选项扫描物理键。
135
+ * @param cursor 当前 Redis 游标。
136
+ * @param options 物理 MATCH pattern 和本页数量提示。
137
+ * @returns node-redis 原始对象 SCAN 响应。
138
+ * @throws 客户端命令错误。
139
+ */
140
+ scan(cursor: string, options: {
141
+ MATCH: string;
142
+ COUNT: number;
143
+ }): Promise<unknown>;
144
+ /**
145
+ * 清空连接当前选择的 Redis 数据库。
146
+ * @returns node-redis 原始 FLUSHDB 响应。
147
+ * @throws 客户端命令错误。
148
+ */
149
+ flushDb(): Promise<unknown>;
150
+ }
151
+ /** node-redis 没有透明 keyPrefix,通过适配器显式保持逻辑命名空间。 */
152
+ export interface NodeRedisCacheClientOptions {
153
+ /** 拼接到每个逻辑 key 前的字面字符串,缺省为空字符串。 */
154
+ readonly keyPrefix?: string;
155
+ }
156
+ //# sourceMappingURL=redis-cache-client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"redis-cache-client.d.ts","sourceRoot":"","sources":["../../src/core/redis-cache-client.ts"],"names":[],"mappings":"AAAA,2CAA2C;AAC3C,MAAM,WAAW,gBAAgB;IAC7B;;;;;OAKG;IACH,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAEzC;;;;;OAKG;IACH,GAAG,CAAC,OAAO,EAAE;QAAE,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAE5G;;;;;OAKG;IACH,UAAU,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEnD;;;;;OAKG;IACH,IAAI,CAAC,OAAO,EAAE;QAAE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC;QAClG,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;QACxB,QAAQ,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,CAAC;KACpC,CAAC,CAAC;IAEH;;;;OAIG;IACH,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CAClC;AAED,4CAA4C;AAC5C,MAAM,WAAW,wBAAwB;IACrC,mCAAmC;IACnC,QAAQ,CAAC,OAAO,EAAE;QAAE,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAElD;;;;;OAKG;IACH,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAEnC;;;;;;OAMG;IACH,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAElD;;;;;;;OAOG;IACH,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAExE;;;;;OAKG;IACH,GAAG,CAAC,GAAG,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAEzC;;;;;OAKG;IACH,IAAI,CAAC,GAAG,IAAI,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAElH;;;;OAIG;IACH,OAAO,IAAI,OAAO,CAAC,OAAO,CAAC,CAAC;CAC/B;AAED,+CAA+C;AAC/C,MAAM,WAAW,0BAA0B;IACvC;;;;;OAKG;IACH,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAEnC;;;;;;OAMG;IACH,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAElD;;;;;;;OAOG;IACH,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAExE;;;;;OAKG;IACH,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAE/C;;;;;;OAMG;IACH,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAElF;;;;OAIG;IACH,OAAO,IAAI,OAAO,CAAC,OAAO,CAAC,CAAC;CAC/B;AAED,iDAAiD;AACjD,MAAM,WAAW,2BAA2B;IACxC,mCAAmC;IACnC,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;CAC/B"}
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=redis-cache-client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"redis-cache-client.js","sourceRoot":"","sources":["../../src/core/redis-cache-client.ts"],"names":[],"mappings":""}
@@ -1,20 +1,52 @@
1
1
  import { CacheProvider } from './cache-provider';
2
- import Redis from 'ioredis';
2
+ import { RedisCacheClient } from './redis-cache-client';
3
3
  /**
4
- * Redis 缓存提供者,基于 ioredis 实现
5
- * 适用于分布式场景,支持跨进程共享缓存
4
+ * Redis 缓存提供者,在客户端无关的字符串命令之上维护统一缓存协议。
5
+ * 适用于分布式场景,支持跨进程及跨客户端共享缓存。
6
6
  */
7
7
  export declare class RedisCacheProvider implements CacheProvider {
8
- private redis;
9
- constructor(redis?: Redis);
8
+ private readonly client;
9
+ /**
10
+ * 创建复用调用方 Redis 连接的缓存提供者。
11
+ * @param client 已适配的最小 Redis 缓存客户端。
12
+ */
13
+ constructor(client: RedisCacheClient);
14
+ /**
15
+ * 按既有协议读取并反序列化缓存值。
16
+ * @param key 未经 Provider 改写的逻辑缓存键。
17
+ * @returns 解析后的业务值;Redis miss 时返回 undefined。
18
+ * @throws GET 命令失败时传播客户端原始错误。
19
+ */
10
20
  get<T>(key: string): Promise<T | undefined>;
21
+ /**
22
+ * 按既有字符串或 JSON 协议写入缓存值。
23
+ * @param key 未经 Provider 改写的逻辑缓存键。
24
+ * @param value 需要缓存的业务值。
25
+ * @param ttl 可选的正整数秒级 TTL;undefined 或 0 表示永不过期。
26
+ * @returns 写入命令成功后完成。
27
+ * @throws 值不能序列化时抛 TypeError,TTL 非法时抛 RangeError,命令失败时传播原始错误。
28
+ */
11
29
  set<T>(key: string, value: T, ttl?: number): Promise<void>;
30
+ /**
31
+ * 幂等删除一个逻辑缓存键。
32
+ * @param key 未经 Provider 改写的逻辑缓存键。
33
+ * @returns 删除命令成功后完成。
34
+ * @throws DEL 命令失败时传播客户端原始错误。
35
+ */
12
36
  delete(key: string): Promise<void>;
37
+ /**
38
+ * 清空客户端当前选择的整个 Redis 数据库。
39
+ * @returns FLUSHDB 命令成功后完成。
40
+ * @throws 清库命令失败时传播客户端原始错误。
41
+ */
13
42
  clear(): Promise<void>;
14
43
  /**
15
- * 根据模式删除匹配的缓存键
16
- * @param pattern glob模式字符串(如 user:*),使用 SCAN 游标迭代避免阻塞 Redis
44
+ * 使用游标扫描并按页删除匹配的逻辑缓存键,避免阻塞 Redis。
45
+ * @param pattern 保留 glob 语义的逻辑 pattern,例如 user:*。
46
+ * @returns 游标归零且所有已扫描页面删除成功后完成。
47
+ * @throws SCAN 或任一页 DEL 失败时传播客户端原始错误,并停止后续页面。
17
48
  */
18
49
  deleteByPattern(pattern: string): Promise<void>;
50
+ private normalizeTtl;
19
51
  }
20
52
  //# sourceMappingURL=redis-cache.d.ts.map
@@ -1 +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"}
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,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAExD;;;GAGG;AACH,qBAAa,kBAAmB,YAAW,aAAa;IACpD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAmB;IAE1C;;;OAGG;gBACS,MAAM,EAAE,gBAAgB;IAIpC;;;;;OAKG;IACG,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC;IAajD;;;;;;;OAOG;IACG,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAahE;;;;;OAKG;IACG,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIxC;;;;OAIG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAI5B;;;;;OAKG;IACG,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAWrD,OAAO,CAAC,YAAY;CASvB"}
@@ -1,58 +1,99 @@
1
1
  "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
2
  Object.defineProperty(exports, "__esModule", { value: true });
6
3
  exports.RedisCacheProvider = void 0;
7
- const ioredis_1 = __importDefault(require("ioredis"));
8
4
  /**
9
- * Redis 缓存提供者,基于 ioredis 实现
10
- * 适用于分布式场景,支持跨进程共享缓存
5
+ * Redis 缓存提供者,在客户端无关的字符串命令之上维护统一缓存协议。
6
+ * 适用于分布式场景,支持跨进程及跨客户端共享缓存。
11
7
  */
12
8
  class RedisCacheProvider {
13
- constructor(redis) {
14
- this.redis = redis ?? new ioredis_1.default();
9
+ /**
10
+ * 创建复用调用方 Redis 连接的缓存提供者。
11
+ * @param client 已适配的最小 Redis 缓存客户端。
12
+ */
13
+ constructor(client) {
14
+ this.client = client;
15
15
  }
16
+ /**
17
+ * 按既有协议读取并反序列化缓存值。
18
+ * @param key 未经 Provider 改写的逻辑缓存键。
19
+ * @returns 解析后的业务值;Redis miss 时返回 undefined。
20
+ * @throws GET 命令失败时传播客户端原始错误。
21
+ */
16
22
  async get(key) {
17
- const value = await this.redis.get(key);
18
- if (value === null)
23
+ const value = await this.client.get(key);
24
+ if (value === null) {
19
25
  return undefined;
26
+ }
20
27
  try {
21
28
  return JSON.parse(value);
22
29
  }
23
30
  catch {
31
+ // 兼容既有协议:不是合法 JSON 的字符串按原值返回。
24
32
  return value;
25
33
  }
26
34
  }
35
+ /**
36
+ * 按既有字符串或 JSON 协议写入缓存值。
37
+ * @param key 未经 Provider 改写的逻辑缓存键。
38
+ * @param value 需要缓存的业务值。
39
+ * @param ttl 可选的正整数秒级 TTL;undefined 或 0 表示永不过期。
40
+ * @returns 写入命令成功后完成。
41
+ * @throws 值不能序列化时抛 TypeError,TTL 非法时抛 RangeError,命令失败时传播原始错误。
42
+ */
27
43
  async set(key, value, ttl) {
44
+ const ttlSeconds = this.normalizeTtl(ttl);
28
45
  const serialized = typeof value === 'string' ? value : JSON.stringify(value);
29
- if (ttl) {
30
- await this.redis.setex(key, ttl, serialized);
46
+ if (typeof serialized !== 'string') {
47
+ throw new TypeError('Cache value must serialize to a string');
31
48
  }
32
- else {
33
- await this.redis.set(key, serialized);
49
+ if (ttlSeconds === undefined) {
50
+ await this.client.set({ key, value: serialized });
51
+ return;
34
52
  }
53
+ await this.client.set({ key, value: serialized, ttlSeconds });
35
54
  }
55
+ /**
56
+ * 幂等删除一个逻辑缓存键。
57
+ * @param key 未经 Provider 改写的逻辑缓存键。
58
+ * @returns 删除命令成功后完成。
59
+ * @throws DEL 命令失败时传播客户端原始错误。
60
+ */
36
61
  async delete(key) {
37
- await this.redis.del(key);
62
+ await this.client.deleteMany([key]);
38
63
  }
64
+ /**
65
+ * 清空客户端当前选择的整个 Redis 数据库。
66
+ * @returns FLUSHDB 命令成功后完成。
67
+ * @throws 清库命令失败时传播客户端原始错误。
68
+ */
39
69
  async clear() {
40
- await this.redis.flushdb();
70
+ await this.client.flushDatabase();
41
71
  }
42
72
  /**
43
- * 根据模式删除匹配的缓存键
44
- * @param pattern glob模式字符串(如 user:*),使用 SCAN 游标迭代避免阻塞 Redis
73
+ * 使用游标扫描并按页删除匹配的逻辑缓存键,避免阻塞 Redis。
74
+ * @param pattern 保留 glob 语义的逻辑 pattern,例如 user:*。
75
+ * @returns 游标归零且所有已扫描页面删除成功后完成。
76
+ * @throws SCAN 或任一页 DEL 失败时传播客户端原始错误,并停止后续页面。
45
77
  */
46
78
  async deleteByPattern(pattern) {
47
79
  let cursor = '0';
48
80
  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);
81
+ const page = await this.client.scan({ cursor, pattern, count: 100 });
82
+ cursor = page.cursor;
83
+ if (page.keys.length > 0) {
84
+ await this.client.deleteMany(page.keys);
53
85
  }
54
86
  } while (cursor !== '0');
55
87
  }
88
+ normalizeTtl(ttl) {
89
+ if (ttl === undefined || ttl === 0) {
90
+ return undefined;
91
+ }
92
+ if (!Number.isFinite(ttl) || !Number.isInteger(ttl) || ttl < 1) {
93
+ throw new RangeError('Redis cache TTL must be a positive finite integer or zero');
94
+ }
95
+ return ttl;
96
+ }
56
97
  }
57
98
  exports.RedisCacheProvider = RedisCacheProvider;
58
99
  //# sourceMappingURL=redis-cache.js.map