TYPESCRIPT

Cache Manager with TTL

Generic cache manager with time-to-live and size limits

TypeScriptCachePerformance

Code

interface CacheOptions<K, V> {
  ttl?: number; // Time to live in milliseconds
  maxSize?: number;
  onEviction?: (key: K, value: V) => void;
}

interface CacheEntry<V> {
  value: V;
  expiresAt: number;
  lastAccessed: number;
}

class CacheManager<K, V> {
  private cache = new Map<K, CacheEntry<V>>();
  private ttl: number;
  private maxSize: number;
  private onEviction?: (key: K, value: V) => void;
  private cleanupInterval: NodeJS.Timeout;

  constructor(options: CacheOptions<K, V> = {}) {
    this.ttl = options.ttl || 60 * 1000; // Default 1 minute
    this.maxSize = options.maxSize || 100;
    this.onEviction = options.onEviction;
    
    // Auto cleanup every minute
    this.cleanupInterval = setInterval(() => this.cleanup(), 60 * 1000);
  }

  set(key: K, value: V, customTTL?: number): void {
    const now = Date.now();
    const ttl = customTTL || this.ttl;
    
    // If cache is full, remove oldest item
    __TOKEN_35__ (this.cache.size >= this.maxSize && !this.cache.has(key)) {
      this.evictOldest();
    }
    
    this.cache.set(key, {
      value,
      expiresAt: now + ttl,
      lastAccessed: now
    });
  }

  get(key: K): V | undefined {
    const entry = this.cache.get(key);
    
    __TOKEN_43__ (!entry) return undefined;
    
    const now = Date.now();
    
    // Check if expired
    __TOKEN_46__ (entry.expiresAt <= now) {
      this.delete(key);
      return undefined;
    }
    
    // Update last accessed time
    entry.lastAccessed = now;
    
    return entry.value;
  }

  has(key: K): boolean {
    return this.get(key) !== undefined;
  }

  delete(key: K): boolean {
    const entry = this.cache.get(key);
    __TOKEN_54__ (entry && this.onEviction) {
      this.onEviction(key, entry.value);
    }
    return this.cache.delete(key);
  }

  clear(): void {
    __TOKEN_59__ (this.onEviction) {
      this.cache.forEach((entry, key) => {
        this.onEviction(key, entry.value);
      });
    }
    this.cache.clear();
  }

  size(): number {
    return this.cache.size;
  }

  keys(): K[] {
    return Array.__TOKEN_67__(this.cache.keys());
  }

  values(): V[] {
    return Array.__TOKEN_70__(this.cache.values()).map(entry => entry.value);
  }

  entries(): [K, V][] {
    return Array.__TOKEN_73__(this.cache.entries()).map(([key, entry]) => [key, entry.value]);
  }

  private evictOldest(): void {
    let oldestKey: K | null = null;
    let oldestAccess = Infinity;
    
    __TOKEN_78__ (const [key, entry] of this.cache.entries()) {
      __TOKEN_82__ (entry.lastAccessed < oldestAccess) {
        oldestAccess = entry.lastAccessed;
        oldestKey = key;
      }
    }
    
    __TOKEN_83__ (oldestKey) {
      this.delete(oldestKey);
    }
  }

  private cleanup(): void {
    const now = Date.now();
    
    __TOKEN_87__ (const [key, entry] of this.cache.entries()) {
      __TOKEN_91__ (entry.expiresAt <= now) {
        this.delete(key);
      }
    }
  }

  // Update TTL for a key
  touch(key: K, newTTL?: number): boolean {
    const entry = this.cache.get(key);
    __TOKEN_95__ (!entry) return false;
    
    const ttl = newTTL || this.ttl;
    entry.expiresAt = Date.now() + ttl;
    entry.lastAccessed = Date.now();
    
    return true;
  }

  // Get remaining TTL
  getRemainingTTL(key: K): number | undefined {
    const entry = this.cache.get(key);
    __TOKEN_102__ (!entry) return undefined;
    
    const remaining = entry.expiresAt - Date.now();
    return remaining > 0 ? remaining : 0;
  }

  // Async version with automatic value fetching
  async getOrSet(
    key: K,
    fetcher: () => Promise<V>,
    customTTL?: number
  ): Promise<V> {
    const cached = this.get(key);
    __TOKEN_109__ (cached !== undefined) {
      return cached;
    }
    
    const value = await fetcher();
    this.set(key, value, customTTL);
    return value;
  }

  dispose(): void {
    clearInterval(this.cleanupInterval);
    this.clear();
  }
}

// Usage examples
const cache = new CacheManager<string, number>({
  ttl: 5000,
  maxSize: 50,
  onEviction: (key, value) => {
    console.log(`Evicted ${key}: ${value}`);
  }
});

// Basic operations
cache.set('result', 42);
const value = cache.get('result');

// Async fetch
const fetchUserData = __TOKEN_121__ (userId: string): Promise<any> => {
  // Simulate API call
  return { id: userId, name: 'John' };
};

const userCache = new CacheManager<string, any>({ ttl: 30000 });

async function getUser(userId: string) {
  return userCache.getOrSet(
    `user:${userId}`,
    () => fetchUserData(userId)
  );
}