/** * Stores key-value pairs. */ export interface Storage { /** * Removes the data from storage. */ clear(): void; /** * Retrieves an array from storage. * * @param name the name of the array to retrieve * @param def the value to return if no array is stored with the given name */ getArray(name: string, def: any[]): any[]; /** * Stores an array. * * @param name the name of the array to store * @param value the array to store under the given name * @protected */ setArray(name: string, value: any[]): void; /** * Retrieves a boolean from storage. * * @param name the name of the boolean to retrieve * @param def the value to return if no boolean is stored with the given name * @protected */ getBoolean(name: string, def: boolean): boolean; /** * Stores a boolean. * * @param name the name of the boolean to store * @param value the boolean to store under the given name * @protected */ setBoolean(name: string, value: boolean): void; /** * Retrieves a number from storage. * * @param name the name of the number to retrieve * @param def the value to return if no number is stored with the given name * @protected */ getNumber(name: string, def: number): number; /** * Stores a number. * * @param name the name of the number to store * @param value the number to store under the given name * @protected */ setNumber(name: string, value: number): void; } /** * Stores key-value pairs in a single entry in `localStorage`. */ export class LocalStorage implements Storage { private readonly key: string; private cache: { [key: string]: string } | null = null; /** * Constructs a new persistent storage item under the given key. * * @param key the unique identifier to store the data under */ constructor(key: string) { this.key = key; } /** * Reads the object stored in local storage. * * @return the object stored in local storage, or an empty object if there is nothing in the local storage * @private */ private read(): { [key: string]: string } { if (this.cache === null) this.cache = JSON.parse(localStorage.getItem(this.key) ?? "{}"); return this.cache!; } /** * Writes the given object to local storage. * * @param item the object to write to local storage * @private */ private write(item: { [key: string]: string }): void { this.cache = item; localStorage.setItem(this.key, JSON.stringify(item)); } clear(): void { this.cache = null; localStorage.removeItem(this.key); } getArray(name: string, def: any[] = []): any[] { const array = this.read()[name]; return array === undefined ? def : JSON.parse(array); } setArray(name: string, value: any[]): void { const item = this.read(); item[name] = JSON.stringify(value); this.write(item); } getBoolean(name: string, def: boolean = false): boolean { return (this.read()[name] ?? `${def}`) === "true"; } setBoolean(name: string, value: boolean): void { const item = this.read(); item[name] = "" + value; this.write(item); } getNumber(name: string, def: number = 0): number { return +(this.read()[name] ?? def); } setNumber(name: string, value: number): void { const item = this.read(); item[name] = "" + value; this.write(item); } } /** * Stores key-value pairs in an object. */ export class MemoryStorage implements Storage { private storage: { [key: string]: any } = {}; clear(): void { this.storage = {}; } setArray(name: string, value: any[] = []): void { this.storage[name] = value; } getArray(name: string, def: any[]): any[] { return this.storage[name] ?? def; } setBoolean(name: string, value: boolean): void { this.storage[name] = value; } getBoolean(name: string, def: boolean): boolean { return this.storage[name] ?? def; } setNumber(name: string, value: number): void { this.storage[name] = value; } getNumber(name: string, def: number): number { return this.storage[name] ?? def; } }