import type { SyncStorageAdapter } from '../types'; import { BROWSER_EVENT_STORAGE, STORAGE_ADAPTER_LOCAL } from '../consts'; function getLocalStorage(): Storage | null { if (typeof window === 'undefined') return null; return window.localStorage; } /** * `localStorage` adapter. Singleton — the underlying store is process-wide, * so multiple `EngineStorage` instances over `localAdapter` write to the * same backend. * * `onChange` subscribes to the `storage` event for cross-tab sync. The event * does **not** fire in the originating tab; intra-tab sync is handled by the * EntryBus inside `EngineStorage`. * * SSR-safe: every method is a no-op outside the browser. Reads return `null` * so consumers fall back to defaults. */ export const localAdapter: SyncStorageAdapter = { name: STORAGE_ADAPTER_LOCAL, getItem(key) { return getLocalStorage()?.getItem(key) ?? null; }, setItem(key, value) { getLocalStorage()?.setItem(key, value); }, removeItem(key) { getLocalStorage()?.removeItem(key); }, onChange: (fn) => { if (typeof window === 'undefined') return () => {}; let storage: Storage; try { const resolved = getLocalStorage(); if (resolved === null) return () => {}; storage = resolved; } catch { return () => {}; } const listener = (event: StorageEvent): void => { // Filter out events from sessionStorage / clearAll (key === null). if (event.storageArea !== storage) return; if (event.key === null) return; fn(event.key, event.newValue); }; window.addEventListener(BROWSER_EVENT_STORAGE, listener); return () => window.removeEventListener(BROWSER_EVENT_STORAGE, listener); } };