@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
|
@@ -0,0 +1,554 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
9
|
+
var __export = (target, all) => {
|
|
10
|
+
for (var name in all)
|
|
11
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
12
|
+
};
|
|
13
|
+
var __copyProps = (to, from, except, desc) => {
|
|
14
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
15
|
+
for (let key of __getOwnPropNames(from))
|
|
16
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
17
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
18
|
+
}
|
|
19
|
+
return to;
|
|
20
|
+
};
|
|
21
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
22
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
23
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
24
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
25
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
26
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
27
|
+
mod
|
|
28
|
+
));
|
|
29
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
30
|
+
|
|
31
|
+
// src/nestjs/index.ts
|
|
32
|
+
var nestjs_exports = {};
|
|
33
|
+
__export(nestjs_exports, {
|
|
34
|
+
CACHE_LOGGER_ADAPTER: () => CACHE_LOGGER_ADAPTER,
|
|
35
|
+
CACHE_OBSERVABLE_ADAPTER: () => CACHE_OBSERVABLE_ADAPTER,
|
|
36
|
+
CACHE_STORE_HANDLE: () => CACHE_STORE_HANDLE,
|
|
37
|
+
NESTJS_CACHE_OPTIONS: () => NESTJS_CACHE_OPTIONS,
|
|
38
|
+
NestjsCacheModule: () => NestjsCacheModule
|
|
39
|
+
});
|
|
40
|
+
module.exports = __toCommonJS(nestjs_exports);
|
|
41
|
+
|
|
42
|
+
// src/nestjs/nestjs-cache.module.ts
|
|
43
|
+
var import_common = require("@nestjs/common");
|
|
44
|
+
var import_cache_manager = require("@nestjs/cache-manager");
|
|
45
|
+
var import_nestjs_common = require("@lark-apaas/nestjs-common");
|
|
46
|
+
var import_nestjs_logger = require("@lark-apaas/nestjs-logger");
|
|
47
|
+
var import_nestjs_observable = require("@lark-apaas/nestjs-observable");
|
|
48
|
+
|
|
49
|
+
// src/core/cache-store.ts
|
|
50
|
+
var import_keyv2 = __toESM(require("keyv"), 1);
|
|
51
|
+
var import_redis = __toESM(require("@keyv/redis"), 1);
|
|
52
|
+
var import_promises = require("fs/promises");
|
|
53
|
+
var import_node_fs = require("fs");
|
|
54
|
+
|
|
55
|
+
// src/core/const.ts
|
|
56
|
+
var DEFAULT_CONNECT_TIMEOUT_MS = 1e4;
|
|
57
|
+
var DEFAULT_SOCKET_TIMEOUT_MS = 5e3;
|
|
58
|
+
var TOKEN_WATCH_INTERVAL_MS = 6e5;
|
|
59
|
+
var TOKEN_WATCH_DEBOUNCE_MS = 300;
|
|
60
|
+
var CACHE_ERROR_CODE_UNAVAILABLE = "CACHE_SERVICE_UNAVAILABLE";
|
|
61
|
+
|
|
62
|
+
// src/core/hooks.ts
|
|
63
|
+
var import_keyv = require("keyv");
|
|
64
|
+
function registerHooks(keyv, observable) {
|
|
65
|
+
const getStart = /* @__PURE__ */ new WeakMap();
|
|
66
|
+
const setStart = /* @__PURE__ */ new WeakMap();
|
|
67
|
+
const delStart = /* @__PURE__ */ new WeakMap();
|
|
68
|
+
keyv.hooks.addHandler(import_keyv.KeyvHooks.PRE_GET, (data) => {
|
|
69
|
+
getStart.set(data, Date.now());
|
|
70
|
+
});
|
|
71
|
+
keyv.hooks.addHandler(import_keyv.KeyvHooks.POST_GET, (data) => {
|
|
72
|
+
const started = getStart.get(data) ?? Date.now();
|
|
73
|
+
const result = data.value === void 0 ? "miss" : "hit";
|
|
74
|
+
observable.log("debug", "cache.read", {
|
|
75
|
+
event: "cache.read",
|
|
76
|
+
cache_key: data.key,
|
|
77
|
+
result,
|
|
78
|
+
duration_ms: Date.now() - started
|
|
79
|
+
});
|
|
80
|
+
getStart.delete(data);
|
|
81
|
+
});
|
|
82
|
+
keyv.hooks.addHandler(import_keyv.KeyvHooks.PRE_SET, (data) => {
|
|
83
|
+
setStart.set(data, Date.now());
|
|
84
|
+
});
|
|
85
|
+
keyv.hooks.addHandler(import_keyv.KeyvHooks.POST_SET, (data) => {
|
|
86
|
+
const started = setStart.get(data) ?? Date.now();
|
|
87
|
+
observable.log("debug", "cache.write", {
|
|
88
|
+
event: "cache.write",
|
|
89
|
+
cache_key: data.key,
|
|
90
|
+
duration_ms: Date.now() - started
|
|
91
|
+
});
|
|
92
|
+
setStart.delete(data);
|
|
93
|
+
});
|
|
94
|
+
keyv.hooks.addHandler(import_keyv.KeyvHooks.PRE_DELETE, (data) => {
|
|
95
|
+
delStart.set(data, Date.now());
|
|
96
|
+
});
|
|
97
|
+
keyv.hooks.addHandler(import_keyv.KeyvHooks.POST_DELETE, (data) => {
|
|
98
|
+
const started = delStart.get(data) ?? Date.now();
|
|
99
|
+
observable.log("debug", "cache.delete", {
|
|
100
|
+
event: "cache.delete",
|
|
101
|
+
cache_key: data.key,
|
|
102
|
+
duration_ms: Date.now() - started
|
|
103
|
+
});
|
|
104
|
+
delStart.delete(data);
|
|
105
|
+
});
|
|
106
|
+
keyv.on("error", (err) => {
|
|
107
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
108
|
+
observable.log("error", "cache.error", {
|
|
109
|
+
event: "cache.error",
|
|
110
|
+
error_code: CACHE_ERROR_CODE_UNAVAILABLE,
|
|
111
|
+
message
|
|
112
|
+
});
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
__name(registerHooks, "registerHooks");
|
|
116
|
+
|
|
117
|
+
// src/core/cache-store.ts
|
|
118
|
+
function createCacheStore(options) {
|
|
119
|
+
const { connectionString, connectionTokenFilePath, connectTimeoutMs = DEFAULT_CONNECT_TIMEOUT_MS, socketTimeoutMs = DEFAULT_SOCKET_TIMEOUT_MS, observable, logger } = options;
|
|
120
|
+
const manager = new StoreManager({
|
|
121
|
+
connectionString,
|
|
122
|
+
connectionTokenFilePath,
|
|
123
|
+
connectTimeoutMs,
|
|
124
|
+
socketTimeoutMs,
|
|
125
|
+
observable,
|
|
126
|
+
logger
|
|
127
|
+
});
|
|
128
|
+
const initialStore = manager.buildStore(null);
|
|
129
|
+
manager.setInitialStore(initialStore);
|
|
130
|
+
const keyv = new import_keyv2.default({
|
|
131
|
+
store: initialStore,
|
|
132
|
+
useKeyPrefix: false,
|
|
133
|
+
throwOnErrors: false
|
|
134
|
+
});
|
|
135
|
+
registerHooks(keyv, observable);
|
|
136
|
+
manager.attach(keyv);
|
|
137
|
+
manager.startTokenWatcher();
|
|
138
|
+
return {
|
|
139
|
+
keyv,
|
|
140
|
+
dispose: /* @__PURE__ */ __name(async () => {
|
|
141
|
+
manager.stopTokenWatcher();
|
|
142
|
+
await manager.disposeCurrentStore();
|
|
143
|
+
}, "dispose")
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
__name(createCacheStore, "createCacheStore");
|
|
147
|
+
var StoreManager = class StoreManager2 {
|
|
148
|
+
static {
|
|
149
|
+
__name(this, "StoreManager");
|
|
150
|
+
}
|
|
151
|
+
config;
|
|
152
|
+
keyv = null;
|
|
153
|
+
currentStore = null;
|
|
154
|
+
currentToken = null;
|
|
155
|
+
currentResolvedConnectionString = null;
|
|
156
|
+
connectionOpPromise = null;
|
|
157
|
+
refreshing = false;
|
|
158
|
+
tokenWatchFilePath = null;
|
|
159
|
+
tokenWatchDebounceTimer = null;
|
|
160
|
+
constructor(config) {
|
|
161
|
+
this.config = config;
|
|
162
|
+
}
|
|
163
|
+
attach(keyv) {
|
|
164
|
+
this.keyv = keyv;
|
|
165
|
+
}
|
|
166
|
+
setInitialStore(store) {
|
|
167
|
+
this.currentStore = store;
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* 基于 connectionString + token 构造新 KeyvRedis
|
|
171
|
+
* 显式列出 socket 超时配置,保持默认值可读
|
|
172
|
+
*/
|
|
173
|
+
buildStore(token) {
|
|
174
|
+
const resolvedConnectionString = this.appendTokenToConnectionString(this.config.connectionString, token);
|
|
175
|
+
const store = new import_redis.default({
|
|
176
|
+
url: resolvedConnectionString,
|
|
177
|
+
socket: {
|
|
178
|
+
connectTimeout: this.config.connectTimeoutMs,
|
|
179
|
+
// node-redis SocketOptions.socketTimeout:读写空闲超时
|
|
180
|
+
socketTimeout: this.config.socketTimeoutMs
|
|
181
|
+
}
|
|
182
|
+
}, {
|
|
183
|
+
// @keyv/redis 层的 connect 超时(覆盖 keyv 侧兜底)
|
|
184
|
+
connectionTimeout: this.config.connectTimeoutMs
|
|
185
|
+
});
|
|
186
|
+
this.currentToken = token;
|
|
187
|
+
this.currentResolvedConnectionString = resolvedConnectionString;
|
|
188
|
+
return store;
|
|
189
|
+
}
|
|
190
|
+
startTokenWatcher() {
|
|
191
|
+
const tokenPath = this.config.connectionTokenFilePath;
|
|
192
|
+
if (!tokenPath) {
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
if (this.tokenWatchFilePath === tokenPath) {
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
if (this.tokenWatchFilePath) {
|
|
199
|
+
(0, import_node_fs.unwatchFile)(this.tokenWatchFilePath);
|
|
200
|
+
this.tokenWatchFilePath = null;
|
|
201
|
+
}
|
|
202
|
+
(0, import_node_fs.watchFile)(tokenPath, {
|
|
203
|
+
interval: TOKEN_WATCH_INTERVAL_MS
|
|
204
|
+
}, () => {
|
|
205
|
+
this.scheduleTokenRefresh();
|
|
206
|
+
});
|
|
207
|
+
this.tokenWatchFilePath = tokenPath;
|
|
208
|
+
}
|
|
209
|
+
stopTokenWatcher() {
|
|
210
|
+
if (this.tokenWatchDebounceTimer) {
|
|
211
|
+
clearTimeout(this.tokenWatchDebounceTimer);
|
|
212
|
+
this.tokenWatchDebounceTimer = null;
|
|
213
|
+
}
|
|
214
|
+
if (this.tokenWatchFilePath) {
|
|
215
|
+
(0, import_node_fs.unwatchFile)(this.tokenWatchFilePath);
|
|
216
|
+
this.tokenWatchFilePath = null;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
async disposeCurrentStore() {
|
|
220
|
+
await this.shutdownStoreSafely(this.currentStore);
|
|
221
|
+
this.currentStore = null;
|
|
222
|
+
}
|
|
223
|
+
scheduleTokenRefresh() {
|
|
224
|
+
if (this.tokenWatchDebounceTimer) {
|
|
225
|
+
clearTimeout(this.tokenWatchDebounceTimer);
|
|
226
|
+
}
|
|
227
|
+
this.tokenWatchDebounceTimer = setTimeout(() => {
|
|
228
|
+
void this.refreshIfTokenChanged();
|
|
229
|
+
}, TOKEN_WATCH_DEBOUNCE_MS);
|
|
230
|
+
}
|
|
231
|
+
async refreshIfTokenChanged() {
|
|
232
|
+
if (this.refreshing) {
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
235
|
+
this.refreshing = true;
|
|
236
|
+
try {
|
|
237
|
+
await this.runWithLock(async () => {
|
|
238
|
+
const nextToken = await this.readTokenFromFile();
|
|
239
|
+
const nextResolvedConnectionString = this.appendTokenToConnectionString(this.config.connectionString, nextToken);
|
|
240
|
+
const sameToken = nextToken === this.currentToken;
|
|
241
|
+
const sameConnStr = nextResolvedConnectionString === this.currentResolvedConnectionString;
|
|
242
|
+
if (sameToken && sameConnStr) {
|
|
243
|
+
return;
|
|
244
|
+
}
|
|
245
|
+
const previousStore = this.currentStore;
|
|
246
|
+
const nextStore = this.buildStore(nextToken);
|
|
247
|
+
this.currentStore = nextStore;
|
|
248
|
+
if (this.keyv) {
|
|
249
|
+
this.keyv.store = nextStore;
|
|
250
|
+
}
|
|
251
|
+
await this.shutdownStoreSafely(previousStore);
|
|
252
|
+
});
|
|
253
|
+
} catch (err) {
|
|
254
|
+
this.config.logger?.error("Failed to refresh cache store from token file", err);
|
|
255
|
+
this.config.observable.log("error", "cache.error", {
|
|
256
|
+
event: "cache.error",
|
|
257
|
+
error_code: CACHE_ERROR_CODE_UNAVAILABLE,
|
|
258
|
+
message: err instanceof Error ? err.message : String(err)
|
|
259
|
+
});
|
|
260
|
+
} finally {
|
|
261
|
+
this.refreshing = false;
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
/**
|
|
265
|
+
* 单例锁:并发 refresh 只跑一次
|
|
266
|
+
*/
|
|
267
|
+
runWithLock(operation) {
|
|
268
|
+
if (this.connectionOpPromise) {
|
|
269
|
+
return this.connectionOpPromise;
|
|
270
|
+
}
|
|
271
|
+
this.connectionOpPromise = (async () => {
|
|
272
|
+
try {
|
|
273
|
+
await operation();
|
|
274
|
+
} finally {
|
|
275
|
+
this.connectionOpPromise = null;
|
|
276
|
+
}
|
|
277
|
+
})();
|
|
278
|
+
return this.connectionOpPromise;
|
|
279
|
+
}
|
|
280
|
+
async readTokenFromFile() {
|
|
281
|
+
const tokenPath = this.config.connectionTokenFilePath;
|
|
282
|
+
if (!tokenPath) {
|
|
283
|
+
return null;
|
|
284
|
+
}
|
|
285
|
+
try {
|
|
286
|
+
const raw = await (0, import_promises.readFile)(tokenPath, "utf-8");
|
|
287
|
+
const trimmed = raw.trim();
|
|
288
|
+
return trimmed.length > 0 ? trimmed : null;
|
|
289
|
+
} catch (err) {
|
|
290
|
+
this.config.logger?.warn("cache-service: failed to read token file, falling back to previous token", {
|
|
291
|
+
tokenPath,
|
|
292
|
+
err
|
|
293
|
+
});
|
|
294
|
+
return null;
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
/**
|
|
298
|
+
* 追加 -c miaoda.zti_token=<token> 到 URL 的 options 参数
|
|
299
|
+
* 与 nestjs-datapaas 对齐
|
|
300
|
+
*/
|
|
301
|
+
appendTokenToConnectionString(connectionString, token) {
|
|
302
|
+
if (!token) {
|
|
303
|
+
return connectionString;
|
|
304
|
+
}
|
|
305
|
+
let url;
|
|
306
|
+
try {
|
|
307
|
+
url = new URL(connectionString);
|
|
308
|
+
} catch (err) {
|
|
309
|
+
this.config.logger?.warn("cache-service: failed to parse connectionString, skipping token append", {
|
|
310
|
+
connectionString: redactConnectionString(connectionString),
|
|
311
|
+
err
|
|
312
|
+
});
|
|
313
|
+
return connectionString;
|
|
314
|
+
}
|
|
315
|
+
const currentOptions = url.searchParams.get("options") || "";
|
|
316
|
+
const optionsWithoutToken = currentOptions.replace(/(?:^|\s)-c\s+miaoda\.zti_token=[^\s]*/g, " ").replace(/\s+/g, " ").trim();
|
|
317
|
+
const tokenOption = `-c miaoda.zti_token=${token}`;
|
|
318
|
+
const nextOptions = [
|
|
319
|
+
optionsWithoutToken,
|
|
320
|
+
tokenOption
|
|
321
|
+
].filter(Boolean).join(" ").trim();
|
|
322
|
+
url.searchParams.set("options", nextOptions);
|
|
323
|
+
return url.toString();
|
|
324
|
+
}
|
|
325
|
+
async shutdownStoreSafely(store) {
|
|
326
|
+
if (!store) {
|
|
327
|
+
return;
|
|
328
|
+
}
|
|
329
|
+
try {
|
|
330
|
+
const disconnectable = store;
|
|
331
|
+
if (typeof disconnectable.disconnect === "function") {
|
|
332
|
+
await disconnectable.disconnect();
|
|
333
|
+
}
|
|
334
|
+
} catch (err) {
|
|
335
|
+
this.config.logger?.error("Error shutting down previous KeyvRedis store", err);
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
};
|
|
339
|
+
function redactConnectionString(input) {
|
|
340
|
+
return input.replace(/(:\/\/[^:@/]*:)[^@/]*(@)/, "$1***$2");
|
|
341
|
+
}
|
|
342
|
+
__name(redactConnectionString, "redactConnectionString");
|
|
343
|
+
|
|
344
|
+
// src/nestjs/const.ts
|
|
345
|
+
var NESTJS_CACHE_OPTIONS = /* @__PURE__ */ Symbol("NESTJS_CACHE_OPTIONS");
|
|
346
|
+
var CACHE_STORE_HANDLE = /* @__PURE__ */ Symbol("CACHE_STORE_HANDLE");
|
|
347
|
+
var CACHE_OBSERVABLE_ADAPTER = /* @__PURE__ */ Symbol("CACHE_OBSERVABLE_ADAPTER");
|
|
348
|
+
var CACHE_LOGGER_ADAPTER = /* @__PURE__ */ Symbol("CACHE_LOGGER_ADAPTER");
|
|
349
|
+
|
|
350
|
+
// src/nestjs/nestjs-cache.module.ts
|
|
351
|
+
function _ts_decorate(decorators, target, key, desc) {
|
|
352
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
353
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
354
|
+
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;
|
|
355
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
356
|
+
}
|
|
357
|
+
__name(_ts_decorate, "_ts_decorate");
|
|
358
|
+
function _ts_metadata(k, v) {
|
|
359
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
360
|
+
}
|
|
361
|
+
__name(_ts_metadata, "_ts_metadata");
|
|
362
|
+
function _ts_param(paramIndex, decorator) {
|
|
363
|
+
return function(target, key) {
|
|
364
|
+
decorator(target, key, paramIndex);
|
|
365
|
+
};
|
|
366
|
+
}
|
|
367
|
+
__name(_ts_param, "_ts_param");
|
|
368
|
+
var NestjsCacheCoreModule = class NestjsCacheCoreModule2 {
|
|
369
|
+
static {
|
|
370
|
+
__name(this, "NestjsCacheCoreModule");
|
|
371
|
+
}
|
|
372
|
+
static forRoot(optionsProvider, imports) {
|
|
373
|
+
return {
|
|
374
|
+
module: NestjsCacheCoreModule2,
|
|
375
|
+
imports,
|
|
376
|
+
providers: [
|
|
377
|
+
optionsProvider,
|
|
378
|
+
{
|
|
379
|
+
provide: CACHE_OBSERVABLE_ADAPTER,
|
|
380
|
+
useFactory: /* @__PURE__ */ __name((observable, rtx) => {
|
|
381
|
+
const impl = observable ?? new import_nestjs_observable.Observable(rtx);
|
|
382
|
+
return {
|
|
383
|
+
log(level, event, attrs) {
|
|
384
|
+
impl.log(level, event, attrs);
|
|
385
|
+
}
|
|
386
|
+
};
|
|
387
|
+
}, "useFactory"),
|
|
388
|
+
inject: [
|
|
389
|
+
{
|
|
390
|
+
token: import_nestjs_common.OBSERVABLE_SERVICE,
|
|
391
|
+
optional: true
|
|
392
|
+
},
|
|
393
|
+
import_nestjs_common.RequestContextService
|
|
394
|
+
]
|
|
395
|
+
},
|
|
396
|
+
{
|
|
397
|
+
provide: CACHE_LOGGER_ADAPTER,
|
|
398
|
+
useFactory: /* @__PURE__ */ __name((appLogger) => {
|
|
399
|
+
if (!appLogger) {
|
|
400
|
+
return {
|
|
401
|
+
warn: /* @__PURE__ */ __name((msg) => console.warn(msg), "warn"),
|
|
402
|
+
error: /* @__PURE__ */ __name((msg, err) => console.error(msg, err), "error")
|
|
403
|
+
};
|
|
404
|
+
}
|
|
405
|
+
return {
|
|
406
|
+
warn: /* @__PURE__ */ __name((msg) => appLogger.warn(msg), "warn"),
|
|
407
|
+
error: /* @__PURE__ */ __name((msg, err) => appLogger.error(msg, err), "error")
|
|
408
|
+
};
|
|
409
|
+
}, "useFactory"),
|
|
410
|
+
inject: [
|
|
411
|
+
{
|
|
412
|
+
token: import_nestjs_logger.AppLogger,
|
|
413
|
+
optional: true
|
|
414
|
+
}
|
|
415
|
+
]
|
|
416
|
+
},
|
|
417
|
+
{
|
|
418
|
+
provide: CACHE_STORE_HANDLE,
|
|
419
|
+
useFactory: /* @__PURE__ */ __name((opts, observable, logger) => createCacheStore({
|
|
420
|
+
connectionString: opts.connectionString,
|
|
421
|
+
connectionTokenFilePath: opts.connectionTokenFilePath,
|
|
422
|
+
connectTimeoutMs: opts.connectTimeoutMs,
|
|
423
|
+
socketTimeoutMs: opts.socketTimeoutMs,
|
|
424
|
+
observable,
|
|
425
|
+
logger
|
|
426
|
+
}), "useFactory"),
|
|
427
|
+
inject: [
|
|
428
|
+
NESTJS_CACHE_OPTIONS,
|
|
429
|
+
CACHE_OBSERVABLE_ADAPTER,
|
|
430
|
+
CACHE_LOGGER_ADAPTER
|
|
431
|
+
]
|
|
432
|
+
}
|
|
433
|
+
],
|
|
434
|
+
exports: [
|
|
435
|
+
CACHE_STORE_HANDLE,
|
|
436
|
+
NESTJS_CACHE_OPTIONS
|
|
437
|
+
]
|
|
438
|
+
};
|
|
439
|
+
}
|
|
440
|
+
};
|
|
441
|
+
NestjsCacheCoreModule = _ts_decorate([
|
|
442
|
+
(0, import_common.Global)(),
|
|
443
|
+
(0, import_common.Module)({})
|
|
444
|
+
], NestjsCacheCoreModule);
|
|
445
|
+
var NestjsCacheModule = class _NestjsCacheModule {
|
|
446
|
+
static {
|
|
447
|
+
__name(this, "NestjsCacheModule");
|
|
448
|
+
}
|
|
449
|
+
storeHandle;
|
|
450
|
+
constructor(storeHandle) {
|
|
451
|
+
this.storeHandle = storeHandle;
|
|
452
|
+
}
|
|
453
|
+
async onApplicationShutdown() {
|
|
454
|
+
await this.storeHandle.dispose();
|
|
455
|
+
}
|
|
456
|
+
/**
|
|
457
|
+
* 同步注册
|
|
458
|
+
*/
|
|
459
|
+
static forRoot(options) {
|
|
460
|
+
const coreModule = NestjsCacheCoreModule.forRoot({
|
|
461
|
+
provide: NESTJS_CACHE_OPTIONS,
|
|
462
|
+
useValue: normalizeOptions(options)
|
|
463
|
+
}, []);
|
|
464
|
+
return {
|
|
465
|
+
module: _NestjsCacheModule,
|
|
466
|
+
imports: [
|
|
467
|
+
coreModule,
|
|
468
|
+
import_cache_manager.CacheModule.registerAsync({
|
|
469
|
+
isGlobal: true,
|
|
470
|
+
imports: [
|
|
471
|
+
coreModule
|
|
472
|
+
],
|
|
473
|
+
inject: [
|
|
474
|
+
CACHE_STORE_HANDLE
|
|
475
|
+
],
|
|
476
|
+
useFactory: /* @__PURE__ */ __name((handle) => ({
|
|
477
|
+
stores: [
|
|
478
|
+
handle.keyv
|
|
479
|
+
]
|
|
480
|
+
}), "useFactory")
|
|
481
|
+
})
|
|
482
|
+
],
|
|
483
|
+
exports: [
|
|
484
|
+
import_cache_manager.CacheModule,
|
|
485
|
+
coreModule
|
|
486
|
+
]
|
|
487
|
+
};
|
|
488
|
+
}
|
|
489
|
+
/**
|
|
490
|
+
* 异步注册(从 ConfigService 等异步源读配置)
|
|
491
|
+
*/
|
|
492
|
+
static forRootAsync(options) {
|
|
493
|
+
const optionsProvider = {
|
|
494
|
+
provide: NESTJS_CACHE_OPTIONS,
|
|
495
|
+
useFactory: /* @__PURE__ */ __name(async (...args) => {
|
|
496
|
+
const raw = await options.useFactory(...args);
|
|
497
|
+
return normalizeOptions(raw);
|
|
498
|
+
}, "useFactory"),
|
|
499
|
+
inject: options.inject ?? []
|
|
500
|
+
};
|
|
501
|
+
const coreModule = NestjsCacheCoreModule.forRoot(optionsProvider, options.imports ?? []);
|
|
502
|
+
return {
|
|
503
|
+
module: _NestjsCacheModule,
|
|
504
|
+
imports: [
|
|
505
|
+
coreModule,
|
|
506
|
+
import_cache_manager.CacheModule.registerAsync({
|
|
507
|
+
isGlobal: true,
|
|
508
|
+
imports: [
|
|
509
|
+
coreModule
|
|
510
|
+
],
|
|
511
|
+
inject: [
|
|
512
|
+
CACHE_STORE_HANDLE
|
|
513
|
+
],
|
|
514
|
+
useFactory: /* @__PURE__ */ __name((handle) => ({
|
|
515
|
+
stores: [
|
|
516
|
+
handle.keyv
|
|
517
|
+
]
|
|
518
|
+
}), "useFactory")
|
|
519
|
+
})
|
|
520
|
+
],
|
|
521
|
+
exports: [
|
|
522
|
+
import_cache_manager.CacheModule,
|
|
523
|
+
coreModule
|
|
524
|
+
]
|
|
525
|
+
};
|
|
526
|
+
}
|
|
527
|
+
};
|
|
528
|
+
NestjsCacheModule = _ts_decorate([
|
|
529
|
+
(0, import_common.Global)(),
|
|
530
|
+
(0, import_common.Module)({}),
|
|
531
|
+
_ts_param(0, (0, import_common.Inject)(CACHE_STORE_HANDLE)),
|
|
532
|
+
_ts_metadata("design:type", Function),
|
|
533
|
+
_ts_metadata("design:paramtypes", [
|
|
534
|
+
typeof CacheStoreHandle === "undefined" ? Object : CacheStoreHandle
|
|
535
|
+
])
|
|
536
|
+
], NestjsCacheModule);
|
|
537
|
+
function normalizeOptions(options) {
|
|
538
|
+
return {
|
|
539
|
+
connectionString: options.connectionString,
|
|
540
|
+
connectionTokenFilePath: options.connectionTokenFilePath,
|
|
541
|
+
connectTimeoutMs: options.connectTimeoutMs ?? DEFAULT_CONNECT_TIMEOUT_MS,
|
|
542
|
+
socketTimeoutMs: options.socketTimeoutMs ?? DEFAULT_SOCKET_TIMEOUT_MS
|
|
543
|
+
};
|
|
544
|
+
}
|
|
545
|
+
__name(normalizeOptions, "normalizeOptions");
|
|
546
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
547
|
+
0 && (module.exports = {
|
|
548
|
+
CACHE_LOGGER_ADAPTER,
|
|
549
|
+
CACHE_OBSERVABLE_ADAPTER,
|
|
550
|
+
CACHE_STORE_HANDLE,
|
|
551
|
+
NESTJS_CACHE_OPTIONS,
|
|
552
|
+
NestjsCacheModule
|
|
553
|
+
});
|
|
554
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/nestjs/index.ts","../../src/nestjs/nestjs-cache.module.ts","../../src/core/cache-store.ts","../../src/core/const.ts","../../src/core/hooks.ts","../../src/nestjs/const.ts"],"sourcesContent":["/**\n * @lark-apaas/cache-service - NestJS 装配层\n *\n * 提供 `NestjsCacheModule.forRoot` / `forRootAsync` 用于 NestJS 应用注册。\n * 装配层会自动桥接 nestjs-observable 与 nestjs-logger,业务侧只需传连接串。\n */\n\nexport {\n NestjsCacheModule,\n type NestjsCacheModuleOptions,\n type NestjsCacheModuleAsyncOptions,\n} from './nestjs-cache.module';\n\nexport {\n NESTJS_CACHE_OPTIONS,\n CACHE_STORE_HANDLE,\n CACHE_OBSERVABLE_ADAPTER,\n CACHE_LOGGER_ADAPTER,\n} from './const';\n","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","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","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","/**\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":["import_keyv","DEFAULT_CONNECT_TIMEOUT_MS","DEFAULT_SOCKET_TIMEOUT_MS","TOKEN_WATCH_INTERVAL_MS","TOKEN_WATCH_DEBOUNCE_MS","CACHE_ERROR_CODE_UNAVAILABLE","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","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","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"]}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { OnApplicationShutdown, DynamicModule, ModuleMetadata, InjectionToken } from '@nestjs/common';
|
|
2
|
+
import { a as CacheStoreHandle } from '../types-CFj87Od0.cjs';
|
|
3
|
+
import 'keyv';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* 用户传入的运行时配置
|
|
7
|
+
*/
|
|
8
|
+
interface NestjsCacheModuleOptions {
|
|
9
|
+
/**
|
|
10
|
+
* Redis 连接串(redis:// 或 rediss://)
|
|
11
|
+
*/
|
|
12
|
+
connectionString: string;
|
|
13
|
+
/**
|
|
14
|
+
* ZTI Token 文件路径。存在则启动 watchFile 热刷新。
|
|
15
|
+
*/
|
|
16
|
+
connectionTokenFilePath?: string;
|
|
17
|
+
/**
|
|
18
|
+
* Socket connect timeout(毫秒)。默认 10_000。
|
|
19
|
+
*/
|
|
20
|
+
connectTimeoutMs?: number;
|
|
21
|
+
/**
|
|
22
|
+
* Socket idle timeout(毫秒)。默认 5_000。
|
|
23
|
+
*/
|
|
24
|
+
socketTimeoutMs?: number;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* 异步配置选项
|
|
28
|
+
*/
|
|
29
|
+
interface NestjsCacheModuleAsyncOptions {
|
|
30
|
+
imports?: ModuleMetadata['imports'];
|
|
31
|
+
useFactory: (...args: unknown[]) => Promise<NestjsCacheModuleOptions> | NestjsCacheModuleOptions;
|
|
32
|
+
inject?: InjectionToken[];
|
|
33
|
+
}
|
|
34
|
+
declare class NestjsCacheModule implements OnApplicationShutdown {
|
|
35
|
+
private readonly storeHandle;
|
|
36
|
+
constructor(storeHandle: CacheStoreHandle);
|
|
37
|
+
onApplicationShutdown(): Promise<void>;
|
|
38
|
+
/**
|
|
39
|
+
* 同步注册
|
|
40
|
+
*/
|
|
41
|
+
static forRoot(options: NestjsCacheModuleOptions): DynamicModule;
|
|
42
|
+
/**
|
|
43
|
+
* 异步注册(从 ConfigService 等异步源读配置)
|
|
44
|
+
*/
|
|
45
|
+
static forRootAsync(options: NestjsCacheModuleAsyncOptions): DynamicModule;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* @lark-apaas/cache-service - NestJS 层 DI tokens
|
|
50
|
+
*/
|
|
51
|
+
declare const NESTJS_CACHE_OPTIONS: unique symbol;
|
|
52
|
+
declare const CACHE_STORE_HANDLE: unique symbol;
|
|
53
|
+
declare const CACHE_OBSERVABLE_ADAPTER: unique symbol;
|
|
54
|
+
declare const CACHE_LOGGER_ADAPTER: unique symbol;
|
|
55
|
+
|
|
56
|
+
export { CACHE_LOGGER_ADAPTER, CACHE_OBSERVABLE_ADAPTER, CACHE_STORE_HANDLE, NESTJS_CACHE_OPTIONS, NestjsCacheModule, type NestjsCacheModuleAsyncOptions, type NestjsCacheModuleOptions };
|