ChemPal Documentation - v1.6.0
    Preparing search index...

    A singleton LRU (Least Recently Used) cache for HTTP responses, backed by cstorage.local for persistence across browser sessions.

    The cache uses a doubly-linked list combined with a Map for O(1) lookups, insertions, and evictions. When the cache exceeds its configured capacity, the least recently accessed entry is automatically evicted.

    State is persisted to cstorage.local on every mutation so that cached responses survive extension restarts and browser closures.

    const cache = await HttpLru.getInstance(50);
    const cached = await cache.getByHash(requestHash);
    if (!cached) {
    const response = await fetchDecorator(url, options);
    await cache.putByHash(requestHash, response);
    }

    HttpLru

    export class HttpLru {
    /** The singleton instance, lazily initialized via {@link HttpLru.getInstance} */
    static #instance: HttpLru | null = null;
    /** Maximum number of entries before the least recently used entry is evicted */
    private capacity: number;
    /** Hash map providing O(1) access to linked-list nodes by request hash */
    private cache: Map<string, LruNode>;
    /** Pointer to the most recently used node (front of the list) */
    private head: LruNode | null;
    /** Pointer to the least recently used node (back of the list) */
    private tail: LruNode | null;

    /**
    * Creates a new HttpLru instance. Private to enforce the singleton pattern —
    * use {@link HttpLru.getInstance} instead.
    *
    * @param capacity - Maximum number of entries to store before evicting (default: `DEFAULT_CAPACITY`)
    * @source
    */
    private constructor(capacity: number = DEFAULT_CAPACITY) {
    this.capacity = capacity;
    this.cache = new Map();
    this.head = null;
    this.tail = null;
    }

    /**
    * Returns the singleton HttpLru instance, restoring persisted state from
    * `cstorage.local` on first access. Subsequent calls return the
    * same in-memory instance without hitting storage.
    *
    * @param capacity - Maximum number of entries (only applied when creating a new instance)
    * @returns The singleton HttpLru instance
    *
    * @example
    * ```typescript
    * const cache = await HttpLru.getInstance();
    * ```
    * @source
    */
    public static async getInstance(capacity: number = DEFAULT_CAPACITY): Promise<HttpLru> {
    if (HttpLru.#instance) {
    return HttpLru.#instance;
    }
    const data = await cstorage.local.get([CACHE.HTTP_LRU]);
    if (!data[CACHE.HTTP_LRU]) {
    HttpLru.#instance = new HttpLru(capacity);
    return HttpLru.#instance;
    }
    const httplru = new HttpLru(capacity);
    httplru.cache = new Map(Object.entries(data[CACHE.HTTP_LRU].cache));
    httplru.head = data[CACHE.HTTP_LRU].head;
    httplru.tail = data[CACHE.HTTP_LRU].tail;
    HttpLru.#instance = httplru;
    return httplru;
    }

    /**
    * Retrieves a cached response by its request hash. This is a convenience
    * wrapper around {@link HttpLru.get} that makes the hash-based lookup
    * intent explicit.
    *
    * @param hash - The request hash generated by `generateRequestHash`
    * @returns The cached response, or `null` if no entry exists for the hash
    * @source
    */
    async getByHash(hash: string): Promise<FetchDecoratorResponse | null> {
    return await this.get(hash);
    }

    /**
    * Stores a response in the cache keyed by its request hash. This is a
    * convenience wrapper around {@link HttpLru.put} that makes the hash-based
    * storage intent explicit.
    *
    * @param hash - The request hash generated by `generateRequestHash`
    * @param value - The HTTP response to cache
    * @source
    */
    async putByHash(hash: string, value: FetchDecoratorResponse): Promise<void> {
    return await this.put(hash, value);
    }

    /**
    * Retrieves a cached response by key and promotes it to the head of the
    * linked list (marking it as most recently used).
    *
    * @param key - The cache key to look up
    * @returns The cached response if found, or `null` if the key is not in the cache
    * @source
    */
    async get(key: string): Promise<FetchDecoratorResponse | null> {
    if (this.cache.has(key)) {
    const node = this.cache.get(key)!;
    this.moveToHead(node);
    return node.value;
    }
    return null;
    }

    /**
    * Inserts or updates a cached response. If the key already exists, its value
    * is updated and the node is promoted to the head. If the key is new, a new
    * node is created at the head. When the cache exceeds its capacity, the
    * least recently used entry (tail) is evicted.
    *
    * Persists the updated cache state to `cstorage.local` after mutation.
    *
    * @param key - The cache key (typically a request hash)
    * @param value - The HTTP response to store
    * @source
    */
    async put(key: string, value: FetchDecoratorResponse): Promise<void> {
    if (this.cache.has(key)) {
    const node = this.cache.get(key)!;
    node.value = value;
    this.moveToHead(node);
    } else {
    const newNode: LruNode = { key, value, prev: null, next: this.head };
    if (this.head) {
    this.head.prev = newNode;
    }
    this.head = newNode;
    if (!this.tail) {
    this.tail = newNode;
    }
    this.cache.set(key, newNode);
    if (this.cache.size > this.capacity) {
    this.cache.delete(this.tail.key);
    this.tail = this.tail.prev;
    if (this.tail) {
    this.tail.next = null;
    } else {
    this.head = null;
    }
    }
    }
    // Save to cstorage.local
    await this.saveToStorage();
    }

    /**
    * Detaches a node from its current position in the doubly-linked list and
    * reinserts it at the head, marking it as the most recently used entry.
    * Persists the reordered state to `cstorage.local`.
    *
    * @param node - The node to promote to the head of the list
    * @source
    */
    private async moveToHead(node: LruNode): Promise<void> {
    if (node === this.head) {
    return;
    }
    if (node === this.tail) {
    this.tail = node.prev;
    } else if (node.prev) {
    node.prev.next = node.next;
    }
    if (node.next) {
    node.next.prev = node.prev;
    }
    node.prev = null;
    node.next = this.head;
    if (this.head) {
    this.head.prev = node;
    }
    this.head = node;
    // Save to cstorage.local after moving node
    await this.saveToStorage();
    }

    /**
    * Serializes the current cache state (map entries, head, and tail pointers)
    * and writes it to `cstorage.local` under the `httplru` key.
    * Called internally after every mutation to ensure persistence.
    *
    * @source
    */
    private async saveToStorage(): Promise<void> {
    const cacheData = Object.fromEntries(this.cache);
    await cstorage.local.set({
    [CACHE.HTTP_LRU]: {
    cache: cacheData,
    head: this.head,
    tail: this.tail,
    },
    });
    }
    }
    Index

    Constructors

    • Creates a new HttpLru instance. Private to enforce the singleton pattern — use HttpLru.getInstance instead.

      Parameters

      • capacity: number = DEFAULT_CAPACITY

        Maximum number of entries to store before evicting (default: DEFAULT_CAPACITY)

      Returns HttpLru

    Methods

    • Returns the singleton HttpLru instance, restoring persisted state from cstorage.local on first access. Subsequent calls return the same in-memory instance without hitting storage.

      Parameters

      • capacity: number = DEFAULT_CAPACITY

        Maximum number of entries (only applied when creating a new instance)

      Returns Promise<HttpLru>

      The singleton HttpLru instance

      const cache = await HttpLru.getInstance();
      
        public static async getInstance(capacity: number = DEFAULT_CAPACITY): Promise<HttpLru> {
      if (HttpLru.#instance) {
      return HttpLru.#instance;
      }
      const data = await cstorage.local.get([CACHE.HTTP_LRU]);
      if (!data[CACHE.HTTP_LRU]) {
      HttpLru.#instance = new HttpLru(capacity);
      return HttpLru.#instance;
      }
      const httplru = new HttpLru(capacity);
      httplru.cache = new Map(Object.entries(data[CACHE.HTTP_LRU].cache));
      httplru.head = data[CACHE.HTTP_LRU].head;
      httplru.tail = data[CACHE.HTTP_LRU].tail;
      HttpLru.#instance = httplru;
      return httplru;
      }
    • Retrieves a cached response by its request hash. This is a convenience wrapper around HttpLru.get that makes the hash-based lookup intent explicit.

      Parameters

      • hash: string

        The request hash generated by generateRequestHash

      Returns Promise<null | FetchDecoratorResponse>

      The cached response, or null if no entry exists for the hash

        async getByHash(hash: string): Promise<FetchDecoratorResponse | null> {
      return await this.get(hash);
      }
    • Stores a response in the cache keyed by its request hash. This is a convenience wrapper around HttpLru.put that makes the hash-based storage intent explicit.

      Parameters

      • hash: string

        The request hash generated by generateRequestHash

      • value: FetchDecoratorResponse

        The HTTP response to cache

      Returns Promise<void>

        async putByHash(hash: string, value: FetchDecoratorResponse): Promise<void> {
      return await this.put(hash, value);
      }
    • Retrieves a cached response by key and promotes it to the head of the linked list (marking it as most recently used).

      Parameters

      • key: string

        The cache key to look up

      Returns Promise<null | FetchDecoratorResponse>

      The cached response if found, or null if the key is not in the cache

        async get(key: string): Promise<FetchDecoratorResponse | null> {
      if (this.cache.has(key)) {
      const node = this.cache.get(key)!;
      this.moveToHead(node);
      return node.value;
      }
      return null;
      }
    • Inserts or updates a cached response. If the key already exists, its value is updated and the node is promoted to the head. If the key is new, a new node is created at the head. When the cache exceeds its capacity, the least recently used entry (tail) is evicted.

      Persists the updated cache state to cstorage.local after mutation.

      Parameters

      Returns Promise<void>

        async put(key: string, value: FetchDecoratorResponse): Promise<void> {
      if (this.cache.has(key)) {
      const node = this.cache.get(key)!;
      node.value = value;
      this.moveToHead(node);
      } else {
      const newNode: LruNode = { key, value, prev: null, next: this.head };
      if (this.head) {
      this.head.prev = newNode;
      }
      this.head = newNode;
      if (!this.tail) {
      this.tail = newNode;
      }
      this.cache.set(key, newNode);
      if (this.cache.size > this.capacity) {
      this.cache.delete(this.tail.key);
      this.tail = this.tail.prev;
      if (this.tail) {
      this.tail.next = null;
      } else {
      this.head = null;
      }
      }
      }
      // Save to cstorage.local
      await this.saveToStorage();
      }
    • Detaches a node from its current position in the doubly-linked list and reinserts it at the head, marking it as the most recently used entry. Persists the reordered state to cstorage.local.

      Parameters

      • node: LruNode

        The node to promote to the head of the list

      Returns Promise<void>

        private async moveToHead(node: LruNode): Promise<void> {
      if (node === this.head) {
      return;
      }
      if (node === this.tail) {
      this.tail = node.prev;
      } else if (node.prev) {
      node.prev.next = node.next;
      }
      if (node.next) {
      node.next.prev = node.prev;
      }
      node.prev = null;
      node.next = this.head;
      if (this.head) {
      this.head.prev = node;
      }
      this.head = node;
      // Save to cstorage.local after moving node
      await this.saveToStorage();
      }
    • Serializes the current cache state (map entries, head, and tail pointers) and writes it to cstorage.local under the httplru key. Called internally after every mutation to ensure persistence.

      Returns Promise<void>

        private async saveToStorage(): Promise<void> {
      const cacheData = Object.fromEntries(this.cache);
      await cstorage.local.set({
      [CACHE.HTTP_LRU]: {
      cache: cacheData,
      head: this.head,
      tail: this.tail,
      },
      });
      }

    Properties

    "#instance": null | HttpLru = null

    The singleton instance, lazily initialized via HttpLru.getInstance

    capacity: number

    Maximum number of entries before the least recently used entry is evicted

    cache: Map<string, LruNode>

    Hash map providing O(1) access to linked-list nodes by request hash

    head: null | LruNode

    Pointer to the most recently used node (front of the list)

    tail: null | LruNode

    Pointer to the least recently used node (back of the list)