UNPKG

@faizaanceg/pandora

Version:

A tiny wrapper over LocalStorage to improve DX

100 lines (99 loc) 2.87 kB
export class KeyValueStore { static DEFAULTS = { shouldPersist: false, ttl: Number.MAX_SAFE_INTEGER, }; adapter; subscriptions = new Map(); constructor(adapter) { this.adapter = adapter; } getInternal(key) { try { const internalItem = this.adapter.getItem(key); if (internalItem === null) { return; } const item = JSON.parse(internalItem); const isExpired = Date.now() >= item.createdOn + item.ttl; return isExpired ? this.remove(key) : item; } catch { return; } } signal(key) { if (this.subscriptions.has(key)) { this.subscriptions.get(key)?.forEach((signal) => signal?.()); } } get(key, defaultValue) { const item = this.getInternal(key); return item ? item.value : defaultValue ?? null; } set(key, value, opts = {}) { this.adapter.setItem(key, JSON.stringify({ createdOn: Date.now(), value, ...KeyValueStore.DEFAULTS, ...opts, })); this.signal(key); } subscribe(key, signal) { let count = 0; let signals = [signal]; if (this.subscriptions.has(key)) { signals = this.subscriptions.get(key); count = signals.push(signal); } this.subscriptions.set(key, signals); return () => this.removeSubscription(key, count); } removeSubscription(key, count) { if (this.subscriptions.has(key)) { if (count === -1) { this.subscriptions.delete(key); } else { const signals = this.subscriptions.get(key); delete signals[count]; } } } forEach(cb) { const size = this.adapter.length; for (let i = 0; i < size; i++) { const key = this.adapter.key(i); if (key === null) { continue; } const item = this.getInternal(key); if (item) { cb(key, item); } } } remove(key) { this.adapter.removeItem(key); this.signal(key); this.removeSubscription(key, -1); } clear(removeAll = false) { const perishedItems = []; this.forEach((key, item) => { if (!item.shouldPersist || removeAll) { perishedItems.push([key, item.value]); } }); perishedItems.forEach(([key]) => this.remove(key)); return Object.fromEntries(perishedItems); } getSnapshot() { const items = []; this.forEach((key, item) => { items.push([key, item.value]); }); return Object.fromEntries(items); } }