{"version":3,"file":"use-observable-box-CvPTJelD.mjs","names":[],"sources":["../src/util/weak-ref-map.ts","../src/util/use-observable-box.ts"],"sourcesContent":["/**\n * A map with strong keys and weak values: entries disappear on their own once nothing else\n * references the value. Useful as an identity map, where the point is to hand back the *same*\n * instance for a given key while it is still in use, without the map itself being the reason it\n * stays alive.\n *\n * ```ts\n * const cache = new WeakRefMap<number, UserModel>();\n * cache.add(user.id, user);\n * cache.get(user.id); // the same instance, until nothing holds it any more\n * ```\n *\n * Identity therefore lasts exactly as long as someone is holding the value. Once the last\n * reference goes, a later lookup misses and a fresh instance is built — which is unobservable,\n * since by definition nothing was holding the old one.\n */\nexport class WeakRefMap<K, V extends object> {\n  private cacheMap = new Map<K, WeakRef<V>>();\n\n  private finalizer = new FinalizationRegistry((key: K) => {\n    // Double check the key hasn't been re-added since the finalizer was queued, so a freshly\n    // set reference isn't deleted by the collection of the value it replaced.\n    if (!this.get(key)) {\n      this.cacheMap.delete(key);\n    }\n  });\n\n  /** Store `value` under `key`, replacing any existing entry, and return it. */\n  add(key: K, value: V): V {\n    const cached = this.get(key);\n    if (cached) {\n      if (cached === value) return value;\n      // Stop the outgoing value's finalizer from later deleting the incoming entry.\n      this.finalizer.unregister(cached);\n    }\n    this.cacheMap.set(key, new WeakRef(value));\n    this.finalizer.register(value, key, value);\n    return value;\n  }\n\n  /** The live value for `key`, or `undefined` if absent or already collected. */\n  get(key: K): V | undefined {\n    return this.cacheMap.get(key)?.deref();\n  }\n\n  has(key: K): boolean {\n    return this.get(key) !== undefined;\n  }\n\n  /**\n   * Drop the entry for `key` immediately rather than waiting for collection. Use when the value\n   * is known to be gone for good — a deleted record, say — so a later lookup builds a fresh\n   * instance instead of reviving the old one.\n   */\n  delete(key: K): boolean {\n    const cached = this.get(key);\n    if (cached) this.finalizer.unregister(cached);\n    return this.cacheMap.delete(key);\n  }\n\n  /** Forget everything. Values held elsewhere stay alive, they are just no longer identity-mapped. */\n  clear(): void {\n    for (const ref of this.cacheMap.values()) {\n      const value = ref.deref();\n      if (value) this.finalizer.unregister(value);\n    }\n    this.cacheMap.clear();\n  }\n}\n","import {\n  comparer,\n  observable,\n  runInAction,\n  type IEqualsComparer,\n  type IObservableValue,\n} from \"mobx\";\nimport { useEffect, useRef } from \"react\";\n\nexport interface UseObservableBoxOptions<T> {\n  /**\n   * How a new value is judged against the one held. Defaults to `comparer.shallow`, which is what\n   * makes an object rebuilt on every render — `{ orgId, query }` — count as unchanged while its\n   * fields are. Pass `comparer.structural` for values nested deeper than a field, or\n   * `comparer.default` to write on every render that isn't referentially equal.\n   */\n  equals?: IEqualsComparer<T>;\n}\n\n/**\n * Mirror a plain React value into an observable box, so mobx code can react to it.\n *\n * React state isn't observable, which leaves a gap wherever the two meet: a `reaction`, an\n * `autorun`, a `computed`, or a lazy observable's `trackDependencies` can't see a value that lives\n * in `useState` or arrives as a prop. This closes it — `useState` stays where it is, and mobx gets\n * something to watch:\n *\n * ```tsx\n * const [query, setQuery] = useState(\"\");\n * const params = useObservableBox({ orgId, query });\n *\n * // ...anywhere mobx is watching:\n * useAutorun(() => console.log(params.get().query));\n * ```\n *\n * The flow is one-way: the box mirrors the value, so writing to it from mobx's side holds only\n * until the next render whose value disagrees. Keep the value where React already keeps it.\n *\n * The box is created once and kept for the component's lifetime, so a reaction holding it stays\n * valid across renders. Two details it settles that are easy to get wrong by hand: the write\n * happens in an effect rather than during render, so the render itself stays free of side effects;\n * and values are compared rather than assigned blindly, so the object literal you rebuild every\n * render doesn't retrigger every reaction that reads it.\n */\nexport function useObservableBox<T>(\n  value: T,\n  options?: UseObservableBoxOptions<T>,\n): IObservableValue<T> {\n  // Built once, via a ref rather than `useMemo` — React documents that as a hint it may discard,\n  // and a second box would leave any reaction that captured the first watching a value nothing\n  // updates any more. `deep: false`: this mirrors a React value, it does not take ownership of its\n  // contents.\n  const ref = useRef<IObservableValue<T>>(undefined);\n  const box = (ref.current ??= observable.box(value, { deep: false }));\n\n  const equals = options?.equals ?? comparer.shallow;\n\n  // No dependency array: `value` is often rebuilt every render, so the comparison *is* the\n  // dependency check — a `deps` argument would only be a second, less reliable spelling of it.\n  useEffect(() => {\n    if (equals(box.get(), value)) return;\n    runInAction(() => box.set(value));\n  });\n\n  return box;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAgBA,IAAa,aAAb,MAA6C;CAC3C,AAAQ,2BAAW,IAAI,IAAmB;CAE1C,AAAQ,YAAY,IAAI,sBAAsB,QAAW;EAGvD,IAAI,CAAC,KAAK,IAAI,GAAG,GACf,KAAK,SAAS,OAAO,GAAG;CAE5B,CAAC;;CAGD,IAAI,KAAQ,OAAa;EACvB,MAAM,SAAS,KAAK,IAAI,GAAG;EAC3B,IAAI,QAAQ;GACV,IAAI,WAAW,OAAO,OAAO;GAE7B,KAAK,UAAU,WAAW,MAAM;EAClC;EACA,KAAK,SAAS,IAAI,KAAK,IAAI,QAAQ,KAAK,CAAC;EACzC,KAAK,UAAU,SAAS,OAAO,KAAK,KAAK;EACzC,OAAO;CACT;;CAGA,IAAI,KAAuB;EACzB,OAAO,KAAK,SAAS,IAAI,GAAG,CAAC,EAAE,MAAM;CACvC;CAEA,IAAI,KAAiB;EACnB,OAAO,KAAK,IAAI,GAAG,MAAM;CAC3B;;;;;;CAOA,OAAO,KAAiB;EACtB,MAAM,SAAS,KAAK,IAAI,GAAG;EAC3B,IAAI,QAAQ,KAAK,UAAU,WAAW,MAAM;EAC5C,OAAO,KAAK,SAAS,OAAO,GAAG;CACjC;;CAGA,QAAc;EACZ,KAAK,MAAM,OAAO,KAAK,SAAS,OAAO,GAAG;GACxC,MAAM,QAAQ,IAAI,MAAM;GACxB,IAAI,OAAO,KAAK,UAAU,WAAW,KAAK;EAC5C;EACA,KAAK,SAAS,MAAM;CACtB;AACF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACxBA,SAAgB,iBACd,OACA,SACqB;CAKrB,MAAM,MAAM,OAA4B,MAAS;CACjD,MAAM,MAAO,IAAI,YAAY,WAAW,IAAI,OAAO,EAAE,MAAM,MAAM,CAAC;CAElE,MAAM,SAAS,SAAS,UAAU,SAAS;CAI3C,gBAAgB;EACd,IAAI,OAAO,IAAI,IAAI,GAAG,KAAK,GAAG;EAC9B,kBAAkB,IAAI,IAAI,KAAK,CAAC;CAClC,CAAC;CAED,OAAO;AACT"}