@mocanvas/state 1.0.0 → 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ARCHITECTURE.md +421 -0
- package/BENCHMARK.md +519 -0
- package/CLEAN_ROOM.md +50 -0
- package/COMPAT.md +282 -0
- package/CUSTOM_SHAPES.md +880 -0
- package/LICENSE +110 -16
- package/MIGRATION.md +807 -0
- package/README.md +24 -4
- package/UI.md +256 -0
- package/dist/{chunk-XCRMDUZF.js → chunk-KGMTQKZV.js} +214 -12
- package/dist/chunk-KGMTQKZV.js.map +1 -0
- package/dist/core-DVexut58.d.ts +325 -0
- package/dist/index.d.ts +91 -137
- package/dist/index.js +284 -1
- package/dist/index.js.map +1 -1
- package/dist/react.d.ts +20 -2
- package/dist/react.js +10 -2
- package/dist/react.js.map +1 -1
- package/package.json +11 -13
- package/dist/chunk-XCRMDUZF.js.map +0 -1
package/dist/react.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/react.ts"],"names":[],"mappings":"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","file":"react.js","sourcesContent":["import { forwardRef, memo, useEffect, useMemo, useState, useSyncExternalStore } from \"react\"\nimport type { ComponentType, ForwardedRef, ReactNode } from \"react\"\nimport {\n\tEMPTY_ARRAY,\n\tRenderTracker,\n\tatom,\n\tcomputed,\n\tgetWithoutCapture,\n\treactor,\n\tsubscribeToSignal,\n} from \"./core\"\nimport type { Atom, Computed, ComputedOptions, Signal } from \"./core\"\n\n// ---------------------------------------------------------------------------\n// Reading signals\n// ---------------------------------------------------------------------------\n\ninterface SignalStore<T> {\n\tsubscribe(onChange: () => void): () => void\n\tgetSnapshot(): T\n}\n\nfunction useSignalStore<T>(signal: Signal<T>): SignalStore<T> {\n\treturn useMemo<SignalStore<T>>(\n\t\t() => ({\n\t\t\tsubscribe: (onChange) => subscribeToSignal(signal, onChange),\n\t\t\t// Reads must not be captured by an enclosing tracked render; the\n\t\t\t// subscription above already re-renders this component.\n\t\t\tgetSnapshot: () => getWithoutCapture(signal),\n\t\t}),\n\t\t[signal]\n\t)\n}\n\n/**\n * Subscribes the component to a signal and returns its current value.\n * The three-argument form creates an inline computed that is recreated when\n * `deps` change.\n */\nexport function useValue<T>(signal: Signal<T>): T\nexport function useValue<T>(name: string, fn: () => T, deps: unknown[]): T\nexport function useValue<T>(\n\tnameOrSignal: string | Signal<T>,\n\tfn?: () => T,\n\tdeps: unknown[] = EMPTY_ARRAY as unknown as unknown[]\n): T {\n\tconst isInline = typeof nameOrSignal === \"string\"\n\tconst signal = useMemo<Signal<T>>(\n\t\t() => (isInline ? computed(nameOrSignal as string, fn as () => T) : (nameOrSignal as Signal<T>)),\n\t\t// The dependency list shape is stable per call site.\n\t\tisInline ? deps : [nameOrSignal]\n\t)\n\tconst store = useSignalStore(signal)\n\treturn useSyncExternalStore(store.subscribe, store.getSnapshot, store.getSnapshot)\n}\n\n// ---------------------------------------------------------------------------\n// Creating signals\n// ---------------------------------------------------------------------------\n\n/** Creates an atom once for the lifetime of the component. */\nexport function useAtom<T>(name: string, initialValue: T | (() => T)): Atom<T> {\n\tconst [value] = useState(() =>\n\t\tatom(\n\t\t\tname,\n\t\t\ttypeof initialValue === \"function\" ? (initialValue as () => T)() : initialValue\n\t\t)\n\t)\n\treturn value\n}\n\n/** Creates a computed, recreated whenever `deps` change. */\nexport function useComputed<T>(name: string, fn: () => T, deps: unknown[]): Computed<T>\nexport function useComputed<T>(\n\tname: string,\n\tfn: () => T,\n\toptions: ComputedOptions<T>,\n\tdeps: unknown[]\n): Computed<T>\nexport function useComputed<T>(\n\tname: string,\n\tfn: () => T,\n\toptionsOrDeps: ComputedOptions<T> | unknown[],\n\tmaybeDeps?: unknown[]\n): Computed<T> {\n\tconst hasOptions = !Array.isArray(optionsOrDeps)\n\tconst options = hasOptions ? (optionsOrDeps as ComputedOptions<T>) : undefined\n\tconst deps = hasOptions ? (maybeDeps ?? (EMPTY_ARRAY as unknown as unknown[])) : optionsOrDeps\n\treturn useMemo(() => computed(name, fn, options), deps)\n}\n\n// ---------------------------------------------------------------------------\n// Effects\n// ---------------------------------------------------------------------------\n\nfunction scheduleOnNextFrame(execute: () => void): void {\n\tif (typeof requestAnimationFrame === \"function\") requestAnimationFrame(execute)\n\telse setTimeout(execute, 0)\n}\n\n/**\n * Runs `fn` as a reaction for the lifetime of the component (or until `deps`\n * change). Re-runs are batched to the next animation frame.\n */\nexport function useReactor(name: string, fn: () => void, deps: unknown[]): void {\n\tuseEffect(() => {\n\t\tconst r = reactor(name, fn, { scheduleEffect: scheduleOnNextFrame })\n\t\tr.start()\n\t\treturn () => r.stop()\n\t}, deps)\n}\n\n/** Like `useReactor` but re-runs synchronously as soon as a dependency changes. */\nexport function useQuickReactor(name: string, fn: () => void, deps: unknown[]): void {\n\tuseEffect(() => {\n\t\tconst r = reactor(name, fn)\n\t\tr.start()\n\t\treturn () => r.stop()\n\t}, deps)\n}\n\n// ---------------------------------------------------------------------------\n// Tracked components\n// ---------------------------------------------------------------------------\n\nconst REACT_MEMO_TYPE = Symbol.for(\"react.memo\")\nconst REACT_FORWARD_REF_TYPE = Symbol.for(\"react.forward_ref\")\n\ntype RenderFn<P> = (props: P, ref: ForwardedRef<unknown>) => ReactNode\n\nfunction useTrackedRender<P>(name: string, render: RenderFn<P>, props: P, ref: ForwardedRef<unknown>): ReactNode {\n\tconst [tracker] = useState(() => new RenderTracker(name))\n\tuseSyncExternalStore(tracker.subscribe, tracker.getSnapshot, tracker.getSnapshot)\n\treturn tracker.track(render, props, ref)\n}\n\n/**\n * Wraps a function component so that it re-renders whenever any signal read\n * during its render changes. The result is memoized on props. Supports plain\n * function components and components wrapped in `memo` and/or `forwardRef`.\n */\nexport function track<C extends ComponentType<any>>(Component: C): C {\n\tif (typeof Component === \"function\") {\n\t\tconst fn = Component as unknown as {\n\t\t\tprototype?: { isReactComponent?: unknown }\n\t\t\tdisplayName?: string\n\t\t\tname?: string\n\t\t}\n\t\tconst name = fn.displayName ?? fn.name ?? \"TrackedComponent\"\n\t\tif (fn.prototype?.isReactComponent) {\n\t\t\tthrow new Error(`track(${name}): class components are not supported`)\n\t\t}\n\t\tconst render = Component as unknown as (props: unknown) => ReactNode\n\t\tconst Tracked = (props: unknown) => useTrackedRender(name, render, props, null)\n\t\tTracked.displayName = name\n\t\treturn memo(Tracked) as unknown as C\n\t}\n\n\tconst exotic = Component as unknown as {\n\t\t$$typeof?: symbol\n\t\ttype?: unknown\n\t\trender?: unknown\n\t\tcompare?: unknown\n\t\tdisplayName?: string\n\t}\n\n\tif (exotic.$$typeof === REACT_MEMO_TYPE) {\n\t\tconst inner = track(exotic.type as ComponentType<any>) as unknown as { type: ComponentType<any> }\n\t\t// `inner` is already memoized; reuse the caller's comparator if any.\n\t\tconst compare = exotic.compare as ((a: unknown, b: unknown) => boolean) | undefined\n\t\treturn (compare ? memo(inner.type, compare) : inner) as unknown as C\n\t}\n\n\tif (exotic.$$typeof === REACT_FORWARD_REF_TYPE) {\n\t\tconst render = exotic.render as RenderFn<unknown>\n\t\tconst name = exotic.displayName ?? (render as { name?: string }).name ?? \"TrackedComponent\"\n\t\tconst Tracked = forwardRef<unknown, object>((props, ref) => useTrackedRender(name, render, props, ref))\n\t\tTracked.displayName = name\n\t\treturn memo(Tracked) as unknown as C\n\t}\n\n\tthrow new Error(\"track() expects a function component, or one wrapped in memo() or forwardRef()\")\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/react.ts"],"names":[],"mappings":"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","file":"react.js","sourcesContent":["import { forwardRef, memo, useEffect, useMemo, useState, useSyncExternalStore } from \"react\"\nimport type { ComponentType, ForwardedRef, ReactNode } from \"react\"\nimport {\n\tEMPTY_ARRAY,\n\tRenderTracker,\n\tatom,\n\tcomputed,\n\tgetWithoutCapture,\n\treactor,\n\tsubscribeToSignal,\n} from \"./core\"\nimport type { Atom, Computed, ComputedOptions, Signal } from \"./core\"\n\n// ---------------------------------------------------------------------------\n// Reading signals\n// ---------------------------------------------------------------------------\n\ninterface SignalStore<T> {\n\tsubscribe(onChange: () => void): () => void\n\tgetSnapshot(): T\n}\n\nfunction useSignalStore<T>(signal: Signal<T>): SignalStore<T> {\n\treturn useMemo<SignalStore<T>>(\n\t\t() => ({\n\t\t\tsubscribe: (onChange) => subscribeToSignal(signal, onChange),\n\t\t\t// Reads must not be captured by an enclosing tracked render; the\n\t\t\t// subscription above already re-renders this component.\n\t\t\tgetSnapshot: () => getWithoutCapture(signal),\n\t\t}),\n\t\t[signal]\n\t)\n}\n\n/**\n * Subscribes the component to a signal and returns its current value.\n * The three-argument form creates an inline computed that is recreated when\n * `deps` change.\n */\nexport function useValue<T>(signal: Signal<T>): T\nexport function useValue<T>(name: string, fn: () => T, deps: unknown[]): T\nexport function useValue<T>(\n\tnameOrSignal: string | Signal<T>,\n\tfn?: () => T,\n\tdeps: unknown[] = EMPTY_ARRAY as unknown as unknown[]\n): T {\n\tconst isInline = typeof nameOrSignal === \"string\"\n\tconst signal = useMemo<Signal<T>>(\n\t\t() => (isInline ? computed(nameOrSignal as string, fn as () => T) : (nameOrSignal as Signal<T>)),\n\t\t// The dependency list shape is stable per call site.\n\t\tisInline ? deps : [nameOrSignal]\n\t)\n\tconst store = useSignalStore(signal)\n\treturn useSyncExternalStore(store.subscribe, store.getSnapshot, store.getSnapshot)\n}\n\n// ---------------------------------------------------------------------------\n// Creating signals\n// ---------------------------------------------------------------------------\n\n/** Creates an atom once for the lifetime of the component. */\nexport function useAtom<T>(name: string, initialValue: T | (() => T)): Atom<T> {\n\tconst [value] = useState(() =>\n\t\tatom(\n\t\t\tname,\n\t\t\ttypeof initialValue === \"function\" ? (initialValue as () => T)() : initialValue\n\t\t)\n\t)\n\treturn value\n}\n\n/** Creates a computed, recreated whenever `deps` change. */\nexport function useComputed<T>(name: string, fn: () => T, deps: unknown[]): Computed<T>\nexport function useComputed<T>(\n\tname: string,\n\tfn: () => T,\n\toptions: ComputedOptions<T>,\n\tdeps: unknown[]\n): Computed<T>\nexport function useComputed<T>(\n\tname: string,\n\tfn: () => T,\n\toptionsOrDeps: ComputedOptions<T> | unknown[],\n\tmaybeDeps?: unknown[]\n): Computed<T> {\n\tconst hasOptions = !Array.isArray(optionsOrDeps)\n\tconst options = hasOptions ? (optionsOrDeps as ComputedOptions<T>) : undefined\n\tconst deps = hasOptions ? (maybeDeps ?? (EMPTY_ARRAY as unknown as unknown[])) : optionsOrDeps\n\treturn useMemo(() => computed(name, fn, options), deps)\n}\n\n// ---------------------------------------------------------------------------\n// Effects\n// ---------------------------------------------------------------------------\n\nfunction scheduleOnNextFrame(execute: () => void): void {\n\tif (typeof requestAnimationFrame === \"function\") requestAnimationFrame(execute)\n\telse setTimeout(execute, 0)\n}\n\n/**\n * Runs `fn` as a reaction for the lifetime of the component (or until `deps`\n * change). Re-runs are batched to the next animation frame.\n */\nexport function useReactor(name: string, fn: () => void, deps: unknown[]): void {\n\tuseEffect(() => {\n\t\tconst r = reactor(name, fn, { scheduleEffect: scheduleOnNextFrame })\n\t\tr.start()\n\t\treturn () => r.stop()\n\t}, deps)\n}\n\n/** Like `useReactor` but re-runs synchronously as soon as a dependency changes. */\nexport function useQuickReactor(name: string, fn: () => void, deps: unknown[]): void {\n\tuseEffect(() => {\n\t\tconst r = reactor(name, fn)\n\t\tr.start()\n\t\treturn () => r.stop()\n\t}, deps)\n}\n\n// ---------------------------------------------------------------------------\n// Tracked components\n// ---------------------------------------------------------------------------\n\nconst REACT_MEMO_TYPE = Symbol.for(\"react.memo\")\nconst REACT_FORWARD_REF_TYPE = Symbol.for(\"react.forward_ref\")\n\ntype RenderFn<P> = (props: P, ref: ForwardedRef<unknown>) => ReactNode\n\nfunction useTrackedRender<P>(name: string, render: RenderFn<P>, props: P, ref: ForwardedRef<unknown>): ReactNode {\n\tconst [tracker] = useState(() => new RenderTracker(name))\n\tuseSyncExternalStore(tracker.subscribe, tracker.getSnapshot, tracker.getSnapshot)\n\treturn tracker.track(render, props, ref)\n}\n\n/**\n * Run `render` with signal tracking on, and re-render the calling component\n * whenever anything it read changes.\n *\n * This is the hook {@link track} is built from, exposed for the cases the\n * wrapper cannot cover: a component that only wants part of its body tracked,\n * a class component's render delegate, or a render function received as a\n * prop, where there is no component to wrap.\n *\n * ```tsx\n * function Panel({ shape }: { shape: TLShape }) {\n * return useStateTracking(\"Panel\", () => <span>{editor.getShapePageBounds(shape)?.w}</span>)\n * }\n * ```\n *\n * The name is for debugging only; it shows up in {@link whyAmIRunning} output.\n */\nexport function useStateTracking<T>(name: string, render: () => T): T {\n\tconst [tracker] = useState(() => new RenderTracker(name))\n\tuseSyncExternalStore(tracker.subscribe, tracker.getSnapshot, tracker.getSnapshot)\n\treturn tracker.track(runRenderFn as (fn: () => T, unused: null) => T, render, null)\n}\n\n/** Adapts a zero-argument render to {@link RenderTracker.track}'s two-argument shape. */\nfunction runRenderFn<T>(fn: () => T): T {\n\treturn fn()\n}\n\n/**\n * Wraps a function component so that it re-renders whenever any signal read\n * during its render changes. The result is memoized on props. Supports plain\n * function components and components wrapped in `memo` and/or `forwardRef`.\n */\nexport function track<C extends ComponentType<any>>(Component: C): C {\n\tif (typeof Component === \"function\") {\n\t\tconst fn = Component as unknown as {\n\t\t\tprototype?: { isReactComponent?: unknown }\n\t\t\tdisplayName?: string\n\t\t\tname?: string\n\t\t}\n\t\tconst name = fn.displayName ?? fn.name ?? \"TrackedComponent\"\n\t\tif (fn.prototype?.isReactComponent) {\n\t\t\tthrow new Error(`track(${name}): class components are not supported`)\n\t\t}\n\t\tconst render = Component as unknown as (props: unknown) => ReactNode\n\t\tconst Tracked = (props: unknown) => useTrackedRender(name, render, props, null)\n\t\tTracked.displayName = name\n\t\treturn memo(Tracked) as unknown as C\n\t}\n\n\tconst exotic = Component as unknown as {\n\t\t$$typeof?: symbol\n\t\ttype?: unknown\n\t\trender?: unknown\n\t\tcompare?: unknown\n\t\tdisplayName?: string\n\t}\n\n\tif (exotic.$$typeof === REACT_MEMO_TYPE) {\n\t\tconst inner = track(exotic.type as ComponentType<any>) as unknown as { type: ComponentType<any> }\n\t\t// `inner` is already memoized; reuse the caller's comparator if any.\n\t\tconst compare = exotic.compare as ((a: unknown, b: unknown) => boolean) | undefined\n\t\treturn (compare ? memo(inner.type, compare) : inner) as unknown as C\n\t}\n\n\tif (exotic.$$typeof === REACT_FORWARD_REF_TYPE) {\n\t\tconst render = exotic.render as RenderFn<unknown>\n\t\tconst name = exotic.displayName ?? (render as { name?: string }).name ?? \"TrackedComponent\"\n\t\tconst Tracked = forwardRef<unknown, object>((props, ref) => useTrackedRender(name, render, props, ref))\n\t\tTracked.displayName = name\n\t\treturn memo(Tracked) as unknown as C\n\t}\n\n\tthrow new Error(\"track() expects a function component, or one wrapped in memo() or forwardRef()\")\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,18 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mocanvas/state",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "4.0.0",
|
|
4
4
|
"description": "Signals for mocanvas: atoms, computed values, transactions and React bindings.",
|
|
5
|
-
"license": "
|
|
5
|
+
"license": "SEE LICENSE IN LICENSE",
|
|
6
6
|
"author": "Symbio Digital",
|
|
7
|
-
"homepage": "https://github.com/SYMBIO/mocanvas/tree/main/packages/state#readme",
|
|
8
|
-
"repository": {
|
|
9
|
-
"type": "git",
|
|
10
|
-
"url": "git+https://github.com/SYMBIO/mocanvas.git",
|
|
11
|
-
"directory": "packages/state"
|
|
12
|
-
},
|
|
13
|
-
"bugs": {
|
|
14
|
-
"url": "https://github.com/SYMBIO/mocanvas/issues"
|
|
15
|
-
},
|
|
16
7
|
"keywords": [
|
|
17
8
|
"signals",
|
|
18
9
|
"reactive",
|
|
@@ -23,12 +14,19 @@
|
|
|
23
14
|
],
|
|
24
15
|
"type": "module",
|
|
25
16
|
"engines": {
|
|
26
|
-
"node": ">=
|
|
17
|
+
"node": ">=22.12.0"
|
|
27
18
|
},
|
|
28
19
|
"files": [
|
|
29
20
|
"dist",
|
|
30
21
|
"README.md",
|
|
31
|
-
"LICENSE"
|
|
22
|
+
"LICENSE",
|
|
23
|
+
"ARCHITECTURE.md",
|
|
24
|
+
"BENCHMARK.md",
|
|
25
|
+
"CLEAN_ROOM.md",
|
|
26
|
+
"COMPAT.md",
|
|
27
|
+
"CUSTOM_SHAPES.md",
|
|
28
|
+
"MIGRATION.md",
|
|
29
|
+
"UI.md"
|
|
32
30
|
],
|
|
33
31
|
"sideEffects": false,
|
|
34
32
|
"main": "./dist/index.js",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/core.ts"],"names":["atom"],"mappings":"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","file":"chunk-XCRMDUZF.js","sourcesContent":["/**\n * Fine-grained reactive signals.\n *\n * Model\n * -----\n * - A single global epoch counter advances on every atom write.\n * - Every signal remembers `lastChangedEpoch`, the epoch of the write that\n * last changed its value.\n * - Computeds are lazy and pull-based: `get()` verifies the cached value by\n * comparing the `lastChangedEpoch` of each recorded parent against the\n * epoch at which the computed was last verified (`lastCheckedEpoch`).\n * Recomputation happens only when a parent actually changed.\n * - Reactions (effects) are push-notified: an atom write walks the children\n * graph and marks reachable reactions pending. Pending reactions execute\n * once, after the outermost transaction commits, and re-check their\n * dependencies' epochs before running so that equal computed values never\n * trigger a run.\n * - Dependency tracking uses a single \"active dependent\" slot that behaves\n * like a capture stack through the native call stack (save/restore).\n */\n\nexport const UNINITIALIZED: unique symbol = Symbol(\"UNINITIALIZED\")\nexport type Uninitialized = typeof UNINITIALIZED\n\nexport const EMPTY_ARRAY: readonly [] = Object.freeze([]) as unknown as readonly []\n\n// ---------------------------------------------------------------------------\n// Public types\n// ---------------------------------------------------------------------------\n\nexport interface Signal<T> {\n\treadonly name: string\n\tget(): T\n\tlastChangedEpoch: number\n}\n\nexport interface Atom<T> extends Signal<T> {\n\tset(value: T): T\n\tupdate(fn: (prev: T) => T): T\n}\n\nexport interface Computed<T> extends Signal<T> {\n\t/** Epoch at which the cached value was last verified against its parents. */\n\treadonly lastCheckedEpoch: number\n}\n\nexport interface AtomOptions<T> {\n\tisEqual?: (a: T, b: T) => boolean\n}\n\nexport interface ComputedOptions<T> {\n\tisEqual?: (a: T, b: T) => boolean\n}\n\nexport interface ReactOptions {\n\t/**\n\t * Called whenever the effect needs to (re)run after its first run. The\n\t * default runs `execute` synchronously. Supply e.g. a requestAnimationFrame\n\t * wrapper to batch effect runs per frame.\n\t */\n\tscheduleEffect?: (execute: () => void) => void\n}\n\nexport interface EffectScheduler {\n\treadonly name: string\n\treadonly isActive: boolean\n\treadonly lastRunEpoch: number\n\t/** Runs the effect now if any dependency changed since its last run. */\n\texecute(): void\n}\n\nexport interface Reactor {\n\tstart(): void\n\tstop(): void\n\treadonly scheduler: EffectScheduler\n}\n\n// ---------------------------------------------------------------------------\n// Internal graph node contracts\n// ---------------------------------------------------------------------------\n\n/** Anything that records parents while it runs (computeds, effects). */\ninterface Dependent {\n\tparents: Set<SignalNode>\n\tprevParents: Set<SignalNode>\n\t__notify(): void\n}\n\n/** An atom as seen by the transaction log (method syntax keeps T bivariant). */\ninterface RollbackTarget {\n\t__restore(value: unknown): void\n}\n\n/** Anything that can be read and can have dependents (atoms, computeds). */\ninterface SignalNode<T = unknown> extends Signal<T> {\n\t__addChild(child: Dependent): void\n\t__removeChild(child: Dependent): void\n\t/** Bring the value up to date without recording a dependency. */\n\t__refresh(): void\n}\n\n// ---------------------------------------------------------------------------\n// Global runtime state\n// ---------------------------------------------------------------------------\n\nlet globalEpoch = 0\nlet activeDependent: Dependent | null = null\nlet currentTx: TxLevel | null = null\nlet isFlushing = false\nconst pendingEffects = new Set<EffectBase>()\nconst flushScratch: EffectBase[] = []\nconst MAX_FLUSH_ROUNDS = 10_000\n\nexport function getGlobalEpoch(): number {\n\treturn globalEpoch\n}\n\nfunction defaultIsEqual(a: unknown, b: unknown): boolean {\n\treturn Object.is(a, b)\n}\n\n// ---------------------------------------------------------------------------\n// Dependency capture\n// ---------------------------------------------------------------------------\n\n/**\n * Starts recording parents into `d.parents`. The previous parent set is kept\n * in `d.prevParents` so `endCapture` can diff subscriptions. Both sets are\n * reused across runs; no allocation happens unless the sets grow.\n */\nfunction beginCapture(d: Dependent): Dependent | null {\n\tconst outer = activeDependent\n\tconst recycled = d.prevParents\n\td.prevParents = d.parents\n\td.parents = recycled\n\trecycled.clear()\n\tactiveDependent = d\n\treturn outer\n}\n\n/**\n * Stops recording. When `attached` is true the dependent is live in the\n * graph, so subscriptions are diffed: dropped parents forget the dependent,\n * new parents learn about it.\n */\nfunction endCapture(d: Dependent, outer: Dependent | null, attached: boolean): void {\n\tactiveDependent = outer\n\tconst prev = d.prevParents\n\tconst next = d.parents\n\tif (attached) {\n\t\tfor (const p of prev) if (!next.has(p)) p.__removeChild(d)\n\t\tfor (const p of next) if (!prev.has(p)) p.__addChild(d)\n\t}\n\tprev.clear()\n}\n\n/** Runs `fn` without recording any dependencies for the current dependent. */\nexport function unsafe__withoutCapture<T>(fn: () => T): T {\n\tconst outer = activeDependent\n\tactiveDependent = null\n\ttry {\n\t\treturn fn()\n\t} finally {\n\t\tactiveDependent = outer\n\t}\n}\n\n/** Reads a signal without recording a dependency (closure-free helper). */\nexport function getWithoutCapture<T>(signal: Signal<T>): T {\n\tconst outer = activeDependent\n\tactiveDependent = null\n\ttry {\n\t\treturn signal.get()\n\t} finally {\n\t\tactiveDependent = outer\n\t}\n}\n\n// ---------------------------------------------------------------------------\n// Atom\n// ---------------------------------------------------------------------------\n\nclass AtomImpl<T> implements SignalNode<T>, Atom<T> {\n\treadonly name: string\n\tlastChangedEpoch: number = globalEpoch\n\treadonly children = new Set<Dependent>()\n\tprivate value: T\n\tprivate readonly isEqual: (a: T, b: T) => boolean\n\n\tconstructor(name: string, value: T, isEqual: ((a: T, b: T) => boolean) | undefined) {\n\t\tthis.name = name\n\t\tthis.value = value\n\t\tthis.isEqual = isEqual ?? defaultIsEqual\n\t}\n\n\tget(): T {\n\t\tif (activeDependent !== null) activeDependent.parents.add(this)\n\t\treturn this.value\n\t}\n\n\tset(value: T): T {\n\t\tif (this.isEqual(this.value, value)) return this.value\n\t\tif (currentTx !== null) currentTx.record(this, this.value)\n\t\tthis.__write(value)\n\t\tif (currentTx === null) flushPending()\n\t\treturn value\n\t}\n\n\tupdate(fn: (prev: T) => T): T {\n\t\treturn this.set(fn(this.value))\n\t}\n\n\t/** Restores a value during rollback; skips the write when already equal. */\n\t__restore(value: T): void {\n\t\tif (this.isEqual(this.value, value)) return\n\t\tthis.__write(value)\n\t}\n\n\tprivate __write(value: T): void {\n\t\tglobalEpoch++\n\t\tthis.value = value\n\t\tthis.lastChangedEpoch = globalEpoch\n\t\tfor (const child of this.children) child.__notify()\n\t}\n\n\t__addChild(child: Dependent): void {\n\t\tthis.children.add(child)\n\t}\n\n\t__removeChild(child: Dependent): void {\n\t\tthis.children.delete(child)\n\t}\n\n\t__refresh(): void {\n\t\t// Atoms are always fresh.\n\t}\n}\n\nexport function atom<T>(name: string, initialValue: T, options?: AtomOptions<T>): Atom<T> {\n\treturn new AtomImpl(name, initialValue, options?.isEqual)\n}\n\n// ---------------------------------------------------------------------------\n// Computed\n// ---------------------------------------------------------------------------\n\nclass ComputedImpl<T> implements SignalNode<T>, Computed<T>, Dependent {\n\treadonly name: string\n\tlastChangedEpoch = -1\n\tlastCheckedEpoch = -1\n\t/**\n\t * Epoch of the most recent push notification received while attached.\n\t * While the computed has children it is subscribed to all of its parents,\n\t * so \"no notification since the last check\" proves the cache is fresh\n\t * without walking the parent list.\n\t */\n\tprivate notifiedEpoch = -1\n\tparents = new Set<SignalNode>()\n\tprevParents = new Set<SignalNode>()\n\treadonly children = new Set<Dependent>()\n\tprivate value: T | Uninitialized = UNINITIALIZED\n\tprivate isComputing = false\n\tprivate readonly fn: (prev: T | Uninitialized) => T\n\tprivate readonly isEqual: (a: T, b: T) => boolean\n\n\tconstructor(\n\t\tname: string,\n\t\tfn: (prev: T | Uninitialized) => T,\n\t\tisEqual: ((a: T, b: T) => boolean) | undefined\n\t) {\n\t\tthis.name = name\n\t\tthis.fn = fn\n\t\tthis.isEqual = isEqual ?? defaultIsEqual\n\t}\n\n\tget(): T {\n\t\tif (activeDependent !== null) activeDependent.parents.add(this)\n\t\tthis.__refresh()\n\t\treturn this.value as T\n\t}\n\n\t__refresh(): void {\n\t\tconst epoch = globalEpoch\n\t\tif (this.lastCheckedEpoch === epoch) return\n\t\tif (this.value !== UNINITIALIZED) {\n\t\t\tif (this.children.size > 0 && this.notifiedEpoch <= this.lastCheckedEpoch) {\n\t\t\t\tthis.lastCheckedEpoch = epoch\n\t\t\t\treturn\n\t\t\t}\n\t\t\tlet changed = false\n\t\t\tfor (const p of this.parents) {\n\t\t\t\tp.__refresh()\n\t\t\t\tif (p.lastChangedEpoch > this.lastCheckedEpoch) {\n\t\t\t\t\tchanged = true\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (!changed) {\n\t\t\t\tthis.lastCheckedEpoch = epoch\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t\tthis.recompute()\n\t}\n\n\tprivate recompute(): void {\n\t\tif (this.isComputing) {\n\t\t\tthrow new Error(`Cycle detected while evaluating computed \"${this.name}\"`)\n\t\t}\n\t\tthis.isComputing = true\n\t\tconst epoch = globalEpoch\n\t\tconst prev = this.value\n\t\tconst outer = beginCapture(this)\n\t\tlet next: T\n\t\ttry {\n\t\t\tnext = this.fn(prev)\n\t\t} finally {\n\t\t\tendCapture(this, outer, this.children.size > 0)\n\t\t\tthis.isComputing = false\n\t\t}\n\t\tif (prev === UNINITIALIZED || !this.isEqual(prev as T, next)) {\n\t\t\tthis.value = next\n\t\t\tthis.lastChangedEpoch = epoch\n\t\t}\n\t\tthis.lastCheckedEpoch = epoch\n\t}\n\n\t__notify(): void {\n\t\tif (this.notifiedEpoch === globalEpoch) return\n\t\tthis.notifiedEpoch = globalEpoch\n\t\tfor (const child of this.children) child.__notify()\n\t}\n\n\t__addChild(child: Dependent): void {\n\t\tthis.children.add(child)\n\t\tif (this.children.size === 1) {\n\t\t\t// Becoming live: anything that happened while detached was not\n\t\t\t// pushed to us, so force a parent walk on the next refresh unless\n\t\t\t// we were verified at the current epoch.\n\t\t\tthis.notifiedEpoch = globalEpoch\n\t\t\tfor (const p of this.parents) p.__addChild(this)\n\t\t}\n\t}\n\n\t__removeChild(child: Dependent): void {\n\t\tif (this.children.delete(child) && this.children.size === 0) {\n\t\t\tfor (const p of this.parents) p.__removeChild(this)\n\t\t}\n\t}\n}\n\nexport function computed<T>(\n\tname: string,\n\tfn: (prev: T | Uninitialized) => T,\n\toptions?: ComputedOptions<T>\n): Computed<T> {\n\treturn new ComputedImpl(name, fn, options?.isEqual)\n}\n\n// ---------------------------------------------------------------------------\n// Effects\n// ---------------------------------------------------------------------------\n\nabstract class EffectBase implements Dependent {\n\treadonly name: string\n\tparents = new Set<SignalNode>()\n\tprevParents = new Set<SignalNode>()\n\tlastRunEpoch = -1\n\tisActive = false\n\n\tconstructor(name: string) {\n\t\tthis.name = name\n\t}\n\n\t__notify(): void {\n\t\tif (this.isActive) pendingEffects.add(this)\n\t}\n\n\t/** Called by the flush loop once per batch of changes. */\n\tabstract __schedule(): void\n\n\tprotected attachToParents(): void {\n\t\tfor (const p of this.parents) p.__addChild(this)\n\t}\n\n\tprotected detachFromParents(): void {\n\t\tfor (const p of this.parents) p.__removeChild(this)\n\t}\n\n\t/** True when any parent changed after this effect last ran. */\n\tprotected depsChanged(): boolean {\n\t\tconst since = this.lastRunEpoch\n\t\tfor (const p of this.parents) {\n\t\t\tp.__refresh()\n\t\t\tif (p.lastChangedEpoch > since) return true\n\t\t}\n\t\treturn false\n\t}\n}\n\nclass Reaction extends EffectBase implements EffectScheduler {\n\tprivate isScheduled = false\n\tprivate readonly fn: () => void\n\tprivate readonly scheduleEffect: ((execute: () => void) => void) | null\n\tprivate readonly boundExecute: () => void\n\n\tconstructor(name: string, fn: () => void, scheduleEffect: ((execute: () => void) => void) | null) {\n\t\tsuper(name)\n\t\tthis.fn = fn\n\t\tthis.scheduleEffect = scheduleEffect\n\t\tthis.boundExecute = () => this.execute()\n\t}\n\n\tstart(): void {\n\t\tif (this.isActive) return\n\t\tthis.isActive = true\n\t\tthis.lastRunEpoch = -1\n\t\tthis.execute()\n\t}\n\n\tstop(): void {\n\t\tif (!this.isActive) return\n\t\tthis.detachFromParents()\n\t\tthis.parents.clear()\n\t\tthis.isActive = false\n\t\tthis.isScheduled = false\n\t\tthis.lastRunEpoch = -1\n\t}\n\n\t__schedule(): void {\n\t\tif (this.isScheduled) return\n\t\tthis.isScheduled = true\n\t\tif (this.scheduleEffect !== null) this.scheduleEffect(this.boundExecute)\n\t\telse this.execute()\n\t}\n\n\texecute(): void {\n\t\tthis.isScheduled = false\n\t\tif (!this.isActive) return\n\t\tif (this.lastRunEpoch !== -1 && !this.depsChanged()) return\n\t\t// Writes made by the effect are batched and flushed after it returns.\n\t\tconst tx = beginTx()\n\t\tconst outer = beginCapture(this)\n\t\tthis.lastRunEpoch = globalEpoch\n\t\ttry {\n\t\t\tthis.fn()\n\t\t} catch (error) {\n\t\t\tendCapture(this, outer, this.isActive)\n\t\t\tabortTx(tx)\n\t\t\tthrow error\n\t\t}\n\t\tendCapture(this, outer, this.isActive)\n\t\tcommitTx(tx)\n\t}\n}\n\nexport function react(name: string, fn: () => void, options?: ReactOptions): () => void {\n\tconst reaction = new Reaction(name, fn, options?.scheduleEffect ?? null)\n\treaction.start()\n\treturn () => reaction.stop()\n}\n\nexport function reactor(name: string, fn: () => void, options?: ReactOptions): Reactor {\n\tconst reaction = new Reaction(name, fn, options?.scheduleEffect ?? null)\n\treturn {\n\t\tscheduler: reaction,\n\t\tstart: () => reaction.start(),\n\t\tstop: () => reaction.stop(),\n\t}\n}\n\n/**\n * Dependency tracker for externally driven executions (e.g. a UI render).\n *\n * The host calls `track(fn)` to run `fn` with dependency capture, and\n * `subscribe(listener)` to be told when any captured dependency changes.\n * `getSnapshot()` returns a version number that increments on each such\n * change, which makes the tracker a drop-in external store for React's\n * `useSyncExternalStore`. Subscribing and unsubscribing repeatedly (as strict\n * mode does) is safe: captured parents are kept across unsubscribe so the next\n * subscribe re-attaches to them.\n */\nexport class RenderTracker extends EffectBase {\n\tprivate version = 0\n\tprivate listener: (() => void) | null = null\n\n\tconstructor(name: string) {\n\t\tsuper(name)\n\t}\n\n\t__schedule(): void {\n\t\tthis.execute()\n\t}\n\n\tprivate execute(): void {\n\t\tif (!this.isActive || this.lastRunEpoch === -1) return\n\t\tif (!this.depsChanged()) return\n\t\tthis.bump()\n\t}\n\n\tprivate bump(): void {\n\t\tthis.version++\n\t\tif (this.listener !== null) this.listener()\n\t}\n\n\t/** Runs `fn(a, b)` while recording every signal read as a dependency. */\n\ttrack<A, B, R>(fn: (a: A, b: B) => R, a: A, b: B): R {\n\t\tconst outer = beginCapture(this)\n\t\tthis.lastRunEpoch = globalEpoch\n\t\ttry {\n\t\t\treturn fn(a, b)\n\t\t} finally {\n\t\t\tendCapture(this, outer, this.isActive)\n\t\t}\n\t}\n\n\treadonly getSnapshot = (): number => this.version\n\n\treadonly subscribe = (listener: () => void): (() => void) => {\n\t\tthis.listener = listener\n\t\tif (!this.isActive) {\n\t\t\tthis.isActive = true\n\t\t\tthis.attachToParents()\n\t\t}\n\t\t// Changes between the tracked run and this subscription were not\n\t\t// observed; report them now so the host re-runs.\n\t\tif (this.lastRunEpoch !== -1 && this.depsChanged()) this.bump()\n\t\treturn this.unsubscribe\n\t}\n\n\treadonly unsubscribe = (): void => {\n\t\tif (!this.isActive) return\n\t\tthis.listener = null\n\t\tthis.isActive = false\n\t\tthis.detachFromParents()\n\t}\n}\n\n/**\n * Calls `onChange` whenever `signal`'s value changes. The initial subscription\n * does not call `onChange`. Returns an unsubscribe function.\n */\nexport function subscribeToSignal(signal: Signal<unknown>, onChange: () => void): () => void {\n\tlet initial = true\n\tconst reaction = new Reaction(\n\t\t`subscribe(${signal.name})`,\n\t\t() => {\n\t\t\tsignal.get()\n\t\t\tif (initial) initial = false\n\t\t\telse onChange()\n\t\t},\n\t\tnull\n\t)\n\treaction.start()\n\treturn () => reaction.stop()\n}\n\n// ---------------------------------------------------------------------------\n// Transactions\n// ---------------------------------------------------------------------------\n\nclass TxLevel {\n\treadonly parent: TxLevel | null\n\tlog: Map<RollbackTarget, unknown> | null = null\n\topen = true\n\n\tconstructor(parent: TxLevel | null) {\n\t\tthis.parent = parent\n\t}\n\n\t/** Remembers the first pre-write value of each atom touched in this level. */\n\trecord(atom: RollbackTarget, previous: unknown): void {\n\t\tif (this.log === null) this.log = new Map()\n\t\telse if (this.log.has(atom)) return\n\t\tthis.log.set(atom, previous)\n\t}\n}\n\nfunction beginTx(): TxLevel {\n\tconst tx = new TxLevel(currentTx)\n\tcurrentTx = tx\n\treturn tx\n}\n\nfunction commitTx(tx: TxLevel): void {\n\ttx.open = false\n\tcurrentTx = tx.parent\n\tif (tx.log !== null && tx.parent !== null) {\n\t\t// Nested transactions merge into the outer one so an outer rollback\n\t\t// can still undo writes made by an already-committed inner level.\n\t\tconst parent = tx.parent\n\t\tif (parent.log === null) parent.log = tx.log\n\t\telse for (const [atom, prev] of tx.log) if (!parent.log.has(atom)) parent.log.set(atom, prev)\n\t\ttx.log = null\n\t}\n\tif (currentTx === null) flushPending()\n}\n\nfunction rollbackTx(tx: TxLevel): void {\n\tif (!tx.open) throw new Error(\"Cannot roll back a transaction that has already ended\")\n\tconst log = tx.log\n\tif (log === null) return\n\ttx.log = null\n\tfor (const [atom, prev] of log) atom.__restore(prev)\n}\n\nfunction abortTx(tx: TxLevel): void {\n\trollbackTx(tx)\n\ttx.open = false\n\tcurrentTx = tx.parent\n\tif (currentTx === null) flushPending()\n}\n\n/**\n * Batches atom writes: dependents are notified as writes happen, but effects\n * run once, after the outermost transaction commits. If `fn` throws, writes\n * made inside the transaction are rolled back and the error is rethrown.\n */\nexport function transact<T>(fn: () => T): T {\n\tconst tx = beginTx()\n\tlet result: T\n\ttry {\n\t\tresult = fn()\n\t} catch (error) {\n\t\tabortTx(tx)\n\t\tthrow error\n\t}\n\tcommitTx(tx)\n\treturn result\n}\n\n/**\n * Like `transact`, but `fn` receives a `rollback` function that restores every\n * atom written inside this transaction (including committed nested ones) to\n * the value it had when this transaction began. The transaction stays open\n * after a rollback, so further writes are recorded again.\n */\nexport function transaction<T>(fn: (rollback: () => void) => T): T {\n\tconst tx = beginTx()\n\tlet result: T\n\ttry {\n\t\tresult = fn(() => rollbackTx(tx))\n\t} catch (error) {\n\t\tabortTx(tx)\n\t\tthrow error\n\t}\n\tcommitTx(tx)\n\treturn result\n}\n\n/**\n * Runs every pending effect. Effects that become pending while flushing\n * (because an effect wrote an atom) are picked up by the next round. A bound\n * on rounds turns an effect that keeps invalidating itself into an error.\n */\nfunction flushPending(): void {\n\tif (isFlushing || currentTx !== null || pendingEffects.size === 0) return\n\tisFlushing = true\n\tlet firstError: unknown = pendingEffects // sentinel that can never be thrown\n\tlet rounds = 0\n\ttry {\n\t\twhile (pendingEffects.size > 0) {\n\t\t\tif (++rounds > MAX_FLUSH_ROUNDS) {\n\t\t\t\tpendingEffects.clear()\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Reaction loop detected: effects kept invalidating each other for ${MAX_FLUSH_ROUNDS} rounds`\n\t\t\t\t)\n\t\t\t}\n\t\t\tconst batch = flushScratch\n\t\t\tfor (const effect of pendingEffects) batch.push(effect)\n\t\t\tpendingEffects.clear()\n\t\t\tfor (let i = 0; i < batch.length; i++) {\n\t\t\t\ttry {\n\t\t\t\t\tbatch[i]!.__schedule()\n\t\t\t\t} catch (error) {\n\t\t\t\t\tif (firstError === pendingEffects) firstError = error\n\t\t\t\t}\n\t\t\t}\n\t\t\tbatch.length = 0\n\t\t}\n\t} finally {\n\t\tflushScratch.length = 0\n\t\tisFlushing = false\n\t}\n\tif (firstError !== pendingEffects) throw firstError\n}\n\n// ---------------------------------------------------------------------------\n// Type guards\n// ---------------------------------------------------------------------------\n\nexport function isSignal(value: unknown): value is Signal<unknown> {\n\treturn value instanceof AtomImpl || value instanceof ComputedImpl\n}\n\nexport function isAtom(value: unknown): value is Atom<unknown> {\n\treturn value instanceof AtomImpl\n}\n\nexport function isComputed(value: unknown): value is Computed<unknown> {\n\treturn value instanceof ComputedImpl\n}\n"]}
|