UNPKG

@alwatr/local-storage

Version:

A modern, simple, and robust solution for managing versioned JSON objects in the browser's `localStorage`. This package provides a clean, class-based API with a factory function to ensure your application's data persistence is safe, maintainable, and futu

154 lines (136 loc) 4.71 kB
import {createLogger} from '@alwatr/logger'; import type {LocalStorageProviderConfig} from './type.js'; /** * A provider class for managing a specific, versioned item in localStorage. * It encapsulates the logic for key generation, serialization, and migration. * * @example * ```typescript * const userSettings = new LocalStorageProvider({ * name: 'user-settings', * version: 1 * }); * * // Write new settings * userSettings.write({ theme: 'dark', notifications: false }); * * // Read the current settings * const currentSettings = userSettings.read(); * console.log(currentSettings); // { theme: 'dark', notifications: false } * ``` */ export class LocalStorageProvider<T> { public static readonly version = __package_version__; private readonly key__: string; protected readonly logger_; protected readonly parse_: (value: string) => T; protected readonly stringify_: (value: T) => string; constructor(config: LocalStorageProviderConfig<T>) { this.logger_ = createLogger(`local-storage-provider: ${config.name}, v: ${config.schemaVersion}`); this.logger_.logMethodArgs?.('constructor', {config}); this.key__ = LocalStorageProvider.getKey(config); LocalStorageProvider.clearPreviousStorageVersions(config); this.parse_ = config.parse ?? (JSON.parse as (value: string) => T); this.stringify_ = config.stringify ?? JSON.stringify; } /** * Generates the versioned storage key. * @param config - An object containing the name and schemaVersion. * @returns The versioned key string. */ public static getKey(config: {name: string; schemaVersion: number}): string { return `${config.name}.v${config.schemaVersion}`; } /** * Manages data migration by removing all previous versions of the item. */ public static clearPreviousStorageVersions(config: {name: string; schemaVersion: number}): void { if (config.schemaVersion < 1) return; // Iterate from v1 up to the version just before the current one and remove them. for (let i = 0; i < config.schemaVersion; i++) { const oldKey = LocalStorageProvider.getKey({name: config.name, schemaVersion: i}); localStorage.removeItem(oldKey); } } /** * Checks if a versioned item exists in localStorage for the given configuration. * This static method allows checking for the existence of a specific versioned item * without instantiating the provider. * * @param config - The configuration object containing the name and schemaVersion. * @returns `true` if the item exists in localStorage, otherwise `false`. * * @example * ```typescript * const exists = LocalStorageProvider.has({ name: 'user-form', schemaVersion: 1 }); * ``` */ public static has(config: LocalStorageProviderConfig): boolean { const key = LocalStorageProvider.getKey(config); return localStorage.getItem(key) !== null; } /** * Checks if the current versioned item exists in localStorage. * * @returns `true` if the item exists in localStorage, otherwise `false`. * * @example * ```typescript * const provider = new LocalStorageProvider({ name: 'profile', schemaVersion: 2 }); * if (provider.has()) { * // Item exists * } * ``` */ public has(): boolean { return localStorage.getItem(this.key__) !== null; } /** * Reads and parses the value from localStorage. * If the item doesn't exist or is invalid JSON, returns null. */ public read(): T | null { let value: string | null = null; try { value = localStorage.getItem(this.key__); } catch (err) { this.logger_.error('read', 'read_local_storage_error', {err}); } if (!value) { this.logger_.logMethod?.('read//no_value'); return null; } try { const parsedValue = this.parse_(value); this.logger_.logMethodFull?.('read//value', undefined, {parsedValue}); return parsedValue; } catch (err) { this.logger_.error('read', 'read_parse_error', {err}); return null; } } /** * Serializes and writes a value to localStorage. */ public write(value: T): void { this.logger_.logMethodArgs?.('write', {value}); let valueStr: string; try { valueStr = this.stringify_(value); } catch (err) { this.logger_.error('write', 'write_stringify_error', {err}); throw new Error('write_stringify_error'); } try { localStorage.setItem(this.key__, valueStr); } catch (err) { this.logger_.error('write', 'write_local_storage_error', {err}); } } /** * Removes the item from localStorage. */ public remove(): void { localStorage.removeItem(this.key__); } }