@lark-apaas/cache-service 0.1.1-alpha.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/LICENSE +13 -0
- package/README.md +59 -0
- package/dist/chunk-BSJRPCEN.js +307 -0
- package/dist/chunk-BSJRPCEN.js.map +1 -0
- package/dist/chunk-SSW7W3K6.js +225 -0
- package/dist/chunk-SSW7W3K6.js.map +1 -0
- package/dist/core/index.cjs +348 -0
- package/dist/core/index.cjs.map +1 -0
- package/dist/core/index.d.cts +32 -0
- package/dist/core/index.d.ts +32 -0
- package/dist/core/index.js +19 -0
- package/dist/core/index.js.map +1 -0
- package/dist/index.cjs +554 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +4 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +16 -0
- package/dist/index.js.map +1 -0
- package/dist/nestjs/index.cjs +554 -0
- package/dist/nestjs/index.cjs.map +1 -0
- package/dist/nestjs/index.d.cts +56 -0
- package/dist/nestjs/index.d.ts +56 -0
- package/dist/nestjs/index.js +16 -0
- package/dist/nestjs/index.js.map +1 -0
- package/dist/types-CFj87Od0.d.cts +38 -0
- package/dist/types-CFj87Od0.d.ts +38 -0
- package/package.json +79 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Lark Technologies Pte. Ltd. and/or its affiliates
|
|
4
|
+
|
|
5
|
+
Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted,provided that the above copyright notice and this permission notice appear in all copies.
|
|
6
|
+
|
|
7
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
|
8
|
+
IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE
|
|
9
|
+
INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO
|
|
10
|
+
EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR
|
|
11
|
+
CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
|
|
12
|
+
DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
|
|
13
|
+
ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# @lark-apaas/cache-service
|
|
2
|
+
|
|
3
|
+
FullStack 应用运行时缓存 SDK。当前提供 NestJS 装配层,`core/` 层框架无关,未来可无缝接入 Express。
|
|
4
|
+
|
|
5
|
+
## 使用
|
|
6
|
+
|
|
7
|
+
`PlatformModule` 已默认挂载 `NestjsCacheModule`,业务代码直接注入 `CACHE_MANAGER` 即可:
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
import { Inject, Injectable } from '@nestjs/common';
|
|
11
|
+
import { CACHE_MANAGER } from '@nestjs/cache-manager';
|
|
12
|
+
import type { Cache } from 'cache-manager';
|
|
13
|
+
|
|
14
|
+
@Injectable()
|
|
15
|
+
export class MyService {
|
|
16
|
+
constructor(@Inject(CACHE_MANAGER) private readonly cache: Cache) {}
|
|
17
|
+
|
|
18
|
+
async getUser(id: string) {
|
|
19
|
+
return this.cache.wrap(`user:${id}`, () => this.fetchUser(id), 60_000);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Trace 事件
|
|
25
|
+
|
|
26
|
+
SDK 通过 `@lark-apaas/nestjs-observable` 上报四类事件:
|
|
27
|
+
|
|
28
|
+
| 事件 | 字段 |
|
|
29
|
+
|---|---|
|
|
30
|
+
| `cache.read` | `cache_key` / `result`(`hit` \| `miss`) / `duration_ms` |
|
|
31
|
+
| `cache.write` | `cache_key` / `duration_ms` |
|
|
32
|
+
| `cache.delete` | `cache_key` / `duration_ms` |
|
|
33
|
+
| `cache.error` | `error_code` / `message` |
|
|
34
|
+
|
|
35
|
+
## 环境变量
|
|
36
|
+
|
|
37
|
+
| 变量 | 说明 |
|
|
38
|
+
|---|---|
|
|
39
|
+
| `SUDA_CACHE_URL` | Redis 协议连接串,由 PlatformModule 装配层读取传入 |
|
|
40
|
+
|
|
41
|
+
## 手动装配(非 PlatformModule 场景)
|
|
42
|
+
|
|
43
|
+
```ts
|
|
44
|
+
import { NestjsCacheModule } from '@lark-apaas/cache-service/nestjs';
|
|
45
|
+
|
|
46
|
+
@Module({
|
|
47
|
+
imports: [
|
|
48
|
+
NestjsCacheModule.forRootAsync({
|
|
49
|
+
imports: [LoggerModule],
|
|
50
|
+
inject: [AppLogger],
|
|
51
|
+
useFactory: () => ({
|
|
52
|
+
connectionString: process.env.SUDA_CACHE_URL!,
|
|
53
|
+
connectionTokenFilePath: '/var/run/secrets/zti/credential',
|
|
54
|
+
}),
|
|
55
|
+
}),
|
|
56
|
+
],
|
|
57
|
+
})
|
|
58
|
+
export class AppModule {}
|
|
59
|
+
```
|
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
3
|
+
|
|
4
|
+
// src/core/const.ts
|
|
5
|
+
var DEFAULT_CONNECT_TIMEOUT_MS = 1e4;
|
|
6
|
+
var DEFAULT_SOCKET_TIMEOUT_MS = 5e3;
|
|
7
|
+
var TOKEN_WATCH_INTERVAL_MS = 6e5;
|
|
8
|
+
var TOKEN_WATCH_DEBOUNCE_MS = 300;
|
|
9
|
+
var CACHE_ERROR_CODE_UNAVAILABLE = "CACHE_SERVICE_UNAVAILABLE";
|
|
10
|
+
|
|
11
|
+
// src/core/hooks.ts
|
|
12
|
+
import { KeyvHooks } from "keyv";
|
|
13
|
+
function registerHooks(keyv, observable) {
|
|
14
|
+
const getStart = /* @__PURE__ */ new WeakMap();
|
|
15
|
+
const setStart = /* @__PURE__ */ new WeakMap();
|
|
16
|
+
const delStart = /* @__PURE__ */ new WeakMap();
|
|
17
|
+
keyv.hooks.addHandler(KeyvHooks.PRE_GET, (data) => {
|
|
18
|
+
getStart.set(data, Date.now());
|
|
19
|
+
});
|
|
20
|
+
keyv.hooks.addHandler(KeyvHooks.POST_GET, (data) => {
|
|
21
|
+
const started = getStart.get(data) ?? Date.now();
|
|
22
|
+
const result = data.value === void 0 ? "miss" : "hit";
|
|
23
|
+
observable.log("debug", "cache.read", {
|
|
24
|
+
event: "cache.read",
|
|
25
|
+
cache_key: data.key,
|
|
26
|
+
result,
|
|
27
|
+
duration_ms: Date.now() - started
|
|
28
|
+
});
|
|
29
|
+
getStart.delete(data);
|
|
30
|
+
});
|
|
31
|
+
keyv.hooks.addHandler(KeyvHooks.PRE_SET, (data) => {
|
|
32
|
+
setStart.set(data, Date.now());
|
|
33
|
+
});
|
|
34
|
+
keyv.hooks.addHandler(KeyvHooks.POST_SET, (data) => {
|
|
35
|
+
const started = setStart.get(data) ?? Date.now();
|
|
36
|
+
observable.log("debug", "cache.write", {
|
|
37
|
+
event: "cache.write",
|
|
38
|
+
cache_key: data.key,
|
|
39
|
+
duration_ms: Date.now() - started
|
|
40
|
+
});
|
|
41
|
+
setStart.delete(data);
|
|
42
|
+
});
|
|
43
|
+
keyv.hooks.addHandler(KeyvHooks.PRE_DELETE, (data) => {
|
|
44
|
+
delStart.set(data, Date.now());
|
|
45
|
+
});
|
|
46
|
+
keyv.hooks.addHandler(KeyvHooks.POST_DELETE, (data) => {
|
|
47
|
+
const started = delStart.get(data) ?? Date.now();
|
|
48
|
+
observable.log("debug", "cache.delete", {
|
|
49
|
+
event: "cache.delete",
|
|
50
|
+
cache_key: data.key,
|
|
51
|
+
duration_ms: Date.now() - started
|
|
52
|
+
});
|
|
53
|
+
delStart.delete(data);
|
|
54
|
+
});
|
|
55
|
+
keyv.on("error", (err) => {
|
|
56
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
57
|
+
observable.log("error", "cache.error", {
|
|
58
|
+
event: "cache.error",
|
|
59
|
+
error_code: CACHE_ERROR_CODE_UNAVAILABLE,
|
|
60
|
+
message
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
__name(registerHooks, "registerHooks");
|
|
65
|
+
|
|
66
|
+
// src/core/cache-store.ts
|
|
67
|
+
import Keyv from "keyv";
|
|
68
|
+
import KeyvRedis from "@keyv/redis";
|
|
69
|
+
import { readFile } from "fs/promises";
|
|
70
|
+
import { watchFile, unwatchFile } from "fs";
|
|
71
|
+
function createCacheStore(options) {
|
|
72
|
+
const { connectionString, connectionTokenFilePath, connectTimeoutMs = DEFAULT_CONNECT_TIMEOUT_MS, socketTimeoutMs = DEFAULT_SOCKET_TIMEOUT_MS, observable, logger } = options;
|
|
73
|
+
const manager = new StoreManager({
|
|
74
|
+
connectionString,
|
|
75
|
+
connectionTokenFilePath,
|
|
76
|
+
connectTimeoutMs,
|
|
77
|
+
socketTimeoutMs,
|
|
78
|
+
observable,
|
|
79
|
+
logger
|
|
80
|
+
});
|
|
81
|
+
const initialStore = manager.buildStore(null);
|
|
82
|
+
manager.setInitialStore(initialStore);
|
|
83
|
+
const keyv = new Keyv({
|
|
84
|
+
store: initialStore,
|
|
85
|
+
useKeyPrefix: false,
|
|
86
|
+
throwOnErrors: false
|
|
87
|
+
});
|
|
88
|
+
registerHooks(keyv, observable);
|
|
89
|
+
manager.attach(keyv);
|
|
90
|
+
manager.startTokenWatcher();
|
|
91
|
+
return {
|
|
92
|
+
keyv,
|
|
93
|
+
dispose: /* @__PURE__ */ __name(async () => {
|
|
94
|
+
manager.stopTokenWatcher();
|
|
95
|
+
await manager.disposeCurrentStore();
|
|
96
|
+
}, "dispose")
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
__name(createCacheStore, "createCacheStore");
|
|
100
|
+
var StoreManager = class StoreManager2 {
|
|
101
|
+
static {
|
|
102
|
+
__name(this, "StoreManager");
|
|
103
|
+
}
|
|
104
|
+
config;
|
|
105
|
+
keyv = null;
|
|
106
|
+
currentStore = null;
|
|
107
|
+
currentToken = null;
|
|
108
|
+
currentResolvedConnectionString = null;
|
|
109
|
+
connectionOpPromise = null;
|
|
110
|
+
refreshing = false;
|
|
111
|
+
tokenWatchFilePath = null;
|
|
112
|
+
tokenWatchDebounceTimer = null;
|
|
113
|
+
constructor(config) {
|
|
114
|
+
this.config = config;
|
|
115
|
+
}
|
|
116
|
+
attach(keyv) {
|
|
117
|
+
this.keyv = keyv;
|
|
118
|
+
}
|
|
119
|
+
setInitialStore(store) {
|
|
120
|
+
this.currentStore = store;
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* 基于 connectionString + token 构造新 KeyvRedis
|
|
124
|
+
* 显式列出 socket 超时配置,保持默认值可读
|
|
125
|
+
*/
|
|
126
|
+
buildStore(token) {
|
|
127
|
+
const resolvedConnectionString = this.appendTokenToConnectionString(this.config.connectionString, token);
|
|
128
|
+
const store = new KeyvRedis({
|
|
129
|
+
url: resolvedConnectionString,
|
|
130
|
+
socket: {
|
|
131
|
+
connectTimeout: this.config.connectTimeoutMs,
|
|
132
|
+
// node-redis SocketOptions.socketTimeout:读写空闲超时
|
|
133
|
+
socketTimeout: this.config.socketTimeoutMs
|
|
134
|
+
}
|
|
135
|
+
}, {
|
|
136
|
+
// @keyv/redis 层的 connect 超时(覆盖 keyv 侧兜底)
|
|
137
|
+
connectionTimeout: this.config.connectTimeoutMs
|
|
138
|
+
});
|
|
139
|
+
this.currentToken = token;
|
|
140
|
+
this.currentResolvedConnectionString = resolvedConnectionString;
|
|
141
|
+
return store;
|
|
142
|
+
}
|
|
143
|
+
startTokenWatcher() {
|
|
144
|
+
const tokenPath = this.config.connectionTokenFilePath;
|
|
145
|
+
if (!tokenPath) {
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
if (this.tokenWatchFilePath === tokenPath) {
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
if (this.tokenWatchFilePath) {
|
|
152
|
+
unwatchFile(this.tokenWatchFilePath);
|
|
153
|
+
this.tokenWatchFilePath = null;
|
|
154
|
+
}
|
|
155
|
+
watchFile(tokenPath, {
|
|
156
|
+
interval: TOKEN_WATCH_INTERVAL_MS
|
|
157
|
+
}, () => {
|
|
158
|
+
this.scheduleTokenRefresh();
|
|
159
|
+
});
|
|
160
|
+
this.tokenWatchFilePath = tokenPath;
|
|
161
|
+
}
|
|
162
|
+
stopTokenWatcher() {
|
|
163
|
+
if (this.tokenWatchDebounceTimer) {
|
|
164
|
+
clearTimeout(this.tokenWatchDebounceTimer);
|
|
165
|
+
this.tokenWatchDebounceTimer = null;
|
|
166
|
+
}
|
|
167
|
+
if (this.tokenWatchFilePath) {
|
|
168
|
+
unwatchFile(this.tokenWatchFilePath);
|
|
169
|
+
this.tokenWatchFilePath = null;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
async disposeCurrentStore() {
|
|
173
|
+
await this.shutdownStoreSafely(this.currentStore);
|
|
174
|
+
this.currentStore = null;
|
|
175
|
+
}
|
|
176
|
+
scheduleTokenRefresh() {
|
|
177
|
+
if (this.tokenWatchDebounceTimer) {
|
|
178
|
+
clearTimeout(this.tokenWatchDebounceTimer);
|
|
179
|
+
}
|
|
180
|
+
this.tokenWatchDebounceTimer = setTimeout(() => {
|
|
181
|
+
void this.refreshIfTokenChanged();
|
|
182
|
+
}, TOKEN_WATCH_DEBOUNCE_MS);
|
|
183
|
+
}
|
|
184
|
+
async refreshIfTokenChanged() {
|
|
185
|
+
if (this.refreshing) {
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
this.refreshing = true;
|
|
189
|
+
try {
|
|
190
|
+
await this.runWithLock(async () => {
|
|
191
|
+
const nextToken = await this.readTokenFromFile();
|
|
192
|
+
const nextResolvedConnectionString = this.appendTokenToConnectionString(this.config.connectionString, nextToken);
|
|
193
|
+
const sameToken = nextToken === this.currentToken;
|
|
194
|
+
const sameConnStr = nextResolvedConnectionString === this.currentResolvedConnectionString;
|
|
195
|
+
if (sameToken && sameConnStr) {
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
const previousStore = this.currentStore;
|
|
199
|
+
const nextStore = this.buildStore(nextToken);
|
|
200
|
+
this.currentStore = nextStore;
|
|
201
|
+
if (this.keyv) {
|
|
202
|
+
this.keyv.store = nextStore;
|
|
203
|
+
}
|
|
204
|
+
await this.shutdownStoreSafely(previousStore);
|
|
205
|
+
});
|
|
206
|
+
} catch (err) {
|
|
207
|
+
this.config.logger?.error("Failed to refresh cache store from token file", err);
|
|
208
|
+
this.config.observable.log("error", "cache.error", {
|
|
209
|
+
event: "cache.error",
|
|
210
|
+
error_code: CACHE_ERROR_CODE_UNAVAILABLE,
|
|
211
|
+
message: err instanceof Error ? err.message : String(err)
|
|
212
|
+
});
|
|
213
|
+
} finally {
|
|
214
|
+
this.refreshing = false;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* 单例锁:并发 refresh 只跑一次
|
|
219
|
+
*/
|
|
220
|
+
runWithLock(operation) {
|
|
221
|
+
if (this.connectionOpPromise) {
|
|
222
|
+
return this.connectionOpPromise;
|
|
223
|
+
}
|
|
224
|
+
this.connectionOpPromise = (async () => {
|
|
225
|
+
try {
|
|
226
|
+
await operation();
|
|
227
|
+
} finally {
|
|
228
|
+
this.connectionOpPromise = null;
|
|
229
|
+
}
|
|
230
|
+
})();
|
|
231
|
+
return this.connectionOpPromise;
|
|
232
|
+
}
|
|
233
|
+
async readTokenFromFile() {
|
|
234
|
+
const tokenPath = this.config.connectionTokenFilePath;
|
|
235
|
+
if (!tokenPath) {
|
|
236
|
+
return null;
|
|
237
|
+
}
|
|
238
|
+
try {
|
|
239
|
+
const raw = await readFile(tokenPath, "utf-8");
|
|
240
|
+
const trimmed = raw.trim();
|
|
241
|
+
return trimmed.length > 0 ? trimmed : null;
|
|
242
|
+
} catch (err) {
|
|
243
|
+
this.config.logger?.warn("cache-service: failed to read token file, falling back to previous token", {
|
|
244
|
+
tokenPath,
|
|
245
|
+
err
|
|
246
|
+
});
|
|
247
|
+
return null;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* 追加 -c miaoda.zti_token=<token> 到 URL 的 options 参数
|
|
252
|
+
* 与 nestjs-datapaas 对齐
|
|
253
|
+
*/
|
|
254
|
+
appendTokenToConnectionString(connectionString, token) {
|
|
255
|
+
if (!token) {
|
|
256
|
+
return connectionString;
|
|
257
|
+
}
|
|
258
|
+
let url;
|
|
259
|
+
try {
|
|
260
|
+
url = new URL(connectionString);
|
|
261
|
+
} catch (err) {
|
|
262
|
+
this.config.logger?.warn("cache-service: failed to parse connectionString, skipping token append", {
|
|
263
|
+
connectionString: redactConnectionString(connectionString),
|
|
264
|
+
err
|
|
265
|
+
});
|
|
266
|
+
return connectionString;
|
|
267
|
+
}
|
|
268
|
+
const currentOptions = url.searchParams.get("options") || "";
|
|
269
|
+
const optionsWithoutToken = currentOptions.replace(/(?:^|\s)-c\s+miaoda\.zti_token=[^\s]*/g, " ").replace(/\s+/g, " ").trim();
|
|
270
|
+
const tokenOption = `-c miaoda.zti_token=${token}`;
|
|
271
|
+
const nextOptions = [
|
|
272
|
+
optionsWithoutToken,
|
|
273
|
+
tokenOption
|
|
274
|
+
].filter(Boolean).join(" ").trim();
|
|
275
|
+
url.searchParams.set("options", nextOptions);
|
|
276
|
+
return url.toString();
|
|
277
|
+
}
|
|
278
|
+
async shutdownStoreSafely(store) {
|
|
279
|
+
if (!store) {
|
|
280
|
+
return;
|
|
281
|
+
}
|
|
282
|
+
try {
|
|
283
|
+
const disconnectable = store;
|
|
284
|
+
if (typeof disconnectable.disconnect === "function") {
|
|
285
|
+
await disconnectable.disconnect();
|
|
286
|
+
}
|
|
287
|
+
} catch (err) {
|
|
288
|
+
this.config.logger?.error("Error shutting down previous KeyvRedis store", err);
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
};
|
|
292
|
+
function redactConnectionString(input) {
|
|
293
|
+
return input.replace(/(:\/\/[^:@/]*:)[^@/]*(@)/, "$1***$2");
|
|
294
|
+
}
|
|
295
|
+
__name(redactConnectionString, "redactConnectionString");
|
|
296
|
+
|
|
297
|
+
export {
|
|
298
|
+
__name,
|
|
299
|
+
DEFAULT_CONNECT_TIMEOUT_MS,
|
|
300
|
+
DEFAULT_SOCKET_TIMEOUT_MS,
|
|
301
|
+
TOKEN_WATCH_INTERVAL_MS,
|
|
302
|
+
TOKEN_WATCH_DEBOUNCE_MS,
|
|
303
|
+
CACHE_ERROR_CODE_UNAVAILABLE,
|
|
304
|
+
registerHooks,
|
|
305
|
+
createCacheStore
|
|
306
|
+
};
|
|
307
|
+
//# sourceMappingURL=chunk-BSJRPCEN.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/core/const.ts","../src/core/hooks.ts","../src/core/cache-store.ts"],"sourcesContent":["export const DEFAULT_CONNECT_TIMEOUT_MS = 10_000;\nexport const DEFAULT_SOCKET_TIMEOUT_MS = 5_000;\n\nexport const TOKEN_WATCH_INTERVAL_MS = 600_000;\nexport const TOKEN_WATCH_DEBOUNCE_MS = 300;\n\nexport const CACHE_ERROR_CODE_UNAVAILABLE = 'CACHE_SERVICE_UNAVAILABLE';\n","import Keyv, { KeyvHooks } from 'keyv';\nimport type { CacheReadResult, ObservableAdapter } from './types';\nimport { CACHE_ERROR_CODE_UNAVAILABLE } from './const';\n\n/**\n * 给 keyv 实例挂 hooks + error 监听,将缓存事件桥接到 Observable\n *\n * 事件契约:\n * - cache.read: cache_key / result(hit|miss) / duration_ms\n * - cache.write: cache_key / duration_ms\n * - cache.delete: cache_key / duration_ms\n * - cache.error: error_code / message\n */\nexport function registerHooks(keyv: Keyv, observable: ObservableAdapter): void {\n const getStart = new WeakMap<object, number>();\n const setStart = new WeakMap<object, number>();\n const delStart = new WeakMap<object, number>();\n\n keyv.hooks.addHandler(KeyvHooks.PRE_GET, (data: { key: string }) => {\n getStart.set(data, Date.now());\n });\n\n keyv.hooks.addHandler(KeyvHooks.POST_GET, (data: { key: string; value: unknown }) => {\n const started = getStart.get(data) ?? Date.now();\n const result: CacheReadResult = data.value === undefined ? 'miss' : 'hit';\n observable.log('debug', 'cache.read', {\n event: 'cache.read',\n cache_key: data.key,\n result,\n duration_ms: Date.now() - started,\n });\n getStart.delete(data);\n });\n\n keyv.hooks.addHandler(KeyvHooks.PRE_SET, (data: { key: string; value: unknown; ttl?: number }) => {\n setStart.set(data, Date.now());\n });\n\n keyv.hooks.addHandler(KeyvHooks.POST_SET, (data: { key: string; value: unknown; ttl?: number }) => {\n const started = setStart.get(data) ?? Date.now();\n observable.log('debug', 'cache.write', {\n event: 'cache.write',\n cache_key: data.key,\n duration_ms: Date.now() - started,\n });\n setStart.delete(data);\n });\n\n keyv.hooks.addHandler(KeyvHooks.PRE_DELETE, (data: { key: string }) => {\n delStart.set(data, Date.now());\n });\n\n keyv.hooks.addHandler(KeyvHooks.POST_DELETE, (data: { key: string; value: unknown }) => {\n const started = delStart.get(data) ?? Date.now();\n observable.log('debug', 'cache.delete', {\n event: 'cache.delete',\n cache_key: data.key,\n duration_ms: Date.now() - started,\n });\n delStart.delete(data);\n });\n\n keyv.on('error', (err: unknown) => {\n const message = err instanceof Error ? err.message : String(err);\n observable.log('error', 'cache.error', {\n event: 'cache.error',\n error_code: CACHE_ERROR_CODE_UNAVAILABLE,\n message,\n });\n });\n}\n","import Keyv from 'keyv';\nimport KeyvRedis from '@keyv/redis';\nimport { readFile } from 'node:fs/promises';\nimport { watchFile, unwatchFile } from 'node:fs';\n\nimport type {\n CacheStoreHandle,\n CacheStoreOptions,\n LoggerAdapter,\n ObservableAdapter,\n} from './types';\nimport {\n CACHE_ERROR_CODE_UNAVAILABLE,\n DEFAULT_CONNECT_TIMEOUT_MS,\n DEFAULT_SOCKET_TIMEOUT_MS,\n TOKEN_WATCH_DEBOUNCE_MS,\n TOKEN_WATCH_INTERVAL_MS,\n} from './const';\nimport { registerHooks } from './hooks';\n\ntype AnyKeyvRedis = KeyvRedis<unknown>;\n\n/**\n * 构造一个带 hooks 上报与 token 热刷新能力的 Keyv 实例\n *\n * 关键设计:\n * - `forRoot` 只构造不握手,KeyvRedis 首次读写时才实际连接 Redis\n * - `connectionTokenFilePath` 存在时启动 watchFile 轮询 + 防抖 + 单例锁重建 store\n * - store 替换通过 `keyv.store = newStore` 完成,业务侧 DI 引用保持稳定\n */\nexport function createCacheStore(options: CacheStoreOptions): CacheStoreHandle {\n const {\n connectionString,\n connectionTokenFilePath,\n connectTimeoutMs = DEFAULT_CONNECT_TIMEOUT_MS,\n socketTimeoutMs = DEFAULT_SOCKET_TIMEOUT_MS,\n observable,\n logger,\n } = options;\n\n const manager = new StoreManager({\n connectionString,\n connectionTokenFilePath,\n connectTimeoutMs,\n socketTimeoutMs,\n observable,\n logger,\n });\n\n const initialStore = manager.buildStore(null);\n manager.setInitialStore(initialStore);\n const keyv = new Keyv({\n store: initialStore,\n useKeyPrefix: false,\n throwOnErrors: false,\n });\n\n registerHooks(keyv, observable);\n manager.attach(keyv);\n manager.startTokenWatcher();\n\n return {\n keyv,\n dispose: async () => {\n manager.stopTokenWatcher();\n await manager.disposeCurrentStore();\n },\n };\n}\n\ninterface StoreManagerConfig {\n connectionString: string;\n connectionTokenFilePath?: string;\n connectTimeoutMs: number;\n socketTimeoutMs: number;\n observable: ObservableAdapter;\n logger?: LoggerAdapter;\n}\n\n/**\n * StoreManager\n *\n * 负责底层 KeyvRedis 的构造、token 热刷新、并发保护。\n * 骨架照搬 @lark-apaas/nestjs-datapaas 的 DrizzleDatabaseManager:\n * - watchFile 轮询 600 s 兜底\n * - 300 ms 防抖\n * - connectionOpPromise 单例锁\n */\nclass StoreManager {\n private keyv: Keyv | null = null;\n private currentStore: AnyKeyvRedis | null = null;\n private currentToken: string | null = null;\n private currentResolvedConnectionString: string | null = null;\n private connectionOpPromise: Promise<void> | null = null;\n private refreshing = false;\n\n private tokenWatchFilePath: string | null = null;\n private tokenWatchDebounceTimer: NodeJS.Timeout | null = null;\n\n constructor(private readonly config: StoreManagerConfig) {}\n\n attach(keyv: Keyv): void {\n this.keyv = keyv;\n }\n\n setInitialStore(store: AnyKeyvRedis): void {\n this.currentStore = store;\n }\n\n /**\n * 基于 connectionString + token 构造新 KeyvRedis\n * 显式列出 socket 超时配置,保持默认值可读\n */\n buildStore(token: string | null): AnyKeyvRedis {\n const resolvedConnectionString = this.appendTokenToConnectionString(\n this.config.connectionString,\n token,\n );\n\n // @keyv/redis v5 直接接 RedisClientOptions;socket 层配置需要下沉到 clientOptions.socket\n const store = new KeyvRedis(\n {\n url: resolvedConnectionString,\n socket: {\n connectTimeout: this.config.connectTimeoutMs,\n // node-redis SocketOptions.socketTimeout:读写空闲超时\n socketTimeout: this.config.socketTimeoutMs,\n },\n },\n {\n // @keyv/redis 层的 connect 超时(覆盖 keyv 侧兜底)\n connectionTimeout: this.config.connectTimeoutMs,\n },\n );\n\n this.currentToken = token;\n this.currentResolvedConnectionString = resolvedConnectionString;\n return store;\n }\n\n startTokenWatcher(): void {\n const tokenPath = this.config.connectionTokenFilePath;\n if (!tokenPath) {\n return;\n }\n if (this.tokenWatchFilePath === tokenPath) {\n return;\n }\n if (this.tokenWatchFilePath) {\n unwatchFile(this.tokenWatchFilePath);\n this.tokenWatchFilePath = null;\n }\n\n watchFile(tokenPath, { interval: TOKEN_WATCH_INTERVAL_MS }, () => {\n this.scheduleTokenRefresh();\n });\n this.tokenWatchFilePath = tokenPath;\n }\n\n stopTokenWatcher(): void {\n if (this.tokenWatchDebounceTimer) {\n clearTimeout(this.tokenWatchDebounceTimer);\n this.tokenWatchDebounceTimer = null;\n }\n if (this.tokenWatchFilePath) {\n unwatchFile(this.tokenWatchFilePath);\n this.tokenWatchFilePath = null;\n }\n }\n\n async disposeCurrentStore(): Promise<void> {\n await this.shutdownStoreSafely(this.currentStore);\n this.currentStore = null;\n }\n\n private scheduleTokenRefresh(): void {\n if (this.tokenWatchDebounceTimer) {\n clearTimeout(this.tokenWatchDebounceTimer);\n }\n this.tokenWatchDebounceTimer = setTimeout(() => {\n void this.refreshIfTokenChanged();\n }, TOKEN_WATCH_DEBOUNCE_MS);\n }\n\n private async refreshIfTokenChanged(): Promise<void> {\n if (this.refreshing) {\n return;\n }\n this.refreshing = true;\n try {\n await this.runWithLock(async () => {\n const nextToken = await this.readTokenFromFile();\n const nextResolvedConnectionString = this.appendTokenToConnectionString(\n this.config.connectionString,\n nextToken,\n );\n\n const sameToken = nextToken === this.currentToken;\n const sameConnStr =\n nextResolvedConnectionString === this.currentResolvedConnectionString;\n if (sameToken && sameConnStr) {\n return;\n }\n\n const previousStore = this.currentStore;\n const nextStore = this.buildStore(nextToken);\n this.currentStore = nextStore;\n\n if (this.keyv) {\n this.keyv.store = nextStore;\n }\n\n await this.shutdownStoreSafely(previousStore);\n });\n } catch (err) {\n this.config.logger?.error(\n 'Failed to refresh cache store from token file',\n err,\n );\n this.config.observable.log('error', 'cache.error', {\n event: 'cache.error',\n error_code: CACHE_ERROR_CODE_UNAVAILABLE,\n message: err instanceof Error ? err.message : String(err),\n });\n } finally {\n this.refreshing = false;\n }\n }\n\n /**\n * 单例锁:并发 refresh 只跑一次\n */\n private runWithLock(operation: () => Promise<void>): Promise<void> {\n if (this.connectionOpPromise) {\n return this.connectionOpPromise;\n }\n this.connectionOpPromise = (async () => {\n try {\n await operation();\n } finally {\n this.connectionOpPromise = null;\n }\n })();\n return this.connectionOpPromise;\n }\n\n private async readTokenFromFile(): Promise<string | null> {\n const tokenPath = this.config.connectionTokenFilePath;\n if (!tokenPath) {\n return null;\n }\n try {\n const raw = await readFile(tokenPath, 'utf-8');\n const trimmed = raw.trim();\n return trimmed.length > 0 ? trimmed : null;\n } catch (err) {\n this.config.logger?.warn(\n 'cache-service: failed to read token file, falling back to previous token',\n { tokenPath, err },\n );\n return null;\n }\n }\n\n /**\n * 追加 -c miaoda.zti_token=<token> 到 URL 的 options 参数\n * 与 nestjs-datapaas 对齐\n */\n private appendTokenToConnectionString(\n connectionString: string,\n token: string | null,\n ): string {\n if (!token) {\n return connectionString;\n }\n let url: URL;\n try {\n url = new URL(connectionString);\n } catch (err) {\n this.config.logger?.warn(\n 'cache-service: failed to parse connectionString, skipping token append',\n {\n connectionString: redactConnectionString(connectionString),\n err,\n },\n );\n return connectionString;\n }\n\n const currentOptions = url.searchParams.get('options') || '';\n const optionsWithoutToken = currentOptions\n .replace(/(?:^|\\s)-c\\s+miaoda\\.zti_token=[^\\s]*/g, ' ')\n .replace(/\\s+/g, ' ')\n .trim();\n const tokenOption = `-c miaoda.zti_token=${token}`;\n const nextOptions = [optionsWithoutToken, tokenOption]\n .filter(Boolean)\n .join(' ')\n .trim();\n\n url.searchParams.set('options', nextOptions);\n return url.toString();\n }\n\n private async shutdownStoreSafely(store: AnyKeyvRedis | null): Promise<void> {\n if (!store) {\n return;\n }\n try {\n const disconnectable = store as unknown as { disconnect?: () => Promise<void> };\n if (typeof disconnectable.disconnect === 'function') {\n await disconnectable.disconnect();\n }\n } catch (err) {\n this.config.logger?.error(\n 'Error shutting down previous KeyvRedis store',\n err,\n );\n }\n }\n}\n\n/**\n * 屏蔽 URL 中的 password 段,避免打 log 时泄漏凭据\n * redis://user:secret@host:6379 → redis://user:***@host:6379\n * 非法 URL 或无凭据时原样返回\n */\nfunction redactConnectionString(input: string): string {\n return input.replace(/(:\\/\\/[^:@/]*:)[^@/]*(@)/, '$1***$2');\n}\n"],"mappings":"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","names":["DEFAULT_CONNECT_TIMEOUT_MS","DEFAULT_SOCKET_TIMEOUT_MS","TOKEN_WATCH_INTERVAL_MS","TOKEN_WATCH_DEBOUNCE_MS","CACHE_ERROR_CODE_UNAVAILABLE","KeyvHooks","registerHooks","keyv","observable","getStart","WeakMap","setStart","delStart","hooks","addHandler","KeyvHooks","PRE_GET","data","set","Date","now","POST_GET","started","get","result","value","undefined","log","event","cache_key","key","duration_ms","delete","PRE_SET","POST_SET","PRE_DELETE","POST_DELETE","on","err","message","Error","String","error_code","CACHE_ERROR_CODE_UNAVAILABLE","Keyv","KeyvRedis","readFile","watchFile","unwatchFile","createCacheStore","options","connectionString","connectionTokenFilePath","connectTimeoutMs","DEFAULT_CONNECT_TIMEOUT_MS","socketTimeoutMs","DEFAULT_SOCKET_TIMEOUT_MS","observable","logger","manager","StoreManager","initialStore","buildStore","setInitialStore","keyv","Keyv","store","useKeyPrefix","throwOnErrors","registerHooks","attach","startTokenWatcher","dispose","stopTokenWatcher","disposeCurrentStore","currentStore","currentToken","currentResolvedConnectionString","connectionOpPromise","refreshing","tokenWatchFilePath","tokenWatchDebounceTimer","config","token","resolvedConnectionString","appendTokenToConnectionString","KeyvRedis","url","socket","connectTimeout","socketTimeout","connectionTimeout","tokenPath","unwatchFile","watchFile","interval","TOKEN_WATCH_INTERVAL_MS","scheduleTokenRefresh","clearTimeout","shutdownStoreSafely","setTimeout","refreshIfTokenChanged","TOKEN_WATCH_DEBOUNCE_MS","runWithLock","nextToken","readTokenFromFile","nextResolvedConnectionString","sameToken","sameConnStr","previousStore","nextStore","err","error","log","event","error_code","CACHE_ERROR_CODE_UNAVAILABLE","message","Error","String","operation","raw","readFile","trimmed","trim","length","warn","URL","redactConnectionString","currentOptions","searchParams","get","optionsWithoutToken","replace","tokenOption","nextOptions","filter","Boolean","join","set","toString","disconnectable","disconnect","input"]}
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
import {
|
|
2
|
+
DEFAULT_CONNECT_TIMEOUT_MS,
|
|
3
|
+
DEFAULT_SOCKET_TIMEOUT_MS,
|
|
4
|
+
__name,
|
|
5
|
+
createCacheStore
|
|
6
|
+
} from "./chunk-BSJRPCEN.js";
|
|
7
|
+
|
|
8
|
+
// src/nestjs/nestjs-cache.module.ts
|
|
9
|
+
import { Global, Inject, Module } from "@nestjs/common";
|
|
10
|
+
import { CacheModule } from "@nestjs/cache-manager";
|
|
11
|
+
import { OBSERVABLE_SERVICE, RequestContextService } from "@lark-apaas/nestjs-common";
|
|
12
|
+
import { AppLogger } from "@lark-apaas/nestjs-logger";
|
|
13
|
+
import { Observable } from "@lark-apaas/nestjs-observable";
|
|
14
|
+
|
|
15
|
+
// src/nestjs/const.ts
|
|
16
|
+
var NESTJS_CACHE_OPTIONS = /* @__PURE__ */ Symbol("NESTJS_CACHE_OPTIONS");
|
|
17
|
+
var CACHE_STORE_HANDLE = /* @__PURE__ */ Symbol("CACHE_STORE_HANDLE");
|
|
18
|
+
var CACHE_OBSERVABLE_ADAPTER = /* @__PURE__ */ Symbol("CACHE_OBSERVABLE_ADAPTER");
|
|
19
|
+
var CACHE_LOGGER_ADAPTER = /* @__PURE__ */ Symbol("CACHE_LOGGER_ADAPTER");
|
|
20
|
+
|
|
21
|
+
// src/nestjs/nestjs-cache.module.ts
|
|
22
|
+
function _ts_decorate(decorators, target, key, desc) {
|
|
23
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
24
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
25
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
26
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
27
|
+
}
|
|
28
|
+
__name(_ts_decorate, "_ts_decorate");
|
|
29
|
+
function _ts_metadata(k, v) {
|
|
30
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
31
|
+
}
|
|
32
|
+
__name(_ts_metadata, "_ts_metadata");
|
|
33
|
+
function _ts_param(paramIndex, decorator) {
|
|
34
|
+
return function(target, key) {
|
|
35
|
+
decorator(target, key, paramIndex);
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
__name(_ts_param, "_ts_param");
|
|
39
|
+
var NestjsCacheCoreModule = class NestjsCacheCoreModule2 {
|
|
40
|
+
static {
|
|
41
|
+
__name(this, "NestjsCacheCoreModule");
|
|
42
|
+
}
|
|
43
|
+
static forRoot(optionsProvider, imports) {
|
|
44
|
+
return {
|
|
45
|
+
module: NestjsCacheCoreModule2,
|
|
46
|
+
imports,
|
|
47
|
+
providers: [
|
|
48
|
+
optionsProvider,
|
|
49
|
+
{
|
|
50
|
+
provide: CACHE_OBSERVABLE_ADAPTER,
|
|
51
|
+
useFactory: /* @__PURE__ */ __name((observable, rtx) => {
|
|
52
|
+
const impl = observable ?? new Observable(rtx);
|
|
53
|
+
return {
|
|
54
|
+
log(level, event, attrs) {
|
|
55
|
+
impl.log(level, event, attrs);
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
}, "useFactory"),
|
|
59
|
+
inject: [
|
|
60
|
+
{
|
|
61
|
+
token: OBSERVABLE_SERVICE,
|
|
62
|
+
optional: true
|
|
63
|
+
},
|
|
64
|
+
RequestContextService
|
|
65
|
+
]
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
provide: CACHE_LOGGER_ADAPTER,
|
|
69
|
+
useFactory: /* @__PURE__ */ __name((appLogger) => {
|
|
70
|
+
if (!appLogger) {
|
|
71
|
+
return {
|
|
72
|
+
warn: /* @__PURE__ */ __name((msg) => console.warn(msg), "warn"),
|
|
73
|
+
error: /* @__PURE__ */ __name((msg, err) => console.error(msg, err), "error")
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
return {
|
|
77
|
+
warn: /* @__PURE__ */ __name((msg) => appLogger.warn(msg), "warn"),
|
|
78
|
+
error: /* @__PURE__ */ __name((msg, err) => appLogger.error(msg, err), "error")
|
|
79
|
+
};
|
|
80
|
+
}, "useFactory"),
|
|
81
|
+
inject: [
|
|
82
|
+
{
|
|
83
|
+
token: AppLogger,
|
|
84
|
+
optional: true
|
|
85
|
+
}
|
|
86
|
+
]
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
provide: CACHE_STORE_HANDLE,
|
|
90
|
+
useFactory: /* @__PURE__ */ __name((opts, observable, logger) => createCacheStore({
|
|
91
|
+
connectionString: opts.connectionString,
|
|
92
|
+
connectionTokenFilePath: opts.connectionTokenFilePath,
|
|
93
|
+
connectTimeoutMs: opts.connectTimeoutMs,
|
|
94
|
+
socketTimeoutMs: opts.socketTimeoutMs,
|
|
95
|
+
observable,
|
|
96
|
+
logger
|
|
97
|
+
}), "useFactory"),
|
|
98
|
+
inject: [
|
|
99
|
+
NESTJS_CACHE_OPTIONS,
|
|
100
|
+
CACHE_OBSERVABLE_ADAPTER,
|
|
101
|
+
CACHE_LOGGER_ADAPTER
|
|
102
|
+
]
|
|
103
|
+
}
|
|
104
|
+
],
|
|
105
|
+
exports: [
|
|
106
|
+
CACHE_STORE_HANDLE,
|
|
107
|
+
NESTJS_CACHE_OPTIONS
|
|
108
|
+
]
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
NestjsCacheCoreModule = _ts_decorate([
|
|
113
|
+
Global(),
|
|
114
|
+
Module({})
|
|
115
|
+
], NestjsCacheCoreModule);
|
|
116
|
+
var NestjsCacheModule = class _NestjsCacheModule {
|
|
117
|
+
static {
|
|
118
|
+
__name(this, "NestjsCacheModule");
|
|
119
|
+
}
|
|
120
|
+
storeHandle;
|
|
121
|
+
constructor(storeHandle) {
|
|
122
|
+
this.storeHandle = storeHandle;
|
|
123
|
+
}
|
|
124
|
+
async onApplicationShutdown() {
|
|
125
|
+
await this.storeHandle.dispose();
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* 同步注册
|
|
129
|
+
*/
|
|
130
|
+
static forRoot(options) {
|
|
131
|
+
const coreModule = NestjsCacheCoreModule.forRoot({
|
|
132
|
+
provide: NESTJS_CACHE_OPTIONS,
|
|
133
|
+
useValue: normalizeOptions(options)
|
|
134
|
+
}, []);
|
|
135
|
+
return {
|
|
136
|
+
module: _NestjsCacheModule,
|
|
137
|
+
imports: [
|
|
138
|
+
coreModule,
|
|
139
|
+
CacheModule.registerAsync({
|
|
140
|
+
isGlobal: true,
|
|
141
|
+
imports: [
|
|
142
|
+
coreModule
|
|
143
|
+
],
|
|
144
|
+
inject: [
|
|
145
|
+
CACHE_STORE_HANDLE
|
|
146
|
+
],
|
|
147
|
+
useFactory: /* @__PURE__ */ __name((handle) => ({
|
|
148
|
+
stores: [
|
|
149
|
+
handle.keyv
|
|
150
|
+
]
|
|
151
|
+
}), "useFactory")
|
|
152
|
+
})
|
|
153
|
+
],
|
|
154
|
+
exports: [
|
|
155
|
+
CacheModule,
|
|
156
|
+
coreModule
|
|
157
|
+
]
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* 异步注册(从 ConfigService 等异步源读配置)
|
|
162
|
+
*/
|
|
163
|
+
static forRootAsync(options) {
|
|
164
|
+
const optionsProvider = {
|
|
165
|
+
provide: NESTJS_CACHE_OPTIONS,
|
|
166
|
+
useFactory: /* @__PURE__ */ __name(async (...args) => {
|
|
167
|
+
const raw = await options.useFactory(...args);
|
|
168
|
+
return normalizeOptions(raw);
|
|
169
|
+
}, "useFactory"),
|
|
170
|
+
inject: options.inject ?? []
|
|
171
|
+
};
|
|
172
|
+
const coreModule = NestjsCacheCoreModule.forRoot(optionsProvider, options.imports ?? []);
|
|
173
|
+
return {
|
|
174
|
+
module: _NestjsCacheModule,
|
|
175
|
+
imports: [
|
|
176
|
+
coreModule,
|
|
177
|
+
CacheModule.registerAsync({
|
|
178
|
+
isGlobal: true,
|
|
179
|
+
imports: [
|
|
180
|
+
coreModule
|
|
181
|
+
],
|
|
182
|
+
inject: [
|
|
183
|
+
CACHE_STORE_HANDLE
|
|
184
|
+
],
|
|
185
|
+
useFactory: /* @__PURE__ */ __name((handle) => ({
|
|
186
|
+
stores: [
|
|
187
|
+
handle.keyv
|
|
188
|
+
]
|
|
189
|
+
}), "useFactory")
|
|
190
|
+
})
|
|
191
|
+
],
|
|
192
|
+
exports: [
|
|
193
|
+
CacheModule,
|
|
194
|
+
coreModule
|
|
195
|
+
]
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
};
|
|
199
|
+
NestjsCacheModule = _ts_decorate([
|
|
200
|
+
Global(),
|
|
201
|
+
Module({}),
|
|
202
|
+
_ts_param(0, Inject(CACHE_STORE_HANDLE)),
|
|
203
|
+
_ts_metadata("design:type", Function),
|
|
204
|
+
_ts_metadata("design:paramtypes", [
|
|
205
|
+
typeof CacheStoreHandle === "undefined" ? Object : CacheStoreHandle
|
|
206
|
+
])
|
|
207
|
+
], NestjsCacheModule);
|
|
208
|
+
function normalizeOptions(options) {
|
|
209
|
+
return {
|
|
210
|
+
connectionString: options.connectionString,
|
|
211
|
+
connectionTokenFilePath: options.connectionTokenFilePath,
|
|
212
|
+
connectTimeoutMs: options.connectTimeoutMs ?? DEFAULT_CONNECT_TIMEOUT_MS,
|
|
213
|
+
socketTimeoutMs: options.socketTimeoutMs ?? DEFAULT_SOCKET_TIMEOUT_MS
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
__name(normalizeOptions, "normalizeOptions");
|
|
217
|
+
|
|
218
|
+
export {
|
|
219
|
+
NESTJS_CACHE_OPTIONS,
|
|
220
|
+
CACHE_STORE_HANDLE,
|
|
221
|
+
CACHE_OBSERVABLE_ADAPTER,
|
|
222
|
+
CACHE_LOGGER_ADAPTER,
|
|
223
|
+
NestjsCacheModule
|
|
224
|
+
};
|
|
225
|
+
//# sourceMappingURL=chunk-SSW7W3K6.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/nestjs/nestjs-cache.module.ts","../src/nestjs/const.ts"],"sourcesContent":["import {\n DynamicModule,\n Global,\n Inject,\n InjectionToken,\n Module,\n ModuleMetadata,\n OnApplicationShutdown,\n Provider,\n} from '@nestjs/common';\nimport { CacheModule } from '@nestjs/cache-manager';\nimport {\n OBSERVABLE_SERVICE,\n RequestContextService,\n type ObservableService,\n} from '@lark-apaas/nestjs-common';\nimport { AppLogger } from '@lark-apaas/nestjs-logger';\nimport { Observable } from '@lark-apaas/nestjs-observable';\n\nimport { createCacheStore } from '../core/cache-store';\nimport {\n DEFAULT_CONNECT_TIMEOUT_MS,\n DEFAULT_SOCKET_TIMEOUT_MS,\n} from '../core/const';\nimport type {\n CacheStoreHandle,\n LoggerAdapter,\n ObservableAdapter,\n} from '../core/types';\nimport {\n CACHE_LOGGER_ADAPTER,\n CACHE_OBSERVABLE_ADAPTER,\n CACHE_STORE_HANDLE,\n NESTJS_CACHE_OPTIONS,\n} from './const';\n\n/**\n * 用户传入的运行时配置\n */\nexport interface NestjsCacheModuleOptions {\n /**\n * Redis 连接串(redis:// 或 rediss://)\n */\n connectionString: string;\n /**\n * ZTI Token 文件路径。存在则启动 watchFile 热刷新。\n */\n connectionTokenFilePath?: string;\n /**\n * Socket connect timeout(毫秒)。默认 10_000。\n */\n connectTimeoutMs?: number;\n /**\n * Socket idle timeout(毫秒)。默认 5_000。\n */\n socketTimeoutMs?: number;\n}\n\n/**\n * 异步配置选项\n */\nexport interface NestjsCacheModuleAsyncOptions {\n imports?: ModuleMetadata['imports'];\n useFactory: (\n ...args: unknown[]\n ) => Promise<NestjsCacheModuleOptions> | NestjsCacheModuleOptions;\n inject?: InjectionToken[];\n}\n\n/**\n * 内部核心模块:只负责组装 options + adapters + CACHE_STORE_HANDLE 三层 provider,\n * 并 export 给 CacheModule 通过 imports 消费。\n *\n * 拆出独立 module 的原因:NestJS DynamicModule 里放在 imports 中的 CacheModule\n * 无法看到同级 providers 定义的 token,必须走 module-to-module 的 exports 链路。\n */\n@Global()\n@Module({})\nclass NestjsCacheCoreModule {\n static forRoot(\n optionsProvider: Provider,\n imports: ModuleMetadata['imports'],\n ): DynamicModule {\n return {\n module: NestjsCacheCoreModule,\n imports,\n providers: [\n optionsProvider,\n {\n provide: CACHE_OBSERVABLE_ADAPTER,\n useFactory: (\n observable: ObservableService | null,\n rtx: RequestContextService,\n ): ObservableAdapter => {\n const impl = observable ?? new Observable(rtx);\n return {\n log(level, event, attrs) {\n impl.log(level, event, attrs);\n },\n };\n },\n inject: [\n { token: OBSERVABLE_SERVICE, optional: true },\n RequestContextService,\n ],\n },\n {\n provide: CACHE_LOGGER_ADAPTER,\n useFactory: (appLogger: AppLogger | null): LoggerAdapter => {\n if (!appLogger) {\n return {\n warn: (msg) => console.warn(msg),\n error: (msg, err) => console.error(msg, err),\n };\n }\n return {\n warn: (msg) => appLogger.warn(msg),\n error: (msg, err) => appLogger.error(msg, err),\n };\n },\n inject: [{ token: AppLogger, optional: true }],\n },\n {\n provide: CACHE_STORE_HANDLE,\n useFactory: (\n opts: NestjsCacheModuleOptions,\n observable: ObservableAdapter,\n logger: LoggerAdapter,\n ): CacheStoreHandle =>\n createCacheStore({\n connectionString: opts.connectionString,\n connectionTokenFilePath: opts.connectionTokenFilePath,\n connectTimeoutMs: opts.connectTimeoutMs,\n socketTimeoutMs: opts.socketTimeoutMs,\n observable,\n logger,\n }),\n inject: [\n NESTJS_CACHE_OPTIONS,\n CACHE_OBSERVABLE_ADAPTER,\n CACHE_LOGGER_ADAPTER,\n ],\n },\n ],\n exports: [CACHE_STORE_HANDLE, NESTJS_CACHE_OPTIONS],\n };\n }\n}\n\n@Global()\n@Module({})\nexport class NestjsCacheModule implements OnApplicationShutdown {\n constructor(\n @Inject(CACHE_STORE_HANDLE)\n private readonly storeHandle: CacheStoreHandle,\n ) {}\n\n async onApplicationShutdown(): Promise<void> {\n await this.storeHandle.dispose();\n }\n\n /**\n * 同步注册\n */\n static forRoot(options: NestjsCacheModuleOptions): DynamicModule {\n const coreModule = NestjsCacheCoreModule.forRoot(\n {\n provide: NESTJS_CACHE_OPTIONS,\n useValue: normalizeOptions(options),\n },\n [],\n );\n\n return {\n module: NestjsCacheModule,\n imports: [\n coreModule,\n CacheModule.registerAsync({\n isGlobal: true,\n imports: [coreModule],\n inject: [CACHE_STORE_HANDLE],\n useFactory: (handle: CacheStoreHandle) => ({\n stores: [handle.keyv],\n }),\n }),\n ],\n exports: [CacheModule, coreModule],\n };\n }\n\n /**\n * 异步注册(从 ConfigService 等异步源读配置)\n */\n static forRootAsync(options: NestjsCacheModuleAsyncOptions): DynamicModule {\n const optionsProvider: Provider = {\n provide: NESTJS_CACHE_OPTIONS,\n useFactory: async (...args: unknown[]) => {\n const raw = await options.useFactory(...args);\n return normalizeOptions(raw);\n },\n inject: options.inject ?? [],\n };\n\n const coreModule = NestjsCacheCoreModule.forRoot(\n optionsProvider,\n options.imports ?? [],\n );\n\n return {\n module: NestjsCacheModule,\n imports: [\n coreModule,\n CacheModule.registerAsync({\n isGlobal: true,\n imports: [coreModule],\n inject: [CACHE_STORE_HANDLE],\n useFactory: (handle: CacheStoreHandle) => ({\n stores: [handle.keyv],\n }),\n }),\n ],\n exports: [CacheModule, coreModule],\n };\n }\n}\n\n/**\n * 应用默认值\n */\nfunction normalizeOptions(\n options: NestjsCacheModuleOptions,\n): NestjsCacheModuleOptions {\n return {\n connectionString: options.connectionString,\n connectionTokenFilePath: options.connectionTokenFilePath,\n connectTimeoutMs: options.connectTimeoutMs ?? DEFAULT_CONNECT_TIMEOUT_MS,\n socketTimeoutMs: options.socketTimeoutMs ?? DEFAULT_SOCKET_TIMEOUT_MS,\n };\n}\n","/**\n * @lark-apaas/cache-service - NestJS 层 DI tokens\n */\n\nexport const NESTJS_CACHE_OPTIONS = Symbol('NESTJS_CACHE_OPTIONS');\n\nexport const CACHE_STORE_HANDLE = Symbol('CACHE_STORE_HANDLE');\n\nexport const CACHE_OBSERVABLE_ADAPTER = Symbol('CACHE_OBSERVABLE_ADAPTER');\n\nexport const CACHE_LOGGER_ADAPTER = Symbol('CACHE_LOGGER_ADAPTER');\n"],"mappings":"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","names":["Global","Inject","Module","CacheModule","OBSERVABLE_SERVICE","RequestContextService","AppLogger","Observable","NESTJS_CACHE_OPTIONS","Symbol","CACHE_STORE_HANDLE","CACHE_OBSERVABLE_ADAPTER","CACHE_LOGGER_ADAPTER","NestjsCacheCoreModule","forRoot","optionsProvider","imports","module","providers","provide","CACHE_OBSERVABLE_ADAPTER","useFactory","observable","rtx","impl","Observable","log","level","event","attrs","inject","token","OBSERVABLE_SERVICE","optional","RequestContextService","CACHE_LOGGER_ADAPTER","appLogger","warn","msg","console","error","err","AppLogger","CACHE_STORE_HANDLE","opts","logger","createCacheStore","connectionString","connectionTokenFilePath","connectTimeoutMs","socketTimeoutMs","NESTJS_CACHE_OPTIONS","exports","NestjsCacheModule","storeHandle","onApplicationShutdown","dispose","options","coreModule","useValue","normalizeOptions","CacheModule","registerAsync","isGlobal","handle","stores","keyv","forRootAsync","args","raw","DEFAULT_CONNECT_TIMEOUT_MS","DEFAULT_SOCKET_TIMEOUT_MS"]}
|