@happyvertical/cache 0.74.8

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.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-DyC_7WDS.js","sources":["../../src/providers/file.ts"],"sourcesContent":["/**\n * File cache provider implementation with compression\n */\n\nimport {\n mkdir,\n readdir,\n readFile,\n rm,\n stat,\n writeFile,\n} from 'node:fs/promises';\nimport { join, resolve } from 'node:path';\nimport { promisify } from 'node:util';\nimport { gunzip, gzip } from 'node:zlib';\nimport type {\n CacheEntry,\n CacheProvider,\n CacheStats,\n FileOptions,\n} from '../shared/types';\nimport {\n CacheError,\n CacheKeyError,\n CacheSerializationError,\n CacheSizeError,\n} from '../shared/types';\nimport {\n calculateExpiration,\n calculateSize,\n deserialize,\n extractKey,\n formatKey,\n isExpired,\n isValidKey,\n matchesPattern,\n serialize,\n} from '../shared/utils';\n\nconst gzipAsync = promisify(gzip);\nconst gunzipAsync = promisify(gunzip);\n\n/**\n * File cache provider implementation\n * Stores cache entries as files with optional compression\n */\nexport class FileProvider implements CacheProvider {\n private cacheDir: string;\n private namespace?: string;\n private defaultTTL?: number;\n private maxSize: number;\n private compression: boolean;\n private fileExtension: string;\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: FileOptions) {\n this.cacheDir = resolve(options.cacheDir);\n this.namespace = options.namespace;\n this.defaultTTL = options.defaultTTL;\n this.maxSize = options.maxSize || 500 * 1024 * 1024; // 500MB default\n this.compression = options.compression ?? false;\n this.fileExtension = options.fileExtension || '.cache';\n this.checkPeriod = options.checkPeriod || 300000; // 5 minutes default\n this.stats = {\n hits: 0,\n misses: 0,\n evictions: 0,\n };\n\n // Ensure cache directory exists\n this.ensureCacheDir();\n\n // Start background cleanup\n this.startCleanup();\n }\n\n async get<T = any>(key: string): Promise<T | undefined> {\n if (!isValidKey(key)) {\n throw new CacheKeyError(key, 'file');\n }\n\n const fullKey = formatKey(this.namespace, key);\n const filePath = this.getFilePath(fullKey);\n\n try {\n const fileContent = await readFile(filePath);\n let data: Buffer;\n\n // Decompress if needed\n if (this.compression) {\n data = await gunzipAsync(fileContent);\n } else {\n data = fileContent;\n }\n\n const entry: CacheEntry<T> = deserialize(data.toString('utf-8'));\n\n // Check if expired\n if (isExpired(entry.expiresAt)) {\n await rm(filePath, { force: true });\n this.stats.misses++;\n return undefined;\n }\n\n // Update hit count\n entry.hits++;\n this.stats.hits++;\n\n // Write back updated entry (for hit tracking)\n await this.writeEntry(filePath, entry);\n\n return entry.value;\n } catch (error: any) {\n if (error.code === 'ENOENT') {\n this.stats.misses++;\n return undefined;\n }\n throw new CacheError(\n `Failed to read cache entry: ${error.message}`,\n 'READ_ERROR',\n 'file',\n );\n }\n }\n\n async set<T = any>(key: string, value: T, ttl?: number): Promise<void> {\n if (!isValidKey(key)) {\n throw new CacheKeyError(key, 'file');\n }\n\n const fullKey = formatKey(this.namespace, key);\n const filePath = this.getFilePath(fullKey);\n const expiresAt = calculateExpiration(ttl ?? this.defaultTTL);\n\n const entry: CacheEntry<T> = {\n value,\n createdAt: Date.now(),\n expiresAt,\n size: calculateSize(value),\n hits: 0,\n metadata: {\n compressed: this.compression,\n namespace: this.namespace,\n },\n };\n\n // Check if we need to evict files\n await this.evictIfNeeded(entry.size);\n\n // Write entry to file\n await this.writeEntry(filePath, entry);\n }\n\n async has(key: string): Promise<boolean> {\n if (!isValidKey(key)) {\n throw new CacheKeyError(key, 'file');\n }\n\n const fullKey = formatKey(this.namespace, key);\n const filePath = this.getFilePath(fullKey);\n\n try {\n const fileContent = await readFile(filePath);\n let data: Buffer;\n\n if (this.compression) {\n data = await gunzipAsync(fileContent);\n } else {\n data = fileContent;\n }\n\n const entry: CacheEntry = deserialize(data.toString('utf-8'));\n\n // Check if expired\n if (isExpired(entry.expiresAt)) {\n await rm(filePath, { force: true });\n return false;\n }\n\n return true;\n } catch (error: any) {\n if (error.code === 'ENOENT') {\n return false;\n }\n throw new CacheError(\n `Failed to check cache entry: ${error.message}`,\n 'CHECK_ERROR',\n 'file',\n );\n }\n }\n\n async delete(key: string): Promise<boolean> {\n if (!isValidKey(key)) {\n throw new CacheKeyError(key, 'file');\n }\n\n const fullKey = formatKey(this.namespace, key);\n const filePath = this.getFilePath(fullKey);\n\n try {\n await rm(filePath);\n return true;\n } catch (error: any) {\n if (error.code === 'ENOENT') {\n return false;\n }\n throw new CacheError(\n `Failed to delete cache entry: ${error.message}`,\n 'DELETE_ERROR',\n 'file',\n );\n }\n }\n\n async clear(namespace?: string): Promise<void> {\n if (namespace) {\n // Clear specific namespace\n const prefix = this.sanitizeKey(`${namespace}:`);\n const files = await this.getAllFiles();\n\n for (const file of files) {\n if (file.startsWith(prefix)) {\n await rm(join(this.cacheDir, file), { force: true });\n }\n }\n } else {\n // Clear all cache files\n try {\n await rm(this.cacheDir, { recursive: true, force: true });\n await this.ensureCacheDir();\n this.stats.hits = 0;\n this.stats.misses = 0;\n this.stats.evictions = 0;\n } catch (error: any) {\n throw new CacheError(\n `Failed to clear cache: ${error.message}`,\n 'CLEAR_ERROR',\n 'file',\n );\n }\n }\n }\n\n async keys(pattern?: string): Promise<string[]> {\n const files = await this.getAllFiles();\n const keys: string[] = [];\n\n for (const file of files) {\n // Remove file extension\n const key = file.replace(this.fileExtension, '');\n\n // Check if file is expired\n const filePath = join(this.cacheDir, file);\n try {\n const fileContent = await readFile(filePath);\n let data: Buffer;\n\n if (this.compression) {\n data = await gunzipAsync(fileContent);\n } else {\n data = fileContent;\n }\n\n const entry: CacheEntry = deserialize(data.toString('utf-8'));\n\n if (!isExpired(entry.expiresAt)) {\n const desanitized = this.desanitizeKey(key);\n keys.push(extractKey(this.namespace, desanitized));\n }\n } catch {}\n }\n\n // Apply pattern filter if provided\n if (pattern) {\n return keys.filter((key) => matchesPattern(pattern, key));\n }\n\n return keys;\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 const files = await this.getAllFiles();\n let totalSize = 0;\n let entries = 0;\n\n for (const file of files) {\n const filePath = join(this.cacheDir, file);\n try {\n const stats = await stat(filePath);\n totalSize += stats.size;\n\n // Check if expired\n const fileContent = await readFile(filePath);\n let data: Buffer;\n\n if (this.compression) {\n data = await gunzipAsync(fileContent);\n } else {\n data = fileContent;\n }\n\n const entry: CacheEntry = deserialize(data.toString('utf-8'));\n\n if (!isExpired(entry.expiresAt)) {\n entries++;\n }\n } catch {}\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: 'file',\n cacheDir: this.cacheDir,\n compression: this.compression,\n maxSize: this.maxSize,\n },\n };\n }\n\n async touch(key: string, ttl: number): Promise<boolean> {\n if (!isValidKey(key)) {\n throw new CacheKeyError(key, 'file');\n }\n\n const fullKey = formatKey(this.namespace, key);\n const filePath = this.getFilePath(fullKey);\n\n try {\n const fileContent = await readFile(filePath);\n let data: Buffer;\n\n if (this.compression) {\n data = await gunzipAsync(fileContent);\n } else {\n data = fileContent;\n }\n\n const entry: CacheEntry = deserialize(data.toString('utf-8'));\n\n if (isExpired(entry.expiresAt)) {\n return false;\n }\n\n entry.expiresAt = calculateExpiration(ttl);\n await this.writeEntry(filePath, entry);\n\n return true;\n } catch (error: any) {\n if (error.code === 'ENOENT') {\n return false;\n }\n throw new CacheError(\n `Failed to touch cache entry: ${error.message}`,\n 'TOUCH_ERROR',\n 'file',\n );\n }\n }\n\n async close(): Promise<void> {\n // Stop cleanup interval\n if (this.checkInterval) {\n clearInterval(this.checkInterval);\n this.checkInterval = undefined;\n }\n }\n\n /**\n * Ensures cache directory exists\n */\n private async ensureCacheDir(): Promise<void> {\n try {\n await mkdir(this.cacheDir, { recursive: true });\n } catch (error: any) {\n throw new CacheError(\n `Failed to create cache directory: ${error.message}`,\n 'INIT_ERROR',\n 'file',\n );\n }\n }\n\n /**\n * Gets the file path for a cache key\n */\n private getFilePath(key: string): string {\n const sanitizedKey = this.sanitizeKey(key);\n return join(this.cacheDir, `${sanitizedKey}${this.fileExtension}`);\n }\n\n /**\n * Sanitizes a key for use as a filename\n */\n private sanitizeKey(key: string): string {\n return key.replace(/[^a-zA-Z0-9_:-]/g, '_');\n }\n\n /**\n * Desanitizes a filename back to the original key\n */\n private desanitizeKey(sanitized: string): string {\n // This is a simple implementation - in practice, you might need\n // a more sophisticated mapping\n return sanitized;\n }\n\n /**\n * Gets all cache file names\n */\n private async getAllFiles(): Promise<string[]> {\n try {\n const files = await readdir(this.cacheDir);\n return files.filter((file) => file.endsWith(this.fileExtension));\n } catch (error: any) {\n if (error.code === 'ENOENT') {\n return [];\n }\n throw new CacheError(\n `Failed to list cache files: ${error.message}`,\n 'LIST_ERROR',\n 'file',\n );\n }\n }\n\n /**\n * Writes an entry to a file\n */\n private async writeEntry(filePath: string, entry: CacheEntry): Promise<void> {\n try {\n let data: Buffer;\n const json = serialize(entry);\n data = Buffer.from(json, 'utf-8');\n\n // Compress if enabled\n if (this.compression) {\n data = await gzipAsync(data);\n }\n\n await writeFile(filePath, data);\n } catch (error: any) {\n throw new CacheSerializationError(\n `Failed to write cache entry: ${error.message}`,\n 'file',\n );\n }\n }\n\n /**\n * Evicts files if size limit is exceeded\n */\n private async evictIfNeeded(newEntrySize: number): Promise<void> {\n const stats = await this.getStats();\n\n // Check if we need to evict\n if (stats.totalSize + newEntrySize > this.maxSize) {\n await this.evict();\n\n // Check again after eviction\n const updatedStats = await this.getStats();\n if (updatedStats.totalSize + newEntrySize > this.maxSize) {\n throw new CacheSizeError(\n `Cannot cache entry: would exceed max size of ${this.maxSize} bytes`,\n 'file',\n );\n }\n }\n }\n\n /**\n * Evicts oldest files based on creation time\n */\n private async evict(): Promise<void> {\n const files = await this.getAllFiles();\n const filesWithStats: Array<{ file: string; createdAt: number }> = [];\n\n for (const file of files) {\n const filePath = join(this.cacheDir, file);\n try {\n const fileContent = await readFile(filePath);\n let data: Buffer;\n\n if (this.compression) {\n data = await gunzipAsync(fileContent);\n } else {\n data = fileContent;\n }\n\n const entry: CacheEntry = deserialize(data.toString('utf-8'));\n filesWithStats.push({ file, createdAt: entry.createdAt });\n } catch {}\n }\n\n // Sort by creation time (oldest first)\n filesWithStats.sort((a, b) => a.createdAt - b.createdAt);\n\n // Remove oldest 10% of files\n const toRemove = Math.max(1, Math.floor(filesWithStats.length * 0.1));\n for (let i = 0; i < toRemove; i++) {\n const filePath = join(this.cacheDir, filesWithStats[i].file);\n await rm(filePath, { force: true });\n this.stats.evictions++;\n }\n }\n\n /**\n * Starts background cleanup of expired files\n */\n private startCleanup(): void {\n this.checkInterval = setInterval(() => {\n this.removeExpiredFiles();\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 expired files\n */\n private async removeExpiredFiles(): Promise<void> {\n const files = await this.getAllFiles();\n\n for (const file of files) {\n const filePath = join(this.cacheDir, file);\n try {\n const fileContent = await readFile(filePath);\n let data: Buffer;\n\n if (this.compression) {\n data = await gunzipAsync(fileContent);\n } else {\n data = fileContent;\n }\n\n const entry: CacheEntry = deserialize(data.toString('utf-8'));\n\n if (isExpired(entry.expiresAt)) {\n await rm(filePath, { force: true });\n }\n } catch {}\n }\n }\n}\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,274 @@
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
@@ -0,0 +1 @@
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":"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"}