All files / lib/core/utils LruCache.js

93.47% Statements 86/92
81.25% Branches 13/16
100% Functions 7/7
93.47% Lines 86/92

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93505x 505x 505x 505x 505x 505x 505x 505x 505x 505x 2064x     2064x 2064x 2064x 2064x 505x 505x 505x 505x 505x 505x 505x 36571x 28601x 28601x 7970x 7970x 7970x 7970x 7970x 36571x 505x 505x 505x 505x 505x 505x 505x 28607x   28607x 19301x 19301x 19301x 19301x 19301x 9801x 9801x 9801x       9801x 19301x 28607x 28607x 505x 505x 505x 505x 505x 505x 505x 117x 117x 505x 505x 505x 505x 505x 505x 505x 5x 5x 505x 505x 505x 505x 505x 4x 4x 505x 505x 505x 505x 505x 505x 20002x 20002x 505x  
/**
 * A standard Least Recently Used (LRU) Cache implementation.
 * Uses JavaScript Map's insertion order preservation to maintain recency.
 */
export class LruCache {
  /**
   * @param {number} limit - Maximum number of items allowed in the cache.
   * @param {function(string, *): void} [onEvict] - Optional callback triggered when an item is evicted.
   */
  constructor(limit, onEvict = null) {
    if (typeof limit !== 'number' || limit <= 0) {
      throw new Error('LRU Cache limit must be a positive number');
    }
    this.limit = limit;
    this.onEvict = onEvict;
    this.cache = new Map();
  }
 
  /**
   * Retrieves an item from the cache and updates its recency.
   * @param {string} key
   * @returns {*} The cached value, or undefined if not found.
   */
  get(key) {
    if (!this.cache.has(key)) {
      return undefined;
    }
    const value = this.cache.get(key);
    // Refresh recency by re-inserting
    this.cache.delete(key);
    this.cache.set(key, value);
    return value;
  }
 
  /**
   * Inserts or updates an item in the cache. Evicts the least recently used item if limit is exceeded.
   * @param {string} key
   * @param {*} value
   */
  set(key, value) {
    if (this.cache.has(key)) {
      this.cache.delete(key);
    } else if (this.cache.size >= this.limit) {
      // Evict least recently used (first key in map iterator)
      const lruKey = this.cache.keys().next().value;
      const lruValue = this.cache.get(lruKey);
      this.cache.delete(lruKey);
      if (typeof this.onEvict === 'function') {
        try {
          this.onEvict(lruKey, lruValue);
        } catch (err) {
          // Prevent errors in user-defined callback from breaking set()
          console.error('Error in LRU Cache onEvict callback:', err);
        }
      }
    }
    this.cache.set(key, value);
  }
 
  /**
   * Checks if a key exists in the cache without updating its recency.
   * @param {string} key
   * @returns {boolean}
   */
  has(key) {
    return this.cache.has(key);
  }
 
  /**
   * Deletes an item from the cache.
   * @param {string} key
   * @returns {boolean} True if the item existed and was removed.
   */
  delete(key) {
    return this.cache.delete(key);
  }
 
  /**
   * Clears all items from the cache.
   */
  clear() {
    this.cache.clear();
  }
 
  /**
   * Returns the current number of items in the cache.
   * @returns {number}
   */
  get size() {
    return this.cache.size;
  }
}