1import * as Layer from "effect/Layer";
2import { Atom, AtomRegistry, Reactivity } from "effect/reactivity";
3
4export interface AcceptingHotModule {
5 readonly accept: (callback?: () => void) => void;
6}
7
8interface HotAtomRuntimeEntry {
9 readonly layerAtom: Atom.Writable<
10 Layer.Layer<unknown, unknown, AtomRegistry.AtomRegistry | Reactivity.Reactivity>
11 >;
12 readonly runtime: Atom.AtomRuntime<unknown, unknown>;
13}
14
15const hotAtomRuntimesKey = Symbol.for("t3.mobile.hot-atom-runtimes");
16
17type HotAtomRuntimeGlobal = typeof globalThis & {
18 [hotAtomRuntimesKey]?: Map<string, HotAtomRuntimeEntry>;
19};
20
21function hotAtomRuntimes(): Map<string, HotAtomRuntimeEntry> {
22 const runtimeGlobal = globalThis as HotAtomRuntimeGlobal;
23 return (runtimeGlobal[hotAtomRuntimesKey] ??= new Map());
24}
25
26export function hotSwappableAtomRuntime<R, E>(options: {
27 readonly id: string;
28 readonly hotModule: AcceptingHotModule | undefined;
29 readonly registry: AtomRegistry.AtomRegistry;
30 readonly layer: Layer.Layer<R, E, AtomRegistry.AtomRegistry | Reactivity.Reactivity>;
31}): Atom.AtomRuntime<R, E> {
32 if (options.hotModule === undefined || typeof __DEV__ === "undefined" || !__DEV__) {
33 return Atom.runtime(options.layer);
34 }
35
36 const runtimes = hotAtomRuntimes();
37 const existing = runtimes.get(options.id);
38 let entry: HotAtomRuntimeEntry;
39
40 if (existing === undefined) {
41 const layerAtom = Atom.make(options.layer);
42 entry = {
43 layerAtom: layerAtom as HotAtomRuntimeEntry["layerAtom"],
44 runtime: Atom.runtime((get) => get(layerAtom)) as HotAtomRuntimeEntry["runtime"],
45 };
46 runtimes.set(options.id, entry);
47 } else {
48 entry = existing;
49 options.registry.set(
50 entry.layerAtom,
51 options.layer as Layer.Layer<
52 unknown,
53 unknown,
54 AtomRegistry.AtomRegistry | Reactivity.Reactivity
55 >,
56 );
57 }
58
59 // This is a real HMR boundary: importers retain the stable AtomRuntime while
60 // this module evaluation installs the freshly constructed Layer above.
61 options.hotModule.accept();
62 return entry.runtime as Atom.AtomRuntime<R, E>;
63}