Project Files
src / cache.ts
import { CACHE_DEFAULT_TTL, CACHE_MAX_ENTRIES } from "./constants";
export interface CacheEntry<T> {
value: T;
timestamp: number;
ttl?: number;
}
export class CommandCache {
private cache: Map<string, CacheEntry<any>>;
private maxSize: number;
private defaultTTL: number;
constructor(maxSize: number = CACHE_MAX_ENTRIES, defaultTTL: number = CACHE_DEFAULT_TTL) {
this.cache = new Map();
this.maxSize = maxSize;
this.defaultTTL = defaultTTL;
}
get(key: string): any | null {
const entry = this.cache.get(key);
if (!entry) return null;
// Check if expired
const ttl = entry.ttl ?? this.defaultTTL;
if (Date.now() - entry.timestamp > ttl) {
this.cache.delete(key);
return null;
}
return entry.value;
}
set(key: string, value: any, ttl?: number): void {
// Evict oldest entries when at capacity (LRU-style eviction)
while (this.cache.size >= this.maxSize) {
const oldestKey = this.cache.keys().next().value;
if (oldestKey) {
this.cache.delete(oldestKey);
} else {
break;
}
}
this.cache.set(key, {
value,
timestamp: Date.now(),
ttl
});
}
has(key: string): boolean {
return this.get(key) !== null;
}
delete(key: string): boolean {
return this.cache.delete(key);
}
clear(): void {
this.cache.clear();
}
get size(): number {
return this.cache.size;
}
// Generate cache key from command and options
static generateKey(command: string, options?: Record<string, any>): string {
const sanitizedOptions = JSON.stringify(options ?? {});
return `${command}:${sanitizedOptions}`;
}
}
// Global instance for the plugin
export const globalCache = new CommandCache();