@solidjs/signals
Version:
SolidJS' standalone reactivity implementation
1,576 lines • 129 kB
JavaScript
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.