@burdenoff/be-sdk 2026.510.79
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/README.md +173 -0
- package/configs/eslint/index.js +492 -0
- package/configs/tsconfig/tsconfig.json +53 -0
- package/dist/EventQueueFactory-6NMSGECR.js +8 -0
- package/dist/EventQueueFactory-6NMSGECR.js.map +1 -0
- package/dist/auth/index.d.ts +66 -0
- package/dist/auth/index.js +8 -0
- package/dist/auth/index.js.map +1 -0
- package/dist/cache/index.d.ts +2 -0
- package/dist/cache/index.js +6 -0
- package/dist/cache/index.js.map +1 -0
- package/dist/captcha/index.d.ts +1 -0
- package/dist/captcha/index.js +4 -0
- package/dist/captcha/index.js.map +1 -0
- package/dist/chunk-2HANKZZM.js +245 -0
- package/dist/chunk-2HANKZZM.js.map +1 -0
- package/dist/chunk-2SD5L2F2.js +816 -0
- package/dist/chunk-2SD5L2F2.js.map +1 -0
- package/dist/chunk-3O2RQOPV.js +1068 -0
- package/dist/chunk-3O2RQOPV.js.map +1 -0
- package/dist/chunk-4B43DZIS.js +560 -0
- package/dist/chunk-4B43DZIS.js.map +1 -0
- package/dist/chunk-4XAEES25.js +2444 -0
- package/dist/chunk-4XAEES25.js.map +1 -0
- package/dist/chunk-4XGGWW36.js +1405 -0
- package/dist/chunk-4XGGWW36.js.map +1 -0
- package/dist/chunk-5LXDPGDH.js +245 -0
- package/dist/chunk-5LXDPGDH.js.map +1 -0
- package/dist/chunk-AI7L22ZJ.js +472 -0
- package/dist/chunk-AI7L22ZJ.js.map +1 -0
- package/dist/chunk-CBZNP6RF.js +286 -0
- package/dist/chunk-CBZNP6RF.js.map +1 -0
- package/dist/chunk-ELLX3K54.js +613 -0
- package/dist/chunk-ELLX3K54.js.map +1 -0
- package/dist/chunk-F5XEAGHQ.js +1875 -0
- package/dist/chunk-F5XEAGHQ.js.map +1 -0
- package/dist/chunk-FNZEQOJY.js +628 -0
- package/dist/chunk-FNZEQOJY.js.map +1 -0
- package/dist/chunk-GTEMQXCA.js +516 -0
- package/dist/chunk-GTEMQXCA.js.map +1 -0
- package/dist/chunk-HIWTWIGQ.js +226 -0
- package/dist/chunk-HIWTWIGQ.js.map +1 -0
- package/dist/chunk-IYE2NTRX.js +764 -0
- package/dist/chunk-IYE2NTRX.js.map +1 -0
- package/dist/chunk-J26YGR2U.js +1484 -0
- package/dist/chunk-J26YGR2U.js.map +1 -0
- package/dist/chunk-JG2FTZPI.js +509 -0
- package/dist/chunk-JG2FTZPI.js.map +1 -0
- package/dist/chunk-JVBTNSPG.js +1551 -0
- package/dist/chunk-JVBTNSPG.js.map +1 -0
- package/dist/chunk-L7ZMN2FJ.js +12 -0
- package/dist/chunk-L7ZMN2FJ.js.map +1 -0
- package/dist/chunk-M5IMU3US.js +473 -0
- package/dist/chunk-M5IMU3US.js.map +1 -0
- package/dist/chunk-MLW2HPS5.js +1267 -0
- package/dist/chunk-MLW2HPS5.js.map +1 -0
- package/dist/chunk-MNCGUYLJ.js +269 -0
- package/dist/chunk-MNCGUYLJ.js.map +1 -0
- package/dist/chunk-MOCUBSTW.js +373 -0
- package/dist/chunk-MOCUBSTW.js.map +1 -0
- package/dist/chunk-NGMEI42S.js +103 -0
- package/dist/chunk-NGMEI42S.js.map +1 -0
- package/dist/chunk-NUB5GQTU.js +270 -0
- package/dist/chunk-NUB5GQTU.js.map +1 -0
- package/dist/chunk-OVSYMNCI.js +145 -0
- package/dist/chunk-OVSYMNCI.js.map +1 -0
- package/dist/chunk-QLVKMWFA.js +94 -0
- package/dist/chunk-QLVKMWFA.js.map +1 -0
- package/dist/chunk-SAG6ZYWQ.js +89 -0
- package/dist/chunk-SAG6ZYWQ.js.map +1 -0
- package/dist/chunk-SAK2JCA7.js +198 -0
- package/dist/chunk-SAK2JCA7.js.map +1 -0
- package/dist/chunk-SBS4S2PJ.js +100 -0
- package/dist/chunk-SBS4S2PJ.js.map +1 -0
- package/dist/chunk-SNEOLWXT.js +148 -0
- package/dist/chunk-SNEOLWXT.js.map +1 -0
- package/dist/chunk-TKBD2ONF.js +564 -0
- package/dist/chunk-TKBD2ONF.js.map +1 -0
- package/dist/chunk-UBXFIIAW.js +127 -0
- package/dist/chunk-UBXFIIAW.js.map +1 -0
- package/dist/chunk-VI4VSNTI.js +57 -0
- package/dist/chunk-VI4VSNTI.js.map +1 -0
- package/dist/chunk-VWCN5PMD.js +268 -0
- package/dist/chunk-VWCN5PMD.js.map +1 -0
- package/dist/chunk-XC2YD6CF.js +913 -0
- package/dist/chunk-XC2YD6CF.js.map +1 -0
- package/dist/chunk-XE4OXN2W.js +10 -0
- package/dist/chunk-XE4OXN2W.js.map +1 -0
- package/dist/chunk-YMZEUTD2.js +110 -0
- package/dist/chunk-YMZEUTD2.js.map +1 -0
- package/dist/chunk-Z4IRLBWO.js +758 -0
- package/dist/chunk-Z4IRLBWO.js.map +1 -0
- package/dist/client/internal-client.d.ts +7 -0
- package/dist/client/internal-client.js +5 -0
- package/dist/client/internal-client.js.map +1 -0
- package/dist/client-CSjIrYKS.d.ts +77 -0
- package/dist/config-UKE2E3BW.js +4 -0
- package/dist/config-UKE2E3BW.js.map +1 -0
- package/dist/crypto/index.d.ts +1 -0
- package/dist/crypto/index.js +4 -0
- package/dist/crypto/index.js.map +1 -0
- package/dist/database/index.d.ts +117 -0
- package/dist/database/index.js +7 -0
- package/dist/database/index.js.map +1 -0
- package/dist/dataloader/index.d.ts +1 -0
- package/dist/dataloader/index.js +4 -0
- package/dist/dataloader/index.js.map +1 -0
- package/dist/esm-K6H7EIWK.js +1450 -0
- package/dist/esm-K6H7EIWK.js.map +1 -0
- package/dist/event-queue/index.d.ts +6 -0
- package/dist/event-queue/index.js +10 -0
- package/dist/event-queue/index.js.map +1 -0
- package/dist/export/index.d.ts +2 -0
- package/dist/export/index.js +9 -0
- package/dist/export/index.js.map +1 -0
- package/dist/feature-flags/index.d.ts +1 -0
- package/dist/feature-flags/index.js +4 -0
- package/dist/feature-flags/index.js.map +1 -0
- package/dist/gateway/index.d.ts +4 -0
- package/dist/gateway/index.js +8 -0
- package/dist/gateway/index.js.map +1 -0
- package/dist/gateway/security/index.d.ts +2 -0
- package/dist/gateway/security/index.js +5 -0
- package/dist/gateway/security/index.js.map +1 -0
- package/dist/graph/index.d.ts +9 -0
- package/dist/graph/index.js +13 -0
- package/dist/graph/index.js.map +1 -0
- package/dist/graph-emitter-Bk0FqD53.d.ts +47 -0
- package/dist/graphql/index.d.ts +2 -0
- package/dist/graphql/index.js +7 -0
- package/dist/graphql/index.js.map +1 -0
- package/dist/graphql/services/index.d.ts +24 -0
- package/dist/graphql/services/index.js +5 -0
- package/dist/graphql/services/index.js.map +1 -0
- package/dist/graphql-BAVrUgBF.d.ts +22 -0
- package/dist/i18n/index.d.ts +2 -0
- package/dist/i18n/index.js +5 -0
- package/dist/i18n/index.js.map +1 -0
- package/dist/i18n-DA6IPnYy.d.ts +101 -0
- package/dist/index-4gKQKZ2B.d.ts +71 -0
- package/dist/index-B18rrFru.d.ts +42 -0
- package/dist/index-B70b5mYS.d.ts +33 -0
- package/dist/index-BMq3lFjj.d.ts +247 -0
- package/dist/index-BODgvRtL.d.ts +316 -0
- package/dist/index-BS2idbnr.d.ts +874 -0
- package/dist/index-BV3G48cH.d.ts +124 -0
- package/dist/index-CHbVw5M7.d.ts +73 -0
- package/dist/index-Cq_xNjcv.d.ts +602 -0
- package/dist/index-CrQgidpx.d.ts +77 -0
- package/dist/index-CyVEg86m.d.ts +309 -0
- package/dist/index-DOcBnmMe.d.ts +166 -0
- package/dist/index-DSOpj8DI.d.ts +17 -0
- package/dist/index-DW9q22GH.d.ts +75 -0
- package/dist/index-DlFDFyWD.d.ts +55 -0
- package/dist/index-DoK0bjhH.d.ts +607 -0
- package/dist/index-Dtf6FBaD.d.ts +199 -0
- package/dist/index-KVWGHMbS.d.ts +415 -0
- package/dist/index-PaojfSdd.d.ts +71 -0
- package/dist/index-RU_M5c74.d.ts +163 -0
- package/dist/index-UhkH2eaB.d.ts +192 -0
- package/dist/index-e_18DzAg.d.ts +18 -0
- package/dist/index-jOuh_TKn.d.ts +59 -0
- package/dist/index.d.ts +584 -0
- package/dist/index.js +2626 -0
- package/dist/index.js.map +1 -0
- package/dist/init/index.d.ts +2 -0
- package/dist/init/index.js +12 -0
- package/dist/init/index.js.map +1 -0
- package/dist/instrumentation/index.d.ts +3 -0
- package/dist/instrumentation/index.js +5 -0
- package/dist/instrumentation/index.js.map +1 -0
- package/dist/layer/index.d.ts +2 -0
- package/dist/layer/index.js +6 -0
- package/dist/layer/index.js.map +1 -0
- package/dist/layer-DDip18KY.d.ts +67 -0
- package/dist/logger/index.d.ts +2 -0
- package/dist/logger/index.js +4 -0
- package/dist/logger/index.js.map +1 -0
- package/dist/middleware/index.d.ts +3 -0
- package/dist/middleware/index.js +5 -0
- package/dist/middleware/index.js.map +1 -0
- package/dist/notifications/index.d.ts +4 -0
- package/dist/notifications/index.js +7 -0
- package/dist/notifications/index.js.map +1 -0
- package/dist/observability/index.d.ts +3 -0
- package/dist/observability/index.js +5 -0
- package/dist/observability/index.js.map +1 -0
- package/dist/pino-logger-RYLGXPHZ.js +5 -0
- package/dist/pino-logger-RYLGXPHZ.js.map +1 -0
- package/dist/prisma/generator/index.d.ts +1 -0
- package/dist/prisma/generator/index.js +228 -0
- package/dist/prisma/generator/index.js.map +1 -0
- package/dist/prisma/index.d.ts +5 -0
- package/dist/prisma/index.js +12 -0
- package/dist/prisma/index.js.map +1 -0
- package/dist/prisma-extension-krXnYnrN.d.ts +30 -0
- package/dist/rbac/index.d.ts +116 -0
- package/dist/rbac/index.js +7 -0
- package/dist/rbac/index.js.map +1 -0
- package/dist/sdk-THO3IWEE.js +136 -0
- package/dist/sdk-THO3IWEE.js.map +1 -0
- package/dist/server/index.d.ts +359 -0
- package/dist/server/index.js +884 -0
- package/dist/server/index.js.map +1 -0
- package/dist/telemetry/index.d.ts +83 -0
- package/dist/telemetry/index.js +209 -0
- package/dist/telemetry/index.js.map +1 -0
- package/dist/temporal/index.d.ts +148 -0
- package/dist/temporal/index.js +6 -0
- package/dist/temporal/index.js.map +1 -0
- package/dist/tracker/index.d.ts +5 -0
- package/dist/tracker/index.js +9 -0
- package/dist/tracker/index.js.map +1 -0
- package/dist/types-Bo2wkjSP.d.ts +35 -0
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/index.js +4 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/workspace-guard-LHBNK4TG.js +5 -0
- package/dist/workspace-guard-LHBNK4TG.js.map +1 -0
- package/package.json +241 -0
|
@@ -0,0 +1,1405 @@
|
|
|
1
|
+
import { DEFAULT_LAYER_RESOURCES } from './chunk-NGMEI42S.js';
|
|
2
|
+
import { logger } from './chunk-CBZNP6RF.js';
|
|
3
|
+
import { __export, __name } from './chunk-XE4OXN2W.js';
|
|
4
|
+
|
|
5
|
+
// src/cache/index.ts
|
|
6
|
+
var cache_exports = {};
|
|
7
|
+
__export(cache_exports, {
|
|
8
|
+
CacheFactory: () => CacheFactory,
|
|
9
|
+
CacheProviderType: () => CacheProviderType,
|
|
10
|
+
MemoryCacheProvider: () => MemoryCacheProvider,
|
|
11
|
+
ValkeyGlideProvider: () => ValkeyGlideProvider,
|
|
12
|
+
closeAllCaches: () => closeAllCaches,
|
|
13
|
+
createCache: () => createCache,
|
|
14
|
+
getCache: () => getCache,
|
|
15
|
+
getValkeyClient: () => getValkeyClient
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
// src/cache/implementations/memory/MemoryCacheProvider.ts
|
|
19
|
+
var MemoryCacheProvider = class {
|
|
20
|
+
static {
|
|
21
|
+
__name(this, "MemoryCacheProvider");
|
|
22
|
+
}
|
|
23
|
+
cache = /* @__PURE__ */ new Map();
|
|
24
|
+
namespace;
|
|
25
|
+
defaultTTL;
|
|
26
|
+
maxSize;
|
|
27
|
+
enableLogging;
|
|
28
|
+
connected = false;
|
|
29
|
+
// Statistics
|
|
30
|
+
stats = {
|
|
31
|
+
hits: 0,
|
|
32
|
+
misses: 0,
|
|
33
|
+
sets: 0,
|
|
34
|
+
deletes: 0,
|
|
35
|
+
evictions: 0
|
|
36
|
+
};
|
|
37
|
+
constructor(config, namespace = "default") {
|
|
38
|
+
this.namespace = namespace;
|
|
39
|
+
this.defaultTTL = config.defaultTTL || 300;
|
|
40
|
+
this.maxSize = config.maxSize || 1e3;
|
|
41
|
+
this.enableLogging = config.enableLogging ?? false;
|
|
42
|
+
this.startCleanupInterval();
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Connect (no-op for memory cache)
|
|
46
|
+
*/
|
|
47
|
+
async connect() {
|
|
48
|
+
await Promise.resolve();
|
|
49
|
+
this.connected = true;
|
|
50
|
+
if (this.enableLogging) {
|
|
51
|
+
logger.info(`[MemoryCacheProvider:${this.namespace}] Connected (in-memory)`);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Disconnect (cleanup)
|
|
56
|
+
*/
|
|
57
|
+
async disconnect() {
|
|
58
|
+
await Promise.resolve();
|
|
59
|
+
this.connected = false;
|
|
60
|
+
this.cache.clear();
|
|
61
|
+
if (this.enableLogging) {
|
|
62
|
+
logger.info(`[MemoryCacheProvider:${this.namespace}] Disconnected`);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Check if connected
|
|
67
|
+
*/
|
|
68
|
+
isConnected() {
|
|
69
|
+
return this.connected;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Health check
|
|
73
|
+
*/
|
|
74
|
+
async healthCheck() {
|
|
75
|
+
await Promise.resolve();
|
|
76
|
+
return {
|
|
77
|
+
status: "healthy",
|
|
78
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
79
|
+
connected: this.connected,
|
|
80
|
+
latencyMs: 0,
|
|
81
|
+
details: {
|
|
82
|
+
provider: "memory",
|
|
83
|
+
cacheSize: this.cache.size,
|
|
84
|
+
maxSize: this.maxSize
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Get statistics
|
|
90
|
+
*/
|
|
91
|
+
async getStatistics() {
|
|
92
|
+
await Promise.resolve();
|
|
93
|
+
const totalOperations = this.stats.hits + this.stats.misses;
|
|
94
|
+
const hitRate = totalOperations > 0 ? this.stats.hits / totalOperations : 0;
|
|
95
|
+
const memoryUsed = this.cache.size * 500;
|
|
96
|
+
return {
|
|
97
|
+
hits: this.stats.hits,
|
|
98
|
+
misses: this.stats.misses,
|
|
99
|
+
hitRate,
|
|
100
|
+
keyCount: this.cache.size,
|
|
101
|
+
memoryUsed,
|
|
102
|
+
totalOperations,
|
|
103
|
+
details: {
|
|
104
|
+
sets: this.stats.sets,
|
|
105
|
+
deletes: this.stats.deletes,
|
|
106
|
+
evictions: this.stats.evictions,
|
|
107
|
+
maxSize: this.maxSize
|
|
108
|
+
}
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Build cache key with namespace
|
|
113
|
+
*/
|
|
114
|
+
buildKey(key) {
|
|
115
|
+
return `${this.namespace}:${key}`;
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Check if entry is expired
|
|
119
|
+
*/
|
|
120
|
+
isExpired(expiresAt) {
|
|
121
|
+
return Date.now() > expiresAt;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Evict LRU entry if cache is full
|
|
125
|
+
*/
|
|
126
|
+
evictIfNeeded() {
|
|
127
|
+
if (this.cache.size >= this.maxSize) {
|
|
128
|
+
const firstKey = this.cache.keys().next().value;
|
|
129
|
+
if (firstKey) {
|
|
130
|
+
this.cache.delete(firstKey);
|
|
131
|
+
this.stats.evictions++;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Cleanup expired entries
|
|
137
|
+
*/
|
|
138
|
+
cleanup() {
|
|
139
|
+
let cleaned = 0;
|
|
140
|
+
for (const [key, value] of this.cache.entries()) {
|
|
141
|
+
if (this.isExpired(value.expiresAt)) {
|
|
142
|
+
this.cache.delete(key);
|
|
143
|
+
cleaned++;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
if (cleaned > 0 && this.enableLogging) {
|
|
147
|
+
logger.debug(`[MemoryCacheProvider:${this.namespace}] Cleaned up ${cleaned} expired entries`);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Start cleanup interval
|
|
152
|
+
*/
|
|
153
|
+
startCleanupInterval() {
|
|
154
|
+
setInterval(() => this.cleanup(), 6e4);
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Get value from cache
|
|
158
|
+
*/
|
|
159
|
+
async get(key, _options) {
|
|
160
|
+
await Promise.resolve();
|
|
161
|
+
const cacheKey = this.buildKey(key);
|
|
162
|
+
const cached = this.cache.get(cacheKey);
|
|
163
|
+
if (!cached) {
|
|
164
|
+
this.stats.misses++;
|
|
165
|
+
if (this.enableLogging) {
|
|
166
|
+
logger.debug(`[MemoryCacheProvider:${this.namespace}] Cache miss`, {
|
|
167
|
+
key
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
return null;
|
|
171
|
+
}
|
|
172
|
+
if (this.isExpired(cached.expiresAt)) {
|
|
173
|
+
this.cache.delete(cacheKey);
|
|
174
|
+
this.stats.misses++;
|
|
175
|
+
if (this.enableLogging) {
|
|
176
|
+
logger.debug(`[MemoryCacheProvider:${this.namespace}] Cache miss (expired)`, {
|
|
177
|
+
key
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
return null;
|
|
181
|
+
}
|
|
182
|
+
this.stats.hits++;
|
|
183
|
+
if (this.enableLogging) {
|
|
184
|
+
logger.debug(`[MemoryCacheProvider:${this.namespace}] Cache hit`, {
|
|
185
|
+
key
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
this.cache.delete(cacheKey);
|
|
189
|
+
this.cache.set(cacheKey, cached);
|
|
190
|
+
return cached.entry.data;
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Set value in cache
|
|
194
|
+
*/
|
|
195
|
+
async set(key, value, options) {
|
|
196
|
+
await Promise.resolve();
|
|
197
|
+
const cacheKey = this.buildKey(key);
|
|
198
|
+
const ttlSeconds = options?.ttl || this.defaultTTL;
|
|
199
|
+
this.evictIfNeeded();
|
|
200
|
+
const entry = {
|
|
201
|
+
data: value,
|
|
202
|
+
timestamp: Date.now(),
|
|
203
|
+
ttl: ttlSeconds,
|
|
204
|
+
metadata: options?.metadata
|
|
205
|
+
};
|
|
206
|
+
this.cache.set(cacheKey, {
|
|
207
|
+
entry,
|
|
208
|
+
expiresAt: Date.now() + ttlSeconds * 1e3
|
|
209
|
+
});
|
|
210
|
+
this.stats.sets++;
|
|
211
|
+
if (this.enableLogging) {
|
|
212
|
+
logger.debug(`[MemoryCacheProvider:${this.namespace}] Cache set`, {
|
|
213
|
+
key,
|
|
214
|
+
ttl: ttlSeconds
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
return true;
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* Delete value from cache
|
|
221
|
+
*/
|
|
222
|
+
async delete(key) {
|
|
223
|
+
await Promise.resolve();
|
|
224
|
+
const cacheKey = this.buildKey(key);
|
|
225
|
+
const deleted = this.cache.delete(cacheKey);
|
|
226
|
+
if (deleted) {
|
|
227
|
+
this.stats.deletes++;
|
|
228
|
+
if (this.enableLogging) {
|
|
229
|
+
logger.debug(`[MemoryCacheProvider:${this.namespace}] Cache delete`, {
|
|
230
|
+
key
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
return deleted;
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* Check if key exists
|
|
238
|
+
*/
|
|
239
|
+
async has(key) {
|
|
240
|
+
await Promise.resolve();
|
|
241
|
+
const cacheKey = this.buildKey(key);
|
|
242
|
+
const cached = this.cache.get(cacheKey);
|
|
243
|
+
if (!cached) {
|
|
244
|
+
return false;
|
|
245
|
+
}
|
|
246
|
+
if (this.isExpired(cached.expiresAt)) {
|
|
247
|
+
this.cache.delete(cacheKey);
|
|
248
|
+
return false;
|
|
249
|
+
}
|
|
250
|
+
return true;
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* Get TTL for key
|
|
254
|
+
*/
|
|
255
|
+
async ttl(key) {
|
|
256
|
+
await Promise.resolve();
|
|
257
|
+
const cacheKey = this.buildKey(key);
|
|
258
|
+
const cached = this.cache.get(cacheKey);
|
|
259
|
+
if (!cached) {
|
|
260
|
+
return -2;
|
|
261
|
+
}
|
|
262
|
+
const remaining = Math.floor((cached.expiresAt - Date.now()) / 1e3);
|
|
263
|
+
if (remaining <= 0) {
|
|
264
|
+
this.cache.delete(cacheKey);
|
|
265
|
+
return -2;
|
|
266
|
+
}
|
|
267
|
+
return remaining;
|
|
268
|
+
}
|
|
269
|
+
/**
|
|
270
|
+
* Update TTL for key
|
|
271
|
+
*/
|
|
272
|
+
async expire(key, ttl) {
|
|
273
|
+
await Promise.resolve();
|
|
274
|
+
const cacheKey = this.buildKey(key);
|
|
275
|
+
const cached = this.cache.get(cacheKey);
|
|
276
|
+
if (!cached) {
|
|
277
|
+
return false;
|
|
278
|
+
}
|
|
279
|
+
cached.expiresAt = Date.now() + ttl * 1e3;
|
|
280
|
+
cached.entry.ttl = ttl;
|
|
281
|
+
return true;
|
|
282
|
+
}
|
|
283
|
+
/**
|
|
284
|
+
* Get multiple values (batch operation)
|
|
285
|
+
*/
|
|
286
|
+
async mget(keys, _options) {
|
|
287
|
+
const result = /* @__PURE__ */ new Map();
|
|
288
|
+
for (const key of keys) {
|
|
289
|
+
const value = await this.get(key);
|
|
290
|
+
if (value !== null) {
|
|
291
|
+
result.set(key, value);
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
return result;
|
|
295
|
+
}
|
|
296
|
+
/**
|
|
297
|
+
* Set multiple values (batch operation)
|
|
298
|
+
*/
|
|
299
|
+
async mset(entries, options) {
|
|
300
|
+
for (const [key, value] of entries) {
|
|
301
|
+
await this.set(key, value, options);
|
|
302
|
+
}
|
|
303
|
+
return true;
|
|
304
|
+
}
|
|
305
|
+
/**
|
|
306
|
+
* Delete multiple keys (batch operation)
|
|
307
|
+
*/
|
|
308
|
+
async mdel(keys) {
|
|
309
|
+
let deleted = 0;
|
|
310
|
+
for (const key of keys) {
|
|
311
|
+
const result = await this.delete(key);
|
|
312
|
+
if (result) {
|
|
313
|
+
deleted++;
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
return deleted;
|
|
317
|
+
}
|
|
318
|
+
/**
|
|
319
|
+
* Match key against pattern (supports * wildcard)
|
|
320
|
+
*/
|
|
321
|
+
matchPattern(key, pattern) {
|
|
322
|
+
if (!pattern.includes("*")) {
|
|
323
|
+
return key === pattern;
|
|
324
|
+
}
|
|
325
|
+
const parts = pattern.split("*");
|
|
326
|
+
let position = 0;
|
|
327
|
+
for (let i = 0; i < parts.length; i++) {
|
|
328
|
+
const part = parts[i];
|
|
329
|
+
if (!part || part === "") continue;
|
|
330
|
+
const index = key.indexOf(part, position);
|
|
331
|
+
if (index === -1) return false;
|
|
332
|
+
if (i === 0 && index !== 0) return false;
|
|
333
|
+
position = index + part.length;
|
|
334
|
+
}
|
|
335
|
+
const lastPart = parts[parts.length - 1];
|
|
336
|
+
return !lastPart || lastPart === "" || key.endsWith(lastPart);
|
|
337
|
+
}
|
|
338
|
+
/**
|
|
339
|
+
* Clear keys by pattern
|
|
340
|
+
*/
|
|
341
|
+
async clearPattern(pattern) {
|
|
342
|
+
await Promise.resolve();
|
|
343
|
+
const searchPattern = this.buildKey(pattern);
|
|
344
|
+
let deleted = 0;
|
|
345
|
+
for (const key of this.cache.keys()) {
|
|
346
|
+
if (this.matchPattern(key, searchPattern)) {
|
|
347
|
+
this.cache.delete(key);
|
|
348
|
+
deleted++;
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
if (this.enableLogging) {
|
|
352
|
+
logger.debug(`[MemoryCacheProvider:${this.namespace}] Pattern cleared`, {
|
|
353
|
+
pattern,
|
|
354
|
+
deleted
|
|
355
|
+
});
|
|
356
|
+
}
|
|
357
|
+
return deleted;
|
|
358
|
+
}
|
|
359
|
+
/**
|
|
360
|
+
* Get keys by pattern
|
|
361
|
+
*/
|
|
362
|
+
async keys(options) {
|
|
363
|
+
await Promise.resolve();
|
|
364
|
+
const searchPattern = this.buildKey(options.pattern);
|
|
365
|
+
const matchedKeys = [];
|
|
366
|
+
const prefix = `${this.namespace}:`;
|
|
367
|
+
for (const key of this.cache.keys()) {
|
|
368
|
+
if (this.matchPattern(key, searchPattern)) {
|
|
369
|
+
matchedKeys.push(key.replace(prefix, ""));
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
const limitedKeys = options.limit ? matchedKeys.slice(0, options.limit) : matchedKeys;
|
|
373
|
+
return {
|
|
374
|
+
keys: limitedKeys,
|
|
375
|
+
nextCursor: null,
|
|
376
|
+
totalCount: matchedKeys.length
|
|
377
|
+
};
|
|
378
|
+
}
|
|
379
|
+
/**
|
|
380
|
+
* Clear all keys in namespace
|
|
381
|
+
*/
|
|
382
|
+
async clear() {
|
|
383
|
+
await Promise.resolve();
|
|
384
|
+
const prefix = `${this.namespace}:`;
|
|
385
|
+
let cleared = 0;
|
|
386
|
+
for (const key of Array.from(this.cache.keys())) {
|
|
387
|
+
if (key.startsWith(prefix)) {
|
|
388
|
+
this.cache.delete(key);
|
|
389
|
+
cleared++;
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
if (this.enableLogging) {
|
|
393
|
+
logger.info(`[MemoryCacheProvider:${this.namespace}] Cleared ${cleared} keys`);
|
|
394
|
+
}
|
|
395
|
+
return true;
|
|
396
|
+
}
|
|
397
|
+
/**
|
|
398
|
+
* Increment numeric value
|
|
399
|
+
*/
|
|
400
|
+
async increment(key, amount = 1) {
|
|
401
|
+
const current = await this.get(key);
|
|
402
|
+
const newValue = (current || 0) + amount;
|
|
403
|
+
await this.set(key, newValue);
|
|
404
|
+
return newValue;
|
|
405
|
+
}
|
|
406
|
+
/**
|
|
407
|
+
* Decrement numeric value
|
|
408
|
+
*/
|
|
409
|
+
async decrement(key, amount = 1) {
|
|
410
|
+
const current = await this.get(key);
|
|
411
|
+
const newValue = (current || 0) - amount;
|
|
412
|
+
await this.set(key, newValue);
|
|
413
|
+
return newValue;
|
|
414
|
+
}
|
|
415
|
+
/**
|
|
416
|
+
* Get or set value
|
|
417
|
+
*/
|
|
418
|
+
async getOrSet(key, fetcher, options) {
|
|
419
|
+
const cached = await this.get(key, options);
|
|
420
|
+
if (cached !== null) {
|
|
421
|
+
return cached;
|
|
422
|
+
}
|
|
423
|
+
const value = await fetcher();
|
|
424
|
+
await this.set(key, value, options);
|
|
425
|
+
return value;
|
|
426
|
+
}
|
|
427
|
+
/**
|
|
428
|
+
* Invalidate by tags
|
|
429
|
+
*/
|
|
430
|
+
async invalidateTags(tags) {
|
|
431
|
+
let totalDeleted = 0;
|
|
432
|
+
for (const tag of tags) {
|
|
433
|
+
const pattern = `*:tag:${tag}:*`;
|
|
434
|
+
const deleted = await this.clearPattern(pattern);
|
|
435
|
+
totalDeleted += deleted;
|
|
436
|
+
}
|
|
437
|
+
return totalDeleted;
|
|
438
|
+
}
|
|
439
|
+
};
|
|
440
|
+
|
|
441
|
+
// src/cache/implementations/valkey/ValkeyGlideProvider.ts
|
|
442
|
+
var ValkeyGlideProvider = class {
|
|
443
|
+
static {
|
|
444
|
+
__name(this, "ValkeyGlideProvider");
|
|
445
|
+
}
|
|
446
|
+
client = null;
|
|
447
|
+
clusterScanCursor = null;
|
|
448
|
+
clientIsCluster = false;
|
|
449
|
+
connected = false;
|
|
450
|
+
config;
|
|
451
|
+
namespace;
|
|
452
|
+
defaultTTL;
|
|
453
|
+
enableLogging;
|
|
454
|
+
// Statistics
|
|
455
|
+
stats = {
|
|
456
|
+
hits: 0,
|
|
457
|
+
misses: 0,
|
|
458
|
+
sets: 0,
|
|
459
|
+
deletes: 0,
|
|
460
|
+
errors: 0
|
|
461
|
+
};
|
|
462
|
+
constructor(config, namespace = "default") {
|
|
463
|
+
this.config = config;
|
|
464
|
+
this.namespace = namespace;
|
|
465
|
+
this.defaultTTL = config.defaultTTL || 300;
|
|
466
|
+
this.enableLogging = config.enableLogging ?? process.env["NODE_ENV"] === "development";
|
|
467
|
+
}
|
|
468
|
+
async serverReportsCluster(client) {
|
|
469
|
+
try {
|
|
470
|
+
const info = await client.info("cluster");
|
|
471
|
+
return /(?:^|\n)cluster_enabled:1(?:\r?\n|$)/.test(info);
|
|
472
|
+
} catch {
|
|
473
|
+
return false;
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
async scanClusterKeys(pattern) {
|
|
477
|
+
if (!this.client || !this.clientIsCluster || !this.clusterScanCursor) {
|
|
478
|
+
return [];
|
|
479
|
+
}
|
|
480
|
+
const clusterClient = this.client;
|
|
481
|
+
let cursor = new this.clusterScanCursor();
|
|
482
|
+
const keys = [];
|
|
483
|
+
do {
|
|
484
|
+
const [nextCursor, batch] = await clusterClient.scan(cursor, {
|
|
485
|
+
match: pattern,
|
|
486
|
+
count: 1e3
|
|
487
|
+
});
|
|
488
|
+
keys.push(...batch);
|
|
489
|
+
cursor = nextCursor;
|
|
490
|
+
} while (!cursor.isFinished());
|
|
491
|
+
return keys;
|
|
492
|
+
}
|
|
493
|
+
async getKeys(pattern) {
|
|
494
|
+
if (!this.client) {
|
|
495
|
+
throw new Error("Not connected");
|
|
496
|
+
}
|
|
497
|
+
if (this.clientIsCluster) {
|
|
498
|
+
return this.scanClusterKeys(pattern);
|
|
499
|
+
}
|
|
500
|
+
if (!this.client.keys) {
|
|
501
|
+
throw new Error("KEYS command is not available on this Valkey client");
|
|
502
|
+
}
|
|
503
|
+
return this.client.keys(pattern);
|
|
504
|
+
}
|
|
505
|
+
/**
|
|
506
|
+
* Connect to Valkey server using @valkey/valkey-glide
|
|
507
|
+
*/
|
|
508
|
+
async connect() {
|
|
509
|
+
if (this.connected && this.client) {
|
|
510
|
+
return;
|
|
511
|
+
}
|
|
512
|
+
try {
|
|
513
|
+
const glideModule = await import('@valkey/valkey-glide');
|
|
514
|
+
const GlideClient = glideModule.GlideClient || glideModule.default;
|
|
515
|
+
const GlideClusterClient = glideModule.GlideClusterClient;
|
|
516
|
+
this.clusterScanCursor = glideModule.ClusterScanCursor ?? null;
|
|
517
|
+
if (!GlideClient) {
|
|
518
|
+
throw new Error("Valkey GLIDE standalone client is unavailable");
|
|
519
|
+
}
|
|
520
|
+
let host = this.config.host || "localhost";
|
|
521
|
+
let port = this.config.port || 6379;
|
|
522
|
+
let password = this.config.password;
|
|
523
|
+
let username = this.config.username;
|
|
524
|
+
if (this.config.url) {
|
|
525
|
+
const url = new URL(this.config.url.replace("valkey://", "redis://"));
|
|
526
|
+
host = url.hostname;
|
|
527
|
+
port = parseInt(url.port || "6379", 10);
|
|
528
|
+
if (!password && url.password) {
|
|
529
|
+
try {
|
|
530
|
+
password = decodeURIComponent(url.password);
|
|
531
|
+
} catch {
|
|
532
|
+
password = url.password;
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
if (!username && url.username) {
|
|
536
|
+
try {
|
|
537
|
+
username = decodeURIComponent(url.username);
|
|
538
|
+
} catch {
|
|
539
|
+
username = url.username;
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
const addresses = this.config.clusterNodes?.length ? this.config.clusterNodes : [
|
|
544
|
+
{
|
|
545
|
+
host,
|
|
546
|
+
port
|
|
547
|
+
}
|
|
548
|
+
];
|
|
549
|
+
const baseClientConfig = {
|
|
550
|
+
addresses,
|
|
551
|
+
useTLS: this.config.useTLS,
|
|
552
|
+
credentials: password || username ? {
|
|
553
|
+
password,
|
|
554
|
+
username
|
|
555
|
+
} : void 0,
|
|
556
|
+
clientName: this.config.name || `valkey-glide-${this.namespace}`,
|
|
557
|
+
databaseId: this.config.db,
|
|
558
|
+
connectionTimeout: this.config.connectionTimeout,
|
|
559
|
+
readFrom: "primary"
|
|
560
|
+
};
|
|
561
|
+
if (this.config.cluster === true) {
|
|
562
|
+
if (!GlideClusterClient) {
|
|
563
|
+
throw new Error("Valkey GLIDE cluster client is unavailable");
|
|
564
|
+
}
|
|
565
|
+
this.client = await GlideClusterClient.createClient({
|
|
566
|
+
...baseClientConfig,
|
|
567
|
+
periodicChecks: {
|
|
568
|
+
duration_in_sec: 30
|
|
569
|
+
},
|
|
570
|
+
advancedConfiguration: {
|
|
571
|
+
refreshTopologyFromInitialNodes: true
|
|
572
|
+
}
|
|
573
|
+
});
|
|
574
|
+
this.clientIsCluster = true;
|
|
575
|
+
} else {
|
|
576
|
+
const standaloneClient = await GlideClient.createClient(baseClientConfig);
|
|
577
|
+
if (this.config.cluster !== false && await this.serverReportsCluster(standaloneClient)) {
|
|
578
|
+
if (!GlideClusterClient) {
|
|
579
|
+
throw new Error("Valkey server is clustered but GLIDE cluster client is unavailable");
|
|
580
|
+
}
|
|
581
|
+
await standaloneClient.close();
|
|
582
|
+
this.client = await GlideClusterClient.createClient({
|
|
583
|
+
...baseClientConfig,
|
|
584
|
+
periodicChecks: {
|
|
585
|
+
duration_in_sec: 30
|
|
586
|
+
},
|
|
587
|
+
advancedConfiguration: {
|
|
588
|
+
refreshTopologyFromInitialNodes: true
|
|
589
|
+
}
|
|
590
|
+
});
|
|
591
|
+
this.clientIsCluster = true;
|
|
592
|
+
} else {
|
|
593
|
+
this.client = standaloneClient;
|
|
594
|
+
this.clientIsCluster = false;
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
this.connected = true;
|
|
598
|
+
if (this.enableLogging) {
|
|
599
|
+
logger.info(`[ValkeyGlideProvider:${this.namespace}] Connected to Valkey`, {
|
|
600
|
+
host,
|
|
601
|
+
port,
|
|
602
|
+
cluster: this.clientIsCluster
|
|
603
|
+
});
|
|
604
|
+
}
|
|
605
|
+
} catch (error) {
|
|
606
|
+
this.stats.errors++;
|
|
607
|
+
logger.error(`[ValkeyGlideProvider:${this.namespace}] Failed to connect:`, error);
|
|
608
|
+
throw error;
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
/**
|
|
612
|
+
* Disconnect from Valkey server
|
|
613
|
+
*/
|
|
614
|
+
async disconnect() {
|
|
615
|
+
if (this.client) {
|
|
616
|
+
await this.client.close();
|
|
617
|
+
this.client = null;
|
|
618
|
+
this.clientIsCluster = false;
|
|
619
|
+
this.connected = false;
|
|
620
|
+
if (this.enableLogging) {
|
|
621
|
+
logger.info(`[ValkeyGlideProvider:${this.namespace}] Disconnected`);
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
/**
|
|
626
|
+
* Check if connected
|
|
627
|
+
*/
|
|
628
|
+
isConnected() {
|
|
629
|
+
return this.connected && this.client !== null;
|
|
630
|
+
}
|
|
631
|
+
/**
|
|
632
|
+
* Health check
|
|
633
|
+
*/
|
|
634
|
+
async healthCheck() {
|
|
635
|
+
const startTime = Date.now();
|
|
636
|
+
try {
|
|
637
|
+
if (!this.client) {
|
|
638
|
+
return {
|
|
639
|
+
status: "unhealthy",
|
|
640
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
641
|
+
connected: false,
|
|
642
|
+
error: "Not connected"
|
|
643
|
+
};
|
|
644
|
+
}
|
|
645
|
+
const result = await this.client.ping();
|
|
646
|
+
const latencyMs = Date.now() - startTime;
|
|
647
|
+
if (result === "PONG") {
|
|
648
|
+
return {
|
|
649
|
+
status: "healthy",
|
|
650
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
651
|
+
connected: true,
|
|
652
|
+
latencyMs
|
|
653
|
+
};
|
|
654
|
+
}
|
|
655
|
+
return {
|
|
656
|
+
status: "unhealthy",
|
|
657
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
658
|
+
connected: false,
|
|
659
|
+
error: "Unexpected ping response"
|
|
660
|
+
};
|
|
661
|
+
} catch (error) {
|
|
662
|
+
return {
|
|
663
|
+
status: "unhealthy",
|
|
664
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
665
|
+
connected: false,
|
|
666
|
+
error: error instanceof Error ? error.message : "Unknown error",
|
|
667
|
+
latencyMs: Date.now() - startTime
|
|
668
|
+
};
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
/**
|
|
672
|
+
* Get statistics
|
|
673
|
+
*/
|
|
674
|
+
async getStatistics() {
|
|
675
|
+
const totalOperations = this.stats.hits + this.stats.misses;
|
|
676
|
+
const hitRate = totalOperations > 0 ? this.stats.hits / totalOperations : 0;
|
|
677
|
+
try {
|
|
678
|
+
if (!this.client) {
|
|
679
|
+
throw new Error("Not connected");
|
|
680
|
+
}
|
|
681
|
+
const info = await this.client.info("memory");
|
|
682
|
+
const memoryRegex = /used_memory:(\d+)/;
|
|
683
|
+
const memoryMatch = memoryRegex.exec(info);
|
|
684
|
+
const memoryUsed = memoryMatch && memoryMatch[1] ? parseInt(memoryMatch[1], 10) : void 0;
|
|
685
|
+
const keyCount = await this.client.dbsize();
|
|
686
|
+
return {
|
|
687
|
+
hits: this.stats.hits,
|
|
688
|
+
misses: this.stats.misses,
|
|
689
|
+
hitRate,
|
|
690
|
+
keyCount,
|
|
691
|
+
memoryUsed,
|
|
692
|
+
totalOperations,
|
|
693
|
+
details: {
|
|
694
|
+
sets: this.stats.sets,
|
|
695
|
+
deletes: this.stats.deletes,
|
|
696
|
+
errors: this.stats.errors
|
|
697
|
+
}
|
|
698
|
+
};
|
|
699
|
+
} catch (error) {
|
|
700
|
+
logger.error(`[ValkeyGlideProvider:${this.namespace}] Failed to get statistics:`, error);
|
|
701
|
+
return {
|
|
702
|
+
hits: this.stats.hits,
|
|
703
|
+
misses: this.stats.misses,
|
|
704
|
+
hitRate,
|
|
705
|
+
totalOperations,
|
|
706
|
+
details: {
|
|
707
|
+
sets: this.stats.sets,
|
|
708
|
+
deletes: this.stats.deletes,
|
|
709
|
+
errors: this.stats.errors
|
|
710
|
+
}
|
|
711
|
+
};
|
|
712
|
+
}
|
|
713
|
+
}
|
|
714
|
+
/**
|
|
715
|
+
* Build cache key with namespace
|
|
716
|
+
*/
|
|
717
|
+
buildKey(key) {
|
|
718
|
+
return `${this.namespace}:${key}`;
|
|
719
|
+
}
|
|
720
|
+
/**
|
|
721
|
+
* Get value from cache
|
|
722
|
+
*/
|
|
723
|
+
async get(key, _options) {
|
|
724
|
+
try {
|
|
725
|
+
if (!this.client) {
|
|
726
|
+
throw new Error("Not connected");
|
|
727
|
+
}
|
|
728
|
+
const cacheKey = this.buildKey(key);
|
|
729
|
+
const value = await this.client.get(cacheKey);
|
|
730
|
+
if (value === null) {
|
|
731
|
+
this.stats.misses++;
|
|
732
|
+
if (this.enableLogging) {
|
|
733
|
+
logger.debug(`[ValkeyGlideProvider:${this.namespace}] Cache miss`, {
|
|
734
|
+
key
|
|
735
|
+
});
|
|
736
|
+
}
|
|
737
|
+
return null;
|
|
738
|
+
}
|
|
739
|
+
this.stats.hits++;
|
|
740
|
+
if (this.enableLogging) {
|
|
741
|
+
logger.debug(`[ValkeyGlideProvider:${this.namespace}] Cache hit`, {
|
|
742
|
+
key
|
|
743
|
+
});
|
|
744
|
+
}
|
|
745
|
+
const entry = JSON.parse(value);
|
|
746
|
+
return entry.data;
|
|
747
|
+
} catch (error) {
|
|
748
|
+
this.stats.errors++;
|
|
749
|
+
logger.error(`[ValkeyGlideProvider:${this.namespace}] Error getting cache key:`, error);
|
|
750
|
+
return null;
|
|
751
|
+
}
|
|
752
|
+
}
|
|
753
|
+
/**
|
|
754
|
+
* Set value in cache
|
|
755
|
+
*/
|
|
756
|
+
async set(key, value, options) {
|
|
757
|
+
try {
|
|
758
|
+
if (!this.client) {
|
|
759
|
+
throw new Error("Not connected");
|
|
760
|
+
}
|
|
761
|
+
const cacheKey = this.buildKey(key);
|
|
762
|
+
const ttlSeconds = options?.ttl || this.defaultTTL;
|
|
763
|
+
const entry = {
|
|
764
|
+
data: value,
|
|
765
|
+
timestamp: Date.now(),
|
|
766
|
+
ttl: ttlSeconds,
|
|
767
|
+
metadata: options?.metadata
|
|
768
|
+
};
|
|
769
|
+
await this.client.set(cacheKey, JSON.stringify(entry), {
|
|
770
|
+
expiry: {
|
|
771
|
+
type: "EX",
|
|
772
|
+
count: ttlSeconds
|
|
773
|
+
}
|
|
774
|
+
});
|
|
775
|
+
this.stats.sets++;
|
|
776
|
+
if (this.enableLogging) {
|
|
777
|
+
logger.debug(`[ValkeyGlideProvider:${this.namespace}] Cache set`, {
|
|
778
|
+
key,
|
|
779
|
+
ttl: ttlSeconds
|
|
780
|
+
});
|
|
781
|
+
}
|
|
782
|
+
return true;
|
|
783
|
+
} catch (error) {
|
|
784
|
+
this.stats.errors++;
|
|
785
|
+
logger.error(`[ValkeyGlideProvider:${this.namespace}] Error setting cache key:`, error);
|
|
786
|
+
return false;
|
|
787
|
+
}
|
|
788
|
+
}
|
|
789
|
+
/**
|
|
790
|
+
* Delete value from cache
|
|
791
|
+
*/
|
|
792
|
+
async delete(key) {
|
|
793
|
+
try {
|
|
794
|
+
if (!this.client) {
|
|
795
|
+
throw new Error("Not connected");
|
|
796
|
+
}
|
|
797
|
+
const cacheKey = this.buildKey(key);
|
|
798
|
+
const result = await this.client.del([
|
|
799
|
+
cacheKey
|
|
800
|
+
]);
|
|
801
|
+
this.stats.deletes++;
|
|
802
|
+
if (this.enableLogging) {
|
|
803
|
+
logger.debug(`[ValkeyGlideProvider:${this.namespace}] Cache delete`, {
|
|
804
|
+
key,
|
|
805
|
+
deleted: result > 0
|
|
806
|
+
});
|
|
807
|
+
}
|
|
808
|
+
return result > 0;
|
|
809
|
+
} catch (error) {
|
|
810
|
+
this.stats.errors++;
|
|
811
|
+
logger.error(`[ValkeyGlideProvider:${this.namespace}] Error deleting cache key:`, error);
|
|
812
|
+
return false;
|
|
813
|
+
}
|
|
814
|
+
}
|
|
815
|
+
/**
|
|
816
|
+
* Check if key exists
|
|
817
|
+
*/
|
|
818
|
+
async has(key) {
|
|
819
|
+
try {
|
|
820
|
+
if (!this.client) {
|
|
821
|
+
throw new Error("Not connected");
|
|
822
|
+
}
|
|
823
|
+
const cacheKey = this.buildKey(key);
|
|
824
|
+
const exists = await this.client.exists([
|
|
825
|
+
cacheKey
|
|
826
|
+
]);
|
|
827
|
+
return exists === 1;
|
|
828
|
+
} catch (error) {
|
|
829
|
+
this.stats.errors++;
|
|
830
|
+
logger.error(`[ValkeyGlideProvider:${this.namespace}] Error checking cache key:`, error);
|
|
831
|
+
return false;
|
|
832
|
+
}
|
|
833
|
+
}
|
|
834
|
+
/**
|
|
835
|
+
* Get TTL for key
|
|
836
|
+
*/
|
|
837
|
+
async ttl(key) {
|
|
838
|
+
try {
|
|
839
|
+
if (!this.client) {
|
|
840
|
+
throw new Error("Not connected");
|
|
841
|
+
}
|
|
842
|
+
const cacheKey = this.buildKey(key);
|
|
843
|
+
return await this.client.ttl(cacheKey);
|
|
844
|
+
} catch (error) {
|
|
845
|
+
this.stats.errors++;
|
|
846
|
+
logger.error(`[ValkeyGlideProvider:${this.namespace}] Error getting TTL:`, error);
|
|
847
|
+
return -2;
|
|
848
|
+
}
|
|
849
|
+
}
|
|
850
|
+
/**
|
|
851
|
+
* Update TTL for key
|
|
852
|
+
*/
|
|
853
|
+
async expire(key, ttl) {
|
|
854
|
+
try {
|
|
855
|
+
if (!this.client) {
|
|
856
|
+
throw new Error("Not connected");
|
|
857
|
+
}
|
|
858
|
+
const cacheKey = this.buildKey(key);
|
|
859
|
+
return await this.client.expire(cacheKey, ttl);
|
|
860
|
+
} catch (error) {
|
|
861
|
+
this.stats.errors++;
|
|
862
|
+
logger.error(`[ValkeyGlideProvider:${this.namespace}] Error setting expiration:`, error);
|
|
863
|
+
return false;
|
|
864
|
+
}
|
|
865
|
+
}
|
|
866
|
+
/**
|
|
867
|
+
* Get multiple values (batch operation)
|
|
868
|
+
*/
|
|
869
|
+
async mget(keys, _options) {
|
|
870
|
+
const result = /* @__PURE__ */ new Map();
|
|
871
|
+
if (keys.length === 0) {
|
|
872
|
+
return result;
|
|
873
|
+
}
|
|
874
|
+
try {
|
|
875
|
+
if (!this.client) {
|
|
876
|
+
throw new Error("Not connected");
|
|
877
|
+
}
|
|
878
|
+
const cacheKeys = keys.map((key) => this.buildKey(key));
|
|
879
|
+
const values = await this.client.mget(cacheKeys);
|
|
880
|
+
for (let i = 0; i < keys.length; i++) {
|
|
881
|
+
const key = keys[i];
|
|
882
|
+
if (!key) continue;
|
|
883
|
+
if (values[i] !== null) {
|
|
884
|
+
const entry = JSON.parse(values[i]);
|
|
885
|
+
result.set(key, entry.data);
|
|
886
|
+
this.stats.hits++;
|
|
887
|
+
} else {
|
|
888
|
+
this.stats.misses++;
|
|
889
|
+
}
|
|
890
|
+
}
|
|
891
|
+
if (this.enableLogging) {
|
|
892
|
+
logger.debug(`[ValkeyGlideProvider:${this.namespace}] Batch get`, {
|
|
893
|
+
requested: keys.length,
|
|
894
|
+
found: result.size
|
|
895
|
+
});
|
|
896
|
+
}
|
|
897
|
+
return result;
|
|
898
|
+
} catch (error) {
|
|
899
|
+
this.stats.errors++;
|
|
900
|
+
logger.error(`[ValkeyGlideProvider:${this.namespace}] Error in batch get:`, error);
|
|
901
|
+
return result;
|
|
902
|
+
}
|
|
903
|
+
}
|
|
904
|
+
/**
|
|
905
|
+
* Set multiple values (batch operation)
|
|
906
|
+
*/
|
|
907
|
+
async mset(entries, options) {
|
|
908
|
+
if (entries.size === 0) {
|
|
909
|
+
return true;
|
|
910
|
+
}
|
|
911
|
+
try {
|
|
912
|
+
if (!this.client) {
|
|
913
|
+
throw new Error("Not connected");
|
|
914
|
+
}
|
|
915
|
+
const ttlSeconds = options?.ttl || this.defaultTTL;
|
|
916
|
+
const keyValuePairs = {};
|
|
917
|
+
for (const [key, value] of entries) {
|
|
918
|
+
const cacheKey = this.buildKey(key);
|
|
919
|
+
const entry = {
|
|
920
|
+
data: value,
|
|
921
|
+
timestamp: Date.now(),
|
|
922
|
+
ttl: ttlSeconds,
|
|
923
|
+
metadata: options?.metadata
|
|
924
|
+
};
|
|
925
|
+
keyValuePairs[cacheKey] = JSON.stringify(entry);
|
|
926
|
+
}
|
|
927
|
+
await this.client.mset(keyValuePairs);
|
|
928
|
+
for (const cacheKey of Object.keys(keyValuePairs)) {
|
|
929
|
+
await this.client.expire(cacheKey, ttlSeconds);
|
|
930
|
+
}
|
|
931
|
+
this.stats.sets += entries.size;
|
|
932
|
+
if (this.enableLogging) {
|
|
933
|
+
logger.debug(`[ValkeyGlideProvider:${this.namespace}] Batch set`, {
|
|
934
|
+
count: entries.size,
|
|
935
|
+
ttl: ttlSeconds
|
|
936
|
+
});
|
|
937
|
+
}
|
|
938
|
+
return true;
|
|
939
|
+
} catch (error) {
|
|
940
|
+
this.stats.errors++;
|
|
941
|
+
logger.error(`[ValkeyGlideProvider:${this.namespace}] Error in batch set:`, error);
|
|
942
|
+
return false;
|
|
943
|
+
}
|
|
944
|
+
}
|
|
945
|
+
/**
|
|
946
|
+
* Delete multiple keys (batch operation)
|
|
947
|
+
*/
|
|
948
|
+
async mdel(keys) {
|
|
949
|
+
if (keys.length === 0) {
|
|
950
|
+
return 0;
|
|
951
|
+
}
|
|
952
|
+
try {
|
|
953
|
+
if (!this.client) {
|
|
954
|
+
throw new Error("Not connected");
|
|
955
|
+
}
|
|
956
|
+
const cacheKeys = keys.map((key) => this.buildKey(key));
|
|
957
|
+
const result = await this.client.del(cacheKeys);
|
|
958
|
+
this.stats.deletes += result;
|
|
959
|
+
if (this.enableLogging) {
|
|
960
|
+
logger.debug(`[ValkeyGlideProvider:${this.namespace}] Batch delete`, {
|
|
961
|
+
requested: keys.length,
|
|
962
|
+
deleted: result
|
|
963
|
+
});
|
|
964
|
+
}
|
|
965
|
+
return result;
|
|
966
|
+
} catch (error) {
|
|
967
|
+
this.stats.errors++;
|
|
968
|
+
logger.error(`[ValkeyGlideProvider:${this.namespace}] Error in batch delete:`, error);
|
|
969
|
+
return 0;
|
|
970
|
+
}
|
|
971
|
+
}
|
|
972
|
+
/**
|
|
973
|
+
* Clear keys by pattern
|
|
974
|
+
*/
|
|
975
|
+
async clearPattern(pattern) {
|
|
976
|
+
try {
|
|
977
|
+
if (!this.client) {
|
|
978
|
+
throw new Error("Not connected");
|
|
979
|
+
}
|
|
980
|
+
const searchPattern = this.buildKey(pattern);
|
|
981
|
+
const keys = await this.getKeys(searchPattern);
|
|
982
|
+
if (keys.length === 0) {
|
|
983
|
+
return 0;
|
|
984
|
+
}
|
|
985
|
+
const result = await this.client.del(keys);
|
|
986
|
+
if (this.enableLogging) {
|
|
987
|
+
logger.debug(`[ValkeyGlideProvider:${this.namespace}] Pattern cleared`, {
|
|
988
|
+
pattern,
|
|
989
|
+
deleted: result
|
|
990
|
+
});
|
|
991
|
+
}
|
|
992
|
+
return result;
|
|
993
|
+
} catch (error) {
|
|
994
|
+
this.stats.errors++;
|
|
995
|
+
logger.error(`[ValkeyGlideProvider:${this.namespace}] Error clearing pattern:`, error);
|
|
996
|
+
return 0;
|
|
997
|
+
}
|
|
998
|
+
}
|
|
999
|
+
/**
|
|
1000
|
+
* Get keys by pattern
|
|
1001
|
+
*/
|
|
1002
|
+
async keys(options) {
|
|
1003
|
+
try {
|
|
1004
|
+
if (!this.client) {
|
|
1005
|
+
throw new Error("Not connected");
|
|
1006
|
+
}
|
|
1007
|
+
const searchPattern = this.buildKey(options.pattern);
|
|
1008
|
+
const keys = await this.getKeys(searchPattern);
|
|
1009
|
+
const prefix = `${this.namespace}:`;
|
|
1010
|
+
const cleanKeys = keys.map((key) => key.replace(prefix, ""));
|
|
1011
|
+
const limitedKeys = options.limit ? cleanKeys.slice(0, options.limit) : cleanKeys;
|
|
1012
|
+
return {
|
|
1013
|
+
keys: limitedKeys,
|
|
1014
|
+
nextCursor: null,
|
|
1015
|
+
totalCount: cleanKeys.length
|
|
1016
|
+
};
|
|
1017
|
+
} catch (error) {
|
|
1018
|
+
this.stats.errors++;
|
|
1019
|
+
logger.error(`[ValkeyGlideProvider:${this.namespace}] Error getting keys:`, error);
|
|
1020
|
+
return {
|
|
1021
|
+
keys: [],
|
|
1022
|
+
nextCursor: null,
|
|
1023
|
+
totalCount: 0
|
|
1024
|
+
};
|
|
1025
|
+
}
|
|
1026
|
+
}
|
|
1027
|
+
/**
|
|
1028
|
+
* Clear all keys in namespace
|
|
1029
|
+
*/
|
|
1030
|
+
async clear() {
|
|
1031
|
+
try {
|
|
1032
|
+
if (!this.client) {
|
|
1033
|
+
throw new Error("Not connected");
|
|
1034
|
+
}
|
|
1035
|
+
const pattern = this.buildKey("*");
|
|
1036
|
+
const keys = await this.getKeys(pattern);
|
|
1037
|
+
if (keys.length > 0) {
|
|
1038
|
+
await this.client.del(keys);
|
|
1039
|
+
if (this.enableLogging) {
|
|
1040
|
+
logger.info(`[ValkeyGlideProvider:${this.namespace}] Cleared ${keys.length} keys`);
|
|
1041
|
+
}
|
|
1042
|
+
}
|
|
1043
|
+
return true;
|
|
1044
|
+
} catch (error) {
|
|
1045
|
+
this.stats.errors++;
|
|
1046
|
+
logger.error(`[ValkeyGlideProvider:${this.namespace}] Error clearing cache:`, error);
|
|
1047
|
+
return false;
|
|
1048
|
+
}
|
|
1049
|
+
}
|
|
1050
|
+
/**
|
|
1051
|
+
* Increment numeric value
|
|
1052
|
+
*/
|
|
1053
|
+
async increment(key, amount = 1) {
|
|
1054
|
+
try {
|
|
1055
|
+
if (!this.client) {
|
|
1056
|
+
throw new Error("Not connected");
|
|
1057
|
+
}
|
|
1058
|
+
const cacheKey = this.buildKey(key);
|
|
1059
|
+
return await this.client.incrBy(cacheKey, amount);
|
|
1060
|
+
} catch (error) {
|
|
1061
|
+
this.stats.errors++;
|
|
1062
|
+
logger.error(`[ValkeyGlideProvider:${this.namespace}] Error incrementing:`, error);
|
|
1063
|
+
throw error;
|
|
1064
|
+
}
|
|
1065
|
+
}
|
|
1066
|
+
/**
|
|
1067
|
+
* Decrement numeric value
|
|
1068
|
+
*/
|
|
1069
|
+
async decrement(key, amount = 1) {
|
|
1070
|
+
try {
|
|
1071
|
+
if (!this.client) {
|
|
1072
|
+
throw new Error("Not connected");
|
|
1073
|
+
}
|
|
1074
|
+
const cacheKey = this.buildKey(key);
|
|
1075
|
+
return await this.client.decrBy(cacheKey, amount);
|
|
1076
|
+
} catch (error) {
|
|
1077
|
+
this.stats.errors++;
|
|
1078
|
+
logger.error(`[ValkeyGlideProvider:${this.namespace}] Error decrementing:`, error);
|
|
1079
|
+
throw error;
|
|
1080
|
+
}
|
|
1081
|
+
}
|
|
1082
|
+
/**
|
|
1083
|
+
* Get or set value
|
|
1084
|
+
*/
|
|
1085
|
+
async getOrSet(key, fetcher, options) {
|
|
1086
|
+
const cached = await this.get(key, options);
|
|
1087
|
+
if (cached !== null) {
|
|
1088
|
+
return cached;
|
|
1089
|
+
}
|
|
1090
|
+
const value = await fetcher();
|
|
1091
|
+
await this.set(key, value, options);
|
|
1092
|
+
return value;
|
|
1093
|
+
}
|
|
1094
|
+
/**
|
|
1095
|
+
* Invalidate by tags
|
|
1096
|
+
*/
|
|
1097
|
+
async invalidateTags(tags) {
|
|
1098
|
+
try {
|
|
1099
|
+
let totalDeleted = 0;
|
|
1100
|
+
for (const tag of tags) {
|
|
1101
|
+
const pattern = `*:tag:${tag}:*`;
|
|
1102
|
+
const deleted = await this.clearPattern(pattern);
|
|
1103
|
+
totalDeleted += deleted;
|
|
1104
|
+
}
|
|
1105
|
+
if (this.enableLogging) {
|
|
1106
|
+
logger.debug(`[ValkeyGlideProvider:${this.namespace}] Tags invalidated`, {
|
|
1107
|
+
tags,
|
|
1108
|
+
deleted: totalDeleted
|
|
1109
|
+
});
|
|
1110
|
+
}
|
|
1111
|
+
return totalDeleted;
|
|
1112
|
+
} catch (error) {
|
|
1113
|
+
this.stats.errors++;
|
|
1114
|
+
logger.error(`[ValkeyGlideProvider:${this.namespace}] Error invalidating tags:`, error);
|
|
1115
|
+
return 0;
|
|
1116
|
+
}
|
|
1117
|
+
}
|
|
1118
|
+
};
|
|
1119
|
+
|
|
1120
|
+
// src/cache/factory/CacheFactory.ts
|
|
1121
|
+
var CacheFactory = class _CacheFactory {
|
|
1122
|
+
static {
|
|
1123
|
+
__name(this, "CacheFactory");
|
|
1124
|
+
}
|
|
1125
|
+
static instances = /* @__PURE__ */ new Map();
|
|
1126
|
+
static parseBoolean(value) {
|
|
1127
|
+
if (value === void 0) return void 0;
|
|
1128
|
+
const normalized = value.trim().toLowerCase();
|
|
1129
|
+
if ([
|
|
1130
|
+
"1",
|
|
1131
|
+
"true",
|
|
1132
|
+
"yes",
|
|
1133
|
+
"on"
|
|
1134
|
+
].includes(normalized)) return true;
|
|
1135
|
+
if ([
|
|
1136
|
+
"0",
|
|
1137
|
+
"false",
|
|
1138
|
+
"no",
|
|
1139
|
+
"off"
|
|
1140
|
+
].includes(normalized)) return false;
|
|
1141
|
+
return void 0;
|
|
1142
|
+
}
|
|
1143
|
+
static parseClusterNodes(value) {
|
|
1144
|
+
if (!value) return void 0;
|
|
1145
|
+
const nodes = value.split(",").map((node) => node.trim()).filter(Boolean).map((node) => {
|
|
1146
|
+
const [host, rawPort] = node.replace(/^redis:\/\//, "").replace(/^valkey:\/\//, "").split(":");
|
|
1147
|
+
if (!host) return null;
|
|
1148
|
+
return {
|
|
1149
|
+
host,
|
|
1150
|
+
port: rawPort ? parseInt(rawPort, 10) : 6379
|
|
1151
|
+
};
|
|
1152
|
+
}).filter((node) => node !== null && Number.isFinite(node.port));
|
|
1153
|
+
return nodes.length > 0 ? nodes : void 0;
|
|
1154
|
+
}
|
|
1155
|
+
/**
|
|
1156
|
+
* Resolve port from multiple environment variable sources
|
|
1157
|
+
* @param layerPort - Layer-specific port environment variable value
|
|
1158
|
+
* @param genericPort - Generic port environment variable value
|
|
1159
|
+
* @param defaultPort - Default port if none provided
|
|
1160
|
+
* @returns Resolved port number
|
|
1161
|
+
*/
|
|
1162
|
+
static resolvePort(layerPort, genericPort, defaultPort = 6379) {
|
|
1163
|
+
if (layerPort) {
|
|
1164
|
+
return parseInt(layerPort, 10);
|
|
1165
|
+
}
|
|
1166
|
+
if (genericPort) {
|
|
1167
|
+
return parseInt(genericPort, 10);
|
|
1168
|
+
}
|
|
1169
|
+
return defaultPort;
|
|
1170
|
+
}
|
|
1171
|
+
/**
|
|
1172
|
+
* Create a cache provider based on configuration
|
|
1173
|
+
*/
|
|
1174
|
+
static createCache(config, namespace = "default") {
|
|
1175
|
+
logger.info("[CacheFactory] Creating cache provider", {
|
|
1176
|
+
type: config.type,
|
|
1177
|
+
namespace
|
|
1178
|
+
});
|
|
1179
|
+
switch (config.type) {
|
|
1180
|
+
case "valkey":
|
|
1181
|
+
case "redis":
|
|
1182
|
+
return new ValkeyGlideProvider({
|
|
1183
|
+
type: "valkey-glide",
|
|
1184
|
+
url: config.url,
|
|
1185
|
+
host: config.host,
|
|
1186
|
+
port: config.port,
|
|
1187
|
+
password: config.password,
|
|
1188
|
+
username: config.username,
|
|
1189
|
+
db: config.db,
|
|
1190
|
+
keyPrefix: config.keyPrefix,
|
|
1191
|
+
defaultTTL: config.defaultTTL,
|
|
1192
|
+
enableLogging: config.enableLogging,
|
|
1193
|
+
maxReconnectAttempts: config.maxReconnectAttempts,
|
|
1194
|
+
reconnectTimeWait: config.reconnectTimeWait,
|
|
1195
|
+
connectionTimeout: config.connectionTimeout,
|
|
1196
|
+
name: config.name,
|
|
1197
|
+
useTLS: config.tls?.enabled,
|
|
1198
|
+
cluster: config.cluster,
|
|
1199
|
+
clusterNodes: config.clusterNodes
|
|
1200
|
+
}, namespace);
|
|
1201
|
+
case "memory":
|
|
1202
|
+
return new MemoryCacheProvider(config, namespace);
|
|
1203
|
+
// Future implementations can be added here
|
|
1204
|
+
// case 'memcached':
|
|
1205
|
+
// return new MemcachedCacheProvider(config as MemcachedCacheConfig, namespace);
|
|
1206
|
+
default:
|
|
1207
|
+
throw new Error(`Unsupported cache provider type: ${config.type}`);
|
|
1208
|
+
}
|
|
1209
|
+
}
|
|
1210
|
+
/**
|
|
1211
|
+
* Create a cache from environment variables
|
|
1212
|
+
*/
|
|
1213
|
+
static createCacheFromEnv(namespace = "default") {
|
|
1214
|
+
const envType = process.env["CACHE_PROVIDER_TYPE"];
|
|
1215
|
+
const type = envType === "memory" ? "memory" : "valkey";
|
|
1216
|
+
const config = type === "valkey" ? {
|
|
1217
|
+
type: "valkey",
|
|
1218
|
+
url: process.env["VALKEY_URL"],
|
|
1219
|
+
host: process.env["VALKEY_HOST"] || "localhost",
|
|
1220
|
+
port: process.env["VALKEY_PORT"] ? parseInt(process.env["VALKEY_PORT"], 10) : 6379,
|
|
1221
|
+
password: process.env["VALKEY_PASSWORD"],
|
|
1222
|
+
db: process.env["VALKEY_DB"] ? parseInt(process.env["VALKEY_DB"], 10) : 0,
|
|
1223
|
+
username: process.env["VALKEY_USERNAME"],
|
|
1224
|
+
keyPrefix: process.env["CACHE_KEY_PREFIX"] || "cache",
|
|
1225
|
+
defaultTTL: process.env["CACHE_DEFAULT_TTL"] ? parseInt(process.env["CACHE_DEFAULT_TTL"], 10) : 300,
|
|
1226
|
+
enableLogging: process.env["CACHE_ENABLE_LOGGING"] === "true",
|
|
1227
|
+
maxReconnectAttempts: process.env["VALKEY_MAX_RECONNECT_ATTEMPTS"] ? parseInt(process.env["VALKEY_MAX_RECONNECT_ATTEMPTS"], 10) : -1,
|
|
1228
|
+
reconnectTimeWait: process.env["VALKEY_RECONNECT_TIME_WAIT"] ? parseInt(process.env["VALKEY_RECONNECT_TIME_WAIT"], 10) : 2e3,
|
|
1229
|
+
connectionTimeout: process.env["VALKEY_CONNECTION_TIMEOUT"] ? parseInt(process.env["VALKEY_CONNECTION_TIMEOUT"], 10) : 1e4,
|
|
1230
|
+
name: process.env["CACHE_CLIENT_NAME"] || `cache-${namespace}`,
|
|
1231
|
+
cluster: _CacheFactory.parseBoolean(process.env["VALKEY_CLUSTER"] || process.env["CACHE_CLUSTER"]),
|
|
1232
|
+
clusterNodes: _CacheFactory.parseClusterNodes(process.env["VALKEY_CLUSTER_NODES"] || process.env["CACHE_CLUSTER_NODES"])
|
|
1233
|
+
} : {
|
|
1234
|
+
type: "memory",
|
|
1235
|
+
maxSize: process.env["CACHE_MAX_SIZE"] ? parseInt(process.env["CACHE_MAX_SIZE"], 10) : 1e3,
|
|
1236
|
+
defaultTTL: process.env["CACHE_DEFAULT_TTL"] ? parseInt(process.env["CACHE_DEFAULT_TTL"], 10) : 300,
|
|
1237
|
+
enableLogging: process.env["CACHE_ENABLE_LOGGING"] === "true",
|
|
1238
|
+
keyPrefix: process.env["CACHE_KEY_PREFIX"] || "cache"
|
|
1239
|
+
};
|
|
1240
|
+
return _CacheFactory.createCache(config, namespace);
|
|
1241
|
+
}
|
|
1242
|
+
/**
|
|
1243
|
+
* Get or create a singleton cache instance for a namespace
|
|
1244
|
+
*/
|
|
1245
|
+
static getInstance(namespace = "default", config) {
|
|
1246
|
+
const instanceKey = `${config?.type || "default"}:${namespace}`;
|
|
1247
|
+
if (!_CacheFactory.instances.has(instanceKey)) {
|
|
1248
|
+
const cache = config ? _CacheFactory.createCache(config, namespace) : _CacheFactory.createCacheFromEnv(namespace);
|
|
1249
|
+
_CacheFactory.instances.set(instanceKey, cache);
|
|
1250
|
+
}
|
|
1251
|
+
return _CacheFactory.instances.get(instanceKey);
|
|
1252
|
+
}
|
|
1253
|
+
/**
|
|
1254
|
+
* Close a specific cache instance
|
|
1255
|
+
*/
|
|
1256
|
+
static async closeInstance(namespace) {
|
|
1257
|
+
for (const [key, cache] of _CacheFactory.instances.entries()) {
|
|
1258
|
+
if (key.endsWith(`:${namespace}`)) {
|
|
1259
|
+
await cache.disconnect();
|
|
1260
|
+
_CacheFactory.instances.delete(key);
|
|
1261
|
+
logger.info("[CacheFactory] Closed cache instance", {
|
|
1262
|
+
namespace
|
|
1263
|
+
});
|
|
1264
|
+
}
|
|
1265
|
+
}
|
|
1266
|
+
}
|
|
1267
|
+
/**
|
|
1268
|
+
* Close all cache instances
|
|
1269
|
+
* Call this during application shutdown
|
|
1270
|
+
*/
|
|
1271
|
+
static async closeAll() {
|
|
1272
|
+
const promises = Array.from(_CacheFactory.instances.values()).map(async (cache) => cache.disconnect());
|
|
1273
|
+
await Promise.all(promises);
|
|
1274
|
+
_CacheFactory.instances.clear();
|
|
1275
|
+
logger.info("[CacheFactory] All cache instances closed");
|
|
1276
|
+
}
|
|
1277
|
+
// ========================================================================
|
|
1278
|
+
// Layer-Aware Factory Methods
|
|
1279
|
+
// ========================================================================
|
|
1280
|
+
/**
|
|
1281
|
+
* Create a cache provider for a specific layer
|
|
1282
|
+
*
|
|
1283
|
+
* @param layer - Service layer ('wspace' or 'global')
|
|
1284
|
+
* @param serviceName - Service name for identification
|
|
1285
|
+
* @param namespace - Cache namespace (default: 'default')
|
|
1286
|
+
* @returns Cache provider configured for the layer
|
|
1287
|
+
*
|
|
1288
|
+
* @example
|
|
1289
|
+
* ```typescript
|
|
1290
|
+
* const cache = CacheFactory.createCacheForLayer('wspace', 'wspace-tags-svc');
|
|
1291
|
+
* await cache.connect();
|
|
1292
|
+
* await cache.set('key', { data: 'value' });
|
|
1293
|
+
* ```
|
|
1294
|
+
*/
|
|
1295
|
+
static createCacheForLayer(layer, serviceName, namespace = "default") {
|
|
1296
|
+
const envPrefix = layer.toUpperCase();
|
|
1297
|
+
const defaults = DEFAULT_LAYER_RESOURCES[layer];
|
|
1298
|
+
const url = process.env[`${envPrefix}_VALKEY_URL`] || process.env["VALKEY_URL"] || defaults.valkeyUrl;
|
|
1299
|
+
const resolvedPort = _CacheFactory.resolvePort(process.env[`${envPrefix}_VALKEY_PORT`], process.env["VALKEY_PORT"]);
|
|
1300
|
+
const config = {
|
|
1301
|
+
type: "valkey",
|
|
1302
|
+
url,
|
|
1303
|
+
host: process.env[`${envPrefix}_VALKEY_HOST`] || process.env["VALKEY_HOST"] || "localhost",
|
|
1304
|
+
port: resolvedPort,
|
|
1305
|
+
password: process.env["VALKEY_PASSWORD"],
|
|
1306
|
+
db: process.env["VALKEY_DB"] ? parseInt(process.env["VALKEY_DB"], 10) : 0,
|
|
1307
|
+
username: process.env["VALKEY_USERNAME"],
|
|
1308
|
+
keyPrefix: process.env["CACHE_KEY_PREFIX"] || `${layer}-cache`,
|
|
1309
|
+
defaultTTL: process.env["CACHE_DEFAULT_TTL"] ? parseInt(process.env["CACHE_DEFAULT_TTL"], 10) : 300,
|
|
1310
|
+
enableLogging: process.env["CACHE_ENABLE_LOGGING"] === "true",
|
|
1311
|
+
maxReconnectAttempts: process.env["VALKEY_MAX_RECONNECT_ATTEMPTS"] ? parseInt(process.env["VALKEY_MAX_RECONNECT_ATTEMPTS"], 10) : -1,
|
|
1312
|
+
reconnectTimeWait: process.env["VALKEY_RECONNECT_TIME_WAIT"] ? parseInt(process.env["VALKEY_RECONNECT_TIME_WAIT"], 10) : 2e3,
|
|
1313
|
+
connectionTimeout: process.env["VALKEY_CONNECTION_TIMEOUT"] ? parseInt(process.env["VALKEY_CONNECTION_TIMEOUT"], 10) : 1e4,
|
|
1314
|
+
name: serviceName,
|
|
1315
|
+
cluster: _CacheFactory.parseBoolean(process.env[`${envPrefix}_VALKEY_CLUSTER`] || process.env["VALKEY_CLUSTER"] || process.env["CACHE_CLUSTER"]),
|
|
1316
|
+
clusterNodes: _CacheFactory.parseClusterNodes(process.env[`${envPrefix}_VALKEY_CLUSTER_NODES`] || process.env["VALKEY_CLUSTER_NODES"] || process.env["CACHE_CLUSTER_NODES"])
|
|
1317
|
+
};
|
|
1318
|
+
logger.info("[CacheFactory] Creating layer cache", {
|
|
1319
|
+
layer,
|
|
1320
|
+
serviceName,
|
|
1321
|
+
namespace,
|
|
1322
|
+
url: config.url
|
|
1323
|
+
});
|
|
1324
|
+
return _CacheFactory.createCache(config, namespace);
|
|
1325
|
+
}
|
|
1326
|
+
/**
|
|
1327
|
+
* Get or create a singleton cache instance for a layer
|
|
1328
|
+
*
|
|
1329
|
+
* @param layer - Service layer ('wspace' or 'global')
|
|
1330
|
+
* @param serviceName - Service name for identification
|
|
1331
|
+
* @param namespace - Cache namespace (default: 'default')
|
|
1332
|
+
* @returns Singleton cache provider for the layer
|
|
1333
|
+
*/
|
|
1334
|
+
static getInstanceForLayer(layer, serviceName, namespace = "default") {
|
|
1335
|
+
const instanceKey = `${layer}:${serviceName}:${namespace}`;
|
|
1336
|
+
if (!_CacheFactory.instances.has(instanceKey)) {
|
|
1337
|
+
const cache = _CacheFactory.createCacheForLayer(layer, serviceName, namespace);
|
|
1338
|
+
_CacheFactory.instances.set(instanceKey, cache);
|
|
1339
|
+
}
|
|
1340
|
+
return _CacheFactory.instances.get(instanceKey);
|
|
1341
|
+
}
|
|
1342
|
+
};
|
|
1343
|
+
|
|
1344
|
+
// src/cache/types/config.ts
|
|
1345
|
+
var CacheProviderType = /* @__PURE__ */ (function(CacheProviderType2) {
|
|
1346
|
+
CacheProviderType2["VALKEY"] = "valkey";
|
|
1347
|
+
CacheProviderType2["MEMORY"] = "memory";
|
|
1348
|
+
CacheProviderType2["MEMCACHED"] = "memcached";
|
|
1349
|
+
return CacheProviderType2;
|
|
1350
|
+
})({});
|
|
1351
|
+
|
|
1352
|
+
// src/cache/index.ts
|
|
1353
|
+
function createCache(namespace = "default") {
|
|
1354
|
+
return CacheFactory.createCacheFromEnv(namespace);
|
|
1355
|
+
}
|
|
1356
|
+
__name(createCache, "createCache");
|
|
1357
|
+
function getCache(namespace = "default", config) {
|
|
1358
|
+
return CacheFactory.getInstance(namespace, config);
|
|
1359
|
+
}
|
|
1360
|
+
__name(getCache, "getCache");
|
|
1361
|
+
async function getValkeyClient(config) {
|
|
1362
|
+
const glideModule = await import('@valkey/valkey-glide');
|
|
1363
|
+
const GlideClient = glideModule.GlideClient || glideModule.default;
|
|
1364
|
+
const valkeyConfig = config || {
|
|
1365
|
+
type: CacheProviderType.VALKEY,
|
|
1366
|
+
url: process.env["VALKEY_URL"],
|
|
1367
|
+
host: process.env["VALKEY_HOST"] || "localhost",
|
|
1368
|
+
port: process.env["VALKEY_PORT"] ? parseInt(process.env["VALKEY_PORT"], 10) : 6379,
|
|
1369
|
+
password: process.env["VALKEY_PASSWORD"],
|
|
1370
|
+
db: process.env["VALKEY_DB"] ? parseInt(process.env["VALKEY_DB"], 10) : 0,
|
|
1371
|
+
username: process.env["VALKEY_USERNAME"]
|
|
1372
|
+
};
|
|
1373
|
+
let host = valkeyConfig.host || "localhost";
|
|
1374
|
+
let port = valkeyConfig.port || 6379;
|
|
1375
|
+
if (valkeyConfig.url) {
|
|
1376
|
+
const url = new URL(valkeyConfig.url.replace("valkey://", "redis://"));
|
|
1377
|
+
host = url.hostname;
|
|
1378
|
+
port = parseInt(url.port || "6379", 10);
|
|
1379
|
+
}
|
|
1380
|
+
const createClient = GlideClient;
|
|
1381
|
+
return createClient.createClient({
|
|
1382
|
+
addresses: [
|
|
1383
|
+
{
|
|
1384
|
+
host,
|
|
1385
|
+
port
|
|
1386
|
+
}
|
|
1387
|
+
],
|
|
1388
|
+
useTLS: valkeyConfig.useTLS,
|
|
1389
|
+
credentials: valkeyConfig.password ? {
|
|
1390
|
+
password: valkeyConfig.password,
|
|
1391
|
+
username: valkeyConfig.username
|
|
1392
|
+
} : void 0,
|
|
1393
|
+
clientName: valkeyConfig.name || "valkey-glide-client",
|
|
1394
|
+
readFrom: "primary"
|
|
1395
|
+
});
|
|
1396
|
+
}
|
|
1397
|
+
__name(getValkeyClient, "getValkeyClient");
|
|
1398
|
+
async function closeAllCaches() {
|
|
1399
|
+
await CacheFactory.closeAll();
|
|
1400
|
+
}
|
|
1401
|
+
__name(closeAllCaches, "closeAllCaches");
|
|
1402
|
+
|
|
1403
|
+
export { CacheFactory, CacheProviderType, MemoryCacheProvider, ValkeyGlideProvider, cache_exports, closeAllCaches, createCache, getCache, getValkeyClient };
|
|
1404
|
+
//# sourceMappingURL=chunk-4XGGWW36.js.map
|
|
1405
|
+
//# sourceMappingURL=chunk-4XGGWW36.js.map
|