{"version":3,"file":"persist.mjs","names":[],"sources":["../../../../@mongez/atom/src/persist.ts"],"sourcesContent":["/**\n * @fileoverview Persistence adapters for atoms.\n *\n * Plug in any store-shaped object (cache, localStorage wrapper, cookie\n * helper, IndexedDB layer) and atoms will:\n *\n *   1. Load their initial value from the adapter at creation (sync or async).\n *   2. Write every subsequent update through to the adapter.\n *   3. Remove the entry on `reset()`.\n *\n * The default adapter is a thin localStorage wrapper for the client; it\n * silently no-ops on the server (no `window`). For SSR-safe per-request\n * persistence, supply your own cookie-aware adapter.\n */\nimport type { Atom, AtomOptions } from \"./types\";\n\n/**\n * Shape of an external store. Methods may be sync or async; the\n * persistence layer handles both transparently.\n */\nexport type PersistAdapter<V = unknown> = {\n  /** Read the persisted value for `key`. `undefined` means \"not present\". */\n  get(key: string): V | undefined | Promise<V | undefined>;\n  /** Write `value` to the store under `key`. */\n  set(key: string, value: V): void | Promise<void>;\n  /** Drop the entry for `key`. Called on `reset()`. */\n  remove(key: string): void | Promise<void>;\n};\n\n/**\n * The shape that goes on `AtomOptions.persist`.\n *\n *   - `true` → use the built-in localStorage adapter (client-only).\n *   - `false` / omitted → no persistence.\n *   - Any object matching `PersistAdapter` → use that adapter.\n */\nexport type PersistOption<V = unknown> =\n  | boolean\n  | PersistAdapter<V>;\n\n/**\n * Built-in adapter backed by `window.localStorage`. JSON-encodes the\n * value on write, decodes on read. No-ops on the server.\n */\nexport const localStorageAdapter: PersistAdapter = {\n  get(key) {\n    if (typeof window === \"undefined\" || !window.localStorage) return undefined;\n    const raw = window.localStorage.getItem(key);\n    if (raw === null) return undefined;\n    try {\n      return JSON.parse(raw);\n    } catch {\n      // Corrupt entry — pretend it doesn't exist.\n      return undefined;\n    }\n  },\n  set(key, value) {\n    if (typeof window === \"undefined\" || !window.localStorage) return;\n    try {\n      window.localStorage.setItem(key, JSON.stringify(value));\n    } catch {\n      // QuotaExceededError or private-mode storage block — silently drop.\n    }\n  },\n  remove(key) {\n    if (typeof window === \"undefined\" || !window.localStorage) return;\n    window.localStorage.removeItem(key);\n  },\n};\n\n/**\n * Resolve a `PersistOption` to an actual adapter, or `undefined` if\n * persistence is disabled.\n */\nexport function resolvePersistAdapter<V>(\n  option: PersistOption<V> | undefined,\n): PersistAdapter<V> | undefined {\n  if (!option) return undefined;\n  if (option === true) return localStorageAdapter as PersistAdapter<V>;\n  return option;\n}\n\n/**\n * Wire an atom up to a persistence adapter.\n *\n * On creation, asynchronously reads the stored value and applies it via\n * `silentUpdate` (so subscribers see it on next render but no `update`\n * event fires). On every update afterwards, writes through to the\n * adapter. On `reset`, removes the entry.\n *\n * This is internal — `createAtom` calls it when `options.persist` is\n * truthy. Consumers don't call it directly.\n */\nexport function attachPersist<V, A extends Record<string, any>>(\n  atom: Atom<V, A>,\n  adapter: PersistAdapter<V>,\n  options: AtomOptions<V, any>,\n): void {\n  // Bootstrap: read the stored value. We don't block the constructor on\n  // an async adapter — the consumer sees the default until the read\n  // resolves, then a silentUpdate flips the value in place.\n  // Both sync throws and async rejections are caught so a broken\n  // adapter never crashes atom creation.\n  try {\n    const stored = adapter.get(atom.key);\n    if (stored instanceof Promise) {\n      stored\n        .then(value => {\n          if (value !== undefined) atom.silentUpdate(value);\n        })\n        .catch(() => {\n          /* keep default */\n        });\n    } else if (stored !== undefined) {\n      atom.silentUpdate(stored);\n    }\n  } catch {\n    /* sync throw on read — keep default */\n  }\n\n  // Write-through on every update. Using onChange instead of replacing\n  // beforeUpdate so we don't fight the user's own beforeUpdate hook.\n  // Sync throws are swallowed so a transient storage error (quota,\n  // private-mode block, etc.) doesn't break the consumer's update flow;\n  // async rejections are caught the same way.\n  const updateSub = atom.onChange(newValue => {\n    try {\n      const result = adapter.set(atom.key, newValue);\n      if (result instanceof Promise) result.catch(() => {});\n    } catch {\n      /* adapter blew up — keep going */\n    }\n  });\n\n  // Drop the entry on reset so the next session starts fresh.\n  const resetSub = atom.onReset(() => {\n    try {\n      const result = adapter.remove(atom.key);\n      if (result instanceof Promise) result.catch(() => {});\n    } catch {\n      /* same — non-fatal */\n    }\n  });\n\n  // Clean up subscriptions when the atom dies.\n  atom.onDestroy(() => {\n    updateSub.unsubscribe();\n    resetSub.unsubscribe();\n  });\n\n  // Suppress unused-options lint when not consumed by future logic.\n  void options;\n}\n"],"mappings":";;;;;AA4CA,MAAa,sBAAsC;CACjD,IAAI,KAAK;EACP,IAAI,OAAO,WAAW,eAAe,CAAC,OAAO,cAAc,OAAO;EAClE,MAAM,MAAM,OAAO,aAAa,QAAQ,GAAG;EAC3C,IAAI,QAAQ,MAAM,OAAO;EACzB,IAAI;GACF,OAAO,KAAK,MAAM,GAAG;EACvB,QAAQ;GAEN;EACF;CACF;CACA,IAAI,KAAK,OAAO;EACd,IAAI,OAAO,WAAW,eAAe,CAAC,OAAO,cAAc;EAC3D,IAAI;GACF,OAAO,aAAa,QAAQ,KAAK,KAAK,UAAU,KAAK,CAAC;EACxD,QAAQ,CAER;CACF;CACA,OAAO,KAAK;EACV,IAAI,OAAO,WAAW,eAAe,CAAC,OAAO,cAAc;EAC3D,OAAO,aAAa,WAAW,GAAG;CACpC;AACF;;;;;AAMA,SAAgB,sBACd,QAC+B;CAC/B,IAAI,CAAC,QAAQ,OAAO;CACpB,IAAI,WAAW,MAAM,OAAO;CAC5B,OAAO;AACT;;;;;;;;;;;;AAaA,SAAgB,cACd,MACA,SACA,SACM;CAMN,IAAI;EACF,MAAM,SAAS,QAAQ,IAAI,KAAK,GAAG;EACnC,IAAI,kBAAkB,SACpB,OACG,MAAK,UAAS;GACb,IAAI,UAAU,QAAW,KAAK,aAAa,KAAK;EAClD,CAAC,EACA,YAAY,CAEb,CAAC;OACE,IAAI,WAAW,QACpB,KAAK,aAAa,MAAM;CAE5B,QAAQ,CAER;CAOA,MAAM,YAAY,KAAK,UAAS,aAAY;EAC1C,IAAI;GACF,MAAM,SAAS,QAAQ,IAAI,KAAK,KAAK,QAAQ;GAC7C,IAAI,kBAAkB,SAAS,OAAO,YAAY,CAAC,CAAC;EACtD,QAAQ,CAER;CACF,CAAC;CAGD,MAAM,WAAW,KAAK,cAAc;EAClC,IAAI;GACF,MAAM,SAAS,QAAQ,OAAO,KAAK,GAAG;GACtC,IAAI,kBAAkB,SAAS,OAAO,YAAY,CAAC,CAAC;EACtD,QAAQ,CAER;CACF,CAAC;CAGD,KAAK,gBAAgB;EACnB,UAAU,YAAY;EACtB,SAAS,YAAY;CACvB,CAAC;AAIH"}