Source: gitHubServer/cache.js

/**
 * @fileoverview Smart cache manager for GitHubServer
 * @file cache.js
 * @license Apache-2.0
 * @version 3.0.0
 * 
 * @author Michael Hay <michael.hay@mediumroast.io>
 * @copyright 2025 Mediumroast, Inc. All rights reserved.
 */
export class CacheManager {
  constructor() {
    this._cache = new Map();
    this._dependencyMap = new Map();
  }
    
  /**
     * Gets a value from cache or fetches it
     * @param {string} key - Cache key
     * @param {Function} fetchFn - Function to fetch data if not cached
     * @param {number} ttl - Time to live in milliseconds
     * @param {Array<string>} dependencies - Keys this cache depends on
     */
  async getOrFetch(key, fetchFn, ttl, dependencies = []) {
    const cached = this._cache.get(key);
    const now = Date.now();
        
    if (cached && (now - cached.timestamp < ttl)) {
      return cached.data;
    }
        
    const data = await fetchFn();
        
    if (data[0]) { // Only cache successful responses
      this._cache.set(key, {
        timestamp: now,
        data
      });
            
      // Register dependencies
      dependencies.forEach(depKey => {
        if (!this._dependencyMap.has(depKey)) {
          this._dependencyMap.set(depKey, new Set());
        }
        this._dependencyMap.get(depKey).add(key);
      });
    }
        
    return data;
  }
    
  /**
     * Invalidate cache entry and its dependents
     * @param {string} key - Cache key to invalidate
     */
  invalidate(key) {
    // Invalidate the key itself
    this._cache.delete(key);
        
    // Invalidate dependents
    const dependents = this._dependencyMap.get(key);
    if (dependents) {
      dependents.forEach(dependentKey => {
        this.invalidate(dependentKey);
      });
    }
  }
    
  /**
     * Clear entire cache
     */
  clear() {
    this._cache.clear();
    this._dependencyMap.clear();
  }
}