Source: cache/TipCache.ts

/**
 * Cache implementation module
 * @module cache
 */

/**
 * Class representing a cache entry with optional expiry
 * @template T
 */
export class CacheEntry<T> {
  /** The cached value */
  value: T;
  
  /** Expiry timestamp or null if no expiry */
  expiry: number | null;

  /**
   * Creates a cache entry
   * @param {T} value - The value to cache
   * @param {number|null} expiry - Expiry timestamp or null
   */
  constructor(value: T, expiry: number | null) {
    this.value = value;
    this.expiry = expiry;
  }
}

/**
 * LRU Cache implementation with TTL support
 * @template T
 */
export class TipCache<T> {
  /** Map to store cache entries */
  private _cachePool: Map<string, CacheEntry<T>>;
  /** Maximum size of the cache */
  private _maxSize: number;

  /**
   * Creates a TipCache instance
   * @param {number} [maxSize=5] - Maximum number of entries to store
   */
  constructor(maxSize = 5) {
    this._cachePool = new Map();
    this._maxSize = maxSize;
  }

  /**
   * Checks if a cache entry has expired
   * @private
   * @param {CacheEntry<T>} entry - The cache entry to check
   * @returns {boolean} True if the entry has expired
   */
  private _isExpired(entry: CacheEntry<T>): boolean {
    if (entry.expiry && Date.now() > entry.expiry) {
      return true;
    }
    return false;
  }

  /**
   * Sets a value in the cache
   * @param {string} key - The key to store the value under
   * @param {T} value - The value to store
   * @param {number} [ttl=0] - Time to live in milliseconds (0 for no expiry)
   */
  public set(key: string, value: T, ttl: number = 0): void {
    if (this._cachePool.has(key)) {
      this._cachePool.delete(key);
    } else if (this._cachePool.size >= this._maxSize) {
      const oldestKey = this._cachePool.keys().next().value;
      if (oldestKey) this._cachePool.delete(oldestKey);
    }

    const expiry = ttl > 0 ? Date.now() + ttl : null;
    this._cachePool.set(key, new CacheEntry(value, expiry));
  }

  /**
   * Gets a value from the cache
   * @param {string} key - The key to retrieve
   * @returns {T|null} The cached value or null if not found/expired
   */
  public get(key: string): T | null {
    if (!this._cachePool.has(key)) {
      return null;
    }

    const entry = this._cachePool.get(key)!;
    if (this._isExpired(entry)) {
      this._cachePool.delete(key);
      return null;
    }

    this._cachePool.delete(key);
    this._cachePool.set(key, entry);

    return entry.value;
  }

  /**
   * Deletes a value from the cache
   * @param {string} key - The key to delete
   * @returns {boolean} True if the key was found and deleted
   */
  public delete(key: string): boolean {
    return this._cachePool.delete(key);
  }

  /**
   * Checks if a key exists in the cache and is not expired
   * @param {string} key - The key to check
   * @returns {boolean} True if the key exists and is not expired
   */
  public has(key: string): boolean {
    if (!this._cachePool.has(key)) {
      return false;
    }

    const entry = this._cachePool.get(key)!;
    if (this._isExpired(entry)) {
      this._cachePool.delete(key);
      return false;
    }

    return true;
  }

  /**
   * Gets the current size of the cache
   * @returns {number} Number of entries in the cache
   */
  public size(): number {
    return this._cachePool.size;
  }
}