@happyvertical/cache 0.80.0 → 0.80.1
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/dist/chunks/file-BiNbZgFQ.js +350 -0
- package/dist/chunks/file-BiNbZgFQ.js.map +1 -0
- package/dist/chunks/memory-7jBOw6kZ.js +224 -0
- package/dist/chunks/memory-7jBOw6kZ.js.map +1 -0
- package/dist/chunks/redis-BMACvZJq.js +268 -0
- package/dist/chunks/redis-BMACvZJq.js.map +1 -0
- package/dist/chunks/s3-DG191QH0.js +348 -0
- package/dist/chunks/s3-DG191QH0.js.map +1 -0
- package/dist/cli/claude-context.js +17 -17
- package/dist/cli/claude-context.js.map +1 -1
- package/dist/index.js +272 -141
- package/dist/index.js.map +1 -1
- package/package.json +5 -5
- package/dist/chunks/file-DyC_7WDS.js +0 -450
- package/dist/chunks/file-DyC_7WDS.js.map +0 -1
- package/dist/chunks/memory-C6vfNZYg.js +0 -274
- package/dist/chunks/memory-C6vfNZYg.js.map +0 -1
- package/dist/chunks/redis-D-SNLXE_.js +0 -365
- package/dist/chunks/redis-D-SNLXE_.js.map +0 -1
- package/dist/chunks/s3-ByokNFv_.js +0 -427
- package/dist/chunks/s3-ByokNFv_.js.map +0 -1
|
@@ -1,274 +0,0 @@
|
|
|
1
|
-
import { isValidKey, CacheKeyError, formatKey, isExpired, calculateSize, calculateExpiration, extractKey, matchesPattern, CacheSizeError } from "../index.js";
|
|
2
|
-
class MemoryProvider {
|
|
3
|
-
cache;
|
|
4
|
-
namespace;
|
|
5
|
-
defaultTTL;
|
|
6
|
-
maxSize;
|
|
7
|
-
maxEntries;
|
|
8
|
-
evictionPolicy;
|
|
9
|
-
checkPeriod;
|
|
10
|
-
checkInterval;
|
|
11
|
-
stats;
|
|
12
|
-
constructor(options) {
|
|
13
|
-
this.cache = /* @__PURE__ */ new Map();
|
|
14
|
-
this.namespace = options.namespace;
|
|
15
|
-
this.defaultTTL = options.defaultTTL;
|
|
16
|
-
this.maxSize = options.maxSize || 100 * 1024 * 1024;
|
|
17
|
-
this.maxEntries = options.maxEntries || 1e4;
|
|
18
|
-
this.evictionPolicy = options.evictionPolicy || "lru";
|
|
19
|
-
this.checkPeriod = options.checkPeriod || 6e4;
|
|
20
|
-
this.stats = {
|
|
21
|
-
hits: 0,
|
|
22
|
-
misses: 0,
|
|
23
|
-
evictions: 0
|
|
24
|
-
};
|
|
25
|
-
this.startExpirationCheck();
|
|
26
|
-
}
|
|
27
|
-
async get(key) {
|
|
28
|
-
if (!isValidKey(key)) {
|
|
29
|
-
throw new CacheKeyError(key, "memory");
|
|
30
|
-
}
|
|
31
|
-
const fullKey = formatKey(this.namespace, key);
|
|
32
|
-
const entry = this.cache.get(fullKey);
|
|
33
|
-
if (!entry) {
|
|
34
|
-
this.stats.misses++;
|
|
35
|
-
return void 0;
|
|
36
|
-
}
|
|
37
|
-
if (isExpired(entry.expiresAt)) {
|
|
38
|
-
this.cache.delete(fullKey);
|
|
39
|
-
this.stats.misses++;
|
|
40
|
-
return void 0;
|
|
41
|
-
}
|
|
42
|
-
entry.hits++;
|
|
43
|
-
if (this.evictionPolicy === "lru") {
|
|
44
|
-
this.cache.delete(fullKey);
|
|
45
|
-
this.cache.set(fullKey, entry);
|
|
46
|
-
}
|
|
47
|
-
this.stats.hits++;
|
|
48
|
-
return entry.value;
|
|
49
|
-
}
|
|
50
|
-
async set(key, value, ttl) {
|
|
51
|
-
if (!isValidKey(key)) {
|
|
52
|
-
throw new CacheKeyError(key, "memory");
|
|
53
|
-
}
|
|
54
|
-
const fullKey = formatKey(this.namespace, key);
|
|
55
|
-
const size = calculateSize(value);
|
|
56
|
-
const expiresAt = calculateExpiration(ttl ?? this.defaultTTL);
|
|
57
|
-
const entry = {
|
|
58
|
-
value,
|
|
59
|
-
createdAt: Date.now(),
|
|
60
|
-
expiresAt,
|
|
61
|
-
size,
|
|
62
|
-
hits: 0,
|
|
63
|
-
metadata: {
|
|
64
|
-
namespace: this.namespace
|
|
65
|
-
}
|
|
66
|
-
};
|
|
67
|
-
await this.evictIfNeeded(size);
|
|
68
|
-
this.cache.set(fullKey, entry);
|
|
69
|
-
}
|
|
70
|
-
async has(key) {
|
|
71
|
-
if (!isValidKey(key)) {
|
|
72
|
-
throw new CacheKeyError(key, "memory");
|
|
73
|
-
}
|
|
74
|
-
const fullKey = formatKey(this.namespace, key);
|
|
75
|
-
const entry = this.cache.get(fullKey);
|
|
76
|
-
if (!entry) {
|
|
77
|
-
return false;
|
|
78
|
-
}
|
|
79
|
-
if (isExpired(entry.expiresAt)) {
|
|
80
|
-
this.cache.delete(fullKey);
|
|
81
|
-
return false;
|
|
82
|
-
}
|
|
83
|
-
return true;
|
|
84
|
-
}
|
|
85
|
-
async delete(key) {
|
|
86
|
-
if (!isValidKey(key)) {
|
|
87
|
-
throw new CacheKeyError(key, "memory");
|
|
88
|
-
}
|
|
89
|
-
const fullKey = formatKey(this.namespace, key);
|
|
90
|
-
return this.cache.delete(fullKey);
|
|
91
|
-
}
|
|
92
|
-
async clear(namespace) {
|
|
93
|
-
if (namespace) {
|
|
94
|
-
const prefix = `${namespace}:`;
|
|
95
|
-
for (const key of this.cache.keys()) {
|
|
96
|
-
if (key.startsWith(prefix)) {
|
|
97
|
-
this.cache.delete(key);
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
} else {
|
|
101
|
-
this.cache.clear();
|
|
102
|
-
this.stats.hits = 0;
|
|
103
|
-
this.stats.misses = 0;
|
|
104
|
-
this.stats.evictions = 0;
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
async keys(pattern) {
|
|
108
|
-
const allKeys = Array.from(this.cache.keys());
|
|
109
|
-
const validKeys = [];
|
|
110
|
-
for (const key of allKeys) {
|
|
111
|
-
const entry = this.cache.get(key);
|
|
112
|
-
if (entry && !isExpired(entry.expiresAt)) {
|
|
113
|
-
validKeys.push(extractKey(this.namespace, key));
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
if (pattern) {
|
|
117
|
-
return validKeys.filter((key) => matchesPattern(pattern, key));
|
|
118
|
-
}
|
|
119
|
-
return validKeys;
|
|
120
|
-
}
|
|
121
|
-
async getMany(keys) {
|
|
122
|
-
const result = /* @__PURE__ */ new Map();
|
|
123
|
-
for (const key of keys) {
|
|
124
|
-
const value = await this.get(key);
|
|
125
|
-
if (value !== void 0) {
|
|
126
|
-
result.set(key, value);
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
return result;
|
|
130
|
-
}
|
|
131
|
-
async setMany(entries) {
|
|
132
|
-
for (const entry of entries) {
|
|
133
|
-
await this.set(entry.key, entry.value, entry.ttl);
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
async deleteMany(keys) {
|
|
137
|
-
let deleted = 0;
|
|
138
|
-
for (const key of keys) {
|
|
139
|
-
const wasDeleted = await this.delete(key);
|
|
140
|
-
if (wasDeleted) {
|
|
141
|
-
deleted++;
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
return deleted;
|
|
145
|
-
}
|
|
146
|
-
async getStats() {
|
|
147
|
-
let totalSize = 0;
|
|
148
|
-
let entries = 0;
|
|
149
|
-
for (const entry of this.cache.values()) {
|
|
150
|
-
if (!isExpired(entry.expiresAt)) {
|
|
151
|
-
totalSize += entry.size;
|
|
152
|
-
entries++;
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
const totalAccesses = this.stats.hits + this.stats.misses;
|
|
156
|
-
const hitRate = totalAccesses > 0 ? this.stats.hits / totalAccesses : 0;
|
|
157
|
-
return {
|
|
158
|
-
entries,
|
|
159
|
-
totalSize,
|
|
160
|
-
hits: this.stats.hits,
|
|
161
|
-
misses: this.stats.misses,
|
|
162
|
-
hitRate,
|
|
163
|
-
evictions: this.stats.evictions,
|
|
164
|
-
backend: {
|
|
165
|
-
type: "memory",
|
|
166
|
-
evictionPolicy: this.evictionPolicy,
|
|
167
|
-
maxSize: this.maxSize,
|
|
168
|
-
maxEntries: this.maxEntries
|
|
169
|
-
}
|
|
170
|
-
};
|
|
171
|
-
}
|
|
172
|
-
async touch(key, ttl) {
|
|
173
|
-
if (!isValidKey(key)) {
|
|
174
|
-
throw new CacheKeyError(key, "memory");
|
|
175
|
-
}
|
|
176
|
-
const fullKey = formatKey(this.namespace, key);
|
|
177
|
-
const entry = this.cache.get(fullKey);
|
|
178
|
-
if (!entry || isExpired(entry.expiresAt)) {
|
|
179
|
-
return false;
|
|
180
|
-
}
|
|
181
|
-
entry.expiresAt = calculateExpiration(ttl);
|
|
182
|
-
return true;
|
|
183
|
-
}
|
|
184
|
-
async close() {
|
|
185
|
-
if (this.checkInterval) {
|
|
186
|
-
clearInterval(this.checkInterval);
|
|
187
|
-
this.checkInterval = void 0;
|
|
188
|
-
}
|
|
189
|
-
this.cache.clear();
|
|
190
|
-
}
|
|
191
|
-
/**
|
|
192
|
-
* Evicts entries if size or count limits are exceeded
|
|
193
|
-
*/
|
|
194
|
-
async evictIfNeeded(newEntrySize) {
|
|
195
|
-
const stats = await this.getStats();
|
|
196
|
-
if (stats.entries >= this.maxEntries) {
|
|
197
|
-
await this.evict(1);
|
|
198
|
-
}
|
|
199
|
-
while (stats.totalSize + newEntrySize > this.maxSize && this.cache.size > 0) {
|
|
200
|
-
await this.evict(1);
|
|
201
|
-
const updatedStats = await this.getStats();
|
|
202
|
-
if (updatedStats.totalSize + newEntrySize <= this.maxSize) {
|
|
203
|
-
break;
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
const finalStats = await this.getStats();
|
|
207
|
-
if (finalStats.totalSize + newEntrySize > this.maxSize) {
|
|
208
|
-
throw new CacheSizeError(
|
|
209
|
-
`Cannot cache entry: would exceed max size of ${this.maxSize} bytes`,
|
|
210
|
-
"memory"
|
|
211
|
-
);
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
/**
|
|
215
|
-
* Evicts entries based on eviction policy
|
|
216
|
-
*/
|
|
217
|
-
async evict(count) {
|
|
218
|
-
if (this.cache.size === 0) {
|
|
219
|
-
return;
|
|
220
|
-
}
|
|
221
|
-
const entries = Array.from(this.cache.entries());
|
|
222
|
-
switch (this.evictionPolicy) {
|
|
223
|
-
case "lru": {
|
|
224
|
-
for (let i = 0; i < count && i < entries.length; i++) {
|
|
225
|
-
this.cache.delete(entries[i][0]);
|
|
226
|
-
this.stats.evictions++;
|
|
227
|
-
}
|
|
228
|
-
break;
|
|
229
|
-
}
|
|
230
|
-
case "lfu": {
|
|
231
|
-
const sorted = entries.sort((a, b) => a[1].hits - b[1].hits);
|
|
232
|
-
for (let i = 0; i < count && i < sorted.length; i++) {
|
|
233
|
-
this.cache.delete(sorted[i][0]);
|
|
234
|
-
this.stats.evictions++;
|
|
235
|
-
}
|
|
236
|
-
break;
|
|
237
|
-
}
|
|
238
|
-
case "fifo": {
|
|
239
|
-
const sorted = entries.sort((a, b) => a[1].createdAt - b[1].createdAt);
|
|
240
|
-
for (let i = 0; i < count && i < sorted.length; i++) {
|
|
241
|
-
this.cache.delete(sorted[i][0]);
|
|
242
|
-
this.stats.evictions++;
|
|
243
|
-
}
|
|
244
|
-
break;
|
|
245
|
-
}
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
/**
|
|
249
|
-
* Starts background task to remove expired entries
|
|
250
|
-
*/
|
|
251
|
-
startExpirationCheck() {
|
|
252
|
-
this.checkInterval = setInterval(() => {
|
|
253
|
-
this.removeExpiredEntries();
|
|
254
|
-
}, this.checkPeriod);
|
|
255
|
-
if (this.checkInterval.unref) {
|
|
256
|
-
this.checkInterval.unref();
|
|
257
|
-
}
|
|
258
|
-
}
|
|
259
|
-
/**
|
|
260
|
-
* Removes all expired entries from the cache
|
|
261
|
-
*/
|
|
262
|
-
removeExpiredEntries() {
|
|
263
|
-
const now = Date.now();
|
|
264
|
-
for (const [key, entry] of this.cache.entries()) {
|
|
265
|
-
if (entry.expiresAt && now >= entry.expiresAt) {
|
|
266
|
-
this.cache.delete(key);
|
|
267
|
-
}
|
|
268
|
-
}
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
export {
|
|
272
|
-
MemoryProvider
|
|
273
|
-
};
|
|
274
|
-
//# sourceMappingURL=memory-C6vfNZYg.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"memory-C6vfNZYg.js","sources":["../../src/providers/memory.ts"],"sourcesContent":["/**\n * Memory cache provider implementation with LRU eviction\n */\n\nimport type {\n CacheEntry,\n CacheProvider,\n CacheStats,\n MemoryOptions,\n} from '../shared/types';\nimport { CacheKeyError, CacheSizeError } from '../shared/types';\nimport {\n calculateExpiration,\n calculateSize,\n extractKey,\n formatKey,\n isExpired,\n isValidKey,\n matchesPattern,\n} from '../shared/utils';\n\n/**\n * Memory cache provider implementation\n * Stores cache entries in memory with LRU eviction\n */\nexport class MemoryProvider implements CacheProvider {\n private cache: Map<string, CacheEntry>;\n private namespace?: string;\n private defaultTTL?: number;\n private maxSize: number;\n private maxEntries: number;\n private evictionPolicy: 'lru' | 'lfu' | 'fifo';\n private checkPeriod: number;\n private checkInterval?: NodeJS.Timeout;\n private stats: {\n hits: number;\n misses: number;\n evictions: number;\n };\n\n constructor(options: MemoryOptions) {\n this.cache = new Map();\n this.namespace = options.namespace;\n this.defaultTTL = options.defaultTTL;\n this.maxSize = options.maxSize || 100 * 1024 * 1024; // 100MB default\n this.maxEntries = options.maxEntries || 10000; // 10k entries default\n this.evictionPolicy = options.evictionPolicy || 'lru';\n this.checkPeriod = options.checkPeriod || 60000; // 1 minute default\n this.stats = {\n hits: 0,\n misses: 0,\n evictions: 0,\n };\n\n // Start background expiration check\n this.startExpirationCheck();\n }\n\n async get<T = any>(key: string): Promise<T | undefined> {\n if (!isValidKey(key)) {\n throw new CacheKeyError(key, 'memory');\n }\n\n const fullKey = formatKey(this.namespace, key);\n const entry = this.cache.get(fullKey);\n\n if (!entry) {\n this.stats.misses++;\n return undefined;\n }\n\n // Check if expired\n if (isExpired(entry.expiresAt)) {\n this.cache.delete(fullKey);\n this.stats.misses++;\n return undefined;\n }\n\n // Update access statistics for LRU/LFU\n entry.hits++;\n\n // For LRU, move to end of map (most recently used)\n if (this.evictionPolicy === 'lru') {\n this.cache.delete(fullKey);\n this.cache.set(fullKey, entry);\n }\n\n this.stats.hits++;\n return entry.value as T;\n }\n\n async set<T = any>(key: string, value: T, ttl?: number): Promise<void> {\n if (!isValidKey(key)) {\n throw new CacheKeyError(key, 'memory');\n }\n\n const fullKey = formatKey(this.namespace, key);\n const size = calculateSize(value);\n const expiresAt = calculateExpiration(ttl ?? this.defaultTTL);\n\n const entry: CacheEntry<T> = {\n value,\n createdAt: Date.now(),\n expiresAt,\n size,\n hits: 0,\n metadata: {\n namespace: this.namespace,\n },\n };\n\n // Check if we need to evict entries\n await this.evictIfNeeded(size);\n\n this.cache.set(fullKey, entry);\n }\n\n async has(key: string): Promise<boolean> {\n if (!isValidKey(key)) {\n throw new CacheKeyError(key, 'memory');\n }\n\n const fullKey = formatKey(this.namespace, key);\n const entry = this.cache.get(fullKey);\n\n if (!entry) {\n return false;\n }\n\n // Check if expired\n if (isExpired(entry.expiresAt)) {\n this.cache.delete(fullKey);\n return false;\n }\n\n return true;\n }\n\n async delete(key: string): Promise<boolean> {\n if (!isValidKey(key)) {\n throw new CacheKeyError(key, 'memory');\n }\n\n const fullKey = formatKey(this.namespace, key);\n return this.cache.delete(fullKey);\n }\n\n async clear(namespace?: string): Promise<void> {\n if (namespace) {\n // Clear specific namespace\n const prefix = `${namespace}:`;\n for (const key of this.cache.keys()) {\n if (key.startsWith(prefix)) {\n this.cache.delete(key);\n }\n }\n } else {\n // Clear all entries\n this.cache.clear();\n this.stats.hits = 0;\n this.stats.misses = 0;\n this.stats.evictions = 0;\n }\n }\n\n async keys(pattern?: string): Promise<string[]> {\n const allKeys = Array.from(this.cache.keys());\n\n // Filter out expired entries and extract keys without namespace\n const validKeys: string[] = [];\n for (const key of allKeys) {\n const entry = this.cache.get(key);\n if (entry && !isExpired(entry.expiresAt)) {\n validKeys.push(extractKey(this.namespace, key));\n }\n }\n\n // Apply pattern filter if provided (pattern is applied to the extracted key, not the full key)\n if (pattern) {\n return validKeys.filter((key) => matchesPattern(pattern, key));\n }\n\n return validKeys;\n }\n\n async getMany<T = any>(keys: string[]): Promise<Map<string, T>> {\n const result = new Map<string, T>();\n\n for (const key of keys) {\n const value = await this.get<T>(key);\n if (value !== undefined) {\n result.set(key, value);\n }\n }\n\n return result;\n }\n\n async setMany<T = any>(\n entries: Array<{ key: string; value: T; ttl?: number }>,\n ): Promise<void> {\n for (const entry of entries) {\n await this.set(entry.key, entry.value, entry.ttl);\n }\n }\n\n async deleteMany(keys: string[]): Promise<number> {\n let deleted = 0;\n\n for (const key of keys) {\n const wasDeleted = await this.delete(key);\n if (wasDeleted) {\n deleted++;\n }\n }\n\n return deleted;\n }\n\n async getStats(): Promise<CacheStats> {\n // Calculate current cache size\n let totalSize = 0;\n let entries = 0;\n\n for (const entry of this.cache.values()) {\n if (!isExpired(entry.expiresAt)) {\n totalSize += entry.size;\n entries++;\n }\n }\n\n const totalAccesses = this.stats.hits + this.stats.misses;\n const hitRate = totalAccesses > 0 ? this.stats.hits / totalAccesses : 0;\n\n return {\n entries,\n totalSize,\n hits: this.stats.hits,\n misses: this.stats.misses,\n hitRate,\n evictions: this.stats.evictions,\n backend: {\n type: 'memory',\n evictionPolicy: this.evictionPolicy,\n maxSize: this.maxSize,\n maxEntries: this.maxEntries,\n },\n };\n }\n\n async touch(key: string, ttl: number): Promise<boolean> {\n if (!isValidKey(key)) {\n throw new CacheKeyError(key, 'memory');\n }\n\n const fullKey = formatKey(this.namespace, key);\n const entry = this.cache.get(fullKey);\n\n if (!entry || isExpired(entry.expiresAt)) {\n return false;\n }\n\n entry.expiresAt = calculateExpiration(ttl);\n return true;\n }\n\n async close(): Promise<void> {\n // Stop expiration check interval\n if (this.checkInterval) {\n clearInterval(this.checkInterval);\n this.checkInterval = undefined;\n }\n\n // Clear cache\n this.cache.clear();\n }\n\n /**\n * Evicts entries if size or count limits are exceeded\n */\n private async evictIfNeeded(newEntrySize: number): Promise<void> {\n const stats = await this.getStats();\n\n // Check if we need to evict based on entry count\n if (stats.entries >= this.maxEntries) {\n await this.evict(1);\n }\n\n // Check if we need to evict based on size\n while (\n stats.totalSize + newEntrySize > this.maxSize &&\n this.cache.size > 0\n ) {\n await this.evict(1);\n const updatedStats = await this.getStats();\n if (updatedStats.totalSize + newEntrySize <= this.maxSize) {\n break;\n }\n }\n\n // Final check - if still too large, throw error\n const finalStats = await this.getStats();\n if (finalStats.totalSize + newEntrySize > this.maxSize) {\n throw new CacheSizeError(\n `Cannot cache entry: would exceed max size of ${this.maxSize} bytes`,\n 'memory',\n );\n }\n }\n\n /**\n * Evicts entries based on eviction policy\n */\n private async evict(count: number): Promise<void> {\n if (this.cache.size === 0) {\n return;\n }\n\n const entries = Array.from(this.cache.entries());\n\n switch (this.evictionPolicy) {\n case 'lru': {\n // LRU: Remove oldest (first in map, since we move accessed items to end)\n for (let i = 0; i < count && i < entries.length; i++) {\n this.cache.delete(entries[i][0]);\n this.stats.evictions++;\n }\n break;\n }\n\n case 'lfu': {\n // LFU: Remove least frequently used\n const sorted = entries.sort((a, b) => a[1].hits - b[1].hits);\n for (let i = 0; i < count && i < sorted.length; i++) {\n this.cache.delete(sorted[i][0]);\n this.stats.evictions++;\n }\n break;\n }\n\n case 'fifo': {\n // FIFO: Remove oldest by creation time\n const sorted = entries.sort((a, b) => a[1].createdAt - b[1].createdAt);\n for (let i = 0; i < count && i < sorted.length; i++) {\n this.cache.delete(sorted[i][0]);\n this.stats.evictions++;\n }\n break;\n }\n }\n }\n\n /**\n * Starts background task to remove expired entries\n */\n private startExpirationCheck(): void {\n this.checkInterval = setInterval(() => {\n this.removeExpiredEntries();\n }, this.checkPeriod);\n\n // Don't block process exit\n if (this.checkInterval.unref) {\n this.checkInterval.unref();\n }\n }\n\n /**\n * Removes all expired entries from the cache\n */\n private removeExpiredEntries(): void {\n const now = Date.now();\n\n for (const [key, entry] of this.cache.entries()) {\n if (entry.expiresAt && now >= entry.expiresAt) {\n this.cache.delete(key);\n }\n }\n }\n}\n"],"names":[],"mappings":";AAyBO,MAAM,eAAwC;AAAA,EAC3C;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAMR,YAAY,SAAwB;AAClC,SAAK,4BAAY,IAAA;AACjB,SAAK,YAAY,QAAQ;AACzB,SAAK,aAAa,QAAQ;AAC1B,SAAK,UAAU,QAAQ,WAAW,MAAM,OAAO;AAC/C,SAAK,aAAa,QAAQ,cAAc;AACxC,SAAK,iBAAiB,QAAQ,kBAAkB;AAChD,SAAK,cAAc,QAAQ,eAAe;AAC1C,SAAK,QAAQ;AAAA,MACX,MAAM;AAAA,MACN,QAAQ;AAAA,MACR,WAAW;AAAA,IAAA;AAIb,SAAK,qBAAA;AAAA,EACP;AAAA,EAEA,MAAM,IAAa,KAAqC;AACtD,QAAI,CAAC,WAAW,GAAG,GAAG;AACpB,YAAM,IAAI,cAAc,KAAK,QAAQ;AAAA,IACvC;AAEA,UAAM,UAAU,UAAU,KAAK,WAAW,GAAG;AAC7C,UAAM,QAAQ,KAAK,MAAM,IAAI,OAAO;AAEpC,QAAI,CAAC,OAAO;AACV,WAAK,MAAM;AACX,aAAO;AAAA,IACT;AAGA,QAAI,UAAU,MAAM,SAAS,GAAG;AAC9B,WAAK,MAAM,OAAO,OAAO;AACzB,WAAK,MAAM;AACX,aAAO;AAAA,IACT;AAGA,UAAM;AAGN,QAAI,KAAK,mBAAmB,OAAO;AACjC,WAAK,MAAM,OAAO,OAAO;AACzB,WAAK,MAAM,IAAI,SAAS,KAAK;AAAA,IAC/B;AAEA,SAAK,MAAM;AACX,WAAO,MAAM;AAAA,EACf;AAAA,EAEA,MAAM,IAAa,KAAa,OAAU,KAA6B;AACrE,QAAI,CAAC,WAAW,GAAG,GAAG;AACpB,YAAM,IAAI,cAAc,KAAK,QAAQ;AAAA,IACvC;AAEA,UAAM,UAAU,UAAU,KAAK,WAAW,GAAG;AAC7C,UAAM,OAAO,cAAc,KAAK;AAChC,UAAM,YAAY,oBAAoB,OAAO,KAAK,UAAU;AAE5D,UAAM,QAAuB;AAAA,MAC3B;AAAA,MACA,WAAW,KAAK,IAAA;AAAA,MAChB;AAAA,MACA;AAAA,MACA,MAAM;AAAA,MACN,UAAU;AAAA,QACR,WAAW,KAAK;AAAA,MAAA;AAAA,IAClB;AAIF,UAAM,KAAK,cAAc,IAAI;AAE7B,SAAK,MAAM,IAAI,SAAS,KAAK;AAAA,EAC/B;AAAA,EAEA,MAAM,IAAI,KAA+B;AACvC,QAAI,CAAC,WAAW,GAAG,GAAG;AACpB,YAAM,IAAI,cAAc,KAAK,QAAQ;AAAA,IACvC;AAEA,UAAM,UAAU,UAAU,KAAK,WAAW,GAAG;AAC7C,UAAM,QAAQ,KAAK,MAAM,IAAI,OAAO;AAEpC,QAAI,CAAC,OAAO;AACV,aAAO;AAAA,IACT;AAGA,QAAI,UAAU,MAAM,SAAS,GAAG;AAC9B,WAAK,MAAM,OAAO,OAAO;AACzB,aAAO;AAAA,IACT;AAEA,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,OAAO,KAA+B;AAC1C,QAAI,CAAC,WAAW,GAAG,GAAG;AACpB,YAAM,IAAI,cAAc,KAAK,QAAQ;AAAA,IACvC;AAEA,UAAM,UAAU,UAAU,KAAK,WAAW,GAAG;AAC7C,WAAO,KAAK,MAAM,OAAO,OAAO;AAAA,EAClC;AAAA,EAEA,MAAM,MAAM,WAAmC;AAC7C,QAAI,WAAW;AAEb,YAAM,SAAS,GAAG,SAAS;AAC3B,iBAAW,OAAO,KAAK,MAAM,KAAA,GAAQ;AACnC,YAAI,IAAI,WAAW,MAAM,GAAG;AAC1B,eAAK,MAAM,OAAO,GAAG;AAAA,QACvB;AAAA,MACF;AAAA,IACF,OAAO;AAEL,WAAK,MAAM,MAAA;AACX,WAAK,MAAM,OAAO;AAClB,WAAK,MAAM,SAAS;AACpB,WAAK,MAAM,YAAY;AAAA,IACzB;AAAA,EACF;AAAA,EAEA,MAAM,KAAK,SAAqC;AAC9C,UAAM,UAAU,MAAM,KAAK,KAAK,MAAM,MAAM;AAG5C,UAAM,YAAsB,CAAA;AAC5B,eAAW,OAAO,SAAS;AACzB,YAAM,QAAQ,KAAK,MAAM,IAAI,GAAG;AAChC,UAAI,SAAS,CAAC,UAAU,MAAM,SAAS,GAAG;AACxC,kBAAU,KAAK,WAAW,KAAK,WAAW,GAAG,CAAC;AAAA,MAChD;AAAA,IACF;AAGA,QAAI,SAAS;AACX,aAAO,UAAU,OAAO,CAAC,QAAQ,eAAe,SAAS,GAAG,CAAC;AAAA,IAC/D;AAEA,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,QAAiB,MAAyC;AAC9D,UAAM,6BAAa,IAAA;AAEnB,eAAW,OAAO,MAAM;AACtB,YAAM,QAAQ,MAAM,KAAK,IAAO,GAAG;AACnC,UAAI,UAAU,QAAW;AACvB,eAAO,IAAI,KAAK,KAAK;AAAA,MACvB;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,QACJ,SACe;AACf,eAAW,SAAS,SAAS;AAC3B,YAAM,KAAK,IAAI,MAAM,KAAK,MAAM,OAAO,MAAM,GAAG;AAAA,IAClD;AAAA,EACF;AAAA,EAEA,MAAM,WAAW,MAAiC;AAChD,QAAI,UAAU;AAEd,eAAW,OAAO,MAAM;AACtB,YAAM,aAAa,MAAM,KAAK,OAAO,GAAG;AACxC,UAAI,YAAY;AACd;AAAA,MACF;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,WAAgC;AAEpC,QAAI,YAAY;AAChB,QAAI,UAAU;AAEd,eAAW,SAAS,KAAK,MAAM,OAAA,GAAU;AACvC,UAAI,CAAC,UAAU,MAAM,SAAS,GAAG;AAC/B,qBAAa,MAAM;AACnB;AAAA,MACF;AAAA,IACF;AAEA,UAAM,gBAAgB,KAAK,MAAM,OAAO,KAAK,MAAM;AACnD,UAAM,UAAU,gBAAgB,IAAI,KAAK,MAAM,OAAO,gBAAgB;AAEtE,WAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA,MAAM,KAAK,MAAM;AAAA,MACjB,QAAQ,KAAK,MAAM;AAAA,MACnB;AAAA,MACA,WAAW,KAAK,MAAM;AAAA,MACtB,SAAS;AAAA,QACP,MAAM;AAAA,QACN,gBAAgB,KAAK;AAAA,QACrB,SAAS,KAAK;AAAA,QACd,YAAY,KAAK;AAAA,MAAA;AAAA,IACnB;AAAA,EAEJ;AAAA,EAEA,MAAM,MAAM,KAAa,KAA+B;AACtD,QAAI,CAAC,WAAW,GAAG,GAAG;AACpB,YAAM,IAAI,cAAc,KAAK,QAAQ;AAAA,IACvC;AAEA,UAAM,UAAU,UAAU,KAAK,WAAW,GAAG;AAC7C,UAAM,QAAQ,KAAK,MAAM,IAAI,OAAO;AAEpC,QAAI,CAAC,SAAS,UAAU,MAAM,SAAS,GAAG;AACxC,aAAO;AAAA,IACT;AAEA,UAAM,YAAY,oBAAoB,GAAG;AACzC,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,QAAuB;AAE3B,QAAI,KAAK,eAAe;AACtB,oBAAc,KAAK,aAAa;AAChC,WAAK,gBAAgB;AAAA,IACvB;AAGA,SAAK,MAAM,MAAA;AAAA,EACb;AAAA;AAAA;AAAA;AAAA,EAKA,MAAc,cAAc,cAAqC;AAC/D,UAAM,QAAQ,MAAM,KAAK,SAAA;AAGzB,QAAI,MAAM,WAAW,KAAK,YAAY;AACpC,YAAM,KAAK,MAAM,CAAC;AAAA,IACpB;AAGA,WACE,MAAM,YAAY,eAAe,KAAK,WACtC,KAAK,MAAM,OAAO,GAClB;AACA,YAAM,KAAK,MAAM,CAAC;AAClB,YAAM,eAAe,MAAM,KAAK,SAAA;AAChC,UAAI,aAAa,YAAY,gBAAgB,KAAK,SAAS;AACzD;AAAA,MACF;AAAA,IACF;AAGA,UAAM,aAAa,MAAM,KAAK,SAAA;AAC9B,QAAI,WAAW,YAAY,eAAe,KAAK,SAAS;AACtD,YAAM,IAAI;AAAA,QACR,gDAAgD,KAAK,OAAO;AAAA,QAC5D;AAAA,MAAA;AAAA,IAEJ;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAc,MAAM,OAA8B;AAChD,QAAI,KAAK,MAAM,SAAS,GAAG;AACzB;AAAA,IACF;AAEA,UAAM,UAAU,MAAM,KAAK,KAAK,MAAM,SAAS;AAE/C,YAAQ,KAAK,gBAAA;AAAA,MACX,KAAK,OAAO;AAEV,iBAAS,IAAI,GAAG,IAAI,SAAS,IAAI,QAAQ,QAAQ,KAAK;AACpD,eAAK,MAAM,OAAO,QAAQ,CAAC,EAAE,CAAC,CAAC;AAC/B,eAAK,MAAM;AAAA,QACb;AACA;AAAA,MACF;AAAA,MAEA,KAAK,OAAO;AAEV,cAAM,SAAS,QAAQ,KAAK,CAAC,GAAG,MAAM,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI;AAC3D,iBAAS,IAAI,GAAG,IAAI,SAAS,IAAI,OAAO,QAAQ,KAAK;AACnD,eAAK,MAAM,OAAO,OAAO,CAAC,EAAE,CAAC,CAAC;AAC9B,eAAK,MAAM;AAAA,QACb;AACA;AAAA,MACF;AAAA,MAEA,KAAK,QAAQ;AAEX,cAAM,SAAS,QAAQ,KAAK,CAAC,GAAG,MAAM,EAAE,CAAC,EAAE,YAAY,EAAE,CAAC,EAAE,SAAS;AACrE,iBAAS,IAAI,GAAG,IAAI,SAAS,IAAI,OAAO,QAAQ,KAAK;AACnD,eAAK,MAAM,OAAO,OAAO,CAAC,EAAE,CAAC,CAAC;AAC9B,eAAK,MAAM;AAAA,QACb;AACA;AAAA,MACF;AAAA,IAAA;AAAA,EAEJ;AAAA;AAAA;AAAA;AAAA,EAKQ,uBAA6B;AACnC,SAAK,gBAAgB,YAAY,MAAM;AACrC,WAAK,qBAAA;AAAA,IACP,GAAG,KAAK,WAAW;AAGnB,QAAI,KAAK,cAAc,OAAO;AAC5B,WAAK,cAAc,MAAA;AAAA,IACrB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKQ,uBAA6B;AACnC,UAAM,MAAM,KAAK,IAAA;AAEjB,eAAW,CAAC,KAAK,KAAK,KAAK,KAAK,MAAM,WAAW;AAC/C,UAAI,MAAM,aAAa,OAAO,MAAM,WAAW;AAC7C,aAAK,MAAM,OAAO,GAAG;AAAA,MACvB;AAAA,IACF;AAAA,EACF;AACF;"}
|
|
@@ -1,365 +0,0 @@
|
|
|
1
|
-
import { promisify } from "node:util";
|
|
2
|
-
import { gunzip, gzip } from "node:zlib";
|
|
3
|
-
import { createClient } from "redis";
|
|
4
|
-
import { CacheConnectionError, isValidKey, CacheKeyError, formatKey, deserialize, CacheError, serialize, CacheSerializationError } from "../index.js";
|
|
5
|
-
const gzipAsync = promisify(gzip);
|
|
6
|
-
const gunzipAsync = promisify(gunzip);
|
|
7
|
-
class RedisProvider {
|
|
8
|
-
constructor(options) {
|
|
9
|
-
this.options = options;
|
|
10
|
-
this.namespace = options.namespace || options.keyPrefix;
|
|
11
|
-
this.defaultTTL = options.defaultTTL;
|
|
12
|
-
this.enableCompression = options.enableCompression ?? false;
|
|
13
|
-
this.compressionThreshold = options.compressionThreshold || 1024;
|
|
14
|
-
this.stats = {
|
|
15
|
-
hits: 0,
|
|
16
|
-
misses: 0
|
|
17
|
-
};
|
|
18
|
-
this.client = createClient({
|
|
19
|
-
socket: {
|
|
20
|
-
host: options.host || "localhost",
|
|
21
|
-
port: options.port || 6379,
|
|
22
|
-
connectTimeout: options.connectTimeout || 5e3
|
|
23
|
-
},
|
|
24
|
-
password: options.password,
|
|
25
|
-
database: options.db || 0,
|
|
26
|
-
commandsQueueMaxLength: 1e3
|
|
27
|
-
});
|
|
28
|
-
this.client.on("error", (error) => {
|
|
29
|
-
console.error("Redis connection error:", error);
|
|
30
|
-
});
|
|
31
|
-
this.client.on("connect", () => {
|
|
32
|
-
this.connected = true;
|
|
33
|
-
});
|
|
34
|
-
this.client.on("end", () => {
|
|
35
|
-
this.connected = false;
|
|
36
|
-
});
|
|
37
|
-
}
|
|
38
|
-
client;
|
|
39
|
-
namespace;
|
|
40
|
-
defaultTTL;
|
|
41
|
-
enableCompression;
|
|
42
|
-
compressionThreshold;
|
|
43
|
-
stats;
|
|
44
|
-
connected = false;
|
|
45
|
-
/**
|
|
46
|
-
* Ensures the client is connected
|
|
47
|
-
*/
|
|
48
|
-
async ensureConnected() {
|
|
49
|
-
if (!this.connected) {
|
|
50
|
-
try {
|
|
51
|
-
await this.client.connect();
|
|
52
|
-
this.connected = true;
|
|
53
|
-
} catch (error) {
|
|
54
|
-
throw new CacheConnectionError(
|
|
55
|
-
`Failed to connect to Redis: ${error.message}`,
|
|
56
|
-
"redis"
|
|
57
|
-
);
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
async get(key) {
|
|
62
|
-
if (!isValidKey(key)) {
|
|
63
|
-
throw new CacheKeyError(key, "redis");
|
|
64
|
-
}
|
|
65
|
-
await this.ensureConnected();
|
|
66
|
-
const fullKey = formatKey(this.namespace, key);
|
|
67
|
-
try {
|
|
68
|
-
const value = await this.client.get(fullKey);
|
|
69
|
-
if (value === null) {
|
|
70
|
-
this.stats.misses++;
|
|
71
|
-
return void 0;
|
|
72
|
-
}
|
|
73
|
-
this.stats.hits++;
|
|
74
|
-
let data = value;
|
|
75
|
-
if (this.enableCompression && value.startsWith("gzip:")) {
|
|
76
|
-
const compressed = Buffer.from(value.slice(5), "base64");
|
|
77
|
-
const decompressed = await gunzipAsync(compressed);
|
|
78
|
-
data = decompressed.toString("utf-8");
|
|
79
|
-
}
|
|
80
|
-
return deserialize(data);
|
|
81
|
-
} catch (error) {
|
|
82
|
-
throw new CacheError(
|
|
83
|
-
`Failed to get cache entry: ${error.message}`,
|
|
84
|
-
"GET_ERROR",
|
|
85
|
-
"redis"
|
|
86
|
-
);
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
async set(key, value, ttl) {
|
|
90
|
-
if (!isValidKey(key)) {
|
|
91
|
-
throw new CacheKeyError(key, "redis");
|
|
92
|
-
}
|
|
93
|
-
await this.ensureConnected();
|
|
94
|
-
const fullKey = formatKey(this.namespace, key);
|
|
95
|
-
try {
|
|
96
|
-
let data = serialize(value);
|
|
97
|
-
if (this.enableCompression && data.length > this.compressionThreshold) {
|
|
98
|
-
const compressed = await gzipAsync(Buffer.from(data, "utf-8"));
|
|
99
|
-
data = `gzip:${compressed.toString("base64")}`;
|
|
100
|
-
}
|
|
101
|
-
const effectiveTTL = ttl ?? this.defaultTTL;
|
|
102
|
-
if (effectiveTTL !== void 0 && effectiveTTL > 0) {
|
|
103
|
-
await this.client.setEx(fullKey, effectiveTTL, data);
|
|
104
|
-
} else {
|
|
105
|
-
await this.client.set(fullKey, data);
|
|
106
|
-
}
|
|
107
|
-
} catch (error) {
|
|
108
|
-
if (error.message?.includes("serialize")) {
|
|
109
|
-
throw new CacheSerializationError(
|
|
110
|
-
`Failed to serialize value: ${error.message}`,
|
|
111
|
-
"redis"
|
|
112
|
-
);
|
|
113
|
-
}
|
|
114
|
-
throw new CacheError(
|
|
115
|
-
`Failed to set cache entry: ${error.message}`,
|
|
116
|
-
"SET_ERROR",
|
|
117
|
-
"redis"
|
|
118
|
-
);
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
async has(key) {
|
|
122
|
-
if (!isValidKey(key)) {
|
|
123
|
-
throw new CacheKeyError(key, "redis");
|
|
124
|
-
}
|
|
125
|
-
await this.ensureConnected();
|
|
126
|
-
const fullKey = formatKey(this.namespace, key);
|
|
127
|
-
try {
|
|
128
|
-
const exists = await this.client.exists(fullKey);
|
|
129
|
-
return exists === 1;
|
|
130
|
-
} catch (error) {
|
|
131
|
-
throw new CacheError(
|
|
132
|
-
`Failed to check cache entry: ${error.message}`,
|
|
133
|
-
"EXISTS_ERROR",
|
|
134
|
-
"redis"
|
|
135
|
-
);
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
async delete(key) {
|
|
139
|
-
if (!isValidKey(key)) {
|
|
140
|
-
throw new CacheKeyError(key, "redis");
|
|
141
|
-
}
|
|
142
|
-
await this.ensureConnected();
|
|
143
|
-
const fullKey = formatKey(this.namespace, key);
|
|
144
|
-
try {
|
|
145
|
-
const deleted = await this.client.del(fullKey);
|
|
146
|
-
return deleted === 1;
|
|
147
|
-
} catch (error) {
|
|
148
|
-
throw new CacheError(
|
|
149
|
-
`Failed to delete cache entry: ${error.message}`,
|
|
150
|
-
"DELETE_ERROR",
|
|
151
|
-
"redis"
|
|
152
|
-
);
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
async clear(namespace) {
|
|
156
|
-
await this.ensureConnected();
|
|
157
|
-
try {
|
|
158
|
-
if (namespace) {
|
|
159
|
-
const pattern = `${namespace}:*`;
|
|
160
|
-
let cursor = "0";
|
|
161
|
-
do {
|
|
162
|
-
const reply = await this.client.scan(cursor, {
|
|
163
|
-
MATCH: pattern,
|
|
164
|
-
COUNT: 100
|
|
165
|
-
});
|
|
166
|
-
cursor = reply.cursor;
|
|
167
|
-
if (reply.keys.length > 0) {
|
|
168
|
-
await this.client.del(reply.keys);
|
|
169
|
-
}
|
|
170
|
-
} while (cursor !== "0");
|
|
171
|
-
} else {
|
|
172
|
-
await this.client.flushDb();
|
|
173
|
-
this.stats.hits = 0;
|
|
174
|
-
this.stats.misses = 0;
|
|
175
|
-
}
|
|
176
|
-
} catch (error) {
|
|
177
|
-
throw new CacheError(
|
|
178
|
-
`Failed to clear cache: ${error.message}`,
|
|
179
|
-
"CLEAR_ERROR",
|
|
180
|
-
"redis"
|
|
181
|
-
);
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
async keys(pattern) {
|
|
185
|
-
await this.ensureConnected();
|
|
186
|
-
try {
|
|
187
|
-
const searchPattern = pattern ? formatKey(this.namespace, pattern) : formatKey(this.namespace, "*");
|
|
188
|
-
const keys = [];
|
|
189
|
-
let cursor = "0";
|
|
190
|
-
do {
|
|
191
|
-
const reply = await this.client.scan(cursor, {
|
|
192
|
-
MATCH: searchPattern,
|
|
193
|
-
COUNT: 100
|
|
194
|
-
});
|
|
195
|
-
cursor = reply.cursor;
|
|
196
|
-
keys.push(...reply.keys);
|
|
197
|
-
} while (cursor !== "0");
|
|
198
|
-
if (this.namespace) {
|
|
199
|
-
const prefix = `${this.namespace}:`;
|
|
200
|
-
return keys.map(
|
|
201
|
-
(key) => key.startsWith(prefix) ? key.slice(prefix.length) : key
|
|
202
|
-
);
|
|
203
|
-
}
|
|
204
|
-
return keys;
|
|
205
|
-
} catch (error) {
|
|
206
|
-
throw new CacheError(
|
|
207
|
-
`Failed to get cache keys: ${error.message}`,
|
|
208
|
-
"KEYS_ERROR",
|
|
209
|
-
"redis"
|
|
210
|
-
);
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
async getMany(keys) {
|
|
214
|
-
if (keys.length === 0) {
|
|
215
|
-
return /* @__PURE__ */ new Map();
|
|
216
|
-
}
|
|
217
|
-
await this.ensureConnected();
|
|
218
|
-
const fullKeys = keys.map((key) => formatKey(this.namespace, key));
|
|
219
|
-
try {
|
|
220
|
-
const values = await this.client.mGet(fullKeys);
|
|
221
|
-
const result = /* @__PURE__ */ new Map();
|
|
222
|
-
for (let i = 0; i < keys.length; i++) {
|
|
223
|
-
const value = values[i];
|
|
224
|
-
if (value !== null) {
|
|
225
|
-
let data = value;
|
|
226
|
-
if (this.enableCompression && value.startsWith("gzip:")) {
|
|
227
|
-
const compressed = Buffer.from(value.slice(5), "base64");
|
|
228
|
-
const decompressed = await gunzipAsync(compressed);
|
|
229
|
-
data = decompressed.toString("utf-8");
|
|
230
|
-
}
|
|
231
|
-
result.set(keys[i], deserialize(data));
|
|
232
|
-
this.stats.hits++;
|
|
233
|
-
} else {
|
|
234
|
-
this.stats.misses++;
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
return result;
|
|
238
|
-
} catch (error) {
|
|
239
|
-
throw new CacheError(
|
|
240
|
-
`Failed to get multiple cache entries: ${error.message}`,
|
|
241
|
-
"MGET_ERROR",
|
|
242
|
-
"redis"
|
|
243
|
-
);
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
async setMany(entries) {
|
|
247
|
-
if (entries.length === 0) {
|
|
248
|
-
return;
|
|
249
|
-
}
|
|
250
|
-
await this.ensureConnected();
|
|
251
|
-
try {
|
|
252
|
-
const pipeline = this.client.multi();
|
|
253
|
-
for (const entry of entries) {
|
|
254
|
-
const fullKey = formatKey(this.namespace, entry.key);
|
|
255
|
-
let data = serialize(entry.value);
|
|
256
|
-
if (this.enableCompression && data.length > this.compressionThreshold) {
|
|
257
|
-
const compressed = await gzipAsync(Buffer.from(data, "utf-8"));
|
|
258
|
-
data = `gzip:${compressed.toString("base64")}`;
|
|
259
|
-
}
|
|
260
|
-
const effectiveTTL = entry.ttl ?? this.defaultTTL;
|
|
261
|
-
if (effectiveTTL !== void 0 && effectiveTTL > 0) {
|
|
262
|
-
pipeline.setEx(fullKey, effectiveTTL, data);
|
|
263
|
-
} else {
|
|
264
|
-
pipeline.set(fullKey, data);
|
|
265
|
-
}
|
|
266
|
-
}
|
|
267
|
-
await pipeline.exec();
|
|
268
|
-
} catch (error) {
|
|
269
|
-
throw new CacheError(
|
|
270
|
-
`Failed to set multiple cache entries: ${error.message}`,
|
|
271
|
-
"MSET_ERROR",
|
|
272
|
-
"redis"
|
|
273
|
-
);
|
|
274
|
-
}
|
|
275
|
-
}
|
|
276
|
-
async deleteMany(keys) {
|
|
277
|
-
if (keys.length === 0) {
|
|
278
|
-
return 0;
|
|
279
|
-
}
|
|
280
|
-
await this.ensureConnected();
|
|
281
|
-
const fullKeys = keys.map((key) => formatKey(this.namespace, key));
|
|
282
|
-
try {
|
|
283
|
-
const deleted = await this.client.del(fullKeys);
|
|
284
|
-
return deleted;
|
|
285
|
-
} catch (error) {
|
|
286
|
-
throw new CacheError(
|
|
287
|
-
`Failed to delete multiple cache entries: ${error.message}`,
|
|
288
|
-
"MDEL_ERROR",
|
|
289
|
-
"redis"
|
|
290
|
-
);
|
|
291
|
-
}
|
|
292
|
-
}
|
|
293
|
-
async getStats() {
|
|
294
|
-
await this.ensureConnected();
|
|
295
|
-
try {
|
|
296
|
-
const info = await this.client.info("stats");
|
|
297
|
-
const dbInfo = await this.client.info("keyspace");
|
|
298
|
-
const dbMatch = dbInfo.match(/db\d+:keys=(\d+)/);
|
|
299
|
-
const entries = dbMatch ? parseInt(dbMatch[1], 10) : 0;
|
|
300
|
-
const hitsMatch = info.match(/keyspace_hits:(\d+)/);
|
|
301
|
-
const missesMatch = info.match(/keyspace_misses:(\d+)/);
|
|
302
|
-
const redisHits = hitsMatch ? parseInt(hitsMatch[1], 10) : 0;
|
|
303
|
-
const redisMisses = missesMatch ? parseInt(missesMatch[1], 10) : 0;
|
|
304
|
-
const totalHits = this.stats.hits + redisHits;
|
|
305
|
-
const totalMisses = this.stats.misses + redisMisses;
|
|
306
|
-
const totalAccesses = totalHits + totalMisses;
|
|
307
|
-
const hitRate = totalAccesses > 0 ? totalHits / totalAccesses : 0;
|
|
308
|
-
return {
|
|
309
|
-
entries,
|
|
310
|
-
totalSize: 0,
|
|
311
|
-
// Redis doesn't easily expose total memory per keyspace
|
|
312
|
-
hits: totalHits,
|
|
313
|
-
misses: totalMisses,
|
|
314
|
-
hitRate,
|
|
315
|
-
evictions: 0,
|
|
316
|
-
// Would need to parse evicted_keys from stats
|
|
317
|
-
backend: {
|
|
318
|
-
type: "redis",
|
|
319
|
-
host: this.options.host || "localhost",
|
|
320
|
-
port: this.options.port || 6379,
|
|
321
|
-
db: this.options.db || 0,
|
|
322
|
-
compression: this.enableCompression
|
|
323
|
-
}
|
|
324
|
-
};
|
|
325
|
-
} catch (error) {
|
|
326
|
-
throw new CacheError(
|
|
327
|
-
`Failed to get cache stats: ${error.message}`,
|
|
328
|
-
"STATS_ERROR",
|
|
329
|
-
"redis"
|
|
330
|
-
);
|
|
331
|
-
}
|
|
332
|
-
}
|
|
333
|
-
async touch(key, ttl) {
|
|
334
|
-
if (!isValidKey(key)) {
|
|
335
|
-
throw new CacheKeyError(key, "redis");
|
|
336
|
-
}
|
|
337
|
-
await this.ensureConnected();
|
|
338
|
-
const fullKey = formatKey(this.namespace, key);
|
|
339
|
-
try {
|
|
340
|
-
const result = await this.client.expire(fullKey, ttl);
|
|
341
|
-
return result === 1;
|
|
342
|
-
} catch (error) {
|
|
343
|
-
throw new CacheError(
|
|
344
|
-
`Failed to touch cache entry: ${error.message}`,
|
|
345
|
-
"TOUCH_ERROR",
|
|
346
|
-
"redis"
|
|
347
|
-
);
|
|
348
|
-
}
|
|
349
|
-
}
|
|
350
|
-
async close() {
|
|
351
|
-
if (this.connected) {
|
|
352
|
-
try {
|
|
353
|
-
await this.client.quit();
|
|
354
|
-
this.connected = false;
|
|
355
|
-
} catch (_error) {
|
|
356
|
-
await this.client.disconnect();
|
|
357
|
-
this.connected = false;
|
|
358
|
-
}
|
|
359
|
-
}
|
|
360
|
-
}
|
|
361
|
-
}
|
|
362
|
-
export {
|
|
363
|
-
RedisProvider
|
|
364
|
-
};
|
|
365
|
-
//# sourceMappingURL=redis-D-SNLXE_.js.map
|