All files / storage/src memory.ts

100% Statements 48/48
100% Branches 15/15
100% Functions 12/12
100% Lines 48/48

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 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 1161x 1x               1x 48x 48x           48x 50x 50x           48x 2x 2x           48x 48x 40x 40x 48x             48x 39x 39x             48x 17x 17x 17x           48x 12x 12x 12x           48x 5x 5x 5x         48x 1x 1x 1x             48x 4x   4x 4x 4x 4x           48x 35x 35x               48x 43x 43x 48x  
import { anchor } from '@anchorlib/core';
import { isObject } from '@beerush/utils';
import type { StorageEvent, StorageSubscriber } from './types.js';
 
/**
 * A memory-based storage implementation that provides a key-value store with subscription capabilities.
 *
 * @template T - The type of the storage object, defaults to Record<string, unknown>
 */
export class MemoryStorage<T extends Record<string, unknown> = Record<string, unknown>> {
  readonly #storage: T = {} as T;
  readonly #subscribers: Set<StorageSubscriber> = new Set();
 
  /**
   * Gets the number of items in the storage.
   * @returns The number of stored items
   */
  public get length() {
    return Object.keys(this.#storage).length;
  }
 
  /**
   * Gets all the keys in the storage.
   * @returns An array of all storage keys
   */
  public get keys() {
    return Object.keys(this.#storage) as (keyof T)[];
  }
 
  /**
   * Creates a new MemoryStorage instance.
   * @param init - Optional initial data to populate the storage
   */
  constructor(init?: T) {
    if (isObject(init)) {
      this.#storage = init;
    }
  }
 
  /**
   * Gets a value from storage by key.
   * @param key - The key to retrieve
   * @returns The stored value or undefined if not found
   */
  public get(key: keyof T): T[keyof T] | undefined {
    return this.#storage[key] as T[keyof T] | undefined;
  }
 
  /**
   * Sets a value in storage by key.
   * @param key - The key to set
   * @param value - The value to store
   */
  public set(key: keyof T, value: T[keyof T]) {
    this.#storage[key] = value;
    this.publish({ type: 'set', name: key, value });
  }
 
  /**
   * Deletes a value from storage by key.
   * @param key - The key to delete
   */
  public delete(key: keyof T) {
    delete this.#storage[key];
    this.publish({ type: 'delete', name: key });
  }
 
  /**
   * Assigns multiple values to the storage.
   * @param data - The data to merge into storage
   */
  public assign(data: Record<string, unknown>) {
    Object.assign(this.#storage, data);
    this.publish({ type: 'assign', name: '', value: data });
  }
 
  /**
   * Clears all values from the storage.
   */
  public clear() {
    anchor.clear(this.#storage);
    this.publish({ type: 'clear', name: '' });
  }
 
  /**
   * Subscribes to storage events.
   * @param callback - The function to call when storage events occur
   * @returns A function to unsubscribe from events
   */
  public subscribe(callback: StorageSubscriber) {
    this.#subscribers.add(callback);
 
    return () => {
      this.#subscribers.delete(callback);
    };
  }
 
  /**
   * Publishes a storage event to all subscribers.
   * @param event - The event to publish
   */
  public publish(event: StorageEvent) {
    this.#subscribers.forEach((callback) => callback(event));
  }
 
  /**
   * Converts the storage to a JSON string.
   * @param space - Adds indentation, white space, and line break characters to the return-value JSON text
   * @param replacer - A function that alters the behavior of the stringification process
   * @returns A JSON string representation of the storage
   */
  public json(space?: string | number, replacer?: (key: string, value: unknown) => unknown) {
    return JSON.stringify(this.#storage, replacer, space);
  }
}