UNPKG

@solidjs/signals

Version:

SolidJS' standalone reactivity implementation

1,576 lines 129 kB
class NotReadyError extends Error { source; constructor(source) { super(); this.source = source; } } class StatusError extends Error { source; constructor(source, original) { super(original instanceof Error ? original.message : String(original), { cause: original }); this.source = source; } } class NoOwnerError extends Error { constructor() { super("Context can only be accessed under a reactive root."); } } class ContextNotFoundError extends Error { constructor() { super( "Context must either be created with a default value or a value must be provided before accessing it." ); } } const REACTIVE_NONE = 0; const REACTIVE_CHECK = 1 << 0; const REACTIVE_DIRTY = 1 << 1; const REACTIVE_RECOMPUTING_DEPS = 1 << 2; const REACTIVE_IN_HEAP = 1 << 3; const REACTIVE_IN_HEAP_HEIGHT = 1 << 4; const REACTIVE_ZOMBIE = 1 << 5; const REACTIVE_DISPOSED = 1 << 6; const REACTIVE_OPTIMISTIC_DIRTY = 1 << 7; const REACTIVE_SNAPSHOT_STALE = 1 << 8; const REACTIVE_LAZY = 1 << 9; const STATUS_PENDING = 1 << 0; const STATUS_ERROR = 1 << 1; const STATUS_UNINITIALIZED = 1 << 2; const EFFECT_RENDER = 1; const EFFECT_USER = 2; const EFFECT_TRACKED = 3; const NOT_PENDING = {}; const NO_SNAPSHOT = {}; const STORE_SNAPSHOT_PROPS = "sp"; const SUPPORTS_PROXY = typeof Proxy === "function"; const defaultContext = {}; const $REFRESH = Symbol("refresh"); function actualInsertIntoHeap(n, heap) { const parentHeight = (n._parent?._root ? n._parent._parentComputed?._height : n._parent?._height) ?? -1; if (parentHeight >= n._height) n._height = parentHeight + 1; const height = n._height; const heapAtHeight = heap._heap[height]; if (heapAtHeight === undefined) heap._heap[height] = n; else { const tail = heapAtHeight._prevHeap; tail._nextHeap = n; n._prevHeap = tail; heapAtHeight._prevHeap = n; } if (height > heap._max) heap._max = height; } function insertIntoHeap(n, heap) { let flags = n._flags; if (flags & (REACTIVE_IN_HEAP | REACTIVE_RECOMPUTING_DEPS)) return; if (flags & REACTIVE_CHECK) { n._flags = (flags & -4) | REACTIVE_DIRTY | REACTIVE_IN_HEAP; } else n._flags = flags | REACTIVE_IN_HEAP; if (!(flags & REACTIVE_IN_HEAP_HEIGHT)) actualInsertIntoHeap(n, heap); } function insertIntoHeapHeight(n, heap) { let flags = n._flags; if (flags & (REACTIVE_IN_HEAP | REACTIVE_RECOMPUTING_DEPS | REACTIVE_IN_HEAP_HEIGHT)) return; n._flags = flags | REACTIVE_IN_HEAP_HEIGHT; actualInsertIntoHeap(n, heap); } function deleteFromHeap(n, heap) { const flags = n._flags; if (!(flags & (REACTIVE_IN_HEAP | REACTIVE_IN_HEAP_HEIGHT))) return; n._flags = flags & -25; const height = n._height; if (n._prevHeap === n) heap._heap[height] = undefined; else { const next = n._nextHeap; const dhh = heap._heap[height]; const end = next ?? dhh; if (n === dhh) heap._heap[height] = next; else n._prevHeap._nextHeap = next; end._prevHeap = n._prevHeap; } n._prevHeap = n; n._nextHeap = undefined; } function markHeap(heap) { if (heap._marked) return; heap._marked = true; for (let i = 0; i <= heap._max; i++) { for (let el = heap._heap[i]; el !== undefined; el = el._nextHeap) { if (el._flags & REACTIVE_IN_HEAP) markNode(el); } } } function markNode(el, newState = REACTIVE_DIRTY) { const flags = el._flags; if ((flags & (REACTIVE_CHECK | REACTIVE_DIRTY)) >= newState) return; el._flags = (flags & -4) | newState; for (let link = el._subs; link !== null; link = link._nextSub) { markNode(link._sub, REACTIVE_CHECK); } if (el._child !== null) { for (let child = el._child; child !== null; child = child._nextChild) { for (let link = child._subs; link !== null; link = link._nextSub) { markNode(link._sub, REACTIVE_CHECK); } } } } function runHeap(heap, recompute) { heap._marked = false; for (heap._min = 0; heap._min <= heap._max; heap._min++) { let el = heap._heap[heap._min]; while (el !== undefined) { if (el._flags & REACTIVE_IN_HEAP) recompute(el); else adjustHeight(el, heap); el = heap._heap[heap._min]; } } heap._max = 0; } function adjustHeight(el, heap) { deleteFromHeap(el, heap); let newHeight = el._height; for (let d = el._deps; d; d = d._nextDep) { const dep1 = d._dep; const dep = dep1._firewall || dep1; if (dep._fn && dep._height >= newHeight) newHeight = dep._height + 1; } if (el._height !== newHeight) { el._height = newHeight; for (let s = el._subs; s !== null; s = s._nextSub) { insertIntoHeapHeight(s._sub, heap); } } } const hooks = {}; const diagnosticListeners = new Set(); const diagnosticCaptures = new Set(); let diagnosticSequence = 0; const diagnostics = { subscribe(listener) { diagnosticListeners.add(listener); return () => diagnosticListeners.delete(listener); }, capture() { const events = []; diagnosticCaptures.add(events); return { get events() { return events; }, clear() { events.length = 0; }, stop() { diagnosticCaptures.delete(events); return [...events]; } }; } }; const DEV$1 = { hooks: hooks, diagnostics: diagnostics, getChildren: getChildren, getSignals: getSignals, getParent: getParent, getSources: getSources, getObservers: getObservers }; function emitDiagnostic(event) { const entry = { sequence: ++diagnosticSequence, ...event }; for (const listener of diagnosticListeners) listener(entry); for (const capture of diagnosticCaptures) capture.push(entry); return entry; } function registerGraph(value, owner) { value._owner = owner; if (owner) { if (!owner._signals) owner._signals = []; owner._signals.push(value); } DEV$1.hooks.onGraph?.(value, owner); } function clearSignals(node) { node._signals = undefined; } function getChildren(owner) { const children = []; let child = owner._firstChild; while (child) { children.push(child); child = child._nextSibling; } return children; } function getSignals(owner) { return owner._signals ? [...owner._signals] : []; } function getParent(owner) { return owner._parent; } function getSources(computation) { const sources = []; let link = computation._deps; while (link) { sources.push(link._dep); link = link._nextDep; } return sources; } function getObservers(node) { const observers = []; let link = node._subs; while (link) { observers.push(link._sub); link = link._nextSub; } return observers; } const transitions = new Set(); const dirtyQueue = { _heap: new Array(2e3).fill(undefined), _marked: false, _min: 0, _max: 0 }; const zombieQueue = { _heap: new Array(2e3).fill(undefined), _marked: false, _min: 0, _max: 0 }; let clock = 0; let activeTransition = null; let scheduled = false; let projectionWriteActive = false; let inTrackedQueueCallback = false; let _enforceLoadingBoundary = false; let _hitUnhandledAsync = false; let stashedOptimisticReads = null; function resetUnhandledAsync() { _hitUnhandledAsync = false; } function enforceLoadingBoundary(enabled) { _enforceLoadingBoundary = enabled; } function shouldReadStashedOptimisticValue(node) { return !!stashedOptimisticReads?.has(node); } function runLaneEffects(type) { for (const lane of activeLanes) { if (lane._mergedInto || lane._pendingAsync.size > 0) continue; const effects = lane._effectQueues[type - 1]; if (effects.length) { lane._effectQueues[type - 1] = []; runQueue(effects, type); } } } function queueStashedOptimisticEffects(node) { for (let s = node._subs; s !== null; s = s._nextSub) { const sub = s._sub; if (!sub._type) continue; if (sub._type === EFFECT_TRACKED) { if (!sub._modified) { sub._modified = true; sub._queue.enqueue(EFFECT_USER, sub._run); } continue; } const queue = sub._flags & REACTIVE_ZOMBIE ? zombieQueue : dirtyQueue; if (queue._min > sub._height) queue._min = sub._height; insertIntoHeap(sub, queue); } } function setProjectionWriteActive(value) { projectionWriteActive = value; } function setTrackedQueueCallback(value) { inTrackedQueueCallback = value; } function mergeTransitionState(target, outgoing) { outgoing._done = target; target._actions.push(...outgoing._actions); for (const lane of activeLanes) if (lane._transition === outgoing) lane._transition = target; target._optimisticNodes.push(...outgoing._optimisticNodes); for (const store of outgoing._optimisticStores) target._optimisticStores.add(store); for (const [source, reporters] of outgoing._asyncReporters) { let targetReporters = target._asyncReporters.get(source); if (!targetReporters) target._asyncReporters.set(source, (targetReporters = new Set())); for (const reporter of reporters) targetReporters.add(reporter); } } function resolveOptimisticNodes(nodes) { for (let i = 0; i < nodes.length; i++) { const node = nodes[i]; node._optimisticLane = undefined; if (node._pendingValue !== NOT_PENDING) { node._value = node._pendingValue; node._pendingValue = NOT_PENDING; } const prevOverride = node._overrideValue; node._overrideValue = NOT_PENDING; if (prevOverride !== NOT_PENDING && node._value !== prevOverride) insertSubs(node, true); node._transition = null; } nodes.length = 0; } function cleanupCompletedLanes(completingTransition) { for (const lane of activeLanes) { const owned = completingTransition ? lane._transition === completingTransition : !lane._transition; if (!owned) continue; if (!lane._mergedInto) { if (lane._effectQueues[0].length) runQueue(lane._effectQueues[0], EFFECT_RENDER); if (lane._effectQueues[1].length) runQueue(lane._effectQueues[1], EFFECT_USER); } if (lane._source._optimisticLane === lane) lane._source._optimisticLane = undefined; lane._pendingAsync.clear(); lane._effectQueues[0].length = 0; lane._effectQueues[1].length = 0; activeLanes.delete(lane); signalLanes.delete(lane._source); } } function schedule() { if (scheduled) return; scheduled = true; if (!globalQueue._running && !projectionWriteActive) queueMicrotask(flush); } class Queue { _parent = null; _queues = [[], []]; _children = []; created = clock; addChild(child) { this._children.push(child); child._parent = this; } removeChild(child) { const index = this._children.indexOf(child); if (index >= 0) { this._children.splice(index, 1); child._parent = null; } } notify(node, mask, flags, error) { if (this._parent) return this._parent.notify(node, mask, flags, error); return false; } run(type) { if (this._queues[type - 1].length) { const effects = this._queues[type - 1]; this._queues[type - 1] = []; runQueue(effects, type); } for (let i = 0; i < this._children.length; i++) this._children[i].run?.(type); } enqueue(type, fn) { if (type) { if (currentOptimisticLane) { const lane = findLane(currentOptimisticLane); lane._effectQueues[type - 1].push(fn); } else { this._queues[type - 1].push(fn); } } schedule(); } stashQueues(stub) { stub._queues[0].push(...this._queues[0]); stub._queues[1].push(...this._queues[1]); this._queues = [[], []]; for (let i = 0; i < this._children.length; i++) { let child = this._children[i]; let childStub = stub._children[i]; if (!childStub) { childStub = { _queues: [[], []], _children: [] }; stub._children[i] = childStub; } child.stashQueues(childStub); } } restoreQueues(stub) { this._queues[0].push(...stub._queues[0]); this._queues[1].push(...stub._queues[1]); for (let i = 0; i < stub._children.length; i++) { const childStub = stub._children[i]; let child = this._children[i]; if (child) child.restoreQueues(childStub); } } } class GlobalQueue extends Queue { _running = false; _pendingNodes = []; _optimisticNodes = []; _optimisticStores = new Set(); static _update; static _dispose; static _clearOptimisticStore = null; flush() { if (this._running) return; this._running = true; try { runHeap(dirtyQueue, GlobalQueue._update); if (activeTransition) { const isComplete = transitionComplete(activeTransition); if (!isComplete) { const stashedTransition = activeTransition; runHeap(zombieQueue, GlobalQueue._update); this._pendingNodes = []; this._optimisticNodes = []; this._optimisticStores = new Set(); runLaneEffects(EFFECT_RENDER); runLaneEffects(EFFECT_USER); this.stashQueues(stashedTransition._queueStash); clock++; scheduled = dirtyQueue._max >= dirtyQueue._min; reassignPendingTransition(stashedTransition._pendingNodes); activeTransition = null; if (!stashedTransition._actions.length && stashedTransition._optimisticNodes.length) { stashedOptimisticReads = new Set(); for (let i = 0; i < stashedTransition._optimisticNodes.length; i++) { const node = stashedTransition._optimisticNodes[i]; if (node._fn || node._pureWrite) continue; stashedOptimisticReads.add(node); queueStashedOptimisticEffects(node); } } try { finalizePureQueue(null, true); } finally { stashedOptimisticReads = null; } return; } this._pendingNodes !== activeTransition._pendingNodes && this._pendingNodes.push(...activeTransition._pendingNodes); this.restoreQueues(activeTransition._queueStash); transitions.delete(activeTransition); const completingTransition = activeTransition; activeTransition = null; reassignPendingTransition(this._pendingNodes); finalizePureQueue(completingTransition); } else { if (transitions.size) runHeap(zombieQueue, GlobalQueue._update); finalizePureQueue(); } clock++; scheduled = dirtyQueue._max >= dirtyQueue._min; runLaneEffects(EFFECT_RENDER); this.run(EFFECT_RENDER); runLaneEffects(EFFECT_USER); this.run(EFFECT_USER); if (true) DEV$1.hooks.onUpdate?.(); } finally { this._running = false; } } notify(node, mask, flags, error) { if (mask & STATUS_PENDING) { if (flags & STATUS_PENDING) { const actualError = error !== undefined ? error : node._error; if (activeTransition && actualError) { const source = actualError.source; let reporters = activeTransition._asyncReporters.get(source); if (!reporters) activeTransition._asyncReporters.set(source, (reporters = new Set())); const prevSize = reporters.size; reporters.add(node); if (reporters.size !== prevSize) schedule(); } if (_enforceLoadingBoundary) _hitUnhandledAsync = true; } return true; } return false; } initTransition(transition) { if (transition) transition = currentTransition(transition); if (transition && transition === activeTransition) return; if (!transition && activeTransition && activeTransition._time === clock) return; if (!activeTransition) { activeTransition = transition ?? { _time: clock, _pendingNodes: [], _asyncReporters: new Map(), _optimisticNodes: [], _optimisticStores: new Set(), _actions: [], _queueStash: { _queues: [[], []], _children: [] }, _done: false }; } else if (transition) { const outgoing = activeTransition; mergeTransitionState(transition, outgoing); transitions.delete(outgoing); activeTransition = transition; } transitions.add(activeTransition); activeTransition._time = clock; if (this._pendingNodes !== activeTransition._pendingNodes) { for (let i = 0; i < this._pendingNodes.length; i++) { const node = this._pendingNodes[i]; node._transition = activeTransition; activeTransition._pendingNodes.push(node); } this._pendingNodes = activeTransition._pendingNodes; } if (this._optimisticNodes !== activeTransition._optimisticNodes) { for (let i = 0; i < this._optimisticNodes.length; i++) { const node = this._optimisticNodes[i]; node._transition = activeTransition; activeTransition._optimisticNodes.push(node); } this._optimisticNodes = activeTransition._optimisticNodes; } for (const lane of activeLanes) { if (!lane._transition) lane._transition = activeTransition; } if (this._optimisticStores !== activeTransition._optimisticStores) { for (const store of this._optimisticStores) activeTransition._optimisticStores.add(store); this._optimisticStores = activeTransition._optimisticStores; } } } function insertSubs(node, optimistic = false) { const sourceLane = node._optimisticLane || currentOptimisticLane; const hasSnapshot = node._snapshotValue !== undefined; for (let s = node._subs; s !== null; s = s._nextSub) { if (hasSnapshot && s._sub._inSnapshotScope) { s._sub._flags |= REACTIVE_SNAPSHOT_STALE; continue; } if (optimistic && sourceLane) { s._sub._flags |= REACTIVE_OPTIMISTIC_DIRTY; assignOrMergeLane(s._sub, sourceLane); } else if (optimistic) { s._sub._flags |= REACTIVE_OPTIMISTIC_DIRTY; s._sub._optimisticLane = undefined; } const sub = s._sub; if (sub._type === EFFECT_TRACKED) { if (!sub._modified) { sub._modified = true; sub._queue.enqueue(EFFECT_USER, sub._run); } continue; } const queue = s._sub._flags & REACTIVE_ZOMBIE ? zombieQueue : dirtyQueue; if (queue._min > s._sub._height) queue._min = s._sub._height; insertIntoHeap(s._sub, queue); } } function commitPendingNodes() { const pendingNodes = globalQueue._pendingNodes; for (let i = 0; i < pendingNodes.length; i++) { const n = pendingNodes[i]; if (n._pendingValue !== NOT_PENDING) { n._value = n._pendingValue; n._pendingValue = NOT_PENDING; if (n._type && n._type !== EFFECT_TRACKED) n._modified = true; } if (!(n._statusFlags & STATUS_PENDING)) n._statusFlags &= ~STATUS_UNINITIALIZED; if (n._fn) GlobalQueue._dispose(n, false, true); } pendingNodes.length = 0; } function finalizePureQueue(completingTransition = null, incomplete = false) { const resolvePending = !incomplete; if (resolvePending) commitPendingNodes(); if (!incomplete) checkBoundaryChildren(globalQueue); if (dirtyQueue._max >= dirtyQueue._min) runHeap(dirtyQueue, GlobalQueue._update); if (resolvePending) { commitPendingNodes(); resolveOptimisticNodes( completingTransition ? completingTransition._optimisticNodes : globalQueue._optimisticNodes ); const optimisticStores = completingTransition ? completingTransition._optimisticStores : globalQueue._optimisticStores; if (GlobalQueue._clearOptimisticStore && optimisticStores.size) { for (const store of optimisticStores) { GlobalQueue._clearOptimisticStore(store); } optimisticStores.clear(); schedule(); } cleanupCompletedLanes(completingTransition); } } function checkBoundaryChildren(queue) { for (const child of queue._children) { child.checkSources?.(); checkBoundaryChildren(child); } } function trackOptimisticStore(store) { globalQueue._optimisticStores.add(store); schedule(); } function reassignPendingTransition(pendingNodes) { for (let i = 0; i < pendingNodes.length; i++) { pendingNodes[i]._transition = activeTransition; } } const globalQueue = new GlobalQueue(); function flush() { if (globalQueue._running) { if (inTrackedQueueCallback) { throw new Error( "Cannot call flush() from inside onSettled or createTrackedEffect. flush() is not reentrant there." ); } return; } let count = 0; while (scheduled || activeTransition) { if (++count === 1e5) throw new Error("Potential Infinite Loop Detected."); globalQueue.flush(); } } function runQueue(queue, type) { for (let i = 0; i < queue.length; i++) queue[i](type); } function reporterBlocksSource(reporter, source) { if (reporter._flags & (REACTIVE_ZOMBIE | REACTIVE_DISPOSED)) return false; if (reporter._pendingSource === source || reporter._pendingSources?.has(source)) return true; for (let dep = reporter._deps; dep; dep = dep._nextDep) { let current = dep._dep; while (current) { if (current === source || current._firewall === source) return true; current = current._parentSource; } } return !!( reporter._statusFlags & STATUS_PENDING && reporter._error instanceof NotReadyError && reporter._error.source === source ); } function transitionComplete(transition) { if (transition._done) return true; if (transition._actions.length) return false; let done = true; for (const [source, reporters] of transition._asyncReporters) { let hasLive = false; for (const reporter of reporters) { if (reporterBlocksSource(reporter, source)) { hasLive = true; break; } reporters.delete(reporter); } if (!hasLive) transition._asyncReporters.delete(source); else if (source._statusFlags & STATUS_PENDING && source._error?.source === source) { done = false; break; } } if (done) { for (let i = 0; i < transition._optimisticNodes.length; i++) { const node = transition._optimisticNodes[i]; if ( hasActiveOverride(node) && "_statusFlags" in node && node._statusFlags & STATUS_PENDING && node._error instanceof NotReadyError && node._error.source !== node ) { done = false; break; } } } done && (transition._done = true); return done; } function currentTransition(transition) { while (transition._done && typeof transition._done === "object") transition = transition._done; return transition; } function setActiveTransition(transition) { activeTransition = transition; } function runInTransition(transition, fn) { const prevTransition = activeTransition; try { activeTransition = currentTransition(transition); return fn(); } finally { activeTransition = prevTransition; } } const signalLanes = new WeakMap(); const activeLanes = new Set(); function getOrCreateLane(signal) { let lane = signalLanes.get(signal); if (lane) { return findLane(lane); } const parentSource = signal._parentSource; const parentLane = parentSource?._optimisticLane ? findLane(parentSource._optimisticLane) : null; lane = { _source: signal, _pendingAsync: new Set(), _effectQueues: [[], []], _mergedInto: null, _transition: activeTransition, _parentLane: parentLane }; signalLanes.set(signal, lane); activeLanes.add(lane); signal._overrideSinceLane = false; return lane; } function findLane(lane) { while (lane._mergedInto) lane = lane._mergedInto; return lane; } function mergeLanes(lane1, lane2) { lane1 = findLane(lane1); lane2 = findLane(lane2); if (lane1 === lane2) return lane1; lane2._mergedInto = lane1; for (const node of lane2._pendingAsync) lane1._pendingAsync.add(node); lane1._effectQueues[0].push(...lane2._effectQueues[0]); lane1._effectQueues[1].push(...lane2._effectQueues[1]); return lane1; } function resolveLane(el) { const lane = el._optimisticLane; if (!lane) return undefined; const root = findLane(lane); if (activeLanes.has(root)) return root; el._optimisticLane = undefined; return undefined; } function resolveTransition(el) { return resolveLane(el)?._transition ?? el._transition; } function hasActiveOverride(el) { return !!(el._overrideValue !== undefined && el._overrideValue !== NOT_PENDING); } function assignOrMergeLane(el, sourceLane) { const sourceRoot = findLane(sourceLane); const existing = el._optimisticLane; if (existing) { if (existing._mergedInto) { el._optimisticLane = sourceLane; return; } const existingRoot = findLane(existing); if (activeLanes.has(existingRoot)) { if (existingRoot !== sourceRoot && !hasActiveOverride(el)) { if (sourceRoot._parentLane && findLane(sourceRoot._parentLane) === existingRoot) { el._optimisticLane = sourceLane; } else if (existingRoot._parentLane && findLane(existingRoot._parentLane) === sourceRoot); else mergeLanes(sourceRoot, existingRoot); } return; } } el._optimisticLane = sourceLane; } function unlinkSubs(link) { const dep = link._dep; const nextDep = link._nextDep; const nextSub = link._nextSub; const prevSub = link._prevSub; if (nextSub !== null) nextSub._prevSub = prevSub; else dep._subsTail = prevSub; if (prevSub !== null) prevSub._nextSub = nextSub; else { dep._subs = nextSub; if (nextSub === null) { dep._unobserved?.(); dep._fn && !dep._preventAutoDisposal && !(dep._flags & REACTIVE_ZOMBIE) && unobserved(dep); } } return nextDep; } function unobserved(el) { deleteFromHeap(el, el._flags & REACTIVE_ZOMBIE ? zombieQueue : dirtyQueue); let dep = el._deps; while (dep !== null) { dep = unlinkSubs(dep); } el._deps = null; disposeChildren(el, true); } function link(dep, sub) { const prevDep = sub._depsTail; if (prevDep !== null && prevDep._dep === dep) return; let nextDep = null; const isRecomputing = sub._flags & REACTIVE_RECOMPUTING_DEPS; if (isRecomputing) { nextDep = prevDep !== null ? prevDep._nextDep : sub._deps; if (nextDep !== null && nextDep._dep === dep) { sub._depsTail = nextDep; return; } } const prevSub = dep._subsTail; if (prevSub !== null && prevSub._sub === sub && (!isRecomputing || isValidLink(prevSub, sub))) return; const newLink = (sub._depsTail = dep._subsTail = { _dep: dep, _sub: sub, _nextDep: nextDep, _prevSub: prevSub, _nextSub: null }); if (prevDep !== null) prevDep._nextDep = newLink; else sub._deps = newLink; if (prevSub !== null) prevSub._nextSub = newLink; else dep._subs = newLink; } function isValidLink(checkLink, sub) { const depsTail = sub._depsTail; if (depsTail !== null) { let link = sub._deps; do { if (link === checkLink) return true; if (link === depsTail) break; link = link._nextDep; } while (link !== null); } return false; } const PENDING_OWNER = {}; function markDisposal(el) { let child = el._firstChild; while (child) { child._flags |= REACTIVE_ZOMBIE; if (child._flags & REACTIVE_IN_HEAP) { deleteFromHeap(child, dirtyQueue); insertIntoHeap(child, zombieQueue); } markDisposal(child); child = child._nextSibling; } } function dispose(node) { let toRemove = node._deps || null; do { toRemove = unlinkSubs(toRemove); } while (toRemove !== null); node._deps = null; node._depsTail = null; disposeChildren(node, true); } function disposeChildren(node, self = false, zombie) { if (node._flags & REACTIVE_DISPOSED) return; if (self) node._flags = REACTIVE_DISPOSED; if (self && true) clearSignals(node); if (self && node._fn) node._inFlight = null; let child = zombie ? node._pendingFirstChild : node._firstChild; while (child) { const nextChild = child._nextSibling; if (child._deps) { const n = child; deleteFromHeap(n, n._flags & REACTIVE_ZOMBIE ? zombieQueue : dirtyQueue); let toRemove = n._deps; do { toRemove = unlinkSubs(toRemove); } while (toRemove !== null); n._deps = null; n._depsTail = null; } disposeChildren(child, true); child = nextChild; } if (zombie) { node._pendingFirstChild = null; } else { node._firstChild = null; node._childCount = 0; } runDisposal(node, zombie); } function runDisposal(node, zombie) { let disposal = zombie ? node._pendingDisposal : node._disposal; if (!disposal) return; if (Array.isArray(disposal)) { for (let i = 0; i < disposal.length; i++) { const callable = disposal[i]; callable.call(callable); } } else { disposal.call(disposal); } zombie ? (node._pendingDisposal = null) : (node._disposal = null); } function childId(owner, consume) { let counter = owner; while (counter._transparent && counter._parent) counter = counter._parent; if (counter.id != null) return formatId(counter.id, consume ? counter._childCount++ : counter._childCount); throw new Error("Cannot get child id from owner without an id"); } function getNextChildId(owner) { return childId(owner, true); } function peekNextChildId(owner) { return childId(owner, false); } function formatId(prefix, id) { const num = id.toString(36), len = num.length - 1; return prefix + (len ? String.fromCharCode(64 + len) : "") + num; } function getObserver() { if (pendingCheckActive || latestReadActive) return PENDING_OWNER; return tracking ? context : null; } function getOwner() { return context; } function cleanup(fn) { if (!context) return fn; if (!context._disposal) context._disposal = fn; else if (Array.isArray(context._disposal)) context._disposal.push(fn); else context._disposal = [context._disposal, fn]; return fn; } function isDisposed(node) { return !!(node._flags & (REACTIVE_DISPOSED | REACTIVE_ZOMBIE)); } function createOwner(options) { const parent = context; const transparent = options?.transparent ?? false; const owner = { id: options?.id ?? (transparent ? parent?.id : parent?.id != null ? getNextChildId(parent) : undefined), _transparent: transparent || undefined, _root: true, _parentComputed: parent?._root ? parent._parentComputed : parent, _firstChild: null, _nextSibling: null, _disposal: null, _queue: parent?._queue ?? globalQueue, _context: parent?._context || defaultContext, _childCount: 0, _pendingDisposal: null, _pendingFirstChild: null, _parent: parent, dispose(self = true) { disposeChildren(owner, self); } }; if (parent?._childrenForbidden) { throw new Error( "Cannot create reactive primitives inside createTrackedEffect or owner-backed onSettled" ); } if (parent) { const lastChild = parent._firstChild; if (lastChild === null) { parent._firstChild = owner; } else { owner._nextSibling = lastChild; parent._firstChild = owner; } } DEV$1.hooks.onOwner?.(owner); return owner; } function createRoot(init, options) { const owner = createOwner(options); return runWithOwner(owner, () => init(owner.dispose)); } function addPendingSource(el, source) { if (el._pendingSource === source || el._pendingSources?.has(source)) return false; if (!el._pendingSource) { el._pendingSource = source; return true; } if (!el._pendingSources) { el._pendingSources = new Set([el._pendingSource, source]); } else { el._pendingSources.add(source); } el._pendingSource = undefined; return true; } function removePendingSource(el, source) { if (el._pendingSource) { if (el._pendingSource !== source) return false; el._pendingSource = undefined; return true; } if (!el._pendingSources?.delete(source)) return false; if (el._pendingSources.size === 1) { el._pendingSource = el._pendingSources.values().next().value; el._pendingSources = undefined; } else if (el._pendingSources.size === 0) { el._pendingSources = undefined; } return true; } function clearPendingSources(el) { el._pendingSource = undefined; el._pendingSources?.clear(); el._pendingSources = undefined; } function setPendingError(el, source, error) { if (!source) { el._error = null; return; } if (error instanceof NotReadyError && error.source === source) { el._error = error; return; } const current = el._error; if (!(current instanceof NotReadyError) || current.source !== source) { el._error = new NotReadyError(source); } } function forEachDependent(el, fn) { for (let s = el._subs; s !== null; s = s._nextSub) fn(s._sub); for (let child = el._child; child !== null; child = child._nextChild) { for (let s = child._subs; s !== null; s = s._nextSub) fn(s._sub); } } function settlePendingSource(el) { let scheduled = false; const visited = new Set(); const settle = node => { if (visited.has(node) || !removePendingSource(node, el)) return; visited.add(node); node._time = clock; const source = node._pendingSource ?? node._pendingSources?.values().next().value; if (source) { setPendingError(node, source); updatePendingSignal(node); } else { node._statusFlags &= ~STATUS_PENDING; setPendingError(node); updatePendingSignal(node); if (node._blocked) { if (node._type === EFFECT_TRACKED) { const tracked = node; if (!tracked._modified) { tracked._modified = true; tracked._queue.enqueue(EFFECT_USER, tracked._run); } } else { const queue = node._flags & REACTIVE_ZOMBIE ? zombieQueue : dirtyQueue; if (queue._min > node._height) queue._min = node._height; insertIntoHeap(node, queue); } scheduled = true; } node._blocked = false; } forEachDependent(node, settle); }; forEachDependent(el, settle); if (scheduled) schedule(); } function handleAsync(el, result, setter) { const isObject = typeof result === "object" && result !== null; const iterator = isObject && untrack(() => result[Symbol.asyncIterator]); const isThenable = !iterator && isObject && untrack(() => typeof result.then === "function"); if (!isThenable && !iterator) { el._inFlight = null; return result; } el._inFlight = result; let syncValue; const handleError = error => { if (el._inFlight !== result) return; globalQueue.initTransition(resolveTransition(el)); notifyStatus(el, error instanceof NotReadyError ? STATUS_PENDING : STATUS_ERROR, error); el._time = clock; }; const asyncWrite = (value, then) => { if (el._inFlight !== result) return; if (el._flags & (REACTIVE_DIRTY | REACTIVE_OPTIMISTIC_DIRTY)) return; globalQueue.initTransition(resolveTransition(el)); const wasUninitialized = !!(el._statusFlags & STATUS_UNINITIALIZED); clearStatus(el); const lane = resolveLane(el); if (lane) lane._pendingAsync.delete(el); if (setter) setter(value); else if (el._overrideValue !== undefined) { if (el._overrideValue !== undefined && el._overrideValue !== NOT_PENDING) el._pendingValue = value; else { el._value = value; insertSubs(el); } el._time = clock; } else if (lane) { const isEffect = el._type; const prevValue = el._value; const equals = el._equals; if ((!isEffect && wasUninitialized) || !equals || !equals(value, prevValue)) { el._value = value; el._time = clock; if (el._latestValueComputed) { setSignal(el._latestValueComputed, value); } insertSubs(el, true); } } else { setSignal(el, () => value); } settlePendingSource(el); schedule(); flush(); then?.(); }; if (isThenable) { let resolved = false, isSync = true; result.then( v => { if (isSync) { syncValue = v; resolved = true; } else asyncWrite(v); }, e => { if (!isSync) handleError(e); } ); isSync = false; if (!resolved) { globalQueue.initTransition(resolveTransition(el)); throw new NotReadyError(context); } } if (iterator) { const it = result[Symbol.asyncIterator](); let hadSyncValue = false; let completed = false; cleanup(() => { if (completed) return; completed = true; try { const returned = it.return?.(); if (returned && typeof returned.then === "function") { returned.then(undefined, () => {}); } } catch {} }); const iterate = () => { let syncResult, resolved = false, isSync = true; it.next().then( r => { if (isSync) { syncResult = r; resolved = true; if (r.done) completed = true; } else if (el._inFlight !== result) { return; } else if (!r.done) asyncWrite(r.value, iterate); else { completed = true; schedule(); flush(); } }, e => { if (!isSync && el._inFlight === result) { completed = true; handleError(e); } } ); isSync = false; if (resolved && !syncResult.done) { syncValue = syncResult.value; hadSyncValue = true; return iterate(); } return resolved && syncResult.done; }; const immediatelyDone = iterate(); if (!hadSyncValue && !immediatelyDone) { globalQueue.initTransition(resolveTransition(el)); throw new NotReadyError(context); } } return syncValue; } function clearStatus(el, clearUninitialized = false) { clearPendingSources(el); el._blocked = false; el._statusFlags = clearUninitialized ? 0 : el._statusFlags & STATUS_UNINITIALIZED; setPendingError(el); updatePendingSignal(el); el._notifyStatus?.(); } function notifyStatus(el, status, error, blockStatus, lane) { if ( status === STATUS_ERROR && !(error instanceof StatusError) && !(error instanceof NotReadyError) ) error = new StatusError(el, error); const pendingSource = status === STATUS_PENDING && error instanceof NotReadyError ? error.source : undefined; const isSource = pendingSource === el; const isOptimisticBoundary = status === STATUS_PENDING && el._overrideValue !== undefined && !isSource; const startsBlocking = isOptimisticBoundary && hasActiveOverride(el); if (!blockStatus) { if (status === STATUS_PENDING && pendingSource) { addPendingSource(el, pendingSource); el._statusFlags = STATUS_PENDING | (el._statusFlags & STATUS_UNINITIALIZED); setPendingError(el, pendingSource, error); } else { clearPendingSources(el); el._statusFlags = status | (status !== STATUS_ERROR ? el._statusFlags & STATUS_UNINITIALIZED : 0); el._error = error; } updatePendingSignal(el); } if (lane && !blockStatus) { assignOrMergeLane(el, lane); } const downstreamBlockStatus = blockStatus || startsBlocking; const downstreamLane = blockStatus || isOptimisticBoundary ? undefined : lane; if (el._notifyStatus) { if (blockStatus && status === STATUS_PENDING) { return; } if (downstreamBlockStatus) { el._notifyStatus(status, error); } else { el._notifyStatus(); } return; } forEachDependent(el, sub => { sub._time = clock; if ( (status === STATUS_PENDING && pendingSource && sub._pendingSource !== pendingSource && !sub._pendingSources?.has(pendingSource)) || (status !== STATUS_PENDING && (sub._error !== error || sub._pendingSource || sub._pendingSources)) ) { if (!downstreamBlockStatus && !sub._transition) globalQueue._pendingNodes.push(sub); notifyStatus(sub, status, error, downstreamBlockStatus, downstreamLane); } }); } let externalSourceConfig = null; function enableExternalSource(config) { const { factory: factory, untrack: untrackFn = fn => fn() } = config; if (externalSourceConfig) { const { factory: oldFactory, untrack: oldUntrack } = externalSourceConfig; externalSourceConfig = { factory: (fn, trigger) => { const oldSource = oldFactory(fn, trigger); const source = factory(x => oldSource.track(x), trigger); return { track: x => source.track(x), dispose() { source.dispose(); oldSource.dispose(); } }; }, untrack: fn => oldUntrack(() => untrackFn(fn)) }; } else { externalSourceConfig = { factory: factory, untrack: untrackFn }; } } GlobalQueue._update = recompute; GlobalQueue._dispose = disposeChildren; let tracking = false; let stale = false; let refreshing = false; let pendingCheckActive = false; let foundPending = false; let latestReadActive = false; let context = null; let currentOptimisticLane = null; let snapshotCaptureActive = false; let snapshotSources = null; function ownerInSnapshotScope(owner) { while (owner) { if (owner._snapshotScope) return true; owner = owner._parent; } return false; } function setSnapshotCapture(active) { snapshotCaptureActive = active; if (active && !snapshotSources) snapshotSources = new Set(); } function markSnapshotScope(owner) { owner._snapshotScope = true; } function releaseSnapshotScope(owner) { owner._snapshotScope = false; releaseSubtree(owner); schedule(); } function releaseSubtree(owner) { let child = owner._firstChild; while (child) { if (child._snapshotScope) { child = child._nextSibling; continue; } if (child._fn) { const comp = child; comp._inSnapshotScope = false; if (comp._flags & REACTIVE_SNAPSHOT_STALE) { comp._flags &= ~REACTIVE_SNAPSHOT_STALE; comp._flags |= REACTIVE_DIRTY; if (dirtyQueue._min > comp._height) dirtyQueue._min = comp._height; insertIntoHeap(comp, dirtyQueue); } } releaseSubtree(child); child = child._nextSibling; } } function clearSnapshots() { if (snapshotSources) { for (const source of snapshotSources) { delete source._snapshotValue; delete source[STORE_SNAPSHOT_PROPS]; } snapshotSources = null; } snapshotCaptureActive = false; } function recompute(el, create = false) { const isEffect = el._type; if (!create) { if (el._transition && (!isEffect || activeTransition) && activeTransition !== el._transition) globalQueue.initTransition(el._transition); deleteFromHeap(el, el._flags & REACTIVE_ZOMBIE ? zombieQueue : dirtyQueue); el._inFlight = null; if (el._transition || isEffect === EFFECT_TRACKED) disposeChildren(el); else { markDisposal(el); el._pendingDisposal = el._disposal; el._pendingFirstChild = el._firstChild; el._disposal = null; el._firstChild = null; el._childCount = 0; clearSignals(el); } } const isOptimisticDirty = !!(el._flags & REACTIVE_OPTIMISTIC_DIRTY); const hasOverride = el._overrideValue !== undefined && el._overrideValue !== NOT_PENDING; const wasPending = !!(el._statusFlags & STATUS_PENDING); const wasUninitialized = !!(el._statusFlags & STATUS_UNINITIALIZED); const oldcontext = context; context = el; el._depsTail = null; el._flags = REACTIVE_RECOMPUTING_DEPS; el._time = clock; let value = el._pendingValue === NOT_PENDING ? el._value : el._pendingValue; let oldHeight = el._height; let prevTracking = tracking; let prevLane = currentOptimisticLane; let prevStrictRead = false; { prevStrictRead = strictRead; strictRead = false; } tracking = true; if (isOptimisticDirty) { const lane = resolveLane(el); if (lane) currentOptimisticLane = lane; } try { value = handleAsync(el, el._fn(value)); clearStatus(el, create); const resolvedLane = resolveLane(el); if (resolvedLane) { resolvedLane._pendingAsync.delete(el); updatePendingSignal(resolvedLane._source); } } catch (e) { if (e instanceof NotReadyError && currentOptimisticLane) { const lane = findLane(currentOptimisticLane); if (lane._source !== el) { lane._pendingAsync.add(el); el._optimisticLane = lane; updatePendingSignal(lane._source); } } if (e instanceof NotReadyError) el._blocked = true; notifyStatus( el, e instanceof NotReadyError ? STATUS_PENDING : STATUS_ERROR, e, undefined, e instanceof NotReadyError ? el._optimisticLane : undefined ); } finally { tracking = prevTracking; strictRead = prevStrictRead; el._flags = REACTIVE_NONE | (create ? el._flags & REACTIVE_SNAPSHOT_STALE : 0); context = oldcontext; } if (!el._error) { const depsTail = el._depsTail; let toRemove = depsTail !== null ? depsTail._nextDep : el._deps; if (toRemove !== null) { do { toRemove = unlinkSubs(toRemove); } while (toRemove !== null); if (depsTail !== null) depsTail._nextDep = null; else el._deps = null; } const compareValue = hasOverride ? el._overrideValue : el._pendingValue === NOT_PENDING ? el._value : el._pendingValue; const valueChanged = (!isEffect && wasUninitialized) || !el._equals || !el._equals(compareValue, value); if (valueChanged) { const prevVisible = hasOverride ? el._overrideValue : undefined; if (create || (isEffect && activeTransition !== el._transition) || isOptimisticDirty) { el._value = value; if (hasOverride && isOptimisticDirty) { el._overrideValue = value; el._pendingValue = value; } } else el._pendingValue = value; if (hasOverride && !isOptimisticDirty && wasPending && !el._overrideSinceLane) el._overrideValue = value; if (!hasOverride || isOptimisticDirty || el._overrideValue !== prevVisible) insertSubs(el, isOptimisticDirty || hasOverride); } else if (hasOverride) { el._pendingValue = value; } else if (el._height != oldHeight) { for (let s = el._subs; s !== null; s = s._nextSub) { insertIntoHeapHeight(s._sub, s._sub._flags & REACTIVE_ZOMBIE ? zombieQueue : dirtyQueue); } } } currentOptimisticLane = prevLane; (!create || el._statusFlags & STATUS_PENDING) && !el._transition && !(activeTransition && hasOverride) && globalQueue._pendingNodes.push(el); el._transition && isEffect && activeTransition !== el._transition && runInTransition(el._transition, () => recompute(el)); } function updateIfNecessary(el) { if (el._flags & REACTIVE_CHECK) { for (let d = el._deps; d; d = d._nextDep) { const dep1 = d._dep; const dep = dep1._firewall || dep1; if (dep._fn) { updateIfNecessary(dep); } if (el._flags & REACTIVE_DIRTY) { break; } } } if ( el._flags & (REACTIVE_DIRTY | REACTIVE_OPTIMISTIC_DIRTY) || (el._error && el._time < clock && !el._inFlight) ) { recompute(el); } el._flags = el._flags & (REACTIVE_SNAPSHOT_STALE | REACTIVE_IN_HEAP | REACTIVE_IN_HEAP_HEIGHT); } function computed(fn, initialValue, options) { const transparent = options?.transparent ?? false; const self = { id: options?.id ?? (transparent ? context?.id : context?.id != null ? getNextChildId(context) : undefined), _transparent: transparent || undefined, _equals: options?.equals != null ? options.equals : isEqual, _pureWrite: !!options?.pureWrite, _unobserved: options?.unobserved, _disposal: null, _queue: context?._queue ?? globalQueue, _context: context?._context ?? defaultContext, _childCount: 0, _fn: fn, _value: initialValue, _height: 0, _child: null, _nextHeap: undefined, _prevHeap: null, _deps: null, _depsTail: null, _subs: null, _subsTail: null, _parent: context, _nextSibling: null, _firstChild: null, _flags: options?.lazy ? REACTIVE_LAZY : REACTIVE_NONE, _statusFlags: STATUS_UNINITIALIZED, _time: clock, _pendingValue: NOT_PENDING, _pendingDisposal: null, _pendingFirstChild: null, _inFlight: null, _transition: null }; self._name = options?.name ?? "computed"; self._prevHeap = self; const parent = context?._root ? context._parentComputed : context; if (context?._childrenForbidden) { throw new Error( "Cannot create reactive primitives inside createTrackedEffect or owner-backed onSettled" ); } if (context) { const lastChild = context._firstChild; if (lastChild === null) { context._firstChild = self; } else { self._nextSibling = lastChild; context._firstChild = self; } } DEV$1.hooks.onOwner?.(self); if (parent) self._height = parent._height + 1; if (snapshotCaptureActive && ownerInSnapshotScope(context)) self._inSnapshotScope = true; if (externalSourceConfig) { const bridgeSignal = signal(undefined, { equals: false, pureWrite: true }); const source = externalSourceConfig.factory(self._fn, () => { setSignal(bridgeSignal, undefined); }); cleanup(() => source.dispose()); self._fn = prev => { read(bridgeSignal); return source.track(prev); }; } !options?.lazy && recompute(self, true); if (snapshotCaptureActive && !options?.lazy) { if (!(self._statusFlags & STATUS_PENDING)) { self._snapshotValue = self._value === undefined ? NO_SNAPSHOT : self._value; snapshotSources.add(self); } } return self; } function signal(v, options, firewall = null) { const s = { _equals: options?.equals != null ? options.equals : isEqual, _pureWrite: !!options?.pureWrite, _noSnapshot: !!options?._noSnapshot, _unobserved: options?.unobserved, _value: v, _subs: null, _subsTail: null, _time: clock, _firewall: firewall, _nextChild: firewall?._child || null, _pendingValue: NOT_PENDING }; { s._name = options?.name ?? "signal"; s.