@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.
- package/CHANGELOG.md +18 -0
- package/README.md +270 -133
- package/dist/adapters/ioredis-cache-client.d.ts +9 -0
- package/dist/adapters/ioredis-cache-client.d.ts.map +1 -0
- package/dist/adapters/ioredis-cache-client.js +73 -0
- package/dist/adapters/ioredis-cache-client.js.map +1 -0
- package/dist/adapters/node-redis-cache-client.d.ts +10 -0
- package/dist/adapters/node-redis-cache-client.d.ts.map +1 -0
- package/dist/adapters/node-redis-cache-client.js +81 -0
- package/dist/adapters/node-redis-cache-client.js.map +1 -0
- package/dist/adapters/redis-key-prefix.d.ts +22 -0
- package/dist/adapters/redis-key-prefix.d.ts.map +1 -0
- package/dist/adapters/redis-key-prefix.js +41 -0
- package/dist/adapters/redis-key-prefix.js.map +1 -0
- package/dist/core/cache-logger.d.ts +28 -0
- package/dist/core/cache-logger.d.ts.map +1 -0
- package/dist/core/cache-logger.js +70 -0
- package/dist/core/cache-logger.js.map +1 -0
- package/dist/core/cache-provider-registry.d.ts.map +1 -1
- package/dist/core/cache-provider-registry.js.map +1 -1
- package/dist/core/cache-provider.d.ts.map +1 -1
- package/dist/core/key-builder.d.ts.map +1 -1
- package/dist/core/key-builder.js +3 -2
- package/dist/core/key-builder.js.map +1 -1
- package/dist/core/native-cache.d.ts.map +1 -1
- package/dist/core/native-cache.js +2 -1
- package/dist/core/native-cache.js.map +1 -1
- package/dist/core/pending-cache.d.ts.map +1 -1
- package/dist/core/pending-cache.js +6 -3
- package/dist/core/pending-cache.js.map +1 -1
- package/dist/core/redis-cache-client.d.ts +156 -0
- package/dist/core/redis-cache-client.d.ts.map +1 -0
- package/dist/core/redis-cache-client.js +3 -0
- package/dist/core/redis-cache-client.js.map +1 -0
- package/dist/core/redis-cache.d.ts +39 -7
- package/dist/core/redis-cache.d.ts.map +1 -1
- package/dist/core/redis-cache.js +63 -22
- package/dist/core/redis-cache.js.map +1 -1
- package/dist/decorators/cache-evict.d.ts +1 -1
- package/dist/decorators/cache-evict.d.ts.map +1 -1
- package/dist/decorators/cache-evict.js +68 -14
- package/dist/decorators/cache-evict.js.map +1 -1
- package/dist/decorators/cache.d.ts +1 -1
- package/dist/decorators/cache.d.ts.map +1 -1
- package/dist/decorators/cache.js +85 -23
- package/dist/decorators/cache.js.map +1 -1
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -1
- package/jest.config.js +11 -14
- package/package.json +26 -29
- package/src/adapters/ioredis-cache-client.ts +89 -0
- package/src/adapters/node-redis-cache-client.ts +95 -0
- package/src/adapters/redis-key-prefix.ts +38 -0
- package/src/core/cache-logger.ts +105 -0
- package/src/core/cache-provider-registry.ts +39 -39
- package/src/core/cache-provider.ts +28 -28
- package/src/core/key-builder.ts +33 -31
- package/src/core/native-cache.ts +55 -53
- package/src/core/pending-cache.ts +29 -26
- package/src/core/redis-cache-client.ts +160 -0
- package/src/core/redis-cache.ts +104 -62
- package/src/decorators/cache-evict.ts +129 -73
- package/src/decorators/cache.ts +203 -129
- package/src/index.ts +11 -9
- package/test/cache-evict-logging.test.ts +362 -0
- package/test/cache-evict.test.ts +165 -165
- package/test/cache-logger.integration.test.ts +129 -0
- package/test/cache-logger.test.ts +153 -0
- package/test/cache-logging.test.ts +544 -0
- package/test/cache-provider-registry.test.ts +24 -24
- package/test/cache.test.ts +255 -256
- package/test/fixtures/cache-logger-child.mjs +108 -0
- package/test/helpers/legacy-redis-cache.ts +22 -0
- package/test/helpers/package-consumer.ts +231 -0
- package/test/helpers/redis-fixture.ts +142 -0
- package/test/ioredis-cache-client.test.ts +143 -0
- package/test/key-builder.test.ts +28 -28
- package/test/native-cache.test.ts +77 -71
- package/test/node-redis-cache-client.test.ts +149 -0
- package/test/pending-cache.test.ts +69 -56
- package/test/redis-cache-client-lifecycle.test.ts +112 -0
- package/test/redis-cache-client-types.test.ts +184 -0
- package/test/redis-cache-client.integration.test.ts +327 -0
- package/test/redis-cache-decorator.test.ts +201 -0
- package/test/redis-cache-package.integration.test.ts +328 -0
- package/test/redis-cache-provider.test.ts +269 -0
- package/test/type-contract/contract.ts +64 -0
- package/test/type-contract/tsconfig.json +12 -0
- package/tsconfig.json +8 -8
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# @jintianxiayu/cache-decorator
|
|
2
|
+
|
|
3
|
+
## 1.0.0
|
|
4
|
+
|
|
5
|
+
### Major Changes
|
|
6
|
+
|
|
7
|
+
- 本次发布包含以下变更:
|
|
8
|
+
|
|
9
|
+
- `@jintianxiayu/logger`:重构日志配置、命名 Logger、Plain/JSON 输出、轮转文件、元数据规范化与脱敏、异步 `traceId` 上下文、进程错误处理及可等待的关闭流程。BREAKING:包根不再导出 `createLogger`、`ConfigLoader` 和 `LogPosition`;`LoggerContext.set/clear/getStore` 改为通过 `withContext/get` 使用,配置结构及脱敏字段格式同步调整,运行时要求 Node.js 20.19+/22.13+/24+。
|
|
10
|
+
- `@jintianxiayu/cache-decorator`:修复并发缓存 Promise 复用及拒绝后的清理;解耦 `RedisCacheProvider` 与具体 Redis SDK,新增 `RedisCacheClient`、ioredis 和 node-redis 适配工厂,并保持 Redis key、值格式及秒级 TTL 兼容;新增缓存命中、回填、淘汰和基础设施失败的结构化日志。BREAKING:调用方必须显式包装并管理 Redis 连接、直接声明所用 Redis 客户端依赖,同时提供兼容的 `@jintianxiayu/logger` peer 并在首次缓存调用前初始化 Logger。
|
|
11
|
+
- `@jintianxiayu/lock-decorator`:解耦 `RedisLockProvider` 与 ioredis,新增 `RedisLockClient`、ioredis 和 node-redis 适配工厂,锁协议及连接所有权语义保持不变;新增锁获取、重试、续期、业务执行和释放的结构化日志。Watchdog 续期 Promise 拒绝会被记录并停止后续续期,不再形成未处理拒绝,已开始的业务仍继续执行并在结束时尝试释放锁。BREAKING:调用方必须显式包装现有 Redis 连接、直接声明客户端依赖,同时提供兼容的 `@jintianxiayu/logger` peer 并在首次锁调用前初始化 Logger。
|
|
12
|
+
- `@jintianxiayu/http-client-decorator`:同步适配 logger 0.2 的 peer 依赖与异步上下文用法;调用方应通过 `LoggerContext.withContext` 建立请求上下文,tracing 中间件继续从中读取并注入 `traceId`。
|
|
13
|
+
- 仓库开发与发布流程迁移到 pnpm 11 原生 workspace,并统一 TypeScript NodeNext、Jest、清理、构建和本地发布配置。
|
|
14
|
+
|
|
15
|
+
### Patch Changes
|
|
16
|
+
|
|
17
|
+
- Updated dependencies:
|
|
18
|
+
- @jintianxiayu/logger@1.0.0
|
package/README.md
CHANGED
|
@@ -1,133 +1,270 @@
|
|
|
1
|
-
# @jintianxiayu/cache-decorator
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
##
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
1
|
+
# @jintianxiayu/cache-decorator
|
|
2
|
+
|
|
3
|
+
为 TypeScript 异步方法提供声明式缓存、缓存清除、请求合并,以及 Memory、Redis 和自定义缓存后端。
|
|
4
|
+
|
|
5
|
+
## 目录
|
|
6
|
+
|
|
7
|
+
- [安装](#安装)
|
|
8
|
+
- [快速开始](#快速开始)
|
|
9
|
+
- [缓存日志](#缓存日志)
|
|
10
|
+
- [Redis](#redis)
|
|
11
|
+
- [API](#api)
|
|
12
|
+
|
|
13
|
+
## 安装
|
|
14
|
+
|
|
15
|
+
Logger 是必需的 peer dependency;基础包不绑定任何 Redis SDK:
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
pnpm add @jintianxiayu/cache-decorator @jintianxiayu/logger reflect-metadata
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
需要 Redis 时,只安装应用实际使用的客户端:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
pnpm add redis
|
|
25
|
+
# 或
|
|
26
|
+
pnpm add ioredis
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## 快速开始
|
|
30
|
+
|
|
31
|
+
```typescript
|
|
32
|
+
import 'reflect-metadata';
|
|
33
|
+
import { Cache, CacheEvict, CacheProviderRegistry, MemoryCacheProvider } from '@jintianxiayu/cache-decorator';
|
|
34
|
+
import { LoggerFactory } from '@jintianxiayu/logger';
|
|
35
|
+
|
|
36
|
+
LoggerFactory.init({
|
|
37
|
+
root: {
|
|
38
|
+
level: 'info',
|
|
39
|
+
console: { enabled: true },
|
|
40
|
+
file: { enabled: false },
|
|
41
|
+
},
|
|
42
|
+
loggers: {
|
|
43
|
+
'@jintianxiayu/cache-decorator': {
|
|
44
|
+
level: 'debug',
|
|
45
|
+
console: { enabled: true, format: 'json' },
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
CacheProviderRegistry.register('memory', new MemoryCacheProvider());
|
|
51
|
+
CacheProviderRegistry.setDefault('memory');
|
|
52
|
+
|
|
53
|
+
class UserService {
|
|
54
|
+
@Cache('user-cache', { ttl: 60 })
|
|
55
|
+
async getUser(id: number): Promise<{ id: number; name: string }> {
|
|
56
|
+
return { id, name: 'test' };
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
@CacheEvict('user-cache')
|
|
60
|
+
async updateUser(id: number): Promise<{ id: number; updated: boolean }> {
|
|
61
|
+
return { id, updated: true };
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
`ttl` 的单位是秒;`undefined` 或 `0` 表示不设置过期时间。
|
|
67
|
+
|
|
68
|
+
> `@Cache` 和 `@CacheEvict` 仅适用于返回 Promise 的方法。装饰同步方法会让调用方收到 Promise,而不是原返回值。
|
|
69
|
+
|
|
70
|
+
### 自定义缓存 Key
|
|
71
|
+
|
|
72
|
+
`key` 可以是固定字符串或根据方法参数计算的函数:
|
|
73
|
+
|
|
74
|
+
```typescript
|
|
75
|
+
class UserService {
|
|
76
|
+
@Cache('config', { key: 'global-config' })
|
|
77
|
+
async getConfig(): Promise<{ enabled: boolean }> {
|
|
78
|
+
return { enabled: true };
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
@Cache('user', { key: (...args: unknown[]) => String(args[0]) })
|
|
82
|
+
async getUser(id: number): Promise<{ id: number }> {
|
|
83
|
+
return { id };
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
@CacheEvict('user', { key: (...args: unknown[]) => String(args[0]) })
|
|
87
|
+
async deleteUser(id: number): Promise<{ id: number; deleted: boolean }> {
|
|
88
|
+
return { id, deleted: true };
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## 缓存日志
|
|
94
|
+
|
|
95
|
+
缓存装饰器使用名称固定为 `@jintianxiayu/cache-decorator` 的 Logger。应用必须安装兼容版本的
|
|
96
|
+
`@jintianxiayu/logger`,并在第一次调用被装饰方法前完成 `LoggerFactory.init()`;应用退出时仍由应用统一调用
|
|
97
|
+
`LoggerFactory.shutdown()`。仅导入包、声明 decorator 或定义 class 不会提前获取 Logger,cache 包也不会自行初始化或关闭
|
|
98
|
+
Logger。
|
|
99
|
+
|
|
100
|
+
日志的 level、console/file transport、格式和脱敏只由同名 Logger profile 控制,不需要也不支持
|
|
101
|
+
`CacheOptions.logging`、`debug` 或 Logger 回调。高频正常决策默认使用 `debug`,可恢复回退使用 `warn`,缓存基础设施失败使用
|
|
102
|
+
`error`;逐调用事件不使用 `info`。
|
|
103
|
+
|
|
104
|
+
| level | event | 含义 |
|
|
105
|
+
| ------- | ------------------------ | ------------------------------------------------------ |
|
|
106
|
+
| `debug` | `cache.pending_hit` | 复用相同 key 的执行中 Promise |
|
|
107
|
+
| `debug` | `cache.hit` | 命中 value 或 error 缓存条目 |
|
|
108
|
+
| `debug` | `cache.miss` | Provider 正常返回未命中 |
|
|
109
|
+
| `debug` | `cache.write_dispatched` | `set()` 已同步返回控制权,不表示异步写入成功 |
|
|
110
|
+
| `debug` | `cache.evict_dispatched` | 单 key `delete()` 已同步返回控制权,不表示异步删除成功 |
|
|
111
|
+
| `debug` | `cache.evict_completed` | 已等待的 `deleteByPattern()` 正常完成 |
|
|
112
|
+
| `warn` | `cache.key_fallback` | 自定义 key resolver 失败,已回退默认 key |
|
|
113
|
+
| `warn` | `cache.evict_skipped` | 业务方法失败,淘汰被跳过 |
|
|
114
|
+
| `error` | `cache.operation_failed` | Provider 解析或可观察的读取、写入、淘汰操作失败 |
|
|
115
|
+
|
|
116
|
+
`write_dispatched` 和 `evict_dispatched` 只描述调用已发起。为保持既有时序,decorator 不等待 `set()` 或单 key
|
|
117
|
+
`delete()` 返回的 Promise,也不为日志附加 rejection handler;异步失败不会被描述为成功或完成。只有本来就会等待的全量淘汰可记录
|
|
118
|
+
`evict_completed`。
|
|
119
|
+
|
|
120
|
+
每条日志只包含 `event`、`cacheName`、`methodName`、`providerName`,并按事件增加 `entryType`、`scope`、
|
|
121
|
+
`reason`、`operation` 或基础设施 `error`。日志不会包含方法参数、业务返回值、缓存值、业务异常内容或完整逻辑/物理 cache
|
|
122
|
+
key,也不会为日志额外序列化这些值。Provider 错误和现有 `LoggerContext` 的 `traceId` 继续由 Logger 统一规范化、脱敏和关联;
|
|
123
|
+
cache 包不直接读写 LoggerContext。Logger 自身同步失败会被隔离,不会替换缓存结果、业务结果或原始 Provider 错误。
|
|
124
|
+
|
|
125
|
+
## Redis
|
|
126
|
+
|
|
127
|
+
`RedisCacheProvider` 从 0.2.0 起必须接收已经适配的 `RedisCacheClient`。库不会创建、连接、重连或关闭 Redis
|
|
128
|
+
连接,也不会修改连接选项与错误监听器;这些生命周期操作始终由应用负责。
|
|
129
|
+
|
|
130
|
+
### node-redis
|
|
131
|
+
|
|
132
|
+
```typescript
|
|
133
|
+
import { createClient } from 'redis';
|
|
134
|
+
import { CacheProviderRegistry, RedisCacheProvider, createNodeRedisCacheClient } from '@jintianxiayu/cache-decorator';
|
|
135
|
+
|
|
136
|
+
const redis = createClient({ url: process.env.REDIS_URL });
|
|
137
|
+
|
|
138
|
+
export async function startCache(): Promise<void> {
|
|
139
|
+
redis.on('error', (error: Error) => console.error('Redis error', error));
|
|
140
|
+
await redis.connect();
|
|
141
|
+
const client = createNodeRedisCacheClient(redis, { keyPrefix: 'my-app:' });
|
|
142
|
+
CacheProviderRegistry.register('redis', new RedisCacheProvider(client));
|
|
143
|
+
CacheProviderRegistry.setDefault('redis');
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
export async function stopCache(): Promise<void> {
|
|
147
|
+
if (redis.isOpen) {
|
|
148
|
+
await redis.quit();
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
### ioredis
|
|
154
|
+
|
|
155
|
+
```typescript
|
|
156
|
+
import Redis from 'ioredis';
|
|
157
|
+
import { CacheProviderRegistry, RedisCacheProvider, createIoredisCacheClient } from '@jintianxiayu/cache-decorator';
|
|
158
|
+
|
|
159
|
+
const redis = new Redis(process.env.REDIS_URL ?? 'redis://127.0.0.1:6379', {
|
|
160
|
+
lazyConnect: true,
|
|
161
|
+
keyPrefix: 'my-app:',
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
export async function startCache(): Promise<void> {
|
|
165
|
+
redis.on('error', (error: Error) => console.error('Redis error', error));
|
|
166
|
+
await redis.connect();
|
|
167
|
+
CacheProviderRegistry.register('redis', new RedisCacheProvider(createIoredisCacheClient(redis)));
|
|
168
|
+
CacheProviderRegistry.setDefault('redis');
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
export function stopCache(): void {
|
|
172
|
+
redis.disconnect();
|
|
173
|
+
}
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
### 自定义 RedisCacheClient
|
|
177
|
+
|
|
178
|
+
其他 Redis SDK 或封装只需实现最小字符串命令接口,不必继承内置适配器:
|
|
179
|
+
|
|
180
|
+
```typescript
|
|
181
|
+
import { CacheProviderRegistry, RedisCacheProvider, type RedisCacheClient } from '@jintianxiayu/cache-decorator';
|
|
182
|
+
|
|
183
|
+
interface StorageCommands {
|
|
184
|
+
read(key: string): Promise<string | null>;
|
|
185
|
+
write(key: string, value: string, ttlSeconds?: number): Promise<void>;
|
|
186
|
+
remove(keys: readonly string[]): Promise<void>;
|
|
187
|
+
scan(cursor: string, pattern: string, count: number): Promise<{ cursor: string; keys: readonly string[] }>;
|
|
188
|
+
clearDatabase(): Promise<void>;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
declare const storage: StorageCommands;
|
|
192
|
+
|
|
193
|
+
const client: RedisCacheClient = {
|
|
194
|
+
get: (key) => storage.read(key),
|
|
195
|
+
set: ({ key, value, ttlSeconds }) => storage.write(key, value, ttlSeconds),
|
|
196
|
+
deleteMany: (keys) => storage.remove(keys),
|
|
197
|
+
scan: ({ cursor, pattern, count }) => storage.scan(cursor, pattern, count),
|
|
198
|
+
flushDatabase: () => storage.clearDatabase(),
|
|
199
|
+
};
|
|
200
|
+
|
|
201
|
+
CacheProviderRegistry.register('redis', new RedisCacheProvider(client));
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
### 前缀与数据兼容
|
|
205
|
+
|
|
206
|
+
- ioredis 继续使用连接的 `keyPrefix`;适配器会补齐 `SCAN` 不自动处理前缀的差异。
|
|
207
|
+
- node-redis 没有透明 `keyPrefix`,应在 `createNodeRedisCacheClient(client, { keyPrefix })` 中传入等价的字面前缀。
|
|
208
|
+
- 两种客户端连接同一 database、使用相同最终物理 key 时,可以互读既有缓存。字符串、JSON、miss 映射和秒级 TTL
|
|
209
|
+
格式均与 0.1.3 保持一致,无需重写或清空数据。
|
|
210
|
+
- 原始字符串读取时仍会优先尝试 `JSON.parse`;例如缓存文本 `"true"` 会读为布尔值 `true`,这是既有兼容行为。
|
|
211
|
+
|
|
212
|
+
`deleteByPattern()` 使用 `SCAN COUNT 100` 分页删除。它不是原子快照;并发写入时不承诺一次删除全部匹配项,命令中途失败后可幂等重试。
|
|
213
|
+
|
|
214
|
+
> **危险:`RedisCacheProvider.clear()` 会执行 `FLUSHDB`,清空连接当前选择的整个 Redis database。**
|
|
215
|
+
> `keyPrefix` 不会缩小其范围;共享 database 时不要调用它。
|
|
216
|
+
|
|
217
|
+
### 异常与支持范围
|
|
218
|
+
|
|
219
|
+
- Redis 命令拒绝会保留原始错误;不受支持的返回结构会抛出 `TypeError`。
|
|
220
|
+
- TTL 必须是正有限整数秒,或使用 `0`/`undefined` 表示永不过期;其他值会在发送命令前抛出 `RangeError`。
|
|
221
|
+
- 0.2.x 首版验证 ioredis 5 和 node-redis 5 的普通单实例连接及默认字符串/数字响应。
|
|
222
|
+
- Cluster 跨分片扫描、Sentinel 专项切换、自定义 RESP Buffer 映射、自动故障切换和连接管理不在首版支持范围。
|
|
223
|
+
|
|
224
|
+
### 从 0.1.3 迁移
|
|
225
|
+
|
|
226
|
+
旧构造方式:
|
|
227
|
+
|
|
228
|
+
```typescript
|
|
229
|
+
const provider = new RedisCacheProvider(redis);
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
新构造方式:
|
|
233
|
+
|
|
234
|
+
```typescript
|
|
235
|
+
const provider = new RedisCacheProvider(createIoredisCacheClient(redis));
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
升级时保留原 database 和物理前缀,并由应用直接声明客户端依赖、先连接、最后自行关闭连接。需要回退时锁定
|
|
239
|
+
`@jintianxiayu/cache-decorator@0.1.3` 并恢复旧构造方式;数据格式未改变,不需要清理 Redis。
|
|
240
|
+
|
|
241
|
+
## API
|
|
242
|
+
|
|
243
|
+
### @Cache(cacheName, options?)
|
|
244
|
+
|
|
245
|
+
缓存装饰器,必须用于异步方法。
|
|
246
|
+
|
|
247
|
+
- `cacheName`:缓存名称。
|
|
248
|
+
- `options.ttl`:过期时间,单位为秒。
|
|
249
|
+
- `options.providerName`:指定已注册的 `CacheProvider`。
|
|
250
|
+
- `options.key`:`undefined`/`null` 使用默认参数 key;字符串使用固定 key;函数根据方法参数返回 key。
|
|
251
|
+
|
|
252
|
+
### @CacheEvict(cacheName, options?)
|
|
253
|
+
|
|
254
|
+
缓存清除装饰器,必须用于异步方法。
|
|
255
|
+
|
|
256
|
+
- `options.allEntries`:为 `true` 时删除该缓存名称下的匹配项,而不是执行数据库级 `clear()`。
|
|
257
|
+
- `options.providerName`:指定已注册的 `CacheProvider`。
|
|
258
|
+
- `options.key`:行为与 `@Cache` 一致;`allEntries: true` 时忽略。
|
|
259
|
+
|
|
260
|
+
### CacheProviderRegistry
|
|
261
|
+
|
|
262
|
+
全局 Provider 注册表,使用 `register(name, provider)` 注册,并通过 `setDefault(name)` 选择默认 Provider。
|
|
263
|
+
|
|
264
|
+
### MemoryCacheProvider
|
|
265
|
+
|
|
266
|
+
基于进程内 Map 的实现,适用于单进程缓存。
|
|
267
|
+
|
|
268
|
+
### RedisCacheProvider
|
|
269
|
+
|
|
270
|
+
维护字符串/JSON、miss、TTL 和扫描删除协议;必须传入 `RedisCacheClient`,可使用内置适配工厂或自定义实现。
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { IoredisCacheClientSource, RedisCacheClient } from '../core/redis-cache-client';
|
|
2
|
+
/**
|
|
3
|
+
* 将调用方的 ioredis 连接适配为客户端无关的缓存命令,不创建或关闭连接。
|
|
4
|
+
* @param client 使用默认字符串响应映射的 ioredis 普通客户端。
|
|
5
|
+
* @returns 复用该连接并恢复逻辑 key 的 Redis 缓存客户端。
|
|
6
|
+
* @throws 返回对象的命令方法传播客户端错误;非标准响应拒绝为 TypeError。
|
|
7
|
+
*/
|
|
8
|
+
export declare function createIoredisCacheClient(client: IoredisCacheClientSource): RedisCacheClient;
|
|
9
|
+
//# sourceMappingURL=ioredis-cache-client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ioredis-cache-client.d.ts","sourceRoot":"","sources":["../../src/adapters/ioredis-cache-client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,wBAAwB,EAAE,gBAAgB,EAAE,MAAM,4BAA4B,CAAC;AAyCxF;;;;;GAKG;AACH,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,wBAAwB,GAAG,gBAAgB,CAyC3F"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createIoredisCacheClient = createIoredisCacheClient;
|
|
4
|
+
const redis_key_prefix_1 = require("./redis-key-prefix");
|
|
5
|
+
function normalizeGetResponse(response) {
|
|
6
|
+
if (typeof response === 'string' || response === null) {
|
|
7
|
+
return response;
|
|
8
|
+
}
|
|
9
|
+
throw new TypeError('Expected Redis GET to return a string or null');
|
|
10
|
+
}
|
|
11
|
+
function validateOkResponse(command, response) {
|
|
12
|
+
if (response !== 'OK') {
|
|
13
|
+
throw new TypeError(`Expected Redis ${command} to return "OK"`);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
function validateDeleteResponse(response) {
|
|
17
|
+
if (typeof response !== 'number' || !Number.isInteger(response) || response < 0) {
|
|
18
|
+
throw new TypeError('Expected Redis DEL to return a non-negative integer');
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
function normalizeScanResponse(response, keyPrefix) {
|
|
22
|
+
if (!Array.isArray(response) ||
|
|
23
|
+
response.length !== 2 ||
|
|
24
|
+
typeof response[0] !== 'string' ||
|
|
25
|
+
!Array.isArray(response[1]) ||
|
|
26
|
+
!response[1].every((key) => typeof key === 'string')) {
|
|
27
|
+
throw new TypeError('Expected ioredis SCAN to return a [cursor, keys] tuple');
|
|
28
|
+
}
|
|
29
|
+
return {
|
|
30
|
+
cursor: response[0],
|
|
31
|
+
keys: response[1].map((key) => (0, redis_key_prefix_1.stripRedisKeyPrefix)(keyPrefix, key)),
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* 将调用方的 ioredis 连接适配为客户端无关的缓存命令,不创建或关闭连接。
|
|
36
|
+
* @param client 使用默认字符串响应映射的 ioredis 普通客户端。
|
|
37
|
+
* @returns 复用该连接并恢复逻辑 key 的 Redis 缓存客户端。
|
|
38
|
+
* @throws 返回对象的命令方法传播客户端错误;非标准响应拒绝为 TypeError。
|
|
39
|
+
*/
|
|
40
|
+
function createIoredisCacheClient(client) {
|
|
41
|
+
const keyPrefix = client.options.keyPrefix ?? '';
|
|
42
|
+
return {
|
|
43
|
+
/** @inheritdoc */
|
|
44
|
+
async get(key) {
|
|
45
|
+
return normalizeGetResponse(await client.get(key));
|
|
46
|
+
},
|
|
47
|
+
/** @inheritdoc */
|
|
48
|
+
async set({ key, value, ttlSeconds }) {
|
|
49
|
+
if (ttlSeconds === undefined) {
|
|
50
|
+
validateOkResponse('SET', await client.set(key, value));
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
validateOkResponse('SETEX', await client.setex(key, ttlSeconds, value));
|
|
54
|
+
},
|
|
55
|
+
/** @inheritdoc */
|
|
56
|
+
async deleteMany(keys) {
|
|
57
|
+
if (keys.length === 0) {
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
validateDeleteResponse(await client.del(...keys));
|
|
61
|
+
},
|
|
62
|
+
/** @inheritdoc */
|
|
63
|
+
async scan({ cursor, pattern, count }) {
|
|
64
|
+
const response = await client.scan(cursor, 'MATCH', (0, redis_key_prefix_1.createRedisScanPattern)(keyPrefix, pattern), 'COUNT', count);
|
|
65
|
+
return normalizeScanResponse(response, keyPrefix);
|
|
66
|
+
},
|
|
67
|
+
/** @inheritdoc */
|
|
68
|
+
async flushDatabase() {
|
|
69
|
+
validateOkResponse('FLUSHDB', await client.flushdb());
|
|
70
|
+
},
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
//# sourceMappingURL=ioredis-cache-client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ioredis-cache-client.js","sourceRoot":"","sources":["../../src/adapters/ioredis-cache-client.ts"],"names":[],"mappings":";;AA+CA,4DAyCC;AAvFD,yDAAiF;AAEjF,SAAS,oBAAoB,CAAC,QAAiB;IAC3C,IAAI,OAAO,QAAQ,KAAK,QAAQ,IAAI,QAAQ,KAAK,IAAI,EAAE,CAAC;QACpD,OAAO,QAAQ,CAAC;IACpB,CAAC;IACD,MAAM,IAAI,SAAS,CAAC,+CAA+C,CAAC,CAAC;AACzE,CAAC;AAED,SAAS,kBAAkB,CAAC,OAAe,EAAE,QAAiB;IAC1D,IAAI,QAAQ,KAAK,IAAI,EAAE,CAAC;QACpB,MAAM,IAAI,SAAS,CAAC,kBAAkB,OAAO,iBAAiB,CAAC,CAAC;IACpE,CAAC;AACL,CAAC;AAED,SAAS,sBAAsB,CAAC,QAAiB;IAC7C,IAAI,OAAO,QAAQ,KAAK,QAAQ,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,QAAQ,CAAC,IAAI,QAAQ,GAAG,CAAC,EAAE,CAAC;QAC9E,MAAM,IAAI,SAAS,CAAC,qDAAqD,CAAC,CAAC;IAC/E,CAAC;AACL,CAAC;AAED,SAAS,qBAAqB,CAC1B,QAAiB,EACjB,SAAiB;IAEjB,IACI,CAAC,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC;QACxB,QAAQ,CAAC,MAAM,KAAK,CAAC;QACrB,OAAO,QAAQ,CAAC,CAAC,CAAC,KAAK,QAAQ;QAC/B,CAAC,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;QAC3B,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,GAAY,EAAE,EAAE,CAAC,OAAO,GAAG,KAAK,QAAQ,CAAC,EAC/D,CAAC;QACC,MAAM,IAAI,SAAS,CAAC,wDAAwD,CAAC,CAAC;IAClF,CAAC;IACD,OAAO;QACH,MAAM,EAAE,QAAQ,CAAC,CAAC,CAAC;QACnB,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,GAAW,EAAE,EAAE,CAAC,IAAA,sCAAmB,EAAC,SAAS,EAAE,GAAG,CAAC,CAAC;KAC9E,CAAC;AACN,CAAC;AAED;;;;;GAKG;AACH,SAAgB,wBAAwB,CAAC,MAAgC;IACrE,MAAM,SAAS,GAAG,MAAM,CAAC,OAAO,CAAC,SAAS,IAAI,EAAE,CAAC;IACjD,OAAO;QACH,kBAAkB;QAClB,KAAK,CAAC,GAAG,CAAC,GAAW;YACjB,OAAO,oBAAoB,CAAC,MAAM,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC;QACvD,CAAC;QACD,kBAAkB;QAClB,KAAK,CAAC,GAAG,CAAC,EAAE,GAAG,EAAE,KAAK,EAAE,UAAU,EAAE;YAChC,IAAI,UAAU,KAAK,SAAS,EAAE,CAAC;gBAC3B,kBAAkB,CAAC,KAAK,EAAE,MAAM,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC,CAAC;gBACxD,OAAO;YACX,CAAC;YACD,kBAAkB,CAAC,OAAO,EAAE,MAAM,MAAM,CAAC,KAAK,CAAC,GAAG,EAAE,UAAU,EAAE,KAAK,CAAC,CAAC,CAAC;QAC5E,CAAC;QACD,kBAAkB;QAClB,KAAK,CAAC,UAAU,CAAC,IAAuB;YACpC,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBACpB,OAAO;YACX,CAAC;YACD,sBAAsB,CAAC,MAAM,MAAM,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC;QACtD,CAAC;QACD,kBAAkB;QAClB,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE;YAIjC,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,IAAI,CAC9B,MAAM,EACN,OAAO,EACP,IAAA,yCAAsB,EAAC,SAAS,EAAE,OAAO,CAAC,EAC1C,OAAO,EACP,KAAK,CACR,CAAC;YACF,OAAO,qBAAqB,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC;QACtD,CAAC;QACD,kBAAkB;QAClB,KAAK,CAAC,aAAa;YACf,kBAAkB,CAAC,SAAS,EAAE,MAAM,MAAM,CAAC,OAAO,EAAE,CAAC,CAAC;QAC1D,CAAC;KACJ,CAAC;AACN,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { NodeRedisCacheClientOptions, NodeRedisCacheClientSource, RedisCacheClient } from '../core/redis-cache-client';
|
|
2
|
+
/**
|
|
3
|
+
* 将调用方的 node-redis 连接适配为客户端无关的缓存命令,不创建或关闭连接。
|
|
4
|
+
* @param client 使用默认字符串响应映射的 node-redis 普通客户端。
|
|
5
|
+
* @param options 可选的字面 key 前缀;缺省时不改写逻辑 key。
|
|
6
|
+
* @returns 复用该连接并统一物理 key 前缀的 Redis 缓存客户端。
|
|
7
|
+
* @throws 返回对象的命令方法传播客户端错误;非标准响应拒绝为 TypeError。
|
|
8
|
+
*/
|
|
9
|
+
export declare function createNodeRedisCacheClient(client: NodeRedisCacheClientSource, options?: NodeRedisCacheClientOptions): RedisCacheClient;
|
|
10
|
+
//# sourceMappingURL=node-redis-cache-client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"node-redis-cache-client.d.ts","sourceRoot":"","sources":["../../src/adapters/node-redis-cache-client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,2BAA2B,EAAE,0BAA0B,EAAE,gBAAgB,EAAE,MAAM,4BAA4B,CAAC;AA4CvH;;;;;;GAMG;AACH,wBAAgB,0BAA0B,CACtC,MAAM,EAAE,0BAA0B,EAClC,OAAO,CAAC,EAAE,2BAA2B,GACtC,gBAAgB,CAwClB"}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createNodeRedisCacheClient = createNodeRedisCacheClient;
|
|
4
|
+
const redis_key_prefix_1 = require("./redis-key-prefix");
|
|
5
|
+
function normalizeGetResponse(response) {
|
|
6
|
+
if (typeof response === 'string' || response === null) {
|
|
7
|
+
return response;
|
|
8
|
+
}
|
|
9
|
+
throw new TypeError('Expected Redis GET to return a string or null');
|
|
10
|
+
}
|
|
11
|
+
function validateOkResponse(command, response) {
|
|
12
|
+
if (response !== 'OK') {
|
|
13
|
+
throw new TypeError(`Expected Redis ${command} to return "OK"`);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
function validateDeleteResponse(response) {
|
|
17
|
+
if (typeof response !== 'number' || !Number.isInteger(response) || response < 0) {
|
|
18
|
+
throw new TypeError('Expected Redis DEL to return a non-negative integer');
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
function isRecord(value) {
|
|
22
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
23
|
+
}
|
|
24
|
+
function normalizeScanResponse(response, keyPrefix) {
|
|
25
|
+
if (!isRecord(response) ||
|
|
26
|
+
typeof response.cursor !== 'string' ||
|
|
27
|
+
!Array.isArray(response.keys) ||
|
|
28
|
+
!response.keys.every((key) => typeof key === 'string')) {
|
|
29
|
+
throw new TypeError('Expected node-redis SCAN to return a { cursor, keys } object');
|
|
30
|
+
}
|
|
31
|
+
return {
|
|
32
|
+
cursor: response.cursor,
|
|
33
|
+
keys: response.keys.map((key) => (0, redis_key_prefix_1.stripRedisKeyPrefix)(keyPrefix, key)),
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* 将调用方的 node-redis 连接适配为客户端无关的缓存命令,不创建或关闭连接。
|
|
38
|
+
* @param client 使用默认字符串响应映射的 node-redis 普通客户端。
|
|
39
|
+
* @param options 可选的字面 key 前缀;缺省时不改写逻辑 key。
|
|
40
|
+
* @returns 复用该连接并统一物理 key 前缀的 Redis 缓存客户端。
|
|
41
|
+
* @throws 返回对象的命令方法传播客户端错误;非标准响应拒绝为 TypeError。
|
|
42
|
+
*/
|
|
43
|
+
function createNodeRedisCacheClient(client, options) {
|
|
44
|
+
const keyPrefix = options?.keyPrefix ?? '';
|
|
45
|
+
const createPhysicalKey = (key) => `${keyPrefix}${key}`;
|
|
46
|
+
return {
|
|
47
|
+
/** @inheritdoc */
|
|
48
|
+
async get(key) {
|
|
49
|
+
return normalizeGetResponse(await client.get(createPhysicalKey(key)));
|
|
50
|
+
},
|
|
51
|
+
/** @inheritdoc */
|
|
52
|
+
async set({ key, value, ttlSeconds }) {
|
|
53
|
+
const physicalKey = createPhysicalKey(key);
|
|
54
|
+
if (ttlSeconds === undefined) {
|
|
55
|
+
validateOkResponse('SET', await client.set(physicalKey, value));
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
validateOkResponse('SETEX', await client.setEx(physicalKey, ttlSeconds, value));
|
|
59
|
+
},
|
|
60
|
+
/** @inheritdoc */
|
|
61
|
+
async deleteMany(keys) {
|
|
62
|
+
if (keys.length === 0) {
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
validateDeleteResponse(await client.del(keys.map(createPhysicalKey)));
|
|
66
|
+
},
|
|
67
|
+
/** @inheritdoc */
|
|
68
|
+
async scan({ cursor, pattern, count }) {
|
|
69
|
+
const response = await client.scan(cursor, {
|
|
70
|
+
MATCH: (0, redis_key_prefix_1.createRedisScanPattern)(keyPrefix, pattern),
|
|
71
|
+
COUNT: count,
|
|
72
|
+
});
|
|
73
|
+
return normalizeScanResponse(response, keyPrefix);
|
|
74
|
+
},
|
|
75
|
+
/** @inheritdoc */
|
|
76
|
+
async flushDatabase() {
|
|
77
|
+
validateOkResponse('FLUSHDB', await client.flushDb());
|
|
78
|
+
},
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
//# sourceMappingURL=node-redis-cache-client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"node-redis-cache-client.js","sourceRoot":"","sources":["../../src/adapters/node-redis-cache-client.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 将普通字符串转换为 Redis glob pattern 中的字面量片段。
|
|
3
|
+
* @param value 仅作为字面前缀使用的字符串。
|
|
4
|
+
* @returns 对反斜杠、星号、问号和方括号增加反斜杠转义后的片段。
|
|
5
|
+
*/
|
|
6
|
+
export declare function escapeRedisGlobLiteral(value: string): string;
|
|
7
|
+
/**
|
|
8
|
+
* 为逻辑 glob pattern 增加按字面量解释的 Redis key 前缀。
|
|
9
|
+
* @param keyPrefix 调用方配置的物理 key 前缀。
|
|
10
|
+
* @param pattern Provider 传入且需要保留 glob 语义的逻辑 pattern。
|
|
11
|
+
* @returns 可直接交给 Redis SCAN MATCH 的物理 pattern。
|
|
12
|
+
*/
|
|
13
|
+
export declare function createRedisScanPattern(keyPrefix: string, pattern: string): string;
|
|
14
|
+
/**
|
|
15
|
+
* 验证扫描结果属于指定物理命名空间并恢复逻辑 key。
|
|
16
|
+
* @param keyPrefix 调用方配置的物理 key 前缀。
|
|
17
|
+
* @param physicalKey Redis SCAN 返回的物理 key。
|
|
18
|
+
* @returns 只剥离一次字面前缀后的逻辑 key。
|
|
19
|
+
* @throws 扫描结果不属于指定前缀时抛出 TypeError,避免扩大后续删除范围。
|
|
20
|
+
*/
|
|
21
|
+
export declare function stripRedisKeyPrefix(keyPrefix: string, physicalKey: string): string;
|
|
22
|
+
//# sourceMappingURL=redis-key-prefix.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"redis-key-prefix.d.ts","sourceRoot":"","sources":["../../src/adapters/redis-key-prefix.ts"],"names":[],"mappings":"AAEA;;;;GAIG;AACH,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAM5D;AAED;;;;;GAKG;AACH,wBAAgB,sBAAsB,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAEjF;AAED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,MAAM,CAKlF"}
|