🎩 You're Invited:Meet the Socket team at Black Hat in Las Vegas, August 3-6.RSVP
Sign In

@solidjs/signals

Package Overview
Dependencies
Maintainers
2
Versions
113
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@solidjs/signals - npm Package Compare versions

Comparing version
2.0.0-beta.19
to
2.0.0-beta.20
+17
-21
dist/prod/affects.js

@@ -1,4 +0,4 @@

import { forEachDependent, notifyStatus, addPendingSource, setPendingError, settlePendingSource } from "./core/async.js";
import { addPendingSource, setPendingError, settlePendingSource, forEachDependent, notifyStatus } from "./core/async.js";
import { $REFRESH, STATUS_PENDING } from "./core/constants.js";
import { STATUS_PENDING, $REFRESH } from "./core/constants.js";

@@ -9,3 +9,3 @@ import { NotReadyError } from "./core/error.js";

import { globalQueue, schedule, GlobalQueue, shiftAffectsMarks } from "./core/scheduler.js";
import { GlobalQueue, shiftAffectsMarks, globalQueue, schedule } from "./core/scheduler.js";

@@ -72,3 +72,3 @@ import "./core/verdict.js";

forEachDependent(e, e => {
if (e.m !== t && !e.M?.has(t)) {
if (!e.m?.has(t)) {
notifyStatus(e, STATUS_PENDING, r);

@@ -89,3 +89,3 @@ }

const o = t[f];
if (!o.P) continue;
if (!o.M) continue;
const s = getAffectsSentinel(o);

@@ -98,3 +98,3 @@ if (addPendingSource(e, s)) {

}
if (r && GlobalQueue._ !== null) GlobalQueue._(e);
if (r && GlobalQueue.P !== null) GlobalQueue.P(e);
}

@@ -108,7 +108,7 @@

*/ function markAffects(e) {
e.P = (e.P || 0) + 1;
e.M = (e.M || 0) + 1;
shiftAffectsMarks(1);
// Companions only exist once the verdict layer (isPending/latest) loaded;
// without them there is no materialized verdict to flip.
if (e.P === 1 && GlobalQueue._ !== null) GlobalQueue._(e);
if (e.M === 1 && GlobalQueue.P !== null) GlobalQueue.P(e);
}

@@ -119,3 +119,3 @@

* registration with the current transaction (after initTransition the queue's
* array aliases the active transition's, mirroring `_optimisticNodes`), and
* batch IS the active transition, mirroring `_optimisticNodes`), and
* propagates STATUS_PENDING downstream on the status rails so everything

@@ -127,3 +127,3 @@ * DERIVED from the marked data reads pending too. Propagation runs on every

markAffects(e);
globalQueue.N.push(e);
globalQueue.N._.push(e);
propagateAffectsMark(e);

@@ -141,4 +141,4 @@ schedule();

shiftAffectsMarks(-1);
e.P--;
if (!e.P) {
e.M--;
if (!e.M) {
const t = e.t;

@@ -167,3 +167,3 @@ if (t) settlePendingSource(e, t, true);

*/ function onlyMarkPending(e) {
const t = e.M;
const t = e.m;
if (t) {

@@ -173,3 +173,3 @@ for (const e of t) if (!e.l) return false;

}
return !!e.m?.l;
return false;
}

@@ -188,10 +188,6 @@

*/ function collectMarkSources(e, t) {
const r = e.m;
if (r) {
const e = r.l;
if (e && e.P) t.push(e);
} else if (e.M) {
for (const r of e.M) {
if (e.m) {
for (const r of e.m) {
const e = r.l;
if (e && e.P) t.push(e);
if (e && e.M) t.push(e);
}

@@ -198,0 +194,0 @@ }

@@ -20,7 +20,19 @@ import { globalQueue, activeTransition, currentTransition, setActiveTransition, schedule, flush } from "./scheduler.js";

*
* Yield promises (or any awaitable) inside the generator — the action waits
* for each before continuing, but the writes you made beforehand are already
* visible (or held by `<Loading>` if optimistic). Yield bare values for
* synchronous batched steps.
* `yield` is the transaction-safe suspension point: the action waits for a
* yielded promise and re-enters the transaction before running the code after
* it. A plain `await` does NOT — the runtime has no hook into an async
* generator's internal await continuations, so writes to fresh signals
* between an `await` and the next `yield` escape the transaction and commit
* immediately. `await` is still the ergonomic choice for typed results; just
* put a bare `yield` before any writes that follow it:
*
* ```ts
* const saved = await api.createTodo(text); // typed result
* yield; // re-enter the transaction before writing
* setTodos(t => { ... });
* ```
*
* (For the same reason, don't call `flush()` inside an action body — it
* drains the transaction mid-step.)
*
* Each call returns a `Promise` that resolves with the generator's return

@@ -35,6 +47,7 @@ * value, or rejects if it throws. Pair with `createOptimistic` /

*
* const addTodo = action(function* (text: string) {
* const addTodo = action(async function* (text: string) {
* const tempId = crypto.randomUUID();
* setTodos(t => { t.push({ id: tempId, text, pending: true }); }); // optimistic
* const saved = yield api.createTodo(text); // network round-trip
* const saved = await api.createTodo(text); // network round-trip, typed
* yield; // re-enter the transaction
* setTodos(t => {

@@ -41,0 +54,0 @@ * const i = t.findIndex(x => x.id === tempId);

@@ -17,15 +17,12 @@ import { STATUS_UNINITIALIZED, STATUS_ERROR, STATUS_PENDING, REACTIVE_DIRTY, REACTIVE_OPTIMISTIC_DIRTY, NOT_PENDING } from "./constants.js";

import { globalQueue, GlobalQueue, clock, schedule, queuePendingNode, flush, insertSubs } from "./scheduler.js";
import { globalQueue, GlobalQueue, clock, schedule, flush, queuePendingNode, insertSubs } from "./scheduler.js";
// The lazily-created Set is the ONE container for pending sources. Its
// predecessor — a singular slot promoted to a Set on the second source —
// created dual state whose migration invariant was easy to break: a third
// overlapping source landed beside the Set and removePendingSource refused
// to clear it, stranding the Set members' pending forever (#2893).
function addPendingSource(e, n) {
if (e.m === n || e.M?.has(n)) return false;
// Once the Set exists it is THE container — the singular slot stays empty
// from migration until removePendingSource collapses back to one entry.
// Landing a third source in the singular slot instead created dual state
// that removePendingSource refused to clear, stranding the Set members'
// pending forever (#2893).
if (e.M) e.M.add(n); else if (!e.m) e.m = n; else {
e.M = new Set([ e.m, n ]);
e.m = undefined;
}
if (e.m?.has(n)) return false;
(e.m ??= new Set).add(n);
return true;

@@ -35,14 +32,4 @@ }

function removePendingSource(e, n) {
if (e.m) {
if (e.m !== n) return false;
e.m = undefined;
return true;
}
if (!e.M?.delete(n)) return false;
if (e.M.size === 1) {
e.m = e.M.values().next().value;
e.M = undefined;
} else if (e.M.size === 0) {
e.M = undefined;
}
if (!e.m?.delete(n)) return false;
if (e.m.size === 0) e.m = undefined;
return true;

@@ -52,5 +39,4 @@ }

function clearPendingSources(e) {
e.m?.clear();
e.m = undefined;
e.M?.clear();
e.M = undefined;
}

@@ -90,17 +76,17 @@

let t = false;
const u = new Set;
const o = new Set;
// Companion updates no-op without the verdict layer (null hooks).
const o = r ? GlobalQueue.R : GlobalQueue._;
const u = r ? GlobalQueue.R : GlobalQueue.P;
const settle = e => {
if (u.has(e) || !removePendingSource(e, n)) return;
u.add(e);
if (o.has(e) || !removePendingSource(e, n)) return;
o.add(e);
e.Ie = clock;
const r = e.m ?? e.M?.values().next().value;
const r = e.m?.values().next().value;
if (r) {
setPendingError(e, r);
o !== null && o(e);
u !== null && u(e);
} else {
e.i &= ~STATUS_PENDING;
setPendingError(e);
o !== null && o(e);
u !== null && u(e);
if (e.Re) {

@@ -125,10 +111,10 @@ enqueueSub(e);

let t = false;
let u = false;
let o = false;
if (typeof n === "object" && n !== null) {
untrack(() => {
t = n[Symbol.asyncIterator];
u = !t && isThenable(n);
o = !t && isThenable(n);
});
}
if (!u && !t) {
if (!o && !t) {
e.Ae = null;

@@ -138,3 +124,3 @@ return n;

e.Ae = n;
let o;
let u;
const handleError = r => {

@@ -147,3 +133,3 @@ if (e.Ae !== n) return;

};
const asyncWrite = (t, u) => {
const asyncWrite = (t, o) => {
if (e.Ae !== n) return;

@@ -155,3 +141,3 @@ // If the node was dirtied by a newer write (optimistic override or regular),

globalQueue.initTransition(resolveTransition(e));
const o = !!(e.i & STATUS_UNINITIALIZED);
const u = !!(e.i & STATUS_UNINITIALIZED);
trimStaleDeps(e);

@@ -163,3 +149,3 @@ clearStatus(e);

r(t);
if (o) clearStatus(e, true);
if (u) clearStatus(e, true);
} else if (e.be !== undefined) {

@@ -191,5 +177,5 @@ // Optimistic node — resting OR covered by an active override — holds

const r = e.Ue;
const u = e.Ge;
const o = e.Ge;
try {
if (!n && o || !u || !u(t, r)) {
if (!n && u || !o || !o(t, r)) {
e.Ue = t;

@@ -222,9 +208,9 @@ e.Ie = clock;

flush();
u?.();
o?.();
};
if (u) {
let r = false, t = false, u, l = true;
if (o) {
let r = false, t = false, o, l = true;
n.then(e => {
if (l) {
o = e;
u = e;
r = true;

@@ -234,3 +220,3 @@ } else asyncWrite(e);

if (l) {
u = e;
o = e;
t = true;

@@ -244,4 +230,4 @@ } else handleError(e);

// momentarily read as `undefined`.
handleError(u);
throw u;
handleError(o);
throw o;
} else if (!r) {

@@ -255,7 +241,7 @@ globalQueue.initTransition(resolveTransition(e));

let t = false;
let u = false;
let o = false;
let l = true;
cleanup(() => {
if (u) return;
u = true;
if (o) return;
o = true;
try {

@@ -272,3 +258,3 @@ const e = r.return?.();

f = true;
if (r.done) u = true;
if (r.done) o = true;
} else if (e.Ae !== n) {

@@ -280,3 +266,3 @@ return;

} else {
u = true;
o = true;
if (t) {

@@ -295,3 +281,3 @@ schedule();

} else if (e.Ae === n) {
u = true;
o = true;
handleError(r);

@@ -303,3 +289,3 @@ }

// Match the promise branch, but only rethrow during the initial read.
u = true;
o = true;
handleError(s);

@@ -310,3 +296,3 @@ if (l) throw s;

if (f && !i.done) {
o = i.value;
u = i.value;
t = true;

@@ -325,7 +311,7 @@ return iterate();

}
return o;
return u;
}
function clearStatus(e, n = false) {
if (e.m || e.M) clearPendingSources(e);
if (e.m) clearPendingSources(e);
if (e.Re) e.Re = false;

@@ -340,3 +326,3 @@ // The pending window is over; its quiet classification dies with it.

// once the verdict layer created them, which installs the hooks).
if (e.ye || e.pe) GlobalQueue._(e);
if (e.ye || e.pe) GlobalQueue.P(e);
if (e.G && GlobalQueue.me !== null) GlobalQueue.me(e);

@@ -346,6 +332,6 @@ if (e.C) e.C();

function notifyStatus(e, n, r, t, u) {
function notifyStatus(e, n, r, t, o) {
// Wrap regular errors to track source node
if (n === STATUS_ERROR && !(r instanceof StatusError) && !(r instanceof NotReadyError)) r = new StatusError(e, r);
const o = n === STATUS_PENDING && r instanceof NotReadyError ? r.source : undefined;
const u = n === STATUS_PENDING && r instanceof NotReadyError ? r.source : undefined;
// Mark-sourced propagation must not capture subscribers into the marking

@@ -356,3 +342,3 @@ // action's transaction (#2893): they carry no held value needing a

// settles. Real async keeps queuing (its commits ride the transition).
const l = o?.l !== undefined;
const l = u?.l !== undefined;
// A real error is a settled verdict a mark must not erase (#2893): landing

@@ -364,12 +350,12 @@ // STATUS_PENDING here would clobber `_error` with the sentinel's

if (l && e.i & STATUS_ERROR) return;
const i = o === e;
const i = u === e;
const s = n === STATUS_PENDING && e.be !== undefined && !i;
const f = s && hasActiveOverride(e);
if (!t) {
if (n === STATUS_PENDING && o) {
addPendingSource(e, o);
if (n === STATUS_PENDING && u) {
addPendingSource(e, u);
e.i = STATUS_PENDING | e.i & STATUS_UNINITIALIZED;
// Preserve the current source on this propagation so render-effect notification
// can register every distinct pending source with the transition.
setPendingError(e, o, r);
setPendingError(e, u, r);
} else {

@@ -380,10 +366,10 @@ clearPendingSources(e);

}
GlobalQueue._ !== null && GlobalQueue._(e);
GlobalQueue.P !== null && GlobalQueue.P(e);
if (e.G && GlobalQueue.me !== null) GlobalQueue.me(e);
}
if (u && !t) {
assignOrMergeLane(e, u);
if (o && !t) {
assignOrMergeLane(e, o);
}
const a = t || f;
const c = t || s ? undefined : u;
const c = t || s ? undefined : o;
if (e.C) {

@@ -402,3 +388,3 @@ if (t && n === STATUS_PENDING) {

e.Ie = clock;
if (n === STATUS_PENDING && o && e.m !== o && !e.M?.has(o) || n !== STATUS_PENDING && (e.k !== r || e.m || e.M)) {
if (n === STATUS_PENDING && u && !e.m?.has(u) || n !== STATUS_PENDING && (e.k !== r || e.m)) {
// A pending-observer link is the subscription an `isPending` read created.

@@ -405,0 +391,0 @@ // It exists so the observer re-runs when the source settles, but it must

@@ -64,2 +64,16 @@ const REACTIVE_NONE = 0;

/**
* Stand-in stored in `_overrideValue` for an optimistic write of literal
* `undefined` (#2898). The slot doubles as the optimistic-node brand
* (`undefined` = not optimistic, `NOT_PENDING` = at rest), so the raw value
* would erase the node's optimistic identity: the write turns invisible and
* follow-up writes route off the optimistic path and commit permanently.
* Same shape as NO_SNAPSHOT. Sites that surface the override VALUE unwrap
* via `visibleOverrideValue`; slot identity tests stay raw.
*/ const OVERRIDE_UNDEFINED = {};
/** Unwrap an active override's stored value for surfacing to readers (#2898). */ function unwrapOverride(E) {
return E === OVERRIDE_UNDEFINED ? undefined : E;
}
const STORE_SNAPSHOT_PROPS = "sp";

@@ -79,2 +93,2 @@

export { $REFRESH, CONFIG_AUTO_DISPOSE, CONFIG_CHILDREN_FORBIDDEN, CONFIG_IN_SNAPSHOT_SCOPE, CONFIG_NO_SNAPSHOT, CONFIG_OWNED_WRITE, CONFIG_SYNC, CONFIG_TRANSPARENT, EFFECT_RENDER, EFFECT_TRACKED, EFFECT_USER, NOT_PENDING, NO_SNAPSHOT, REACTIVE_CHECK, REACTIVE_DIRTY, REACTIVE_DISPOSED, REACTIVE_IN_HEAP, REACTIVE_IN_HEAP_HEIGHT, REACTIVE_LAZY, REACTIVE_MANUAL_WRITE, REACTIVE_NONE, REACTIVE_OPTIMISTIC_DIRTY, REACTIVE_REASK, REACTIVE_RECOMPUTING_DEPS, REACTIVE_SNAPSHOT_STALE, REACTIVE_ZOMBIE, STATUS_ERROR, STATUS_PENDING, STATUS_UNINITIALIZED, STORE_SNAPSHOT_PROPS, SUPPORTS_PROXY, defaultContext };
export { $REFRESH, CONFIG_AUTO_DISPOSE, CONFIG_CHILDREN_FORBIDDEN, CONFIG_IN_SNAPSHOT_SCOPE, CONFIG_NO_SNAPSHOT, CONFIG_OWNED_WRITE, CONFIG_SYNC, CONFIG_TRANSPARENT, EFFECT_RENDER, EFFECT_TRACKED, EFFECT_USER, NOT_PENDING, NO_SNAPSHOT, OVERRIDE_UNDEFINED, REACTIVE_CHECK, REACTIVE_DIRTY, REACTIVE_DISPOSED, REACTIVE_IN_HEAP, REACTIVE_IN_HEAP_HEIGHT, REACTIVE_LAZY, REACTIVE_MANUAL_WRITE, REACTIVE_NONE, REACTIVE_OPTIMISTIC_DIRTY, REACTIVE_REASK, REACTIVE_RECOMPUTING_DEPS, REACTIVE_SNAPSHOT_STALE, REACTIVE_ZOMBIE, STATUS_ERROR, STATUS_PENDING, STATUS_UNINITIALIZED, STORE_SNAPSHOT_PROPS, SUPPORTS_PROXY, defaultContext, unwrapOverride };
import { handleAsync, clearStatus, notifyStatus } from "./async.js";
import { $REFRESH, REACTIVE_DISPOSED, REACTIVE_MANUAL_WRITE, REACTIVE_DIRTY, REACTIVE_CHECK, REACTIVE_IN_HEAP, REACTIVE_REASK, REACTIVE_LAZY, REACTIVE_NONE, defaultContext, CONFIG_TRANSPARENT, CONFIG_OWNED_WRITE, CONFIG_AUTO_DISPOSE, CONFIG_SYNC, CONFIG_NO_SNAPSHOT, CONFIG_IN_SNAPSHOT_SCOPE, NOT_PENDING, STATUS_UNINITIALIZED, STATUS_PENDING, NO_SNAPSHOT, REACTIVE_SNAPSHOT_STALE, EFFECT_TRACKED, REACTIVE_OPTIMISTIC_DIRTY, REACTIVE_RECOMPUTING_DEPS, STATUS_ERROR, REACTIVE_IN_HEAP_HEIGHT, STORE_SNAPSHOT_PROPS, EFFECT_USER } from "./constants.js";
import { EFFECT_TRACKED, REACTIVE_OPTIMISTIC_DIRTY, NOT_PENDING, STATUS_UNINITIALIZED, REACTIVE_REASK, REACTIVE_RECOMPUTING_DEPS, CONFIG_SYNC, STATUS_PENDING, STATUS_ERROR, REACTIVE_NONE, REACTIVE_SNAPSHOT_STALE, unwrapOverride, OVERRIDE_UNDEFINED, CONFIG_AUTO_DISPOSE, REACTIVE_LAZY, REACTIVE_DISPOSED, REACTIVE_CHECK, REACTIVE_DIRTY, REACTIVE_IN_HEAP, REACTIVE_IN_HEAP_HEIGHT, defaultContext, CONFIG_IN_SNAPSHOT_SCOPE, CONFIG_TRANSPARENT, CONFIG_OWNED_WRITE, CONFIG_NO_SNAPSHOT, $REFRESH, REACTIVE_MANUAL_WRITE, NO_SNAPSHOT, STORE_SNAPSHOT_PROPS, EFFECT_USER } from "./constants.js";

@@ -9,9 +9,9 @@ import { NotReadyError } from "./error.js";

import { insertIntoHeap, queueFor, deleteFromHeap, insertIntoHeapHeight, markNode, markHeap } from "./heap.js";
import { deleteFromHeap, queueFor, insertIntoHeapHeight, markNode, markHeap, insertIntoHeap } from "./heap.js";
import { schedule, GlobalQueue, globalQueue, clock, activeTransition, queuePendingNode, insertSubs, dirtyQueue, activeAffectsMarks, runInTransition, projectionWriteActive, armReaskClear } from "./scheduler.js";
import { GlobalQueue, activeTransition, globalQueue, clock, insertSubs, queuePendingNode, activeAffectsMarks, runInTransition, schedule, dirtyQueue, projectionWriteActive, armReaskClear } from "./scheduler.js";
import "./invariants.js";
import { inheritId, disposeChildren, markDisposal } from "./owner.js";
import { disposeChildren, markDisposal, inheritId } from "./owner.js";

@@ -64,3 +64,3 @@ GlobalQueue.Ce = recompute;

if (e.ke) return true;
e = e.He;
e = e.Fe;
}

@@ -86,3 +86,3 @@ return false;

function releaseSubtree(e) {
let t = e.Fe;
let t = e.He;
while (t) {

@@ -126,8 +126,8 @@ if (t.ke) {

// Tracked effects run after finalizePureQueue, so dispose immediately instead of deferring
if (e.ve || n === EFFECT_TRACKED) disposeChildren(e); else if (e.Fe !== null || e.Me !== null) {
if (e.ve || n === EFFECT_TRACKED) disposeChildren(e); else if (e.He !== null || e.Me !== null) {
markDisposal(e);
e.We = e.Me;
e.Ze = e.Fe;
e.Ze = e.He;
e.Me = null;
e.Fe = null;
e.He = null;
e.je = 0;

@@ -172,5 +172,5 @@ } else ;

}
const T = n && n !== EFFECT_USER;
const N = stale;
if (T) stale = true;
const N = n && n !== EFFECT_USER;
const T = stale;
if (N) stale = true;
try {

@@ -209,3 +209,3 @@ if (!false && e.U & CONFIG_SYNC) {

tracking = c;
if (T) stale = N;
if (N) stale = T;
e.u = REACTIVE_NONE | (t ? e.u & REACTIVE_SNAPSHOT_STALE : 0);

@@ -218,3 +218,3 @@ context = o;

trimStaleDeps(e);
const s = l ? e.be : e.ge === NOT_PENDING ? e.Ue : e.ge;
const s = l ? unwrapOverride(e.be) : e.ge === NOT_PENDING ? e.Ue : e.ge;
let o = false;

@@ -250,3 +250,3 @@ try {

if (l && i) {
e.be = a;
e.be = a === undefined ? OVERRIDE_UNDEFINED : a;
e.ge = NOT_PENDING;

@@ -290,3 +290,3 @@ }

const I = e.i & (STATUS_PENDING | STATUS_UNINITIALIZED);
const S = e.ge !== NOT_PENDING || e.Ze !== null || e.We !== null || I !== 0 && (I !== STATUS_PENDING || activeAffectsMarks === 0 || !GlobalQueue.$(e));
const p = e.ge !== NOT_PENDING || e.Ze !== null || e.We !== null || I !== 0 && (I !== STATUS_PENDING || activeAffectsMarks === 0 || !GlobalQueue.$(e));
// Override-covered holds (hasOverride) always queue: their commit belongs

@@ -297,3 +297,3 @@ // to their own transition's schedule (A18 re-rule) and is unobservable

// _transition.
S && (!t || e.i & STATUS_PENDING) && (!e.ve || l) && queuePendingNode(e);
p && (!t || e.i & STATUS_PENDING) && (!e.ve || l) && queuePendingNode(e);
e.ve && n && activeTransition !== e.ve && runInTransition(e.ve, () => recompute(e));

@@ -343,6 +343,6 @@ }

Et: null,
He: context,
Fe: context,
Ve: null,
Tt: null,
Fe: null,
Nt: null,
He: null,
u: t?.lazy ? REACTIVE_LAZY : REACTIVE_NONE,

@@ -389,6 +389,6 @@ i: STATUS_UNINITIALIZED,

Et: null,
He: context,
Fe: context,
Ve: null,
Tt: null,
Fe: null,
Nt: null,
He: null,
u: REACTIVE_LAZY,

@@ -404,6 +404,6 @@ i: STATUS_UNINITIALIZED,

it: false,
Nt: undefined,
Tt: undefined,
It: t,
St: n,
At: undefined,
dt: undefined,
De: i,

@@ -422,11 +422,11 @@ C: l

e.ft = e;
const n = context?.dt ? context.Ct : context;
const n = context?.At ? context.Ct : context;
if (context) {
const t = context.Fe;
const t = context.He;
if (t === null) {
context.Fe = e;
context.He = e;
} else {
e.Ve = t;
t.Tt = e;
context.Fe = e;
t.Nt = e;
context.He = e;
}

@@ -537,3 +537,3 @@ }

let t = context;
if (t?.dt) t = t.Ct;
if (t?.At) t = t.Ct;
const n = e;

@@ -556,3 +556,3 @@ const i = e.rt;

// the enclosing memo would make an `isPending` wrapper itself pending).
if (activeAffectsMarks !== 0 && e.P && !pendingCheckActive) (affectsReads ??= []).push(e);
if (activeAffectsMarks !== 0 && e.M && !pendingCheckActive) (affectsReads ??= []).push(e);
}

@@ -569,3 +569,3 @@ return !t || e.ge === NOT_PENDING ? e.Ue : e.ge;

if (activeAffectsMarks !== 0 && !pendingCheckActive) {
if (e.P) (affectsReads ??= []).push(e);
if (e.M) (affectsReads ??= []).push(e);
if (l.i & STATUS_PENDING) GlobalQueue.I(l, affectsReads ??= []);

@@ -582,3 +582,3 @@ }

// parent check is shallow, might need to be recursive
if (i >= t.qe && e.He !== t) {
if (i >= t.qe && e.Fe !== t) {
t.qe = i + 1;

@@ -632,4 +632,4 @@ }

// the value for every reader — that check itself stays right here).
if (t && stale && GlobalQueue.gt(e)) return e.Ue;
return e.be;
if (t && stale && GlobalQueue.Dt(e)) return e.Ue;
return unwrapOverride(e.be);
}

@@ -643,3 +643,3 @@ // Entanglement gate: a reader recomputing under an optimistic lane that reads

// engine — a non-null lane implies it is installed.)
if (currentOptimisticLane !== null && activeTransition !== null && t !== null && GlobalQueue.Dt(e, l, t)) {
if (currentOptimisticLane !== null && activeTransition !== null && t !== null && GlobalQueue.gt(e, l, t)) {
return e.Ue;

@@ -651,6 +651,6 @@ }

// (The lane-context clause lives with the engine.)
const u = !t || currentOptimisticLane !== null && GlobalQueue.kt(e, l, t) || e.ge === NOT_PENDING || stale && e.ve && activeTransition !== e.ve ? e.Ue : e.ge;
const u = !t || currentOptimisticLane !== null && GlobalQueue.vt(e, l, t) || e.ge === NOT_PENDING || stale && e.ve && activeTransition !== e.ve ? e.Ue : e.ge;
// Record that this isPending() probe observed the fresh pending value, so
// the probe doesn't pair "pending" with the new value (#2831).
if (pendingCheckActive) GlobalQueue.vt(e, u);
if (pendingCheckActive) GlobalQueue.kt(e, u);
if (!t && l === e && typeof n.xe === "function" && e.U & CONFIG_AUTO_DISPOSE && !(l.i & STATUS_PENDING) && !e.p) {

@@ -657,0 +657,0 @@ unobserved(e);

@@ -1,2 +0,2 @@

import { EFFECT_USER, CONFIG_AUTO_DISPOSE, CONFIG_CHILDREN_FORBIDDEN, EFFECT_TRACKED, STATUS_ERROR, STATUS_PENDING, EFFECT_RENDER, REACTIVE_DISPOSED } from "./constants.js";
import { REACTIVE_DISPOSED, STATUS_ERROR, EFFECT_USER, CONFIG_AUTO_DISPOSE, CONFIG_CHILDREN_FORBIDDEN, EFFECT_TRACKED, STATUS_PENDING, EFFECT_RENDER } from "./constants.js";

@@ -7,3 +7,3 @@ import { createEffectNode, recompute, computed, staleValues } from "./core.js";

import { haltReactivity, GlobalQueue } from "./scheduler.js";
import { GlobalQueue, haltReactivity } from "./scheduler.js";

@@ -66,8 +66,8 @@ /**

const E = unwrapStatusError(t.k);
t.Nt = t.Ue;
t.Tt = t.Ue;
t.it = false;
try {
t.St ? t.St(E, () => {
const E = t.At;
t.At = undefined;
const E = t.dt;
t.dt = undefined;
E?.();

@@ -83,10 +83,10 @@ }) : console.error(E);

}
const E = t.At;
t.At = undefined;
const E = t.dt;
t.dt = undefined;
try {
E?.();
const e = t.It(t.Ue, t.Nt);
const e = t.It(t.Ue, t.Tt);
if (false && e !== undefined && typeof e !== "function") ;
// The final cleanup is invoked by disposeChildren at true disposal.
t.At = e;
t.dt = e;
} catch (E) {

@@ -100,3 +100,3 @@ t.k = new StatusError(t, E);

} finally {
t.Nt = t.Ue;
t.Tt = t.Ue;
t.it = false;

@@ -121,7 +121,7 @@ }

const e = computed(() => {
const E = e.At;
e.At = undefined;
const E = e.dt;
e.dt = undefined;
E?.();
const R = staleValues(t);
e.At = R;
e.dt = R;
}, {

@@ -131,3 +131,3 @@ ...E,

});
e.At = undefined;
e.dt = undefined;
e.U = e.U & ~CONFIG_AUTO_DISPOSE | CONFIG_CHILDREN_FORBIDDEN;

@@ -134,0 +134,0 @@ e.it = true;

@@ -1,2 +0,2 @@

import { REACTIVE_RECOMPUTING_DEPS, CONFIG_AUTO_DISPOSE, REACTIVE_ZOMBIE } from "./constants.js";
import { CONFIG_AUTO_DISPOSE, REACTIVE_ZOMBIE, REACTIVE_RECOMPUTING_DEPS } from "./constants.js";

@@ -3,0 +3,0 @@ import { deleteFromHeap, queueFor } from "./heap.js";

@@ -1,2 +0,2 @@

import { REACTIVE_IN_HEAP, REACTIVE_RECOMPUTING_DEPS, REACTIVE_MANUAL_WRITE, REACTIVE_CHECK, REACTIVE_DIRTY, REACTIVE_IN_HEAP_HEIGHT, REACTIVE_ZOMBIE, EFFECT_TRACKED, EFFECT_USER } from "./constants.js";
import { REACTIVE_IN_HEAP, EFFECT_TRACKED, EFFECT_USER, REACTIVE_IN_HEAP_HEIGHT, REACTIVE_RECOMPUTING_DEPS, REACTIVE_MANUAL_WRITE, REACTIVE_ZOMBIE, REACTIVE_CHECK, REACTIVE_DIRTY } from "./constants.js";

@@ -28,3 +28,3 @@ import { zombieQueue, dirtyQueue } from "./scheduler.js";

function actualInsertIntoHeap(e, E) {
const t = (e.He?.dt ? e.He.Ct?.qe : e.He?.qe) ?? -1;
const t = (e.Fe?.At ? e.Fe.Ct?.qe : e.Fe?.qe) ?? -1;
if (t >= e.qe) e.qe = t + 1;

@@ -31,0 +31,0 @@ const n = e.qe;

import { NOT_PENDING } from "./constants.js";
import { activeTransition } from "./scheduler.js";
import { currentTransition, activeTransition } from "./scheduler.js";

@@ -22,10 +22,10 @@ // Map from optimistic signal to its lane (reused for multiple writes to same signal)

const i = n.en;
const a = i?.Je ? findLane(i.Je) : null;
const r = i?.Je ? findLane(i.Je) : null;
e = {
an: n,
rn: n,
Pe: new Set,
tn: [ [], [] ],
rn: null,
an: null,
ve: activeTransition,
sn: a
sn: r
};

@@ -49,6 +49,6 @@ signalLanes.set(n, e);

if (!i) return;
const a = findLane(i);
const r = findLane(i);
// Only the companion's own unmerged root is safely re-parentable: a root
// that absorbed other lanes carries work that is not a child of this owner.
if (a !== e && a.an === n && !a.sn) a.sn = e;
if (r !== e && r.rn === n && !r.sn) r.sn = e;
}

@@ -59,3 +59,3 @@

*/ function findLane(n) {
while (n.rn) n = n.rn;
while (n.an) n = n.an;
return n;

@@ -70,3 +70,3 @@ }

if (n === e) return n;
e.rn = n;
e.an = n;
// Move (not copy) the merged lane's work: after the merge all routing goes

@@ -96,2 +96,12 @@ // through findLane() to the root, so anything left behind here is dead —

function resolveTransition(n) {
// An active override answers with its owner, not its lane: lanes are
// scheduling affinity and a shared subscriber merges them across
// transactions (#2912) — the merged root's _transition would hand this
// node's override to whichever action wrote last through the shared
// reader. Chase merge chains; a dead owner settled through another path.
if (hasActiveOverride(n) && n.fn) {
const e = n.fn = currentTransition(n.fn);
if (e.cn !== true) return e;
n.fn = null;
}
return resolveLane(n)?.ve ?? n.ve;

@@ -111,12 +121,12 @@ }

const i = findLane(e);
const a = n.Je;
if (a) {
const r = n.Je;
if (r) {
// If the subscriber's lane was merged into another lane, it's stale —
// replace it with the new source lane instead of following the merge chain
// (which would incorrectly merge the new lane into the old group)
if (a.rn) {
if (r.an) {
n.Je = e;
return;
}
const t = findLane(a);
const t = findLane(r);
if (activeLanes.has(t)) {

@@ -123,0 +133,0 @@ if (t !== i && !hasActiveOverride(n)) {

@@ -1,4 +0,4 @@

import { NOT_PENDING, STATUS_UNINITIALIZED, STATUS_PENDING, CONFIG_OWNED_WRITE, REACTIVE_MANUAL_WRITE, REACTIVE_OPTIMISTIC_DIRTY, EFFECT_RENDER, EFFECT_USER } from "./constants.js";
import { NOT_PENDING, unwrapOverride, STATUS_UNINITIALIZED, OVERRIDE_UNDEFINED, STATUS_PENDING, CONFIG_OWNED_WRITE, REACTIVE_MANUAL_WRITE, REACTIVE_OPTIMISTIC_DIRTY, EFFECT_RENDER, EFFECT_USER } from "./constants.js";
import { latestReadActive, stale, currentOptimisticLane } from "./core.js";
import { latestReadActive, currentOptimisticLane, stale } from "./core.js";

@@ -32,29 +32,36 @@ import { NotReadyError } from "./error.js";

/** The optimistic half of setSignal, fired when `_overrideValue !== undefined`. */ function optimisticWrite(e, t) {
const n = e.be !== NOT_PENDING;
const i = n ? e.be : e.Ue;
if (typeof t === "function") t = t(i);
const u = !!(e.i & STATUS_UNINITIALIZED) || !e.Ge || !e.Ge(i, t);
/** The optimistic half of setSignal, fired when `_overrideValue !== undefined`. */ function optimisticWrite(e, n) {
const t = e.be !== NOT_PENDING;
const i = t ? unwrapOverride(e.be) : e.Ue;
if (typeof n === "function") n = n(i);
const u = !!(e.i & STATUS_UNINITIALIZED) || !e.Ge || !e.Ge(i, n);
if (!u) {
// Same-value write with an active override still entangles the current
// action's transition — the hold must outlast all overlapping actions.
if (n) {
const t = resolveTransition(e);
if (t && activeTransition !== t) globalQueue.initTransition(t);
if (t) {
const n = resolveTransition(e);
if (n && activeTransition !== n) globalQueue.initTransition(n);
}
return t;
return n;
}
if (n) globalQueue.initTransition(resolveTransition(e));
if (t) globalQueue.initTransition(resolveTransition(e));
// No revert target is stashed: while the override is active every reader
// sees it (A17), so authoritative arrivals commit silently into _value and
// reverting is just dropping the override — _value is already correct.
else globalQueue.Be.push(e);
else globalQueue.N.Be.push(e);
// Stamp ownership on the node (post-merge, so entangled writers share the
// joint root). resolveTransition prefers this over the lane's _transition,
// which a shared subscriber can merge across transactions (#2912).
e.fn = activeTransition;
const s = getOrCreateLane(e);
e.Je = s;
e.be = t;
GlobalQueue._e !== null && GlobalQueue._e(e, t);
// Literal undefined must not land raw: the slot doubles as the optimistic
// brand, and erasing it makes the write invisible and routes follow-up
// writes off the optimistic path into permanent commits (#2898).
e.be = n === undefined ? OVERRIDE_UNDEFINED : n;
GlobalQueue._e !== null && GlobalQueue._e(e, n);
e.Ie = clock;
insertSubs(e, true);
schedule();
return t;
return n;
}

@@ -67,4 +74,4 @@

function queueStashedOptimisticEffects(e) {
for (let t = e.p; t !== null; t = t.de) {
const e = t.Ee;
for (let n = e.p; n !== null; n = n.de) {
const e = n.Ee;
if (!e.De) continue;

@@ -80,7 +87,7 @@ enqueueSub(e);

stashedOptimisticReads = new Set;
for (let t = 0; t < e.Be.length; t++) {
const n = e.Be[t];
if (n.xe || n.U & CONFIG_OWNED_WRITE) continue;
stashedOptimisticReads.add(n);
queueStashedOptimisticEffects(n);
for (let n = 0; n < e.Be.length; n++) {
const t = e.Be[n];
if (t.xe || t.U & CONFIG_OWNED_WRITE) continue;
stashedOptimisticReads.add(t);
queueStashedOptimisticEffects(t);
}

@@ -99,9 +106,9 @@ try {

*/ function transitionBlocked(e) {
for (let t = 0; t < e.Be.length; t++) {
const n = e.Be[t];
if (hasActiveOverride(n) && "i" in n && n.i & STATUS_PENDING && n.k instanceof NotReadyError &&
for (let n = 0; n < e.Be.length; n++) {
const t = e.Be[n];
if (hasActiveOverride(t) && "i" in t && t.i & STATUS_PENDING && t.k instanceof NotReadyError &&
// Mark-sourced pending never blocks settlement: affects() releases AT
// settle, so counting its sentinel here would deadlock the window it
// is scoped to.
!n.k.source?.l) {
!t.k.source?.l) {
return true;

@@ -117,14 +124,15 @@ }

// now, so iterate a fixed window and splice it out at the end.
const t = e.length;
for (let n = 0; n < t; n++) {
const t = e[n];
t.Je = undefined;
const n = e.length;
for (let t = 0; t < n; t++) {
const n = e[t];
n.Je = undefined;
// Revert is a pure drop: there is no revert target to commit —
// override-covered authoritative values hold in _pendingValue and
// elevate on their OWN transition's schedule (A18 as re-ruled 2026-07-07).
if (!(t.i & STATUS_PENDING)) t.i &= ~STATUS_UNINITIALIZED;
const i = t.be;
t.be = NOT_PENDING;
if (i !== NOT_PENDING && t.Ue !== i) insertSubs(t, true);
t.ve = null;
if (!(n.i & STATUS_PENDING)) n.i &= ~STATUS_UNINITIALIZED;
const i = n.be;
n.be = NOT_PENDING;
if (i !== NOT_PENDING && n.Ue !== unwrapOverride(i)) insertSubs(n, true);
n.ve = null;
n.fn = null;
}

@@ -134,13 +142,13 @@ // Settlement checkpoint (#2838): companions caught in this batch (or owned

// transition that produced them (A19 — pending is a property of the data).
for (let n = 0; n < t; n++) {
const t = e[n];
if (t.ye || t.pe) GlobalQueue.R(t);
const i = t.en;
if (i && (i.ye === t || i.pe === t)) GlobalQueue.R(i);
for (let t = 0; t < n; t++) {
const n = e[t];
if (n.ye || n.pe) GlobalQueue.R(n);
const i = n.en;
if (i && (i.ye === n || i.pe === n)) GlobalQueue.R(i);
}
e.splice(0, t);
e.splice(0, n);
}
function runQueue(e, t) {
for (let n = 0; n < e.length; n++) e[n](t);
function runQueue(e, n) {
for (let t = 0; t < e.length; t++) e[t](n);
}

@@ -151,8 +159,8 @@

*/ function runLaneEffects(e) {
for (const t of activeLanes) {
if (t.rn || t.Pe.size > 0) continue;
const n = t.tn[e - 1];
if (n.length) {
t.tn[e - 1] = [];
runQueue(n, e);
for (const n of activeLanes) {
if (n.an || n.Pe.size > 0) continue;
const t = n.tn[e - 1];
if (t.length) {
n.tn[e - 1] = [];
runQueue(t, e);
}

@@ -163,15 +171,15 @@ }

function cleanupCompletedLanes(e) {
for (const t of activeLanes) {
const n = e ? t.ve === e : !t.ve;
if (!n) continue;
if (!t.rn) {
if (t.tn[0].length) runQueue(t.tn[0], EFFECT_RENDER);
if (t.tn[1].length) runQueue(t.tn[1], EFFECT_USER);
for (const n of activeLanes) {
const t = e ? n.ve === e : !n.ve;
if (!t) continue;
if (!n.an) {
if (n.tn[0].length) runQueue(n.tn[0], EFFECT_RENDER);
if (n.tn[1].length) runQueue(n.tn[1], EFFECT_USER);
}
if (t.an.Je === t) t.an.Je = undefined;
t.Pe.clear();
t.tn[0].length = 0;
t.tn[1].length = 0;
activeLanes.delete(t);
signalLanes.delete(t.an);
if (n.rn.Je === n) n.rn.Je = undefined;
n.Pe.clear();
n.tn[0].length = 0;
n.tn[1].length = 0;
activeLanes.delete(n);
signalLanes.delete(n.rn);
}

@@ -184,5 +192,5 @@ }

// downstream in the lane should read the override, not throw)
const t = e.Je;
if (!t) return false;
return findLane(t) === findLane(currentOptimisticLane) && !hasActiveOverride(e);
const n = e.Je;
if (!n) return false;
return findLane(n) === findLane(currentOptimisticLane) && !hasActiveOverride(e);
}

@@ -194,7 +202,7 @@

* is recorded for replay at commit.
*/ function gatedRead(e, t, n) {
if (latestReadActive || e.ge === NOT_PENDING || e.xe || t !== e && !(t.u & REACTIVE_MANUAL_WRITE)) {
*/ function gatedRead(e, n, t) {
if (latestReadActive || e.ge === NOT_PENDING || e.xe || n !== e && !(n.u & REACTIVE_MANUAL_WRITE)) {
return false;
}
activeTransition.Qt.add(n);
activeTransition.un.add(t);
return true;

@@ -206,4 +214,4 @@ }

* optimistic/lane-assigned signals, stale-mode reads, and pending owners.
*/ function laneReadsCommitted(e, t, n) {
return e.be !== undefined || !!e.Je || t === e && stale && n.en !== e || !!(t.i & STATUS_PENDING);
*/ function laneReadsCommitted(e, n, t) {
return e.be !== undefined || !!e.Je || n === e && stale && t.en !== e || !!(n.i & STATUS_PENDING);
}

@@ -215,12 +223,12 @@

* can run before its OPT-dirty child propagates).
*/ function recomputeLane(e, t) {
if (t) return resolveLane(e) ?? null;
for (let t = e.S; t; t = t.st) {
const n = t.ot;
if (n.u & REACTIVE_OPTIMISTIC_DIRTY) {
const t = resolveLane(n);
if (t) {
*/ function recomputeLane(e, n) {
if (n) return resolveLane(e) ?? null;
for (let n = e.S; n; n = n.st) {
const t = n.ot;
if (t.u & REACTIVE_OPTIMISTIC_DIRTY) {
const n = resolveLane(t);
if (n) {
e.u |= REACTIVE_OPTIMISTIC_DIRTY;
assignOrMergeLane(e, t);
return t;
assignOrMergeLane(e, n);
return n;
}

@@ -233,7 +241,7 @@ }

/** recompute()'s catch path: track pending async in the current lane. */ function laneAsyncPending(e) {
const t = findLane(currentOptimisticLane);
if (t.an !== e) {
t.Pe.add(e);
e.Je = t;
GlobalQueue._ !== null && GlobalQueue._(t.an);
const n = findLane(currentOptimisticLane);
if (n.rn !== e) {
n.Pe.add(e);
e.Je = n;
GlobalQueue.P !== null && GlobalQueue.P(n.rn);
}

@@ -243,6 +251,6 @@ }

/** recompute()'s success path: the node's async settled, clear it from its lane. */ function laneAsyncSettled(e) {
const t = resolveLane(e);
if (t) {
t.Pe.delete(e);
GlobalQueue._ !== null && GlobalQueue._(t.an);
const n = resolveLane(e);
if (n) {
n.Pe.delete(e);
GlobalQueue.P !== null && GlobalQueue.P(n.rn);
}

@@ -252,4 +260,4 @@ }

function trackOptimisticStore(e) {
// After initTransition, globalQueue._optimisticStores IS activeTransition._optimisticStores (same reference)
globalQueue.Lt.add(e);
// After initTransition, globalQueue._batch IS activeTransition (same reference)
globalQueue.N.dn.add(e);
schedule();

@@ -265,17 +273,17 @@ }

GlobalQueue.bt = optimisticWrite;
GlobalQueue.Ut = resolveOptimisticNodes;
GlobalQueue.yt = stashOptimistic;
GlobalQueue.Wt = transitionBlocked;
GlobalQueue.jt = cleanupCompletedLanes;
GlobalQueue.Mt = runLaneEffects;
GlobalQueue.gt = readStashed;
GlobalQueue.Dt = gatedRead;
GlobalQueue.En = resolveOptimisticNodes;
GlobalQueue.Tn = stashOptimistic;
GlobalQueue.In = transitionBlocked;
GlobalQueue.Nn = cleanupCompletedLanes;
GlobalQueue.On = runLaneEffects;
GlobalQueue.Dt = readStashed;
GlobalQueue.gt = gatedRead;
GlobalQueue.ht = laneSuspends;
GlobalQueue.kt = laneReadsCommitted;
GlobalQueue.vt = laneReadsCommitted;
GlobalQueue.$e = recomputeLane;
GlobalQueue.et = laneAsyncPending;
GlobalQueue.Xe = laneAsyncSettled;
GlobalQueue.Vt = trackOptimisticStore;
GlobalQueue.mn = trackOptimisticStore;
}
export { installOptimisticEngine };

@@ -1,2 +0,2 @@

import { defaultContext, CONFIG_TRANSPARENT, REACTIVE_DISPOSED, REACTIVE_ZOMBIE, REACTIVE_IN_HEAP, REACTIVE_IN_HEAP_HEIGHT } from "./constants.js";
import { REACTIVE_DISPOSED, REACTIVE_ZOMBIE, REACTIVE_IN_HEAP, REACTIVE_IN_HEAP_HEIGHT, CONFIG_TRANSPARENT, defaultContext } from "./constants.js";

@@ -9,3 +9,3 @@ import { context, runWithOwner, pendingCheckActive, latestReadActive, tracking } from "./core.js";

import { globalQueue, GlobalQueue, zombieQueue, dirtyQueue } from "./scheduler.js";
import { GlobalQueue, zombieQueue, dirtyQueue, globalQueue } from "./scheduler.js";

@@ -16,3 +16,3 @@ const PENDING_OWNER = {};

function markDisposal(e) {
let n = e.Fe;
let n = e.He;
while (n) {

@@ -59,3 +59,3 @@ const e = n.u;

if (n && e.xe) e.Ae = null;
let l = t ? e.Ze : e.Fe;
let l = t ? e.Ze : e.He;
while (l) {

@@ -79,3 +79,3 @@ const e = l.Ve;

} else {
e.Fe = null;
e.He = null;
e.je = 0;

@@ -87,8 +87,8 @@ }

// walks that already advanced past us still reach later siblings.
if (n && !t && !(i & REACTIVE_ZOMBIE) && e.He !== null && !(e.He.u & REACTIVE_DISPOSED)) {
const n = e.Tt;
if (n && !t && !(i & REACTIVE_ZOMBIE) && e.Fe !== null && !(e.Fe.u & REACTIVE_DISPOSED)) {
const n = e.Nt;
const t = e.Ve;
if (n !== null) n.Ve = t; else e.He.Fe = t;
if (t !== null) t.Tt = n;
e.Tt = null;
if (n !== null) n.Ve = t; else e.Fe.He = t;
if (t !== null) t.Nt = n;
e.Nt = null;
}

@@ -98,5 +98,5 @@ runDisposal(e, t);

// to mirror rerun ordering (compute-phase teardown first, cleanup last).
if (n && e.At) {
const n = e.At;
e.At = undefined;
if (n && e.dt) {
const n = e.dt;
e.dt = undefined;
n();

@@ -122,3 +122,3 @@ }

let t = e;
while (t.U & CONFIG_TRANSPARENT && t.He) t = t.He;
while (t.U & CONFIG_TRANSPARENT && t.Fe) t = t.Fe;
if (t.id != null) return formatId(t.id, n ? t.je++ : t.je);

@@ -245,7 +245,7 @@ throw new Error("");

U: t ? CONFIG_TRANSPARENT : 0,
dt: true,
Ct: n?.dt ? n.Ct : n,
Fe: null,
At: true,
Ct: n?.At ? n.Ct : n,
He: null,
Ve: null,
Tt: null,
Nt: null,
Me: null,

@@ -257,13 +257,13 @@ v: n?.v ?? globalQueue,

Ze: null,
He: n,
Fe: n,
dispose: disposeRootSelf
};
if (n) {
const e = n.Fe;
const e = n.He;
if (e === null) {
n.Fe = i;
n.He = i;
} else {
i.Ve = e;
e.Tt = i;
n.Fe = i;
e.Nt = i;
n.He = i;
}

@@ -270,0 +270,0 @@ }

@@ -1,2 +0,2 @@

import { EFFECT_RENDER, EFFECT_USER, STATUS_PENDING, REACTIVE_DISPOSED, REACTIVE_REASK, CONFIG_IN_SNAPSHOT_SCOPE, REACTIVE_SNAPSHOT_STALE, REACTIVE_OPTIMISTIC_DIRTY, REACTIVE_ZOMBIE, NOT_PENDING, EFFECT_TRACKED, REACTIVE_MANUAL_WRITE, STATUS_UNINITIALIZED } from "./constants.js";
import { EFFECT_RENDER, EFFECT_USER, STATUS_PENDING, REACTIVE_DISPOSED, REACTIVE_ZOMBIE, NOT_PENDING, EFFECT_TRACKED, CONFIG_IN_SNAPSHOT_SCOPE, REACTIVE_SNAPSHOT_STALE, REACTIVE_OPTIMISTIC_DIRTY, REACTIVE_REASK, REACTIVE_MANUAL_WRITE, STATUS_UNINITIALIZED } from "./constants.js";

@@ -58,3 +58,4 @@ import { currentOptimisticLane } from "./core.js";

function canUseSimpleSyncFlush(e) {
return transitions.size === 0 && activeLanes.size === 0 && e.wt.length === 0 && e.Be.length === 0 && e.N.length === 0 && e.Lt.size === 0 && transientStoreNodes.size === 0;
const t = e.N;
return transitions.size === 0 && activeLanes.size === 0 && e.Qt.length === 0 && t.Be.length === 0 && t._.length === 0 && t.dn.size === 0 && transientStoreNodes.size === 0;
}

@@ -74,3 +75,3 @@

// unmarked node for the same property).
if (e.P) continue;
if (e.M) continue;
transientStoreNodes.delete(e);

@@ -94,9 +95,35 @@ e.ct?.();

/**
* Ambient work IS a transaction: the global queue always carries one
* current-transaction-shaped batch (`globalQueue._batch`). With no transition
* active, registrations (pending commits, optimistic nodes, affects marks,
* optimistic stores) land in a plain ambient batch that the plain flush
* finalizes; when a transition initializes it adopts the ambient batch's
* contents and `_batch` becomes the transition itself, so later registrations
* land there directly — no per-field aliasing.
*/ function createBatch() {
return {
Ie: clock,
yt: [],
Lt: new Map,
Be: [],
_: [],
dn: new Set,
Te: [],
Bt: {
Mt: [ [], [] ],
Qt: []
},
cn: false,
un: new Set
};
}
function mergeTransitionState(e, t) {
t.Ht = e;
t.cn = e;
e.Te.push(...t.Te);
for (const i of activeLanes) if (i.ve === t) i.ve = e;
if (t.Be.length) {
// Move (don't copy): the global queue may still alias the outgoing
// array, and the adoption pass in initTransition would re-push its
// Move (don't copy): the global queue's batch may still be the outgoing
// transition, and the adoption pass in initTransition would re-push its
// contents into the target — duplicating every entry.

@@ -106,16 +133,16 @@ e.Be.push(...t.Be);

}
if (t.N.length) {
// Move (don't copy): the global queue may still alias the outgoing
// array, and the adoption pass in initTransition would re-push its
if (t._.length) {
// Move (don't copy): the global queue's batch may still be the outgoing
// transition, and the adoption pass in initTransition would re-push its
// contents into the target — double-releasing every mark.
e.N.push(...t.N);
t.N.length = 0;
e._.push(...t._);
t._.length = 0;
}
for (const i of t.Lt) e.Lt.add(i);
for (const [i, n] of t.qt) {
let t = e.qt.get(i);
if (!t) e.qt.set(i, t = new Set);
for (const i of t.dn) e.dn.add(i);
for (const [i, n] of t.Lt) {
let t = e.Lt.get(i);
if (!t) e.Lt.set(i, t = new Set);
for (const e of n) t.add(e);
}
for (const i of t.Qt) e.Qt.add(i);
for (const i of t.un) e.un.add(i);
}

@@ -130,3 +157,3 @@

scheduled = true;
if (!syncDepth && !globalQueue.Bt && !projectionWriteActive) queueMicrotask(flush);
if (!syncDepth && !globalQueue.Ut && !projectionWriteActive) queueMicrotask(flush);
}

@@ -161,28 +188,28 @@

class Queue {
He=null;
zt=[ [], [] ];
wt=[];
Fe=null;
Mt=[ [], [] ];
Qt=[];
created=clock;
addChild(e) {
this.wt.push(e);
e.He = this;
this.Qt.push(e);
e.Fe = this;
}
removeChild(e) {
const t = this.wt.indexOf(e);
const t = this.Qt.indexOf(e);
if (t >= 0) {
this.wt.splice(t, 1);
e.He = null;
this.Qt.splice(t, 1);
e.Fe = null;
}
}
notify(e, t, i, n) {
if (this.He) return this.He.notify(e, t, i, n);
if (this.Fe) return this.Fe.notify(e, t, i, n);
return false;
}
run(e) {
if (this.zt[e - 1].length) {
const t = this.zt[e - 1];
this.zt[e - 1] = [];
if (this.Mt[e - 1].length) {
const t = this.Mt[e - 1];
this.Mt[e - 1] = [];
runQueue(t, e);
}
for (let t = 0; t < this.wt.length; t++) this.wt[t].run?.(e);
for (let t = 0; t < this.Qt.length; t++) this.Qt[t].run?.(e);
}

@@ -196,3 +223,3 @@ enqueue(e, t) {

} else {
this.zt[e - 1].push(t);
this.Mt[e - 1].push(t);
}

@@ -203,14 +230,14 @@ }

stashQueues(e) {
e.zt[0].push(...this.zt[0]);
e.zt[1].push(...this.zt[1]);
this.zt = [ [], [] ];
for (let t = 0; t < this.wt.length; t++) {
let i = this.wt[t];
let n = e.wt[t];
e.Mt[0].push(...this.Mt[0]);
e.Mt[1].push(...this.Mt[1]);
this.Mt = [ [], [] ];
for (let t = 0; t < this.Qt.length; t++) {
let i = this.Qt[t];
let n = e.Qt[t];
if (!n) {
n = {
zt: [ [], [] ],
wt: []
Mt: [ [], [] ],
Qt: []
};
e.wt[t] = n;
e.Qt[t] = n;
}

@@ -221,7 +248,7 @@ i.stashQueues(n);

restoreQueues(e) {
this.zt[0].push(...e.zt[0]);
this.zt[1].push(...e.zt[1]);
for (let t = 0; t < e.wt.length; t++) {
const i = e.wt[t];
let n = this.wt[t];
this.Mt[0].push(...e.Mt[0]);
this.Mt[1].push(...e.Mt[1]);
for (let t = 0; t < e.Qt.length; t++) {
const i = e.Qt[t];
let n = this.Qt[t];
if (n) n.restoreQueues(i);

@@ -233,12 +260,10 @@ }

class GlobalQueue extends Queue {
Bt=false;
xt=null;
Yt=[];
Be=[];
N=[];
Lt=new Set;
Ut=false;
// The current transaction-shaped batch: a plain ambient batch while no
// transition is active, the active transition itself after initTransition.
N=createBatch();
static Ce;
static Oe;
static ut;
static Kt=null;
static Vt=null;
// Store-side hook: drops a keyless affects() mark's identity scope when the

@@ -269,3 +294,3 @@ // carrier node's last registration releases (wired by store.ts, mirroring

static _e=null;
static _=null;
static P=null;
static me=null;

@@ -275,6 +300,6 @@ static R=null;

static Pt=null;
static vt=null;
static kt=null;
static tt=null;
static nt=null;
static Zt=null;
static wt=null;
// Optimistic-engine hooks (wired by core/optimistic.ts via

@@ -288,18 +313,18 @@ // installOptimisticEngine(), called from verdict.ts / createOptimistic /

static bt=null;
static Ut=null;
static yt=null;
static Wt=null;
static jt=null;
static Mt=null;
static En=null;
static Tn=null;
static In=null;
static Nn=null;
static On=null;
static Dt=null;
static gt=null;
static Dt=null;
static ht=null;
static kt=null;
static vt=null;
static $e=null;
static et=null;
static Xe=null;
static Vt=null;
static mn=null;
flush() {
if (this.Bt) return;
this.Bt = true;
if (this.Ut) return;
this.Ut = true;
try {

@@ -313,16 +338,14 @@ if (false) ;

runHeap(zombieQueue, GlobalQueue.Ce);
this.xt = null;
this.Yt = [];
this.Be = [];
this.N = [];
this.Lt = new Set;
// Detach: the stashed transition keeps its batch; ambient work that
// follows lands in a fresh one.
currentBatch = this.N = createBatch();
// Run lane effects immediately (before stashing) - lanes with no pending async
if (activeLanes.size) {
GlobalQueue.Mt(EFFECT_RENDER);
GlobalQueue.Mt(EFFECT_USER);
GlobalQueue.On(EFFECT_RENDER);
GlobalQueue.On(EFFECT_USER);
}
this.stashQueues(e.Jt);
this.stashQueues(e.Bt);
clock++;
scheduled = dirtyQueue.EE >= dirtyQueue.Le;
reassignPendingTransition(e.Yt);
reassignPendingTransition(e.yt);
activeTransition = null;

@@ -332,4 +355,4 @@ // The stash pass (committed-view rerun of plain optimistic signals)

// means _optimisticWrite ran, which installed the hook.
if (!e.Te.length && !e.qt.size && e.Be.length) {
GlobalQueue.yt(e);
if (!e.Te.length && !e.Lt.size && e.Be.length) {
GlobalQueue.Tn(e);
} else {

@@ -340,9 +363,21 @@ finalizePureQueue(null, true);

}
this.Yt !== activeTransition.Yt && this.Yt.push(...activeTransition.Yt);
this.restoreQueues(activeTransition.Jt);
transitions.delete(activeTransition);
const t = activeTransition;
const i = this.N;
i !== t && i.yt.push(...t.yt);
this.restoreQueues(t.Bt);
transitions.delete(t);
activeTransition = null;
reassignPendingTransition(this.Yt);
reassignPendingTransition(i.yt);
finalizePureQueue(t);
if (i === t) {
// Drop the dead Transition wrapper but keep its (drained) containers
// as the ambient batch — late registrations during finalization live
// there and must survive to the next flush.
const e = createBatch();
e.yt = i.yt;
e.Be = i.Be;
e._ = i._;
e.dn = i.dn;
currentBatch = this.N = e;
}
} else {

@@ -364,5 +399,5 @@ if (canUseSimpleSyncFlush(this)) {

// Run lane effects first (for ready lanes), then regular effects
activeLanes.size && GlobalQueue.Mt(EFFECT_RENDER);
activeLanes.size && GlobalQueue.On(EFFECT_RENDER);
this.run(EFFECT_RENDER);
activeLanes.size && GlobalQueue.Mt(EFFECT_USER);
activeLanes.size && GlobalQueue.On(EFFECT_USER);
this.run(EFFECT_USER);

@@ -373,3 +408,3 @@ if (false) ;

} finally {
this.Bt = false;
this.Ut = false;
}

@@ -384,4 +419,4 @@ }

const i = t.source;
let n = activeTransition.qt.get(i);
if (!n) activeTransition.qt.set(i, n = new Set);
let n = activeTransition.Lt.get(i);
if (!n) activeTransition.Lt.set(i, n = new Set);
const s = n.size;

@@ -401,17 +436,3 @@ n.add(e);

if (!activeTransition) {
activeTransition = e ?? {
Ie: clock,
Yt: [],
qt: new Map,
Be: [],
N: [],
Lt: new Set,
Te: [],
Jt: {
zt: [ [], [] ],
wt: []
},
Ht: false,
Qt: new Set
};
activeTransition = e ?? createBatch();
} else if (e) {

@@ -425,40 +446,28 @@ const t = activeTransition;

activeTransition.Ie = clock;
if (this.xt !== null) {
this.xt.ve = activeTransition;
activeTransition.Yt.push(this.xt);
this.xt = null;
}
if (this.Yt !== activeTransition.Yt) {
for (let e = 0; e < this.Yt.length; e++) {
const t = this.Yt[e];
t.ve = activeTransition;
activeTransition.Yt.push(t);
const t = this.N;
if (t !== activeTransition) {
// Adopt the ambient batch into the transaction, then make the
// transaction the batch so later registrations land there directly.
// Pending and optimistic nodes are re-stamped as the transaction's;
// marks don't hijack the node's _transition — a mark on a plain signal
// must not entangle unrelated writes to it; the same rule holds one hop
// downstream: propagation never queues pended subscribers as pending
// nodes, see propagateAffectsMark, #2893.
for (let e = 0; e < t.yt.length; e++) {
const i = t.yt[e];
i.ve = activeTransition;
activeTransition.yt.push(i);
}
this.Yt = activeTransition.Yt;
}
if (this.Be !== activeTransition.Be) {
for (let e = 0; e < this.Be.length; e++) {
const t = this.Be[e];
t.ve = activeTransition;
activeTransition.Be.push(t);
for (let e = 0; e < t.Be.length; e++) {
const i = t.Be[e];
i.ve = activeTransition;
activeTransition.Be.push(i);
}
this.Be = activeTransition.Be;
if (t._.length) activeTransition._.push(...t._);
for (const e of t.dn) activeTransition.dn.add(e);
currentBatch = this.N = activeTransition;
}
if (this.N !== activeTransition.N) {
// Adopt ambient marks into the transaction (marks don't hijack the
// node's _transition — a mark on a plain signal must not entangle
// unrelated writes to it; the same rule holds one hop downstream:
// propagation never queues pended subscribers as pending nodes, see
// propagateAffectsMark, #2893). After adoption the queue aliases the
// transition's array, so later registrations land there directly.
activeTransition.N.push(...this.N);
this.N = activeTransition.N;
}
for (const e of activeLanes) {
if (!e.ve) e.ve = activeTransition;
}
if (this.Lt !== activeTransition.Lt) {
for (const e of this.Lt) activeTransition.Lt.add(e);
this.Lt = activeTransition.Lt;
}
}

@@ -468,15 +477,3 @@ }

function queuePendingNode(e) {
if (activeTransition) {
globalQueue.Yt.push(e);
return;
}
if (globalQueue.xt === null && globalQueue.Yt.length === 0) {
globalQueue.xt = e;
return;
}
if (globalQueue.xt !== null) {
globalQueue.Yt.push(globalQueue.xt);
globalQueue.xt = null;
}
globalQueue.Yt.push(e);
currentBatch.yt.push(e);
}

@@ -499,19 +496,19 @@

const s = reaskArmed;
for (let a = e.p; a !== null; a = a.de) {
for (let r = e.p; r !== null; r = r.de) {
// A value-change notification is a new question for the subscriber: any
// pending re-ask mark (refresh) it carried is superseded.
if (s) a.Ee.u &= ~REACTIVE_REASK;
if (n && a.Ee.U & CONFIG_IN_SNAPSHOT_SCOPE) {
a.Ee.u |= REACTIVE_SNAPSHOT_STALE;
if (s) r.Ee.u &= ~REACTIVE_REASK;
if (n && r.Ee.U & CONFIG_IN_SNAPSHOT_SCOPE) {
r.Ee.u |= REACTIVE_SNAPSHOT_STALE;
continue;
}
if (t && i) {
a.Ee.u |= REACTIVE_OPTIMISTIC_DIRTY;
assignOrMergeLane(a.Ee, i);
r.Ee.u |= REACTIVE_OPTIMISTIC_DIRTY;
assignOrMergeLane(r.Ee, i);
} else if (t) {
a.Ee.u |= REACTIVE_OPTIMISTIC_DIRTY;
r.Ee.u |= REACTIVE_OPTIMISTIC_DIRTY;
// No source lane means reversion - clear subscriber's lane so effects go to regular queue
a.Ee.Je = undefined;
r.Ee.Je = undefined;
}
enqueueSub(a.Ee);
enqueueSub(r.Ee);
}

@@ -543,7 +540,3 @@ }

function commitPendingNodes() {
if (globalQueue.xt !== null) {
commitPendingNode(globalQueue.xt);
globalQueue.xt = null;
}
const e = globalQueue.Yt;
const e = currentBatch.yt;
for (let t = 0; t < e.length; t++) {

@@ -560,3 +553,3 @@ commitPendingNode(e[t]);

if (i) commitPendingNodes();
if (!t && globalQueue.wt.length) checkBoundaryChildren(globalQueue);
if (!t && globalQueue.Qt.length) checkBoundaryChildren(globalQueue);
const n = dirtyQueue.EE >= dirtyQueue.Le;

@@ -566,14 +559,15 @@ if (n) runHeap(dirtyQueue, GlobalQueue.Ce);

if (n) commitPendingNodes();
// The settling batch: the completing transaction's, or the ambient one.
const t = e ?? globalQueue.N;
// Optimistic reversion: a non-empty batch means _optimisticWrite ran,
// which installed the engine's hooks.
const t = e ? e.Be : globalQueue.Be;
if (t.length) GlobalQueue.Ut(t);
if (t.Be.length) GlobalQueue.En(t.Be);
// Replay entanglement: subs recorded by the read-time gate get rescheduled
// so they re-run with the now-committed values visible.
if (e && e.Qt.size) {
for (const t of e.Qt) {
if (e && e.un.size) {
for (const t of e.un) {
if (t.u & REACTIVE_DISPOSED) continue;
enqueueSub(t);
}
e.Qt.clear();
e.un.clear();
}

@@ -583,12 +577,11 @@ // Declared motion ends with the transaction: settle (or plain flush end

// batch means registerAffectsMark ran, which installed the hook.
const i = e ? e.N : globalQueue.N;
if (i.length) GlobalQueue.h(i);
const s = e ? e.Lt : globalQueue.Lt;
if (t._.length) GlobalQueue.h(t._);
// A non-empty set means trackOptimisticStore ran, which installed the
// hook; the hook iterates, clears, and schedules (keeping the loop out of
// core lets esbuild shake it — rollup already folds the null guard).
if (s.size) GlobalQueue.Kt(s);
// core lets esbuild shake it — rollup already folds the null guard). The
// completing transition scopes the clear to its own layer keys (#2899).
if (t.dn.size) GlobalQueue.Vt(t.dn, e);
sweepTransientStoreNodes();
// Lanes only enter activeLanes through the engine's getOrCreateLane.
if (activeLanes.size) GlobalQueue.jt(e);
if (activeLanes.size) GlobalQueue.Nn(e);
}

@@ -598,3 +591,3 @@ }

function checkBoundaryChildren(e) {
for (const t of e.wt) {
for (const t of e.Qt) {
t.Se?.();

@@ -629,2 +622,9 @@ checkBoundaryChildren(t);

// Hot-path mirror of `globalQueue._batch`: `queuePendingNode` runs once per
// staged write and `commitPendingNodes` once per flush, and the extra
// property hop through `_batch` was a measured instruction-count regression
// (CodSpeed update1to1, PR #2905). The field stays authoritative for
// cross-module readers; every `_batch` assignment updates both.
let currentBatch = globalQueue.N;
function flush(e) {

@@ -645,3 +645,3 @@ if (e) {

}
if (globalQueue.Bt) {
if (globalQueue.Ut) {
return;

@@ -663,3 +663,3 @@ }

if (e.u & (REACTIVE_ZOMBIE | REACTIVE_DISPOSED)) return false;
if (e.m === t || e.M?.has(t)) return true;
if (e.m?.has(t)) return true;
for (let i = e.S; i; i = i.st) {

@@ -676,6 +676,6 @@ let e = i.ot;

function transitionComplete(e) {
if (e.Ht) return true;
if (e.cn) return true;
if (e.Te.length) return false;
let t = true;
for (const [i, n] of e.qt) {
for (const [i, n] of e.Lt) {
let s = false;

@@ -689,3 +689,3 @@ for (const e of n) {

}
if (!s) e.qt.delete(i); else if (i.i & STATUS_PENDING && i.k?.source === i) {
if (!s) e.Lt.delete(i); else if (i.i & STATUS_PENDING && i.k?.source === i) {
t = false;

@@ -698,4 +698,4 @@ break;

// _optimisticNodes, so without the engine the loop was vacuous anyway.
if (t && e.Be.length && GlobalQueue.Wt(e)) t = false;
t && (e.Ht = true);
if (t && e.Be.length && GlobalQueue.In(e)) t = false;
t && (e.cn = true);
return t;

@@ -705,3 +705,3 @@ }

function currentTransition(e) {
while (e.Ht && typeof e.Ht === "object") e = e.Ht;
while (e.cn && typeof e.cn === "object") e = e.cn;
return e;

@@ -708,0 +708,0 @@ }

@@ -1,4 +0,4 @@

import { STATUS_UNINITIALIZED, REACTIVE_DISPOSED, NOT_PENDING, REACTIVE_MANUAL_WRITE, STATUS_PENDING, REACTIVE_DIRTY, REACTIVE_CHECK } from "./constants.js";
import { NOT_PENDING, REACTIVE_DISPOSED, REACTIVE_DIRTY, REACTIVE_CHECK, unwrapOverride, STATUS_UNINITIALIZED, STATUS_PENDING, REACTIVE_MANUAL_WRITE } from "./constants.js";
import { context, setPendingCheckActive, read, optimisticSignal, setSignal, setLatestReadActive, pendingCheckActive, latestReadActive, stale, currentOptimisticLane, prepareComputed, tracking, setContextInternal, optimisticComputed } from "./core.js";
import { setSignal, read, context, stale, currentOptimisticLane, prepareComputed, tracking, setLatestReadActive, setContextInternal, optimisticComputed, setPendingCheckActive, latestReadActive, optimisticSignal, pendingCheckActive } from "./core.js";

@@ -13,3 +13,3 @@ import { NotReadyError } from "./error.js";

import { hasActiveOverride, findLane } from "./lanes.js";
import { findLane, hasActiveOverride } from "./lanes.js";

@@ -65,7 +65,6 @@ import { installOptimisticEngine } from "./optimistic.js";

function quietPending(e) {
if (e.M) {
for (const t of e.M) if (!t.A) return false;
if (e.m) {
for (const t of e.m) if (!t.A) return false;
return true;
}
if (e.m) return e.m.A;
return e.A;

@@ -81,7 +80,7 @@ }

if (t.u & REACTIVE_DISPOSED) return false;
if (e.P) return true;
if (e.M) return true;
const n = e.rt;
if (e.en) {
const t = e.en;
if (t.P) return true;
if (t.M) return true;
const n = t.rt || t;

@@ -94,3 +93,3 @@ return newQuestionInFlight(n);

if (e.ge !== NOT_PENDING && !(t.i & STATUS_UNINITIALIZED)) {
if (hasActiveOverride(e)) return !e.Ge || !e.Ge(e.ge, e.be);
if (hasActiveOverride(e)) return !e.Ge || !e.Ge(e.ge, unwrapOverride(e.be));
return true;

@@ -180,3 +179,3 @@ }

setLatestReadActive(false);
const i = e.be !== undefined && e.be !== NOT_PENDING ? e.be : e.Ue;
const i = e.be !== undefined && e.be !== NOT_PENDING ? unwrapOverride(e.be) : e.Ue;
let r;

@@ -280,3 +279,3 @@ try {

GlobalQueue._ = updatePendingSignal;
GlobalQueue.P = updatePendingSignal;

@@ -291,3 +290,3 @@ GlobalQueue.me = updateChildCompanions;

GlobalQueue.vt = recordFreshRead;
GlobalQueue.kt = recordFreshRead;

@@ -298,4 +297,4 @@ GlobalQueue.tt = applyReask;

GlobalQueue.Zt = witnessAffects;
GlobalQueue.wt = witnessAffects;
export { isPending, latest };

@@ -21,22 +21,22 @@ import { computed, runWithOwner, signal, setSignal } from "./core/core.js";

const e = typeof i?.keyed === "function" ? i.keyed : undefined;
const h = s.length > 1;
const r = s;
const n = {
$t: createOwner(),
Xt: 0,
ts: t,
ss: [],
es: r,
hs: [],
rs: [],
ns: e,
cs: e || i?.keyed === false ? [] : undefined,
fs: h && i?.keyed !== false ? [] : undefined,
us: i?.keyed === false,
ps: i?.fallback
const r = s.length > 1;
const n = s;
const h = {
jt: createOwner(),
Wt: 0,
Kt: t,
xt: [],
$t: n,
qt: [],
zt: [],
Ht: e,
Jt: e || i?.keyed === false ? [] : undefined,
Xt: r && i?.keyed !== false ? [] : undefined,
Yt: i?.keyed === false,
Zt: i?.fallback
};
const o = computed(updateKeyedMap.bind(n));
const o = computed(updateKeyedMap.bind(h));
// Untracked reads inside the internal owner resolve via _parentComputed; routing
// them through node lets store-proxy lookups see pending writes (not stale _value).
n.$t.Ct = o;
h.jt.Ct = o;
o.U &= ~CONFIG_AUTO_DISPOSE;

@@ -50,110 +50,145 @@ return accessor(o);

// Exception safety (#2903): a map callback can throw NotReadyError mid-pass
// (async read), and the computed re-runs the whole pass after settle. Every
// pass therefore STAGES its work — new rows are created into temp arrays and
// removals are deferred — and commits to `this` only after every mapper
// succeeded. An aborted pass disposes just the owners it created and leaves
// `_items`/`_mappings`/`_nodes`/`_rows`/`_indexes`/`_len` exactly as they
// were, so the retry diffs against uncorrupted state. Consequence of the
// strong-abort ordering: removed rows now dispose AFTER the pass's new rows
// are created (you cannot destroy state before knowing the pass will land).
function updateKeyedMap() {
const t = this.ts() || [], s = t.length;
const t = this.Kt() || [], s = t.length;
t[$TRACK];
// top level tracking
runWithOwner(this.$t, () => {
let i, e, h = this.cs ? this.us ? () => {
this.cs[e] = signal(t[e], pureOptions);
return this.es(accessor(this.cs[e]), e);
runWithOwner(this.jt, () => {
let i, e, r, n,
// Mappers write freshly-created row/index signals into the STAGE
// arrays (`rows`/`indexes`), never into `this._rows`/`this._indexes`.
h = this.Jt ? this.Yt ? () => {
r[e] = signal(t[e], pureOptions);
return this.$t(accessor(r[e]), e);
} : () => {
this.cs[e] = signal(t[e], pureOptions);
this.fs && (this.fs[e] = signal(e, pureOptions));
return this.es(accessor(this.cs[e]), this.fs ? accessor(this.fs[e]) : undefined);
} : this.fs ? () => {
r[e] = signal(t[e], pureOptions);
n && (n[e] = signal(e, pureOptions));
return this.$t(accessor(r[e]), n ? accessor(n[e]) : undefined);
} : this.Xt ? () => {
const s = t[e];
this.fs[e] = signal(e, pureOptions);
return this.es(s, accessor(this.fs[e]));
n[e] = signal(e, pureOptions);
return this.$t(s, accessor(n[e]));
} : () => {
const s = t[e];
return this.es(s);
return this.$t(s);
};
// fast path for empty arrays
if (s === 0) {
if (this.Xt !== 0) {
this.$t.dispose(false);
this.rs = [];
this.ss = [];
this.hs = [];
this.Xt = 0;
this.cs && (this.cs = []);
this.fs && (this.fs = []);
if (this.Wt !== 0) {
this.jt.dispose(false);
this.zt = [];
this.xt = [];
this.qt = [];
this.Wt = 0;
this.Jt && (this.Jt = []);
this.Xt && (this.Xt = []);
}
if (this.ps && !this.hs[0]) {
// create fallback
this.hs[0] = runWithOwner(this.rs[0] = createOwner(), this.ps);
if (this.Zt && !this.qt[0]) {
// an aborted fallback attempt leaves an owner without a mapping;
// dispose it before re-creating
this.zt[0]?.dispose();
this.qt[0] = runWithOwner(this.zt[0] = createOwner(), this.Zt);
}
}
// fast path for new create
else if (this.Xt === 0) {
// dispose previous fallback
if (this.rs[0]) this.rs[0].dispose();
this.hs = new Array(s);
for (e = 0; e < s; e++) {
this.ss[e] = t[e];
this.hs[e] = runWithOwner(this.rs[e] = createOwner(), h);
else if (this.Wt === 0) {
const o = new Array(s);
const c = new Array(s);
r = this.Jt && new Array(s);
n = this.Xt && new Array(s);
try {
for (e = 0; e < s; e++) o[e] = runWithOwner(c[e] = createOwner(), h);
} catch (t) {
for (i = 0; i <= e; i++) c[i]?.dispose();
throw t;
}
this.Xt = s;
// commit
if (this.zt[0]) this.zt[0].dispose();
// previous fallback
this.qt = o;
this.zt = c;
r && (this.Jt = r);
n && (this.Xt = n);
this.xt = t.slice(0);
this.Wt = s;
} else {
let r, n, o, a, c, f, u, p = new Array(s), l = new Array(s), w = this.cs ? new Array(s) : undefined, O = this.fs ? new Array(s) : undefined;
let o, c, a, f, u, p, w, l, d, O = new Array(s), m = new Array(s);
r = this.Jt ? new Array(s) : undefined;
n = this.Xt ? new Array(s) : undefined;
// skip common prefix
for (r = 0, n = Math.min(this.Xt, s); r < n && (this.ss[r] === t[r] || this.cs && compare(this.ns, this.ss[r], t[r])); r++) {
if (this.cs) setSignal(this.cs[r], t[r]);
for (o = 0, c = Math.min(this.Wt, s); o < c && (this.xt[o] === t[o] || this.Jt && compare(this.Ht, this.xt[o], t[o])); o++) {
if (this.Jt) setSignal(this.Jt[o], t[o]);
}
// common suffix
for (n = this.Xt - 1, o = s - 1; n >= r && o >= r && (this.ss[n] === t[o] || this.cs && compare(this.ns, this.ss[n], t[o])); n--,
o--) {
p[o] = this.hs[n];
l[o] = this.rs[n];
w && (w[o] = this.cs[n]);
O && (O[o] = this.fs[n]);
for (c = this.Wt - 1, a = s - 1; c >= o && a >= o && (this.xt[c] === t[a] || this.Jt && compare(this.Ht, this.xt[c], t[a])); c--,
a--) {
O[a] = this.qt[c];
m[a] = this.zt[c];
r && (r[a] = this.Jt[c]);
n && (n[a] = this.Xt[c]);
}
// 0) prepare a map of all indices in newItems, scanning backwards so we encounter them in natural order
f = new Map;
u = new Array(o + 1);
for (e = o; e >= r; e--) {
a = t[e];
c = this.ns ? this.ns(a) : a;
i = f.get(c);
u[e] = i === undefined ? -1 : i;
f.set(c, e);
p = new Map;
w = new Array(a + 1);
for (e = a; e >= o; e--) {
f = t[e];
u = this.Ht ? this.Ht(f) : f;
i = p.get(u);
w[e] = i === undefined ? -1 : i;
p.set(u, e);
}
// 1) step through all old items and see if they can be found in the new set; if so, save them in a temp array and mark them moved; if not, exit them
for (i = r; i <= n; i++) {
a = this.ss[i];
c = this.ns ? this.ns(a) : a;
e = f.get(c);
// 1) step through all old items and see if they can be found in the new set; if so, save them in a temp array and mark them moved; if not, queue them for disposal at commit
for (i = o; i <= c; i++) {
f = this.xt[i];
u = this.Ht ? this.Ht(f) : f;
e = p.get(u);
if (e !== undefined && e !== -1) {
p[e] = this.hs[i];
l[e] = this.rs[i];
w && (w[e] = this.cs[i]);
O && (O[e] = this.fs[i]);
e = u[e];
f.set(c, e);
} else this.rs[i].dispose();
O[e] = this.qt[i];
m[e] = this.zt[i];
r && (r[e] = this.Jt[i]);
n && (n[e] = this.Xt[i]);
e = w[e];
p.set(u, e);
} else (l ??= []).push(this.zt[i]);
}
// 2) set all the new values, pulling from the temp array if copied, otherwise entering the new value
for (e = r; e < s; e++) {
if (e in p) {
this.hs[e] = p[e];
this.rs[e] = l[e];
if (w) {
this.cs[e] = w[e];
setSignal(this.cs[e], t[e]);
}
if (O) {
this.fs[e] = O[e];
setSignal(this.fs[e], e);
}
} else {
this.hs[e] = runWithOwner(this.rs[e] = createOwner(), h);
// 2) create new rows into the temp arrays; an abort disposes only these
try {
for (e = o; e < s; e++) {
if (e in O) continue;
(d ??= []).push(m[e] = createOwner());
O[e] = runWithOwner(m[e], h);
}
} catch (t) {
if (d) for (i = 0; i < d.length; i++) d[i].dispose();
throw t;
}
// 3) in case the new set is shorter than the old, set the length of the mapped array
this.hs = this.hs.slice(0, this.Xt = s);
// 4) save a copy of the mapped items for the next update
this.ss = t.slice(0);
// 3) commit: land positions, then dispose exited rows
for (e = o; e < s; e++) {
this.qt[e] = O[e];
this.zt[e] = m[e];
if (r) {
this.Jt[e] = r[e];
setSignal(this.Jt[e], t[e]);
}
if (n) {
this.Xt[e] = n[e];
setSignal(this.Xt[e], e);
}
}
if (l) for (i = 0; i < l.length; i++) l[i].dispose();
// 4) in case the new set is shorter than the old, set the length of the mapped array
this.qt = this.qt.slice(0, this.Wt = s);
// 5) save a copy of the mapped items for the next update
this.xt = t.slice(0);
}
});
return this.hs;
return this.qt;
}

@@ -177,36 +212,47 @@

const e = s;
const h = computed(updateRepeat.bind({
$t: createOwner(),
Xt: 0,
ls: 0,
ws: t,
es: e,
rs: [],
hs: [],
Os: i?.from,
ps: i?.fallback
}));
h.U &= ~CONFIG_AUTO_DISPOSE;
return accessor(h);
const r = {
jt: createOwner(),
Wt: 0,
ts: 0,
ss: t,
$t: e,
zt: [],
qt: [],
es: i?.from,
Zt: i?.fallback
};
const n = computed(updateRepeat.bind(r));
// Same as mapArray: untracked reads inside the internal owner resolve via
// _parentComputed, so async reads in row callbacks register with the node
// (pending tracking + post-settle retry) instead of vanishing.
r.jt.Ct = n;
n.U &= ~CONFIG_AUTO_DISPOSE;
return accessor(n);
}
// Same staged-commit discipline as `updateKeyedMap` (#2903): the retained
// window overlap is copied into fresh arrays, missing indexes are created
// into them, and `this` is only touched — including disposal of rows leaving
// the window — after every `_map` call succeeded. A NotReadyError mid-pass
// disposes only the owners this pass created and leaves prior state intact
// for the post-settle retry. The overlap math also subsumes the previous
// disjoint-window/front-clear/end-clear/shift special cases.
function updateRepeat() {
const t = this.ws();
const s = this.Os?.() || 0;
runWithOwner(this.$t, () => {
const t = this.ss();
const s = this.es?.() || 0;
runWithOwner(this.jt, () => {
if (t === 0) {
if (this.Xt !== 0) {
this.$t.dispose(false);
this.rs = [];
this.hs = [];
this.Xt = 0;
// Reset offset to match the cleared data. Without this, a subsequent
// nonzero render with a smaller `from` would enter the end-clear loop
// with `prevTo = stale_offset + 0` > `to` and dispose `_nodes[-1]`
// (#2767, repro 2).
this.ls = 0;
if (this.Wt !== 0) {
this.jt.dispose(false);
this.zt = [];
this.qt = [];
this.Wt = 0;
// Reset offset to match the cleared data (#2767, repro 2).
this.ts = 0;
}
if (this.ps && !this.hs[0]) {
// create fallback
this.hs[0] = runWithOwner(this.rs[0] = createOwner(), this.ps);
if (this.Zt && !this.qt[0]) {
// an aborted fallback attempt leaves an owner without a mapping;
// dispose it before re-creating
this.zt[0]?.dispose();
this.qt[0] = runWithOwner(this.zt[0] = createOwner(), this.Zt);
}

@@ -216,47 +262,30 @@ return;

const i = s + t;
const e = this.ls + this.Xt;
// remove fallback
if (this.Xt === 0 && this.rs[0]) this.rs[0].dispose();
// Disjoint windows have no rows to retain; replace them wholesale.
if (s >= e || i <= this.ls) {
for (let t = 0; t < this.Xt; t++) this.rs[t].dispose();
this.rs = new Array(t);
this.hs = new Array(t);
for (let i = 0; i < t; i++) this.hs[i] = runWithOwner(this.rs[i] = createOwner(), () => this.es(s + i));
this.ls = s;
this.Xt = t;
return;
const e = this.ts + this.Wt;
// Retained overlap [keepStart, keepEnd) in global indexes; empty when the
// windows are disjoint or when coming from empty/fallback.
const r = Math.max(s, this.ts);
const n = Math.min(i, e);
const h = new Array(t);
const o = new Array(t);
for (let t = r; t < n; t++) {
o[t - s] = this.zt[t - this.ts];
h[t - s] = this.qt[t - this.ts];
}
// clear the end
for (let t = i; t < e; t++) this.rs[t - this.ls].dispose();
if (this.ls < s) {
// clear beginning — `_nodes` is local-indexed, so the rows leaving the
// front sit at local positions 0..(from - _offset), clamped to old len.
const t = s - this.ls;
for (let s = 0; s < t && s < this.Xt; s++) this.rs[s].dispose();
// shift indexes
this.rs.splice(0, t);
this.hs.splice(0, t);
} else if (this.ls > s) {
// shift indexes
let i = e - this.ls - 1;
let h = this.ls - s;
this.rs.length = this.hs.length = t;
while (i >= h) {
this.rs[i] = this.rs[i - h];
this.hs[i] = this.hs[i - h];
i--;
try {
for (let t = s; t < i; t++) {
if (t >= r && t < n) continue;
h[t - s] = runWithOwner(o[t - s] = createOwner(), () => this.$t(t));
}
for (let t = 0; t < h; t++) {
this.hs[t] = runWithOwner(this.rs[t] = createOwner(), () => this.es(t + s));
}
} catch (t) {
for (let t = s; t < i; t++) if ((t < r || t >= n) && o[t - s]) o[t - s].dispose();
throw t;
}
for (let t = e; t < i; t++) {
this.hs[t - s] = runWithOwner(this.rs[t - s] = createOwner(), () => this.es(t));
}
this.hs = this.hs.slice(0, t);
this.ls = s;
this.Xt = t;
// commit: dispose the previous fallback or the rows leaving the window
if (this.Wt === 0) this.zt[0]?.dispose(); else for (let t = this.ts; t < e; t++) if (t < s || t >= i) this.zt[t - this.ts].dispose();
this.qt = h;
this.zt = o;
this.ts = s;
this.Wt = t;
});
return this.hs;
return this.qt;
}

@@ -263,0 +292,0 @@

@@ -7,3 +7,3 @@ import { computed, optimisticComputed, setSignal, optimisticSignal, runWithOwner, setMemo, signal, read, untrack } from "./core/core.js";

import { CONFIG_CHILDREN_FORBIDDEN, EFFECT_USER, $REFRESH, CONFIG_AUTO_DISPOSE } from "./core/constants.js";
import { CONFIG_AUTO_DISPOSE, CONFIG_CHILDREN_FORBIDDEN, EFFECT_USER, $REFRESH } from "./core/constants.js";

@@ -10,0 +10,0 @@ import "./core/invariants.js";

@@ -5,3 +5,3 @@ import { computed } from "../core/core.js";

import { GlobalQueue, schedule, setProjectionWriteActive, insertSubs, projectionWriteActive } from "../core/scheduler.js";
import { GlobalQueue, schedule, currentTransition, insertSubs, setProjectionWriteActive, projectionWriteActive } from "../core/scheduler.js";

@@ -18,5 +18,5 @@ import "../core/invariants.js";

import { storeSetter, $TARGET, STORE_OPTIMISTIC_OVERRIDE, STORE_NODE, getOverlayLayer, STORE_VALUE, $DELETED, isWrappable, wrap, visibleNodeValue, $TRACK, notifySelf, STORE_WRAP, createStoreProxy, storeTraps, STORE_LOOKUP, STORE_OPTIMISTIC, STORE_FIREWALL } from "./store.js";
import { storeSetter, $TARGET, STORE_OPTIMISTIC_OVERRIDE, STORE_NODE, STORE_OPTIMISTIC_OWNERS, getOverlayLayer, STORE_VALUE, $DELETED, isWrappable, wrap, visibleNodeValue, $TRACK, notifySelf, STORE_WRAP, createStoreProxy, storeTraps, STORE_LOOKUP, STORE_OPTIMISTIC, STORE_FIREWALL } from "./store.js";
function createOptimisticStore(t, e, r) {
function createOptimisticStore(e, t, r) {
// Register clear function with scheduler; store nodes marked

@@ -26,9 +26,9 @@ // STORE_OPTIMISTIC take the engine's write path, so install it before any

installOptimisticEngine();
GlobalQueue.Kt ||= clearOptimisticStores;
const i = typeof t === "function";
const o = i ? e : t;
const c = i ? t : undefined;
GlobalQueue.Vt ||= clearOptimisticStores;
const i = typeof e === "function";
const o = i ? t : e;
const n = i ? e : undefined;
// Create optimistic projection store
const {store: n} = createOptimisticProjectionInternal(c, o, r);
return [ n, t => storeSetter(n, t) ];
const {store: c} = createOptimisticProjectionInternal(n, o, r);
return [ c, e => storeSetter(c, e) ];
}

@@ -38,65 +38,100 @@

// signals. Owns the whole batch (iterate + clear + reschedule) so the
// scheduler's flush tail carries only a size-guarded hook call.
function clearOptimisticStores(t) {
for (const e of t) {
const t = e[$TARGET];
if (t?.[STORE_OPTIMISTIC_OVERRIDE]) clearOptimisticOverride(t);
// scheduler's flush tail carries only a size-guarded hook call. The
// completing transition scopes each clear to its own layer keys (#2899).
function clearOptimisticStores(e, t) {
for (const r of e) {
const e = r[$TARGET];
if (e?.[STORE_OPTIMISTIC_OVERRIDE]) clearOptimisticOverride(e, t);
}
t.clear();
e.clear();
schedule();
}
function clearOptimisticOverride(t) {
const e = t[STORE_OPTIMISTIC_OVERRIDE];
if (!e) return;
const r = t[STORE_NODE];
delete t[STORE_OPTIMISTIC_OVERRIDE];
// Notify signals for all overridden properties
/**
* Consume optimistic layer entries and reset their backing nodes to base.
* With `completing` (settle path, #2899) only entries the settling
* transaction owns are consumed — the layer is store-wide but concurrent
* actions revert independently, so keys stamped by a still-in-flight
* transition survive (node-level overrides already have this granularity via
* _optimisticNodes; this is the layer's half). `null` consumes ambient
* (transaction-less) entries at plain flush end. Omitted (projection landing:
* fresh authoritative data) consumes everything — the correction supersedes
* every tentative layer.
*/ function clearOptimisticOverride(e, t) {
const r = e[STORE_OPTIMISTIC_OVERRIDE];
if (!r) return;
const i = e[STORE_NODE];
const o = e[STORE_OPTIMISTIC_OWNERS];
const n = t !== undefined;
let c = false;
let s = false;
// Use projectionWriteActive to bypass optimistic signal behavior (no lane creation)
// This ensures reversion effects go to regular queues, not lane queues
const i = projectionWriteActive;
const O = projectionWriteActive;
setProjectionWriteActive(true);
try {
if (r) {
for (const i of Reflect.ownKeys(e)) {
if (r[i]) {
const e = r[i];
// Clear lane association so effects go to regular queue
e.Je = undefined;
// Re-read from base — the optimistic layer was deleted above, so the
// overlay resolves to STORE_OVERRIDE or STORE_VALUE.
const o = getOverlayLayer(t, i);
const c = o ? o[i] : t[STORE_VALUE][i];
const n = c === $DELETED ? undefined : c;
const s = isWrappable(n) ? wrap(n, t) : n;
const O = visibleNodeValue(e);
e.be = NOT_PENDING;
e.ge = NOT_PENDING;
e.Ue = s;
if (!e.Ge || !e.Ge(O, s)) {
insertSubs(e, true);
schedule();
for (const O of Reflect.ownKeys(r)) {
if (n) {
let e = o?.[O] ?? null;
// Resolve merge chains (entangled actions settle as one); path-compress
// so later keys skip the walk. A dead owner (`_done === true`) settled
// through some other path — never strand its entry. A null owner is an
// ambient write: its batch belongs to whichever transaction adopted it
// (initTransition mid-batch) or to the plain flush, so it clears on
// whichever clear call reaches this store first.
if (e) {
if (typeof e.cn === "object") e = o[O] = currentTransition(e);
if (e !== t && e.cn !== true) {
s = true;
continue;
}
}
}
// Notify $TRACK
if (r[$TRACK]) {
r[$TRACK].Je = undefined;
notifySelf(t);
delete r[O];
if (o) delete o[O];
c = true;
const T = i?.[O];
if (T) {
// Clear lane association so effects go to regular queue
T.Je = undefined;
// Re-read from base — this key left the optimistic layer above, so the
// overlay resolves to STORE_OVERRIDE or STORE_VALUE.
const t = getOverlayLayer(e, O);
const r = t ? t[O] : e[STORE_VALUE][O];
const i = r === $DELETED ? undefined : r;
const o = isWrappable(i) ? wrap(i, e) : i;
const n = visibleNodeValue(T);
T.be = NOT_PENDING;
T.fn = null;
T.ge = NOT_PENDING;
T.Ue = o;
if (!T.Ge || !T.Ge(n, o)) {
insertSubs(T, true);
schedule();
}
}
}
if (!s) {
delete e[STORE_OPTIMISTIC_OVERRIDE];
delete e[STORE_OPTIMISTIC_OWNERS];
}
// Notify $TRACK
if (c && i?.[$TRACK]) {
i[$TRACK].Je = undefined;
notifySelf(e);
}
} finally {
setProjectionWriteActive(i);
setProjectionWriteActive(O);
}
}
function createOptimisticProjectionInternal(t, e, r) {
function createOptimisticProjectionInternal(e, t, r) {
let i;
const o = new WeakMap;
const wrapper = t => {
t[STORE_WRAP] = wrapProjection;
t[STORE_LOOKUP] = o;
t[STORE_OPTIMISTIC] = true;
const wrapper = e => {
e[STORE_WRAP] = wrapProjection;
e[STORE_LOOKUP] = o;
e[STORE_OPTIMISTIC] = true;
// Mark as optimistic store
Object.defineProperty(t, STORE_FIREWALL, {
Object.defineProperty(e, STORE_FIREWALL, {
get() {

@@ -108,12 +143,12 @@ return i;

};
const wrapProjection = t => {
if (o.has(t)) return o.get(t);
if (t[$TARGET]?.[STORE_WRAP] === wrapProjection) return t;
const e = createStoreProxy(t, storeTraps, wrapper);
o.set(t, e);
return e;
const wrapProjection = e => {
if (o.has(e)) return o.get(e);
if (e[$TARGET]?.[STORE_WRAP] === wrapProjection) return e;
const t = createStoreProxy(e, storeTraps, wrapper);
o.set(e, t);
return t;
};
const c = wrapProjection(e);
const n = wrapProjection(t);
// If there's a projection function, create a computed to drive it
if (t) {
if (e) {
// All writes inside firewall recompute must go to STORE_OVERRIDE (base), not

@@ -125,13 +160,13 @@ // STORE_OPTIMISTIC_OVERRIDE. The outer wrap covers the sync body (including

const clearProjectionOverride = () => {
const t = c[$TARGET];
if (t?.[STORE_OPTIMISTIC_OVERRIDE]) clearOptimisticOverride(t);
const e = n[$TARGET];
if (e?.[STORE_OPTIMISTIC_OVERRIDE]) clearOptimisticOverride(e);
};
const wrapCommit = t => {
const e = projectionWriteActive;
const wrapCommit = e => {
const t = projectionWriteActive;
setProjectionWriteActive(true);
try {
t();
e();
clearProjectionOverride();
} finally {
setProjectionWriteActive(e);
setProjectionWriteActive(t);
}

@@ -142,3 +177,3 @@ };

try {
runProjectionComputed(c, t, r?.key || "id", wrapCommit, clearProjectionOverride);
runProjectionComputed(n, e, r?.key || "id", wrapCommit, clearProjectionOverride);
} finally {

@@ -151,3 +186,3 @@ setProjectionWriteActive(false);

return {
store: c,
store: n,
node: i

@@ -154,0 +189,0 @@ };

@@ -11,3 +11,3 @@ import { setSignal, untrack } from "../core/core.js";

import { $TARGET, STORE_OVERRIDE, STORE_OPTIMISTIC_OVERRIDE, STORE_VALUE, STORE_NODE, storeLookup, STORE_LOOKUP, $PROXY, isWrappable, wrap, notifySelf, $TRACK, STORE_HAS, getKeys, $DELETED, symbolKeyedRecords } from "./store.js";
import { $TARGET, STORE_OVERRIDE, STORE_OPTIMISTIC_OVERRIDE, STORE_VALUE, STORE_NODE, storeLookup, STORE_LOOKUP, $PROXY, isWrappable, wrap, notifySelf, $TRACK, STORE_DESC, STORE_HAS, getKeys, $DELETED, symbolKeyedRecords, getStoreSymbols } from "./store.js";

@@ -49,2 +49,4 @@ // Enumerate a node record's keys. Keeps the common string-key path on the

function getAllKeys(e, t, r) {
// Symbols are merged explicitly below; keep the common string-key path on
// Object.keys() and avoid reflecting the base symbols twice.
const n = getKeys(e, t);

@@ -136,2 +138,30 @@ const a = Object.keys(r);

/**
* The captured-proxy half of the object diff (#2902): descend into keyed-
* matching children that have NO node at this level but shelter subscribers
* somewhere below (their target's sticky `STORE_DESC` flag, bubbled up by
* `getNode`). Without this, a proxy captured through untracked reads — a
* `<For>` row handed to a child component — detaches from the diff the
* moment no intermediate level happens to be tracked, and its live
* subscribers go permanently stale. Never-subscribed branches have no flag
* and stay pruned exactly as before; keys the main loop already visited
* (node present) are skipped. Callers gate on the parent's own flag and on
* `$TRACK` absence (an enumeration-tracked record already diffs every key).
*/ function applyDescendants(e, t, r, n, a, i, o) {
const l = r[STORE_LOOKUP] || storeLookup;
const s = (i ? getKeys(e, i) : Object.keys(e)).concat(getStoreSymbols(e, i));
for (let p = 0, f = s.length; p < f; p++) {
const f = s[p];
if (n?.[f]) continue;
// main loop already diffed this slot
const c = unwrap(i ? getOverrideValue(e, i, f, o) : e[f]);
if (!isWrappable(c)) continue;
const u = (l.get(c) ?? storeLookup.get(c))?.[$TARGET];
if (!u?.[STORE_DESC]) continue;
const S = unwrap(t[f]);
if (c === S || !isWrappable(S) || Array.isArray(c) !== Array.isArray(S) || a(c) != null && a(c) !== a(S)) continue;
applyState(S, wrap(c, r), a);
}
}
// Dispatcher: every applyState call (including recursion) checks for the

@@ -167,3 +197,3 @@ // presence of override / optimistic-override slots once and routes to the

if (e.length && o && isWrappable(e[0]) && r(e[0]) != null) {
let l, s, p, f, c, u, y, S;
let l, s, p, f, c, u, S, y;
for (p = 0, f = Math.min(o, e.length); p < f && keyedMatch(u = n[p], e[p], r); p++) {

@@ -191,18 +221,18 @@ isWrappable(u) && isWrappable(e[p]) && applyState(e[p], wrap(u, t), r);

}
y = new Array(c + 1);
S = new Array(c + 1);
for (s = c; s >= p; s--) {
u = e[s];
S = itemKey(u, r);
l = O.get(S);
y[s] = l === undefined ? -1 : l;
O.set(S, s);
y = itemKey(u, r);
l = O.get(y);
S[s] = l === undefined ? -1 : l;
O.set(y, s);
}
for (l = p; l <= f; l++) {
u = n[l];
S = itemKey(u, r);
s = O.get(S);
y = itemKey(u, r);
s = O.get(y);
if (s !== undefined && s !== -1) {
E[s] = u;
s = y[s];
O.set(S, s);
s = S[s];
O.set(y, s);
}

@@ -235,7 +265,8 @@ }

let i = t[STORE_NODE];
let o;
if (i) {
const a = i[$TRACK];
const o = a ? getAllKeys(n, undefined, e) : nodeKeys(i);
for (let l = 0, s = o.length; l < s; l++) {
const s = o[l];
o = i[$TRACK];
const a = o ? getAllKeys(n, undefined, e) : nodeKeys(i);
for (let l = 0, s = a.length; l < s; l++) {
const s = a[l];
const p = i[s];

@@ -246,3 +277,3 @@ const f = unwrap(n[s]);

if (!f || !isWrappable(f) || !isWrappable(c) || Array.isArray(f) !== Array.isArray(c) || r(f) != null && r(f) !== r(c)) {
a && setSignal(a, void 0);
o && setSignal(o, void 0);
p && setSignal(p, isWrappable(c) ? wrap(c, t) : c);

@@ -252,2 +283,3 @@ } else applyState(c, wrap(f, t), r);

}
if (!o && t[STORE_DESC]) applyDescendants(n, e, t, i, r);
// has

@@ -277,13 +309,13 @@ if (i = t[STORE_HAS]) {

if (e.length && s && isWrappable(e[0]) && r(e[0]) != null) {
let p, f, c, u, y, S, E, O;
for (c = 0, u = Math.min(s, e.length); c < u && keyedMatch(S = getOverrideValue(n, a, c, i), e[c], r); c++) {
isWrappable(S) && isWrappable(e[c]) && applyState(e[c], wrap(S, t), r);
let p, f, c, u, S, y, E, O;
for (c = 0, u = Math.min(s, e.length); c < u && keyedMatch(y = getOverrideValue(n, a, c, i), e[c], r); c++) {
isWrappable(y) && isWrappable(e[c]) && applyState(e[c], wrap(y, t), r);
}
const d = new Array(e.length), R = new Map;
for (u = s - 1, y = e.length - 1; u >= c && y >= c && keyedMatch(S = getOverrideValue(n, a, u, i), e[y], r); u--,
y--) {
d[y] = S;
const R = new Array(e.length), d = new Map;
for (u = s - 1, S = e.length - 1; u >= c && S >= c && keyedMatch(y = getOverrideValue(n, a, u, i), e[S], r); u--,
S--) {
R[S] = y;
}
if (c > y || c > u) {
for (f = c; f <= y; f++) {
if (c > S || c > u) {
for (f = c; f <= S; f++) {
l = true;

@@ -294,3 +326,3 @@ o?.[f] && setSignal(o[f], wrapValue(e[f], t));

l = true;
applyArrayItem(e[f], d[f], t, o?.[f], r);
applyArrayItem(e[f], R[f], t, o?.[f], r);
}

@@ -303,23 +335,23 @@ const n = e.length;

}
E = new Array(y + 1);
for (f = y; f >= c; f--) {
S = e[f];
O = itemKey(S, r);
p = R.get(O);
E = new Array(S + 1);
for (f = S; f >= c; f--) {
y = e[f];
O = itemKey(y, r);
p = d.get(O);
E[f] = p === undefined ? -1 : p;
R.set(O, f);
d.set(O, f);
}
for (p = c; p <= u; p++) {
S = getOverrideValue(n, a, p, i);
O = itemKey(S, r);
f = R.get(O);
y = getOverrideValue(n, a, p, i);
O = itemKey(y, r);
f = d.get(O);
if (f !== undefined && f !== -1) {
d[f] = S;
R[f] = y;
f = E[f];
R.set(O, f);
d.set(O, f);
}
}
for (f = c; f < e.length; f++) {
if (f in d) {
applyArrayItem(e[f], d[f], t, o?.[f], r);
if (f in R) {
applyArrayItem(e[f], R[f], t, o?.[f], r);
} else o?.[f] && setSignal(o[f], wrapValue(e[f], t));

@@ -347,4 +379,5 @@ }

// values
if (o) {
const l = o[$TRACK];
let l;
if (o) {
l = o[$TRACK];
const s = l ? getAllKeys(n, a, e) : nodeKeys(o);

@@ -355,10 +388,11 @@ for (let p = 0, f = s.length; p < f; p++) {

const u = unwrap(getOverrideValue(n, a, f, i));
let y = unwrap(e[f]);
if (u === y) continue;
if (!u || !isWrappable(u) || !isWrappable(y) || Array.isArray(u) !== Array.isArray(y) || r(u) != null && r(u) !== r(y)) {
let S = unwrap(e[f]);
if (u === S) continue;
if (!u || !isWrappable(u) || !isWrappable(S) || Array.isArray(u) !== Array.isArray(S) || r(u) != null && r(u) !== r(S)) {
l && setSignal(l, void 0);
c && setSignal(c, isWrappable(y) ? wrap(y, t) : y);
} else applyState(y, wrap(u, t), r);
c && setSignal(c, isWrappable(S) ? wrap(S, t) : S);
} else applyState(S, wrap(u, t), r);
}
}
if (!l && t[STORE_DESC]) applyDescendants(n, e, t, o, r, a, i);
// has

@@ -365,0 +399,0 @@ if (o = t[STORE_HAS]) {

@@ -1,2 +0,2 @@

import { $REFRESH, STORE_SNAPSHOT_PROPS, NOT_PENDING, STATUS_PENDING, NO_SNAPSHOT } from "../core/constants.js";
import { $REFRESH, STORE_SNAPSHOT_PROPS, NOT_PENDING, unwrapOverride, STATUS_UNINITIALIZED, STATUS_PENDING, NO_SNAPSHOT } from "../core/constants.js";

@@ -7,5 +7,7 @@ import { suppressComputedRecompute, isEqual, signal, pendingCheckActive, untrack, setSignal, read, snapshotCaptureActive, snapshotSources } from "../core/core.js";

import { NotReadyError } from "../core/error.js";
import { getObserver } from "../core/owner.js";
import { GlobalQueue, registerTransientStoreNode, projectionWriteActive, globalQueue } from "../core/scheduler.js";
import { GlobalQueue, registerTransientStoreNode, projectionWriteActive, activeTransition, globalQueue } from "../core/scheduler.js";

@@ -30,3 +32,3 @@ import "../core/invariants.js";

const STORE_VALUE = "v", STORE_OVERRIDE = "o", STORE_OPTIMISTIC_OVERRIDE = "x", STORE_NODE = "n", STORE_HAS = "h", STORE_CUSTOM_PROTO = "c", STORE_WRAP = "w", STORE_LOOKUP = "l", STORE_FIREWALL = "f", STORE_OPTIMISTIC = "p";
const STORE_VALUE = "v", STORE_OVERRIDE = "o", STORE_OPTIMISTIC_OVERRIDE = "x", STORE_NODE = "n", STORE_HAS = "h", STORE_CUSTOM_PROTO = "c", STORE_WRAP = "w", STORE_LOOKUP = "l", STORE_FIREWALL = "f", STORE_OPTIMISTIC = "p", STORE_OPTIMISTIC_OWNERS = "t", STORE_PARENT = "u", STORE_DESC = "d";

@@ -61,5 +63,13 @@ const STORE_SELF_PENDING = Symbol(0);

function wrap(e, t) {
if (t?.[STORE_WRAP]) return t[STORE_WRAP](e, t);
if (t?.[STORE_WRAP]) {
const r = t[STORE_WRAP](e, t);
const n = r[$TARGET];
if (n && !n[STORE_PARENT] && n !== t) n[STORE_PARENT] = t;
return r;
}
let r = e[$PROXY] || storeLookup.get(e);
if (!r) storeLookup.set(e, r = createStoreProxy(e));
if (!r) {
storeLookup.set(e, r = createStoreProxy(e));
if (t) r[$TARGET][STORE_PARENT] = t;
}
return r;

@@ -97,3 +107,3 @@ }

r = n[STORE_LOOKUP] ?? storeLookup;
for (const e of getKeys(i, o)) {
for (const e of getStoreKeys(i, o)) {
if (s && e === "length") continue;

@@ -116,2 +126,19 @@ const n = e in o ? o[e] : i[e];

function ownEnumerableSymbols(e) {
const t = Object.getOwnPropertySymbols(e);
const r = [];
for (let n = 0, o = t.length; n < o; n++) {
const o = t[n];
if (Object.prototype.propertyIsEnumerable.call(e, o)) r.push(o);
}
return r;
}
// Plain-object variant that keeps Object.keys() as the fast path and only pays
// descriptor checks for symbols. Do not use this on store proxies: splitting
// strings/symbols would invoke their ownKeys trap twice.
function ownEnumerableKeysPlain(e) {
return Object.keys(e).concat(ownEnumerableSymbols(e));
}
/**

@@ -135,3 +162,3 @@ * Single chokepoint for the store's layered value resolution: returns the

*/ function visibleNodeValue(e) {
return e.be !== undefined && e.be !== NOT_PENDING ? e.be : e.ge !== NOT_PENDING ? e.ge : e.Ue;
return e.be !== undefined && e.be !== NOT_PENDING ? unwrapOverride(e.be) : e.ge !== NOT_PENDING ? e.ge : e.Ue;
}

@@ -195,6 +222,15 @@

if (typeof r === "symbol" && r !== $TRACK && r !== $AFFECTS) symbolKeyedRecords.add(t);
// A node born inside a live keyless mark's identity scope inherits the mark
// A node born inside a live mark's identity scope inherits the mark
// (the declaration walk could only cover nodes that existed then). The
// record's own $AFFECTS carrier is the mark's channel, never a member.
if (r !== $AFFECTS && affectsScopes.size) inheritAffectsMarks(s, e[STORE_VALUE]);
if (r !== $AFFECTS && affectsScopes.size) inheritAffectsMarks(s, e[STORE_VALUE], r);
// Node presence bubbles up the wrap chain (sticky), so reconcile can see
// "subscribers live somewhere below" through node-less intermediate
// records — the captured-proxy diff gate (#2902). Amortized O(1): stops at
// the first already-flagged ancestor.
let E = e;
while (E && !E[STORE_DESC]) {
E[STORE_DESC] = true;
E = E[STORE_PARENT];
}
return t[r] = s;

@@ -204,12 +240,13 @@ }

/**
* Scope inheritance for late-created nodes: every live keyless mark whose
* identity scope contains the owning record's raw gets counted on the new
* node. Inherited marks live exactly as long as the scope's carrier — the
* release hook below drops them with the entry.
*/ function inheritAffectsMarks(e, t) {
* Scope inheritance for late-created nodes: every live mark whose identity
* scope contains the owning record's raw — and, for keyed marks, whose key
* is this property — gets counted on the new node. Inherited marks live
* exactly as long as the scope's carrier — the release hook below drops
* them with the entry.
*/ function inheritAffectsMarks(e, t, r) {
// A live scope exists, so affects.ts already installed the mark engine.
for (const [r, n] of affectsScopes) {
if (r.P && n.scope.has(t)) {
for (const [n, o] of affectsScopes) {
if (n.M && o.scope.has(t) && (o.key === undefined || o.key === r)) {
GlobalQueue.D(e);
n.inherited.push(e);
o.inherited.push(e);
}

@@ -246,3 +283,6 @@ }

E = mergedOverlay(i);
n = i[STORE_LOOKUP] ?? n;
// Carry the effective lookup into untouched descendants. Default stores
// use the global lookup just like snapshotImpl; without it, nested raw
// objects fall back to string-only enumeration and symbol branches vanish.
n = i[STORE_LOOKUP] ?? n ?? storeLookup;
}

@@ -255,4 +295,14 @@ if (Array.isArray(s)) {

}
// Arrays can also carry symbol metadata. Enumerate symbols separately to
// avoid scanning large index lists twice. Outside a store tree, retain the
// existing index-only walk.
const O = i || n ? getStoreSymbols(s, E) : [];
for (let e = 0, i = O.length; e < i; e++) {
const i = O[e];
const c = getPropertyDescriptor(s, E, i);
if (!c || c.get) continue;
walkAffectsScope(c.value, t, r, n, o);
}
} else {
const e = getKeys(s, E);
const e = i || n ? getStoreKeys(s, E) : getKeys(s, E);
for (let i = 0, O = e.length; i < O; i++) {

@@ -286,11 +336,11 @@ const O = getPropertyDescriptor(s, E, e[i]);

* @internal
*/ function witnessAffectsMark(e) {
*/ function witnessAffectsMark(e, t) {
// Callers guard on `pendingCheckActive`, which only flips inside
// isPending() — the verdict layer is loaded and its hook installed.
const t = e[STORE_NODE]?.[$AFFECTS];
if (t?.P) GlobalQueue.Zt(t);
const r = e[STORE_NODE]?.[$AFFECTS];
if (r?.M) GlobalQueue.wt(r);
if (affectsScopes.size) {
const r = e[STORE_VALUE];
for (const [e, n] of affectsScopes) {
if (e !== t && e.P && n.scope.has(r)) GlobalQueue.Zt(e);
const n = e[STORE_VALUE];
for (const [e, o] of affectsScopes) {
if (e !== r && e.M && o.scope.has(n) && (o.key === undefined || o.key === t)) GlobalQueue.wt(e);
}

@@ -312,10 +362,10 @@ }

const r = getNodes(e, STORE_NODE);
GlobalQueue.T ||= e => {
const t = affectsScopes.get(e);
if (!t) return;
affectsScopes.delete(e);
for (let e = 0; e < t.inherited.length; e++) GlobalQueue.F(t.inherited[e]);
};
if (t === undefined) {
const t = getNode(e, r, $AFFECTS, undefined, false);
GlobalQueue.T ||= e => {
const t = affectsScopes.get(e);
if (!t) return;
affectsScopes.delete(e);
for (let e = 0; e < t.inherited.length; e++) GlobalQueue.F(t.inherited[e]);
};
let n = affectsScopes.get(t);

@@ -330,7 +380,21 @@ if (!n) affectsScopes.set(t, n = {

}
if (r[t]) return [ r[t] ];
const n = getOverlayLayer(e, t);
const o = n ? n[t] : e[STORE_VALUE][t];
const i = o === $DELETED ? undefined : o;
return [ upsertStoreNode(e, r, t, i, e[STORE_SNAPSHOT_PROPS]) ];
let n = r[t];
if (!n) {
const o = getOverlayLayer(e, t);
const i = o ? o[t] : e[STORE_VALUE][t];
n = upsertStoreNode(e, r, t, i === $DELETED ? undefined : i, e[STORE_SNAPSHOT_PROPS]);
}
// Keyed marks resolve by identity too (#2904): another store family's
// proxy can share this record's raw (a derived store swaps its backing to
// the source's raw when its projection lands), and reads through it never
// touch this target's node map. Scope is exactly the owning record's raw,
// narrowed to this key for witness and birth inheritance.
let o = affectsScopes.get(n);
if (!o) affectsScopes.set(n, o = {
scope: new Set,
inherited: [],
key: t
});
o.scope.add(e[STORE_VALUE]);
return [ n ];
}

@@ -370,13 +434,32 @@

function getKeys(e, t, r = true) {
function getKeysImpl(e, t, r, n) {
// Plain objects can't trigger proxy traps — only pay for the untrack
// closure when the source is itself a wrapped store (store-in-store).
const n = e[$TARGET] ? untrack(() => r ? Object.keys(e) : Reflect.ownKeys(e)) : r ? Object.keys(e) : Reflect.ownKeys(e);
if (!t) return n;
const o = new Set(n);
const i = Reflect.ownKeys(t);
for (const e of i) {
if (t[e] !== $DELETED) o.add(e); else o.delete(e);
const o = e[$TARGET] ? untrack(() => r ? n ? ownEnumerableKeys(e) : Object.keys(e) : Reflect.ownKeys(e)) : r ? n ? ownEnumerableKeysPlain(e) : Object.keys(e) : Reflect.ownKeys(e);
return t ? mergeOverrideKeys(o, t) : o;
}
function getKeys(e, t, r = true) {
return getKeysImpl(e, t, r, false);
}
function getStoreKeys(e, t) {
return getKeysImpl(e, t, true, true);
}
function getStoreSymbols(e, t) {
const r = e[$TARGET] ? untrack(() => ownEnumerableSymbols(e)) : ownEnumerableSymbols(e);
return t ? mergeOverrideKeys(r, t, true) : r;
}
// Shared override-layer merge for key enumeration: adds live override keys,
// drops $DELETED ones. `symbolsOnly` scopes the override scan for the
// array-metadata passes.
function mergeOverrideKeys(e, t, r) {
const n = new Set(e);
const o = r ? Object.getOwnPropertySymbols(t) : Reflect.ownKeys(t);
for (const e of o) {
if (t[e] !== $DELETED) n.add(e); else n.delete(e);
}
return Array.from(o);
return Array.from(n);
}

@@ -441,6 +524,18 @@

if (e[STORE_OPTIMISTIC] && !projectionWriteActive) {
GlobalQueue.Vt(t);
GlobalQueue.mn(t);
}
}
/**
* Records which transition owns an optimistic layer entry (#2899), so a
* settling action only consumes its own keys — the layer is store-wide, but
* concurrent actions writing disjoint keys must revert independently, exactly
* like optimistic signal nodes do via the transition's _optimisticNodes.
* `activeTransition` is the write's transaction (action() opens it before the
* body runs); null marks an ambient write that clears at plain flush end.
* Same-key writes across actions keep last-write-wins layer semantics.
*/ function stampOptimisticOwner(e, t, r) {
if (t === STORE_OPTIMISTIC_OVERRIDE) (e[STORE_OPTIMISTIC_OWNERS] ??= Object.create(null))[r] = activeTransition;
}
function upsertStoreNode(e, t, r, n, o) {

@@ -493,2 +588,15 @@ if (t[r]) return t[r];

/**
* A derived store's seed is a draft for the derive function, never an
* observable value (#2897): until the firewall first resolves there is
* nothing to read, so every consumer path throws NotReady — tracked reads
* through their node (core read()), and the untracked fall-throughs in the
* traps through this guard. Returning the seed leaked it; returning
* `undefined` would break non-nullable types. Callers exempt the firewall
* itself (the derive function works its own draft while uninitialized).
*/ function throwIfUninitialized(e) {
const t = e[STORE_FIREWALL];
if (t && t.i & STATUS_UNINITIALIZED) throw t.k ?? new NotReadyError(t);
}
const storeTraps = {

@@ -499,3 +607,3 @@ get(e, t, r) {

if (t === $REFRESH) return e[STORE_FIREWALL];
if (pendingCheckActive) witnessAffectsMark(e);
if (pendingCheckActive) witnessAffectsMark(e, t);
if (t === $TRACK) {

@@ -515,10 +623,10 @@ trackSelf(e);

const c = !!e[STORE_VALUE][$TARGET];
const f = E ?? e[STORE_VALUE];
const S = E ?? e[STORE_VALUE];
if (!i) {
const n = Object.getOwnPropertyDescriptor(f, t);
const n = Object.getOwnPropertyDescriptor(S, t);
if (n && n.get) return n.get.call(r);
if (!n && !O && e[STORE_CUSTOM_PROTO]) {
const e = unwrapStoreValue(f);
const e = unwrapStoreValue(S);
if (hasInheritedAccessor(e, t)) {
return Reflect.get(f, t, r);
return Reflect.get(S, t, r);
}

@@ -529,3 +637,3 @@ }

if (isPrototypePollutionKey(t) && !hasOwnStoreProperty(e, t)) return undefined;
let r = i && (O || !c) ? visibleNodeValue(i) : f[t];
let r = i && (O || !c) ? visibleNodeValue(i) : S[t];
r === $DELETED && (r = undefined);

@@ -537,17 +645,20 @@ if (!isWrappable(r)) return r;

}
let S = i ? O || !c ? read(o[t]) : (read(o[t]), f[t]) : f[t];
S === $DELETED && (S = undefined);
let f = i ? O || !c ? read(o[t]) : (read(o[t]), S[t]) : S[t];
f === $DELETED && (f = undefined);
if (!i) {
if (!O && typeof S === "function" && !Object.prototype.hasOwnProperty.call(f, t)) {
if (!O && typeof f === "function" && !Object.prototype.hasOwnProperty.call(S, t)) {
let t;
return !Array.isArray(e[STORE_VALUE]) && (t = Object.getPrototypeOf(e[STORE_VALUE])) && t !== Object.prototype ? S.bind(f) : S;
return !Array.isArray(e[STORE_VALUE]) && (t = Object.getPrototypeOf(e[STORE_VALUE])) && t !== Object.prototype ? f.bind(S) : f;
} else if (getObserver() && !n) {
return read(getNode(e, o, t, isWrappable(S) ? wrap(S, e) : S, isEqual, e[STORE_SNAPSHOT_PROPS]));
return read(getNode(e, o, t, isWrappable(f) ? wrap(f, e) : f, isEqual, e[STORE_SNAPSHOT_PROPS]));
}
}
return isWrappable(S) ? wrap(S, e) : S;
// Untracked fall-through (tracked reads already threw via their node in
// read(); the dev strictRead error above wins first for memo parity).
if (!n) throwIfUninitialized(e);
return isWrappable(f) ? wrap(f, e) : f;
},
has(e, t) {
if (t === $PROXY || t === $TRACK || t === "__proto__") return true;
if (pendingCheckActive) witnessAffectsMark(e);
if (pendingCheckActive) witnessAffectsMark(e, t);
const r = getOverlayLayer(e, t);

@@ -567,2 +678,3 @@ const n = r ? r[t] !== $DELETED : t in e[STORE_VALUE];

}
throwIfUninitialized(e);
return n;

@@ -579,25 +691,32 @@ },

const c = E ? E[t] !== $DELETED : t in e[STORE_VALUE];
const f = unwrapStoreValue(r);
const S = unwrapStoreValue(r);
// Symbol-keyed writes on arrays are metadata, not index writes — never run
// them through the numeric index/length machinery (`parseInt` on a symbol
// throws). #2769
const S = typeof t === "string" ? Number(t) : -1;
const R = Array.isArray(s) && Number.isInteger(S) && S >= 0 && S < 4294967295 && String(S) === t;
const T = R ? S + 1 : 0;
const u = R && (getOverlayLayer(e, "length") ?? s).length;
const l = R && T > u ? T : undefined;
if (O === f && l === undefined) return true;
const f = typeof t === "string" ? Number(t) : -1;
const T = Array.isArray(s) && Number.isInteger(f) && f >= 0 && f < 4294967295 && String(f) === t;
const R = T ? f + 1 : 0;
const u = T && (getOverlayLayer(e, "length") ?? s).length;
const l = T && R > u ? R : undefined;
if (O === S && l === undefined) return true;
armOptimisticStoreWrite(e, n);
if (f !== undefined && f === o && l === undefined) delete e[i]?.[t]; else {
if (S !== undefined && S === o && l === undefined) {
delete e[i]?.[t];
if (i === STORE_OPTIMISTIC_OVERRIDE) delete e[STORE_OPTIMISTIC_OWNERS]?.[t];
} else {
const r = e[i] || (e[i] = Object.create(null));
r[t] = f;
if (l !== undefined) r.length = l;
r[t] = S;
stampOptimisticOwner(e, i, t);
if (l !== undefined) {
r.length = l;
stampOptimisticOwner(e, i, "length");
}
}
notifyStoreProperty(e, t, "set", f, O, c);
notifyStoreProperty(e, t, "set", S, O, c);
// Shrinking an array's length must remove the truncated indices, otherwise
// they leak through `has`, `ownKeys`, and (tracked) index reads from the
// underlying value. Mark each as deleted and notify so reactive reads update. #2768
if (Array.isArray(s) && t === "length" && typeof f === "number" && typeof O === "number" && f < O) {
if (Array.isArray(s) && t === "length" && typeof S === "number" && typeof O === "number" && S < O) {
const t = e[i] || (e[i] = Object.create(null));
for (let r = f; r < O; r++) {
for (let r = S; r < O; r++) {
if (t[r] === $DELETED) continue;

@@ -607,2 +726,3 @@ const n = r in t ? t[r] : s[r];

t[r] = $DELETED;
stampOptimisticOwner(e, i, r);
notifyStoreProperty(e, r, "delete", undefined, n, true);

@@ -638,2 +758,3 @@ }

Object.defineProperty(e[i] || (e[i] = Object.create(null)), t, s);
stampOptimisticOwner(e, i, t);
notifyStoreProperty(e, t, "invalidate");

@@ -659,5 +780,7 @@ if (false) ;

(e[n] || (e[n] = Object.create(null)))[t] = $DELETED;
stampOptimisticOwner(e, n, t);
} else if (e[n] && t in e[n]) {
armOptimisticStoreWrite(e, e[$PROXY]);
delete e[n][t];
if (n === STORE_OPTIMISTIC_OVERRIDE) delete e[STORE_OPTIMISTIC_OWNERS]?.[t];
} else return true;

@@ -671,3 +794,11 @@ notifyStoreProperty(e, t, "delete", undefined, i, true);

if (pendingCheckActive) witnessAffectsMark(e);
if (getObserver() !== e[STORE_FIREWALL]) trackSelf(e);
if (getObserver() !== e[STORE_FIREWALL]) {
trackSelf(e);
// trackSelf no-ops untracked, so enumeration of an unresolved derived
// store would otherwise leak the seed's structure (#2897). The write
// path is exempt (like the get/has traps' writeOnly early returns):
// the first landing's reconcile enumerates the store while
// STATUS_UNINITIALIZED is still set — it IS the initialization.
if (!getObserver() && !writeOnly(e[$PROXY])) throwIfUninitialized(e);
}
// Merge optimistic override with regular override for key enumeration

@@ -765,2 +896,2 @@ let t = getKeys(e[STORE_VALUE], e[STORE_OVERRIDE], false);

export { $AFFECTS, $DELETED, $PROXY, $TARGET, $TRACK, STORE_CUSTOM_PROTO, STORE_FIREWALL, STORE_HAS, STORE_LOOKUP, STORE_NODE, STORE_OPTIMISTIC, STORE_OPTIMISTIC_OVERRIDE, STORE_OVERRIDE, STORE_VALUE, STORE_WRAP, createStore, createStoreProxy, getKeys, getOverlayLayer, getPropertyDescriptor, getStoreAffectsNodes, isWrappable, mergedOverlay, notifySelf, ownEnumerableKeys, setWriteOverride, storeLookup, storeSetter, storeTraps, symbolKeyedRecords, trackSelf, visibleNodeValue, witnessAffectsMark, wrap };
export { $AFFECTS, $DELETED, $PROXY, $TARGET, $TRACK, STORE_CUSTOM_PROTO, STORE_DESC, STORE_FIREWALL, STORE_HAS, STORE_LOOKUP, STORE_NODE, STORE_OPTIMISTIC, STORE_OPTIMISTIC_OVERRIDE, STORE_OPTIMISTIC_OWNERS, STORE_OVERRIDE, STORE_PARENT, STORE_VALUE, STORE_WRAP, createStore, createStoreProxy, getKeys, getOverlayLayer, getPropertyDescriptor, getStoreAffectsNodes, getStoreKeys, getStoreSymbols, isWrappable, mergedOverlay, notifySelf, ownEnumerableKeys, setWriteOverride, storeLookup, storeSetter, storeTraps, symbolKeyedRecords, trackSelf, visibleNodeValue, witnessAffectsMark, wrap };

@@ -15,6 +15,6 @@ import { pendingCheckActive } from "../core/core.js";

import { ownEnumerableKeys, $PROXY, isWrappable, $TARGET, trackSelf, witnessAffectsMark, mergedOverlay, STORE_VALUE, storeLookup, STORE_LOOKUP, $DELETED, wrap, getKeys, getPropertyDescriptor, $TRACK } from "./store.js";
import { ownEnumerableKeys, $PROXY, isWrappable, $TARGET, trackSelf, witnessAffectsMark, mergedOverlay, STORE_VALUE, storeLookup, STORE_LOOKUP, $DELETED, wrap, getStoreSymbols, getPropertyDescriptor, getStoreKeys, getKeys, $TRACK } from "./store.js";
function snapshotImpl(e, t, r, n) {
let o, s, c, i, f, u;
let o, s, i, c, f, u;
if (!isWrappable(e)) return e;

@@ -30,5 +30,5 @@ if (r && r.has(e)) return r.get(e);

}
c = mergedOverlay(o);
i = mergedOverlay(o);
s = Array.isArray(o[STORE_VALUE]);
r.set(e, c ? i = s ? [] : Object.create(Object.getPrototypeOf(o[STORE_VALUE])) : o[STORE_VALUE]);
r.set(e, i ? c = s ? [] : Object.create(Object.getPrototypeOf(o[STORE_VALUE])) : o[STORE_VALUE]);
e = o[STORE_VALUE];

@@ -41,22 +41,39 @@ n = o[STORE_LOOKUP] ?? storeLookup;

if (s) {
const s = c?.length ?? e.length;
const s = i?.length ?? e.length;
for (let p = 0; p < s; p++) {
u = c && p in c ? c[p] : e[p];
u = i && p in i ? i[p] : e[p];
if (u === $DELETED) continue;
if (t && isWrappable(u)) wrap(u, o);
if ((f = snapshotImpl(u, t, r, n)) !== u || i) {
if (!i) r.set(e, i = [ ...e ]);
i[p] = f;
if ((f = snapshotImpl(u, t, r, n)) !== u || c) {
if (!c) r.set(e, c = [ ...e ]);
c[p] = f;
}
}
// Enumerate array symbols separately to avoid scanning indices twice.
// Spread copies omit symbols, so assign them after the numeric walk.
const p = n ? getStoreSymbols(e, i) : [];
for (let s = 0, l = p.length; s < l; s++) {
const l = p[s];
const a = getPropertyDescriptor(e, i, l);
if (!a || a.get) continue;
u = i && l in i ? i[l] : e[l];
if (t && isWrappable(u)) wrap(u, o);
f = snapshotImpl(u, t, r, n);
if (f !== u || c) {
if (!c) r.set(e, c = Object.assign([ ...e ], e));
c[l] = f;
}
}
// Deleted trailing slots are skipped above, so restore length to preserve
// holes instead of truncating the copy (#2846) — mirrors unwrapStoreValue.
if (i) i.length = s;
} else if (!c) {
if (c) c.length = s;
} else if (!i) {
// Specialized walk for the common no-overlay case (from #2756): the own
// descriptor gives the value directly, so each property is read once with
// no overlay membership checks.
const s = getKeys(e, undefined);
for (let c = 0, p = s.length; c < p; c++) {
const p = s[c];
// A lookup means this object belongs to an immutable store backing tree,
// even if that nested value has not needed its own proxy yet.
const s = n ? getStoreKeys(e, undefined) : getKeys(e, undefined);
for (let i = 0, p = s.length; i < p; i++) {
const p = s[i];
const l = Object.getOwnPropertyDescriptor(e, p);

@@ -66,28 +83,30 @@ if (l.get) continue;

if (t && isWrappable(u)) wrap(u, o);
if ((f = snapshotImpl(u, t, r, n)) !== u || i) {
if (!i) {
i = Object.create(Object.getPrototypeOf(e));
Object.assign(i, e);
if ((f = snapshotImpl(u, t, r, n)) !== u || c) {
if (!c) {
c = Object.create(Object.getPrototypeOf(e));
Object.assign(c, e);
}
i[p] = f;
c[p] = f;
}
}
} else {
const s = getKeys(e, c);
// An override only exists on a store record, and the target branch above
// always set `lookup` alongside it — so this branch is always store-keyed.
const s = getStoreKeys(e, i);
for (let p = 0, l = s.length; p < l; p++) {
let l = s[p];
const a = getPropertyDescriptor(e, c, l);
const a = getPropertyDescriptor(e, i, l);
if (a.get) continue;
u = l in c ? c[l] : e[l];
u = l in i ? i[l] : e[l];
if (t && isWrappable(u)) wrap(u, o);
if ((f = snapshotImpl(u, t, r, n)) !== e[l] || i) {
if (!i) {
i = Object.create(Object.getPrototypeOf(e));
Object.assign(i, e);
if ((f = snapshotImpl(u, t, r, n)) !== e[l] || c) {
if (!c) {
c = Object.create(Object.getPrototypeOf(e));
Object.assign(c, e);
}
i[l] = f;
c[l] = f;
}
}
}
return i || e;
return c || e;
}

@@ -225,10 +244,10 @@

}
const c = Object.getOwnPropertyNames(t);
for (let r = c.length - 1; r >= 0; r--) {
const i = c[r];
if (i === "__proto__" || i === "constructor") continue;
if (!n[i]) {
const i = Object.getOwnPropertyNames(t);
for (let r = i.length - 1; r >= 0; r--) {
const c = i[r];
if (c === "__proto__" || c === "constructor") continue;
if (!n[c]) {
o = o || e !== s;
const r = Object.getOwnPropertyDescriptor(t, i);
n[i] = r.get ? {
const r = Object.getOwnPropertyDescriptor(t, c);
n[c] = r.get ? {
enumerable: true,

@@ -242,10 +261,10 @@ configurable: true,

if (!o) return r[s];
const c = {};
const i = Object.keys(n);
for (let e = i.length - 1; e >= 0; e--) {
const t = i[e], r = n[t];
if (r.get) Object.defineProperty(c, t, r); else c[t] = r.value;
const i = {};
const c = Object.keys(n);
for (let e = c.length - 1; e >= 0; e--) {
const t = c[e], r = n[t];
if (r.get) Object.defineProperty(i, t, r); else i[t] = r.value;
}
c[$SOURCES] = r;
return c;
i[$SOURCES] = r;
return i;
}

@@ -252,0 +271,0 @@

@@ -7,7 +7,19 @@ /**

*
* Yield promises (or any awaitable) inside the generator — the action waits
* for each before continuing, but the writes you made beforehand are already
* visible (or held by `<Loading>` if optimistic). Yield bare values for
* synchronous batched steps.
* `yield` is the transaction-safe suspension point: the action waits for a
* yielded promise and re-enters the transaction before running the code after
* it. A plain `await` does NOT — the runtime has no hook into an async
* generator's internal await continuations, so writes to fresh signals
* between an `await` and the next `yield` escape the transaction and commit
* immediately. `await` is still the ergonomic choice for typed results; just
* put a bare `yield` before any writes that follow it:
*
* ```ts
* const saved = await api.createTodo(text); // typed result
* yield; // re-enter the transaction before writing
* setTodos(t => { ... });
* ```
*
* (For the same reason, don't call `flush()` inside an action body — it
* drains the transaction mid-step.)
*
* Each call returns a `Promise` that resolves with the generator's return

@@ -22,6 +34,7 @@ * value, or rejects if it throws. Pair with `createOptimistic` /

*
* const addTodo = action(function* (text: string) {
* const addTodo = action(async function* (text: string) {
* const tempId = crypto.randomUUID();
* setTodos(t => { t.push({ id: tempId, text, pending: true }); }); // optimistic
* const saved = yield api.createTodo(text); // network round-trip
* const saved = await api.createTodo(text); // network round-trip, typed
* yield; // re-enter the transaction
* setTodos(t => {

@@ -28,0 +41,0 @@ * const i = t.findIndex(x => x.id === tempId);

@@ -38,2 +38,14 @@ export declare const REACTIVE_NONE = 0;

export declare const NO_SNAPSHOT: {};
/**
* Stand-in stored in `_overrideValue` for an optimistic write of literal
* `undefined` (#2898). The slot doubles as the optimistic-node brand
* (`undefined` = not optimistic, `NOT_PENDING` = at rest), so the raw value
* would erase the node's optimistic identity: the write turns invisible and
* follow-up writes route off the optimistic path and commit permanently.
* Same shape as NO_SNAPSHOT. Sites that surface the override VALUE unwrap
* via `visibleOverrideValue`; slot identity tests stay raw.
*/
export declare const OVERRIDE_UNDEFINED: {};
/** Unwrap an active override's stored value for surfacing to readers (#2898). */
export declare function unwrapOverride<T = any>(v: unknown): T;
export declare const STORE_SNAPSHOT_PROPS = "sp";

@@ -40,0 +52,0 @@ export declare const SUPPORTS_PROXY: boolean;

@@ -51,2 +51,9 @@ import type { Computed, Owner, Signal } from "./types.cjs";

export declare function emitDiagnostic(event: Omit<DiagnosticEvent, "sequence">): DiagnosticEvent;
/**
* Shared strict-read diagnostics for core read() and the store proxy traps.
* Single source for the message text — the #2897 safeguard parity between
* memos and stores is exactly these firing identically from both paths.
*/
export declare function throwPendingUntrackedRead(strictReadLabel: string, fields?: Partial<Omit<DiagnosticEvent, "sequence" | "data">>): never;
export declare function warnStrictReadUntracked(strictReadLabel: string, fields?: Partial<Omit<DiagnosticEvent, "sequence">>): void;
export declare function registerGraph(value: any, owner: Owner | null): void;

@@ -53,0 +60,0 @@ export declare function clearSignals(node: Owner): void;

@@ -40,2 +40,4 @@ import { type QueueCallback, type Transition } from "./scheduler.cjs";

_transition?: Transition | null;
_overrideValue?: any;
_overrideOwner?: Transition | null;
}): Transition | null | undefined;

@@ -42,0 +44,0 @@ /**

@@ -78,11 +78,7 @@ import { type Heap } from "./heap.cjs";

_running: boolean;
_pendingNode: Signal<any> | null;
_pendingNodes: Signal<any>[];
_optimisticNodes: OptimisticNode[];
_affectsNodes: OptimisticNode[];
_optimisticStores: Set<any>;
_batch: Transition;
static _update: (el: Computed<unknown>) => void;
static _dispose: (el: Computed<unknown>, self: boolean, zombie: boolean) => void;
static _runEffect: (el: Computed<unknown>) => void;
static _clearOptimisticStores: ((stores: Set<any>) => void) | null;
static _clearOptimisticStores: ((stores: Set<any>, completing: Transition | null) => void) | null;
static _releaseAffectsScope: ((node: OptimisticNode) => void) | null;

@@ -89,0 +85,0 @@ static _applyAffectsReads: ((el: Computed<any>, sources: (Signal<any> | Computed<any>)[]) => void) | null;

@@ -47,2 +47,11 @@ import type { NOT_PENDING } from "./constants.cjs";

_overrideValue?: T | typeof NOT_PENDING;
/**
* The transaction that owns the active override (stamped at optimistic
* write, cleared at settle). Ownership must live on the node: a lane's
* _transition is a scheduling affinity that a shared subscriber can merge
* across transactions (#2912) — following it would let one action's settle
* revert another action's live override. Node-level sibling of the store
* layer's STORE_OPTIMISTIC_OWNERS stamps (#2899). `null` = ambient write.
*/
_overrideOwner?: Transition | null;
_optimisticLane?: OptimisticLane;

@@ -97,3 +106,2 @@ _pendingSignal?: Signal<boolean>;

_blocked?: boolean;
_pendingSource?: Computed<any>;
_pendingSources?: Set<Computed<any>>;

@@ -100,0 +108,0 @@ _error?: unknown;

import { STORE_SNAPSHOT_PROPS, type Computed, type Refreshable, type Signal } from "../core/index.cjs";
import { type Transition } from "../core/scheduler.cjs";
/** A read-only view of a store's value as seen by consumers. Mutate it via the paired `StoreSetter`. */

@@ -44,3 +45,3 @@ export type Store<T> = Readonly<T>;

export declare const $TRACK: unique symbol, $TARGET: unique symbol, $PROXY: unique symbol, $DELETED: unique symbol, $AFFECTS: unique symbol;
export declare const STORE_VALUE = "v", STORE_OVERRIDE = "o", STORE_OPTIMISTIC_OVERRIDE = "x", STORE_NODE = "n", STORE_HAS = "h", STORE_CUSTOM_PROTO = "c", STORE_WRAP = "w", STORE_LOOKUP = "l", STORE_FIREWALL = "f", STORE_OPTIMISTIC = "p";
export declare const STORE_VALUE = "v", STORE_OVERRIDE = "o", STORE_OPTIMISTIC_OVERRIDE = "x", STORE_NODE = "n", STORE_HAS = "h", STORE_CUSTOM_PROTO = "c", STORE_WRAP = "w", STORE_LOOKUP = "l", STORE_FIREWALL = "f", STORE_OPTIMISTIC = "p", STORE_OPTIMISTIC_OWNERS = "t", STORE_PARENT = "u", STORE_DESC = "d";
export type StoreNode = {

@@ -51,2 +52,3 @@ [$PROXY]: any;

[STORE_OPTIMISTIC_OVERRIDE]?: Record<PropertyKey, any>;
[STORE_OPTIMISTIC_OWNERS]?: Record<PropertyKey, Transition | null>;
[STORE_NODE]?: DataNodes;

@@ -60,2 +62,4 @@ [STORE_HAS]?: DataNodes;

[STORE_SNAPSHOT_PROPS]?: Record<PropertyKey, any>;
[STORE_PARENT]?: StoreNode;
[STORE_DESC]?: boolean;
};

@@ -97,3 +101,3 @@ export declare namespace SolidStore {

*/
export declare function witnessAffectsMark(target: StoreNode): void;
export declare function witnessAffectsMark(target: StoreNode, property?: PropertyKey): void;
/**

@@ -121,2 +125,4 @@ * Resolves the store nodes an `affects()` declaration marks: with a `key`,

export declare function getKeys(source: Record<PropertyKey, any>, override: Record<PropertyKey, any> | undefined, enumerable?: boolean): PropertyKey[];
export declare function getStoreKeys(source: Record<PropertyKey, any>, override: Record<PropertyKey, any> | undefined): PropertyKey[];
export declare function getStoreSymbols(source: Record<PropertyKey, any>, override: Record<PropertyKey, any> | undefined): symbol[];
export declare function getPropertyDescriptor(source: Record<PropertyKey, any>, override: Record<PropertyKey, any> | undefined, property: PropertyKey): PropertyDescriptor | undefined;

@@ -123,0 +129,0 @@ export declare const storeTraps: ProxyHandler<StoreNode>;

@@ -7,7 +7,19 @@ /**

*
* Yield promises (or any awaitable) inside the generator — the action waits
* for each before continuing, but the writes you made beforehand are already
* visible (or held by `<Loading>` if optimistic). Yield bare values for
* synchronous batched steps.
* `yield` is the transaction-safe suspension point: the action waits for a
* yielded promise and re-enters the transaction before running the code after
* it. A plain `await` does NOT — the runtime has no hook into an async
* generator's internal await continuations, so writes to fresh signals
* between an `await` and the next `yield` escape the transaction and commit
* immediately. `await` is still the ergonomic choice for typed results; just
* put a bare `yield` before any writes that follow it:
*
* ```ts
* const saved = await api.createTodo(text); // typed result
* yield; // re-enter the transaction before writing
* setTodos(t => { ... });
* ```
*
* (For the same reason, don't call `flush()` inside an action body — it
* drains the transaction mid-step.)
*
* Each call returns a `Promise` that resolves with the generator's return

@@ -22,6 +34,7 @@ * value, or rejects if it throws. Pair with `createOptimistic` /

*
* const addTodo = action(function* (text: string) {
* const addTodo = action(async function* (text: string) {
* const tempId = crypto.randomUUID();
* setTodos(t => { t.push({ id: tempId, text, pending: true }); }); // optimistic
* const saved = yield api.createTodo(text); // network round-trip
* const saved = await api.createTodo(text); // network round-trip, typed
* yield; // re-enter the transaction
* setTodos(t => {

@@ -28,0 +41,0 @@ * const i = t.findIndex(x => x.id === tempId);

@@ -38,2 +38,14 @@ export declare const REACTIVE_NONE = 0;

export declare const NO_SNAPSHOT: {};
/**
* Stand-in stored in `_overrideValue` for an optimistic write of literal
* `undefined` (#2898). The slot doubles as the optimistic-node brand
* (`undefined` = not optimistic, `NOT_PENDING` = at rest), so the raw value
* would erase the node's optimistic identity: the write turns invisible and
* follow-up writes route off the optimistic path and commit permanently.
* Same shape as NO_SNAPSHOT. Sites that surface the override VALUE unwrap
* via `visibleOverrideValue`; slot identity tests stay raw.
*/
export declare const OVERRIDE_UNDEFINED: {};
/** Unwrap an active override's stored value for surfacing to readers (#2898). */
export declare function unwrapOverride<T = any>(v: unknown): T;
export declare const STORE_SNAPSHOT_PROPS = "sp";

@@ -40,0 +52,0 @@ export declare const SUPPORTS_PROXY: boolean;

@@ -51,2 +51,9 @@ import type { Computed, Owner, Signal } from "./types.js";

export declare function emitDiagnostic(event: Omit<DiagnosticEvent, "sequence">): DiagnosticEvent;
/**
* Shared strict-read diagnostics for core read() and the store proxy traps.
* Single source for the message text — the #2897 safeguard parity between
* memos and stores is exactly these firing identically from both paths.
*/
export declare function throwPendingUntrackedRead(strictReadLabel: string, fields?: Partial<Omit<DiagnosticEvent, "sequence" | "data">>): never;
export declare function warnStrictReadUntracked(strictReadLabel: string, fields?: Partial<Omit<DiagnosticEvent, "sequence">>): void;
export declare function registerGraph(value: any, owner: Owner | null): void;

@@ -53,0 +60,0 @@ export declare function clearSignals(node: Owner): void;

@@ -40,2 +40,4 @@ import { type QueueCallback, type Transition } from "./scheduler.js";

_transition?: Transition | null;
_overrideValue?: any;
_overrideOwner?: Transition | null;
}): Transition | null | undefined;

@@ -42,0 +44,0 @@ /**

@@ -78,11 +78,7 @@ import { type Heap } from "./heap.js";

_running: boolean;
_pendingNode: Signal<any> | null;
_pendingNodes: Signal<any>[];
_optimisticNodes: OptimisticNode[];
_affectsNodes: OptimisticNode[];
_optimisticStores: Set<any>;
_batch: Transition;
static _update: (el: Computed<unknown>) => void;
static _dispose: (el: Computed<unknown>, self: boolean, zombie: boolean) => void;
static _runEffect: (el: Computed<unknown>) => void;
static _clearOptimisticStores: ((stores: Set<any>) => void) | null;
static _clearOptimisticStores: ((stores: Set<any>, completing: Transition | null) => void) | null;
static _releaseAffectsScope: ((node: OptimisticNode) => void) | null;

@@ -89,0 +85,0 @@ static _applyAffectsReads: ((el: Computed<any>, sources: (Signal<any> | Computed<any>)[]) => void) | null;

@@ -47,2 +47,11 @@ import type { NOT_PENDING } from "./constants.js";

_overrideValue?: T | typeof NOT_PENDING;
/**
* The transaction that owns the active override (stamped at optimistic
* write, cleared at settle). Ownership must live on the node: a lane's
* _transition is a scheduling affinity that a shared subscriber can merge
* across transactions (#2912) — following it would let one action's settle
* revert another action's live override. Node-level sibling of the store
* layer's STORE_OPTIMISTIC_OWNERS stamps (#2899). `null` = ambient write.
*/
_overrideOwner?: Transition | null;
_optimisticLane?: OptimisticLane;

@@ -97,3 +106,2 @@ _pendingSignal?: Signal<boolean>;

_blocked?: boolean;
_pendingSource?: Computed<any>;
_pendingSources?: Set<Computed<any>>;

@@ -100,0 +108,0 @@ _error?: unknown;

import { STORE_SNAPSHOT_PROPS, type Computed, type Refreshable, type Signal } from "../core/index.js";
import { type Transition } from "../core/scheduler.js";
/** A read-only view of a store's value as seen by consumers. Mutate it via the paired `StoreSetter`. */

@@ -44,3 +45,3 @@ export type Store<T> = Readonly<T>;

export declare const $TRACK: unique symbol, $TARGET: unique symbol, $PROXY: unique symbol, $DELETED: unique symbol, $AFFECTS: unique symbol;
export declare const STORE_VALUE = "v", STORE_OVERRIDE = "o", STORE_OPTIMISTIC_OVERRIDE = "x", STORE_NODE = "n", STORE_HAS = "h", STORE_CUSTOM_PROTO = "c", STORE_WRAP = "w", STORE_LOOKUP = "l", STORE_FIREWALL = "f", STORE_OPTIMISTIC = "p";
export declare const STORE_VALUE = "v", STORE_OVERRIDE = "o", STORE_OPTIMISTIC_OVERRIDE = "x", STORE_NODE = "n", STORE_HAS = "h", STORE_CUSTOM_PROTO = "c", STORE_WRAP = "w", STORE_LOOKUP = "l", STORE_FIREWALL = "f", STORE_OPTIMISTIC = "p", STORE_OPTIMISTIC_OWNERS = "t", STORE_PARENT = "u", STORE_DESC = "d";
export type StoreNode = {

@@ -51,2 +52,3 @@ [$PROXY]: any;

[STORE_OPTIMISTIC_OVERRIDE]?: Record<PropertyKey, any>;
[STORE_OPTIMISTIC_OWNERS]?: Record<PropertyKey, Transition | null>;
[STORE_NODE]?: DataNodes;

@@ -60,2 +62,4 @@ [STORE_HAS]?: DataNodes;

[STORE_SNAPSHOT_PROPS]?: Record<PropertyKey, any>;
[STORE_PARENT]?: StoreNode;
[STORE_DESC]?: boolean;
};

@@ -97,3 +101,3 @@ export declare namespace SolidStore {

*/
export declare function witnessAffectsMark(target: StoreNode): void;
export declare function witnessAffectsMark(target: StoreNode, property?: PropertyKey): void;
/**

@@ -121,2 +125,4 @@ * Resolves the store nodes an `affects()` declaration marks: with a `key`,

export declare function getKeys(source: Record<PropertyKey, any>, override: Record<PropertyKey, any> | undefined, enumerable?: boolean): PropertyKey[];
export declare function getStoreKeys(source: Record<PropertyKey, any>, override: Record<PropertyKey, any> | undefined): PropertyKey[];
export declare function getStoreSymbols(source: Record<PropertyKey, any>, override: Record<PropertyKey, any> | undefined): symbol[];
export declare function getPropertyDescriptor(source: Record<PropertyKey, any>, override: Record<PropertyKey, any> | undefined, property: PropertyKey): PropertyDescriptor | undefined;

@@ -123,0 +129,0 @@ export declare const storeTraps: ProxyHandler<StoreNode>;

{
"name": "@solidjs/signals",
"version": "2.0.0-beta.19",
"version": "2.0.0-beta.20",
"description": "Solid's reactive primitives: signals, memos, effects, stores, and async-aware computations.",

@@ -5,0 +5,0 @@ "author": "Ryan Carniato",

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display