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 46x 46x           46x 49x 49x           46x 2x 2x           46x 46x 38x 38x 46x             46x 37x 37x             46x 16x 16x 16x           46x 12x 12x 12x           46x 5x 5x 5x         46x 1x 1x 1x             46x 4x   4x 4x 4x 4x           46x 34x 34x               46x 42x 42x 46x  
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);
  }
}