/**
* @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();
}
}