"use client" import { createJSONStorage, type PersistStorage, type StateStorage, type StorageValue, } from "zustand/middleware" const DEFAULT_DEBOUNCE_MS = 220 /** In-memory fallback when `localStorage` is missing (SSR) or throws (private mode, etc.). */ function createMemoryStateStorage(): StateStorage { const data = new Map() return { getItem: (name) => data.get(name) ?? null, setItem: (name, value) => { data.set(name, value) }, removeItem: (name) => { data.delete(name) }, } } function getPersistBackingStorage(): StateStorage { if (typeof window === "undefined") { return createMemoryStateStorage() } try { return window.localStorage } catch { return createMemoryStateStorage() } } /** * JSON persist storage that debounces writes to `localStorage` so rapid * store updates do not block the main thread on every mutation. * Flushes pending keys on a timer, `beforeunload`, and `pagehide`. * Uses in-memory storage during SSR or when `localStorage` is unavailable. */ function buildDebouncedJsonStorage(): PersistStorage { const base = createJSONStorage(getPersistBackingStorage) ?? createJSONStorage(() => createMemoryStateStorage()) if (!base) { throw new Error("[debounced-json-storage] failed to create JSON storage") } const pending = new Map>() const timers = new Map>() const flushKey = (name: string) => { const t = timers.get(name) if (t !== undefined) { globalThis.clearTimeout(t) timers.delete(name) } const value = pending.get(name) if (value === undefined) return pending.delete(name) base.setItem(name, value) } const flushAll = () => { for (const name of [...pending.keys()]) flushKey(name) } if (typeof window !== "undefined") { window.addEventListener("beforeunload", flushAll) window.addEventListener("pagehide", flushAll) } const flushPersistStorage = () => { flushAll() } return { getItem: (name) => base.getItem(name), setItem: (name, value) => { pending.set(name, value) const existing = timers.get(name) if (existing !== undefined) globalThis.clearTimeout(existing) const id = globalThis.setTimeout(() => { timers.delete(name) flushKey(name) }, DEFAULT_DEBOUNCE_MS) timers.set(name, id) }, removeItem: (name) => { const existing = timers.get(name) if (existing !== undefined) { globalThis.clearTimeout(existing) timers.delete(name) } pending.delete(name) return base.removeItem(name) }, flush: flushPersistStorage, } } /** Shared instance for all zustand `persist` stores in this app. */ export const debouncedPersistJSONStorage = buildDebouncedJsonStorage() /** Flush pending debounced persist writes (call before hard navigation on logout). */ export function flushPersistStorage() { const storage = debouncedPersistJSONStorage as PersistStorage & { flush?: () => void } storage.flush?.() }