🎩 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.23
to
2.0.0-beta.24
+14
-11
dist/prod/core/core.js

@@ -85,3 +85,3 @@ import { handleAsync, clearStatus, notifyStatus } from "./async.js";

e.se |= REACTIVE_DIRTY;
if (dirtyQueue.Fe > e.He) dirtyQueue.Fe = e.He;
if (dirtyQueue.He > e.Fe) dirtyQueue.He = e.Fe;
insertIntoHeap(e, dirtyQueue);

@@ -99,3 +99,6 @@ }

delete e.Ve;
delete e[STORE_SNAPSHOT_PROPS];
// StoreNode targets share one pre-initialized hidden class (see
// createStoreProxy) — assign undefined instead of deleting, and only
// when present so signal-node sources don't grow the field.
if (e[STORE_SNAPSHOT_PROPS] !== undefined) e[STORE_SNAPSHOT_PROPS] = undefined;
}

@@ -136,3 +139,3 @@ snapshotSources = null;

let a = e.Ne === NOT_PENDING ? e.Pe : e.Ne;
let r = e.He;
let r = e.Fe;
let c = tracking;

@@ -258,3 +261,3 @@ let _ = currentOptimisticLane;

e.Ne = a;
} else if (e.He != r) {
} else if (e.Fe != r) {
for (let t = e.o; t !== null; t = t.ue) {

@@ -308,3 +311,3 @@ insertIntoHeapHeight(t.le, queueFor(t.le));

Pe: undefined,
He: 0,
Fe: 0,
u: null,

@@ -354,3 +357,3 @@ lt: undefined,

Pe: undefined,
He: 0,
Fe: 0,
u: null,

@@ -406,3 +409,3 @@ lt: undefined,

}
if (n) e.He = n.He + 1;
if (n) e.Fe = n.Fe + 1;
if (GlobalQueue.Tt !== null) GlobalQueue.Tt(e);

@@ -530,3 +533,3 @@ !t?.lazy && recompute(e, true);

const n = queueFor(e);
if (l.He >= n.Fe) {
if (l.Fe >= n.He) {
markNode(t);

@@ -536,6 +539,6 @@ markHeap(n);

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

@@ -542,0 +545,0 @@ }

@@ -28,3 +28,12 @@ /**

constructor(r) {
// Control-flow throw: it happens on every read of a pending source, so in
// production skip V8's eager stack capture (proportional to stack depth —
// real cost under SSR) by zeroing the V8-specific stackTraceLimit around
// super(). Dev keeps the stack for debuggability; non-V8 engines (no
// stackTraceLimit) take the plain path.
const o = Error;
const t = o.stackTraceLimit;
if (t !== undefined) o.stackTraceLimit = 0;
super();
if (t !== undefined) o.stackTraceLimit = t;
this.source = r;

@@ -31,0 +40,0 @@ }

@@ -23,3 +23,3 @@ 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";

const E = queueFor(e);
if (E.Fe > e.He) E.Fe = e.He;
if (E.He > e.Fe) E.He = e.Fe;
insertIntoHeap(e, E);

@@ -29,5 +29,5 @@ }

function actualInsertIntoHeap(e, E) {
const t = (e.Ge?.ft ? e.Ge.Nt?.He : e.Ge?.He) ?? -1;
if (t >= e.He) e.He = t + 1;
const n = e.He;
const t = (e.Ge?.ft ? e.Ge.Nt?.Fe : e.Ge?.Fe) ?? -1;
if (t >= e.Fe) e.Fe = t + 1;
const n = e.Fe;
const I = E.eE[n];

@@ -71,3 +71,3 @@ if (I === undefined) E.eE[n] = e; else {

e.se = t & -25;
const n = e.He;
const n = e.Fe;
if (e.ut === e) E.eE[n] = undefined; else {

@@ -112,7 +112,7 @@ const t = e.lt;

e.tE = false;
for (e.Fe = 0; e.Fe <= e.EE; e.Fe++) {
let t = e.eE[e.Fe];
for (e.He = 0; e.He <= e.EE; e.He++) {
let t = e.eE[e.He];
while (t !== undefined) {
if (t.se & REACTIVE_IN_HEAP) E(t); else adjustHeight(t, e);
t = e.eE[e.Fe];
t = e.eE[e.He];
}

@@ -125,10 +125,10 @@ }

deleteFromHeap(e, E);
let t = e.He;
let t = e.Fe;
for (let E = e.Xe; E; E = E.et) {
const e = E.tt;
const n = e.nt || e;
if (n.ke && n.He >= t) t = n.He + 1;
if (n.ke && n.Fe >= t) t = n.Fe + 1;
}
if (e.He !== t) {
e.He = t;
if (e.Fe !== t) {
e.Fe = t;
for (let E = e.o; E !== null; E = E.ue) {

@@ -135,0 +135,0 @@ // Route each subscriber by its own zombie flag, mirroring the

@@ -22,3 +22,3 @@ 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";

tE: false,
Fe: 0,
He: 0,
EE: 0

@@ -30,3 +30,3 @@ };

tE: false,
Fe: 0,
He: 0,
EE: 0

@@ -339,3 +339,3 @@ };

// flush path instead of leaving them until the next natural flush.
scheduled = dirtyQueue.EE >= dirtyQueue.Fe || this.m.Qt.length > 0;
scheduled = dirtyQueue.EE >= dirtyQueue.He || this.m.Qt.length > 0;
reassignPendingTransition(e.Qt);

@@ -368,3 +368,3 @@ activeTransition = null;

commitPendingNodes();
if (dirtyQueue.EE >= dirtyQueue.Fe) {
if (dirtyQueue.EE >= dirtyQueue.He) {
runHeap(dirtyQueue, GlobalQueue.pe);

@@ -380,3 +380,3 @@ commitPendingNodes();

// Check if finalization added items to the heap (from optimistic reversion)
scheduled = dirtyQueue.EE >= dirtyQueue.Fe;
scheduled = dirtyQueue.EE >= dirtyQueue.He;
// Run lane effects first (for ready lanes), then regular effects

@@ -535,3 +535,3 @@ activeLanes.size && GlobalQueue.In(EFFECT_RENDER);

if (!t && globalQueue.vt.length) checkBoundaryChildren(globalQueue);
const n = dirtyQueue.EE >= dirtyQueue.Fe;
const n = dirtyQueue.EE >= dirtyQueue.He;
if (n) runHeap(dirtyQueue, GlobalQueue.pe);

@@ -538,0 +538,0 @@ if (i) {

@@ -235,3 +235,3 @@ import { NOT_PENDING, REACTIVE_DISPOSED, REACTIVE_DIRTY, REACTIVE_CHECK, unwrapOverride, REACTIVE_ZOMBIE, STATUS_UNINITIALIZED, STATUS_PENDING, REACTIVE_MANUAL_WRITE, STATUS_ERROR, REACTIVE_RECOMPUTING_DEPS } from "./constants.js";

const e = queueFor(t);
if (t.He >= e.Fe && !(t.se & (REACTIVE_DISPOSED | REACTIVE_ZOMBIE))) {
if (t.Fe >= e.He && !(t.se & (REACTIVE_DISPOSED | REACTIVE_ZOMBIE))) {
markHeap(e);

@@ -238,0 +238,0 @@ prepareComputed(t, true);

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

} else {
let o, c, a, f, u, p, w, l, d, O = new Array(s), m = new Array(s);
r = this.Ut ? new Array(s) : undefined;
n = this.$t ? new Array(s) : undefined;
let o, c, a, f, u, p, w, l, d;
// skip common prefix

@@ -129,11 +127,20 @@ for (o = 0, c = Math.min(this.jt, s); o < c && (this.Mt[o] === t[o] || this.Ut && compare(this.Gt, this.Mt[o], t[o])); o++) {

}
// common suffix
// skip common suffix — counted only; retained entries land in one pass
// at commit instead of being staged and copied twice
for (c = this.jt - 1, a = s - 1; c >= o && a >= o && (this.Mt[c] === t[a] || this.Ut && compare(this.Gt, this.Mt[c], t[a])); c--,
a--) {
O[a] = this.xt[c];
m[a] = this.Ft[c];
r && (r[a] = this.Ut[c]);
n && (n[a] = this.$t[c]);
a--) ;
// no structural change (every position matched in place at equal
// length — the common post-reconcile shape): keep the same mapped
// array identity so downstream consumers don't re-run at all
if (o === s && this.jt === s) {
this.Mt = t.slice(0);
return;
}
// 0) prepare a map of all indices in newItems, scanning backwards so we encounter them in natural order
const O = s - this.jt;
const m = new Array(s);
const _ = new Array(s);
r = this.Ut ? new Array(s) : undefined;
n = this.$t ? new Array(s) : undefined;
// 0) prepare a map of all indices in the changed window of newItems,
// scanning backwards so we encounter them in natural order
p = new Map;

@@ -148,3 +155,5 @@ w = new Array(a + 1);

}
// 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
// 1) step through the old changed window and see if items can be found
// in the new set; if so, stage them at their new positions; if not,
// queue them for disposal at commit
for (i = o; i <= c; i++) {

@@ -155,4 +164,4 @@ f = this.Mt[i];

if (e !== undefined && e !== -1) {
O[e] = this.xt[i];
m[e] = this.Ft[i];
m[e] = this.xt[i];
_[e] = this.Ft[i];
r && (r[e] = this.Ut[i]);

@@ -166,6 +175,6 @@ n && (n[e] = this.$t[i]);

try {
for (e = o; e < s; e++) {
if (e in O) continue;
(d ??= []).push(m[e] = createOwner());
O[e] = runWithOwner(m[e], h);
for (e = o; e <= a; e++) {
if (_[e] !== undefined) continue;
(d ??= []).push(_[e] = createOwner());
m[e] = runWithOwner(_[e], h);
}

@@ -176,20 +185,35 @@ } catch (t) {

}
// 3) commit: land positions, then dispose exited rows
for (e = o; e < s; e++) {
this.xt[e] = O[e];
this.Ft[e] = m[e];
// 3) commit: land the retained prefix and suffix plus the staged window
// into the fresh arrays, swap them in (new identity for downstream
// change propagation), then dispose exited rows
for (i = 0; i < o; i++) {
m[i] = this.xt[i];
_[i] = this.Ft[i];
r && (r[i] = this.Ut[i]);
n && (n[i] = this.$t[i]);
}
for (e = o; e <= a; e++) {
if (r) setSignal(r[e], t[e]);
if (n) setSignal(n[e], e);
}
for (e = a + 1; e < s; e++) {
m[e] = this.xt[e - O];
_[e] = this.Ft[e - O];
if (r) {
this.Ut[e] = r[e];
setSignal(this.Ut[e], t[e]);
r[e] = this.Ut[e - O];
setSignal(r[e], t[e]);
}
if (n) {
this.$t[e] = n[e];
setSignal(this.$t[e], e);
n[e] = this.$t[e - O];
if (O !== 0) setSignal(n[e], e);
}
}
this.xt = m;
this.Ft = _;
r && (this.Ut = r);
n && (this.$t = n);
this.jt = s;
// save a copy of the mapped items for the next update
this.Mt = t.slice(0);
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.xt = this.xt.slice(0, this.jt = s);
// 5) save a copy of the mapped items for the next update
this.Mt = t.slice(0);
}

@@ -196,0 +220,0 @@ });

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

function createOptimisticStore(e, t, r) {
function createOptimisticStore(e, t, i) {
// Register clear function with scheduler; store nodes marked

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

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

@@ -40,4 +40,4 @@ }

function clearOptimisticStores(e, t) {
for (const r of e) {
const e = r[$TARGET];
for (const i of e) {
const e = i[$TARGET];
if (e?.[STORE_OPTIMISTIC_OVERRIDE]) clearOptimisticOverride(e, t);

@@ -60,5 +60,5 @@ }

*/ function clearOptimisticOverride(e, t) {
const r = e[STORE_OPTIMISTIC_OVERRIDE];
if (!r) return;
const i = e[STORE_NODE];
const i = e[STORE_OPTIMISTIC_OVERRIDE];
if (!i) return;
const r = e[STORE_NODE];
const o = e[STORE_OPTIMISTIC_OWNERS];

@@ -73,3 +73,3 @@ const n = t !== undefined;

try {
for (const O of Reflect.ownKeys(r)) {
for (const O of Reflect.ownKeys(i)) {
if (n) {

@@ -91,6 +91,6 @@ let e = o?.[O] ?? null;

}
delete r[O];
delete i[O];
if (o) delete o[O];
c = true;
const T = i?.[O];
const T = r?.[O];
if (T) {

@@ -102,5 +102,5 @@ // Clear lane association so effects go to regular queue

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 i = t ? t[O] : e[STORE_VALUE][O];
const r = i === $DELETED ? undefined : i;
const o = isWrappable(r) ? wrap(r, e) : r;
const n = visibleNodeValue(T);

@@ -118,8 +118,10 @@ T.Ee = NOT_PENDING;

if (!s) {
delete e[STORE_OPTIMISTIC_OVERRIDE];
delete e[STORE_OPTIMISTIC_OWNERS];
// Assignment, not delete: StoreNode targets share one pre-initialized
// hidden class (see createStoreProxy) and a delete would demote it.
e[STORE_OPTIMISTIC_OVERRIDE] = undefined;
e[STORE_OPTIMISTIC_OWNERS] = undefined;
}
// Notify $TRACK
if (c && i?.[$TRACK]) {
i[$TRACK].je = undefined;
if (c && r?.[$TRACK]) {
r[$TRACK].je = undefined;
notifySelf(e);

@@ -132,4 +134,4 @@ }

function createOptimisticProjectionInternal(e, t, r) {
let i;
function createOptimisticProjectionInternal(e, t, i) {
let r;
const o = new WeakMap;

@@ -143,3 +145,3 @@ const wrapper = e => {

get() {
return i;
return r;
},

@@ -178,6 +180,6 @@ configurable: true

};
i = computed(() => {
r = computed(() => {
setProjectionWriteActive(true);
try {
runProjectionComputed(n, e, r?.key || "id", wrapCommit, clearProjectionOverride);
runProjectionComputed(n, e, i?.key || "id", wrapCommit, clearProjectionOverride);
} finally {

@@ -187,7 +189,7 @@ setProjectionWriteActive(false);

}, undefined);
i.T &= ~CONFIG_AUTO_DISPOSE;
r.T &= ~CONFIG_AUTO_DISPOSE;
}
return {
store: n,
node: i
node: r
};

@@ -194,0 +196,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_DESC, STORE_HAS, getKeys, $DELETED, symbolKeyedRecords, getStoreSymbols } 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, symbolKeyedRecords, getKeys, $DELETED, getStoreSymbols } from "./store.js";

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

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);
const y = unwrap(t[f]);
if (c === y || !isWrappable(y) || Array.isArray(c) !== Array.isArray(y) || a(c) != null && a(c) !== a(y)) continue;
applyState(y, wrap(c, r), a);
}

@@ -196,3 +196,3 @@ }

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

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

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

@@ -267,13 +267,14 @@ }

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];
const f = unwrap(n[s]);
let c = unwrap(e[s]);
if (f === c) continue;
if (!f || !isWrappable(f) || !isWrappable(c) || Array.isArray(f) !== Array.isArray(c) || r(f) != null && r(f) !== r(c)) {
o && setSignal(o, void 0);
p && setSignal(p, isWrappable(c) ? wrap(c, t) : c);
} else applyState(c, wrap(f, t), r);
if (o || symbolKeyedRecords.has(i)) {
const a = o ? getAllKeys(n, undefined, e) : nodeKeys(i);
for (let l = 0, s = a.length; l < s; l++) {
diffNodeKey(a[l], i, n, e, t, o, r);
}
} else {
// Untracked, string-only node records (the overwhelmingly common case)
// iterate in place — nodeKeys() allocated a fresh key array per object
// per pass, which dominates allocation on large-graph reconciles.
for (const a in i) {
diffNodeKey(a, i, n, e, t, o, r);
}
}

@@ -292,2 +293,15 @@ }

// One node-key step of the fast object diff — shared by the array-iterating
// (tracked / symbol-keyed) and for-in (plain) loops in applyStateFast.
function diffNodeKey(e, t, r, n, a, i, o) {
const l = t[e];
const s = unwrap(r[e]);
let p = unwrap(n[e]);
if (s === p) return;
if (!s || !isWrappable(s) || !isWrappable(p) || Array.isArray(s) !== Array.isArray(p) || o(s) != null && o(s) !== o(p)) {
i && setSignal(i, void 0);
l && setSignal(l, isWrappable(p) ? wrap(p, a) : p);
} else applyState(p, wrap(s, a), o);
}
function applyStateSlow(e, t, r) {

@@ -307,13 +321,13 @@ const n = t[STORE_VALUE];

if (e.length && s && isWrappable(e[0]) && r(e[0]) != null) {
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);
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);
}
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;
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;
}
if (c > S || c > u) {
for (f = c; f <= S; f++) {
if (c > y || c > u) {
for (f = c; f <= y; f++) {
l = true;

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

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

@@ -333,23 +347,23 @@ const n = e.length;

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

@@ -385,8 +399,8 @@ }

const u = unwrap(getOverrideValue(n, a, f, i));
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)) {
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)) {
l && setSignal(l, void 0);
c && setSignal(c, isWrappable(S) ? wrap(S, t) : S);
} else applyState(S, wrap(u, t), r);
c && setSignal(c, isWrappable(y) ? wrap(y, t) : y);
} else applyState(y, wrap(u, t), r);
}

@@ -405,13 +419,23 @@ }

// No-key reconcile: every item reports "no key", which routes array diffs to
// the positional branch and object descent to plain per-property merging.
const NOKEY = () => null;
/**
* Returns a draft-mutating function that smart-merges `value` into a store,
* preserving the identity of items whose `key` field matches between old and
* new states. Useful when applying server payloads or full-replacement data
* onto an existing store without losing fine-grained reactivity.
* preserving fine-grained reactivity: only changed leaves trigger updates.
*
* Items with the same key are updated in place (only changed properties
* trigger updates). Items added or removed update the corresponding signals.
* With a `key` (default `"id"`), array items whose key matches between old
* and new states keep their identity (updated in place, moves and removals
* update the corresponding signals) — the shape for keyed server payloads.
* Items without the key field fall back to positional matching.
*
* With `key: null`, matching is purely positional: index N of the new array
* merges into index N of the old, and object properties merge recursively —
* the classic pattern for fixed-shape data that churns in place (dashboards,
* monitors), where no keyed diff pass is needed or wanted.
*
* @param value the next state to merge in
* @param key property name (string) or extractor function for stable identity
* @param key property name (string) or extractor function for stable
* identity (default `"id"`); pass `null` for positional merging
*

@@ -424,8 +448,15 @@ * @example

* const fresh = await api.getTodos();
* setTodos(reconcile(fresh, "id")); // diff-merge by `id`
* setTodos(reconcile(fresh)); // diff-merge by `id`
* }
*
* // fixed-shape polling data — positional merge
* setStats(reconcile(nextStats, null));
* ```
*/ function reconcile(e, t) {
*/ function reconcile(e, t = "id") {
return r => {
if (r == null) throw new Error("");
if (t === null) {
applyState(e, r, NOKEY);
return;
}
const n = typeof t === "string" ? e => e[t] : t;

@@ -432,0 +463,0 @@ const a = n(r);

@@ -35,2 +35,25 @@ import { $REFRESH, STORE_SNAPSHOT_PROPS, NOT_PENDING, unwrapOverride, STATUS_UNINITIALIZED, STATUS_PENDING, NO_SNAPSHOT } from "../core/constants.js";

// Every StoreNode field is initialized up front, in one fixed order, so all
// targets share a single hidden class. The traps and reconcile read these
// fields on their hottest paths; fields added lazily in varying orders made
// those loads megamorphic across a large store graph (dictionary-mode probes
// on every trap hit). Assignments elsewhere must never `delete` a field —
// write `undefined` instead, or the shape degrades again.
function initStoreFields(e) {
e[STORE_OVERRIDE] = undefined;
e[STORE_OPTIMISTIC_OVERRIDE] = undefined;
e[STORE_OPTIMISTIC_OWNERS] = undefined;
e[STORE_NODE] = undefined;
e[STORE_HAS] = undefined;
e[STORE_CUSTOM_PROTO] = undefined;
e[STORE_WRAP] = undefined;
e[STORE_LOOKUP] = undefined;
e[STORE_FIREWALL] = undefined;
e[STORE_OPTIMISTIC] = undefined;
e[STORE_SNAPSHOT_PROPS] = undefined;
e[STORE_PARENT] = undefined;
e[STORE_DESC] = undefined;
e[$PROXY] = null;
}
function createStoreProxy(e, t = storeTraps, r) {

@@ -40,7 +63,9 @@ let n;

n = [];
n.v = e;
n[STORE_VALUE] = e;
initStoreFields(n);
} else {
n = {
v: e
[STORE_VALUE]: e
};
initStoreFields(n);
const t = e?.[$TARGET]?.[STORE_VALUE] ?? e;

@@ -102,13 +127,13 @@ const r = Object.getPrototypeOf(t);

const i = n[STORE_VALUE];
const s = Array.isArray(i);
const E = s ? [] : Object.create(Object.getPrototypeOf(i));
t.set(e, E);
const E = Array.isArray(i);
const O = E ? [] : Object.create(Object.getPrototypeOf(i));
t.set(e, O);
r = n[STORE_LOOKUP] ?? storeLookup;
for (const e of getStoreKeys(i, o)) {
if (s && e === "length") continue;
if (E && e === "length") continue;
const n = e in o ? o[e] : i[e];
if (n !== $DELETED) E[e] = unwrapStoreValue(n, t, r);
if (n !== $DELETED) O[e] = unwrapStoreValue(n, t, r);
}
if (s) E.length = o.length ?? i.length;
return E;
if (E) O.length = o.length ?? i.length;
return O;
}

@@ -188,6 +213,6 @@

if (t[r]) return t[r];
const s = signal(n, {
const E = signal(n, {
equals: o,
unobserved() {
if (t[r] === s) {
if (t[r] === E) {
delete t[r];

@@ -212,8 +237,8 @@ // Drop the symbol-record mark once the last user symbol node is

if (e[STORE_OPTIMISTIC]) {
s.Ee = NOT_PENDING;
E.Ee = NOT_PENDING;
}
if (i && r in i) {
const e = i[r];
s.Ve = e === undefined ? NO_SNAPSHOT : e;
snapshotSources?.add(s);
E.Ve = e === undefined ? NO_SNAPSHOT : e;
snapshotSources?.add(E);
}

@@ -224,3 +249,3 @@ if (typeof r === "symbol" && r !== $TRACK && r !== $AFFECTS) symbolKeyedRecords.add(t);

// record's own $AFFECTS carrier is the mark's channel, never a member.
if (r !== $AFFECTS && affectsScopes.size) inheritAffectsMarks(s, e[STORE_VALUE], r);
if (r !== $AFFECTS && affectsScopes.size) inheritAffectsMarks(E, e[STORE_VALUE], r);
// Node presence bubbles up the wrap chain (sticky), so reconcile can see

@@ -230,8 +255,8 @@ // "subscribers live somewhere below" through node-less intermediate

// the first already-flagged ancestor.
let E = e;
while (E && !E[STORE_DESC]) {
E[STORE_DESC] = true;
E = E[STORE_PARENT];
let O = e;
while (O && !O[STORE_DESC]) {
O[STORE_DESC] = true;
O = O[STORE_PARENT];
}
return t[r] = s;
return t[r] = E;
}

@@ -273,11 +298,11 @@

const i = e[$TARGET] || (n ?? storeLookup).get(e)?.[$TARGET];
const s = i ? i[STORE_VALUE] : e;
if (o.has(s)) return;
o.add(s);
t.scope.add(s);
let E;
const E = i ? i[STORE_VALUE] : e;
if (o.has(E)) return;
o.add(E);
t.scope.add(E);
let O;
if (i) {
collectRecordNodes(i[STORE_NODE], r);
collectRecordNodes(i[STORE_HAS], r);
E = mergedOverlay(i);
O = mergedOverlay(i);
// Carry the effective lookup into untouched descendants. Default stores

@@ -288,6 +313,6 @@ // use the global lookup just like snapshotImpl; without it, nested raw

}
if (Array.isArray(s)) {
const e = E?.length ?? s.length;
if (Array.isArray(E)) {
const e = O?.length ?? E.length;
for (let i = 0; i < e; i++) {
const e = E && i in E ? E[i] : s[i];
const e = O && i in O ? O[i] : E[i];
if (e !== $DELETED) walkAffectsScope(e, t, r, n, o);

@@ -298,15 +323,15 @@ }

// 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);
const s = i || n ? getStoreSymbols(E, O) : [];
for (let e = 0, i = s.length; e < i; e++) {
const i = s[e];
const S = getPropertyDescriptor(E, O, i);
if (!S || S.get) continue;
walkAffectsScope(S.value, t, r, n, o);
}
} else {
const e = i || n ? getStoreKeys(s, E) : getKeys(s, E);
for (let i = 0, O = e.length; i < O; i++) {
const O = getPropertyDescriptor(s, E, e[i]);
if (!O || O.get) continue;
walkAffectsScope(O.value, t, r, n, o);
const e = i || n ? getStoreKeys(E, O) : getKeys(E, O);
for (let i = 0, s = e.length; i < s; i++) {
const s = getPropertyDescriptor(E, O, e[i]);
if (!s || s.get) continue;
walkAffectsScope(s.value, t, r, n, o);
}

@@ -502,6 +527,6 @@ }

const i = e[STORE_OPTIMISTIC] && !projectionWriteActive;
const s = i ? STORE_OPTIMISTIC_OVERRIDE : STORE_OVERRIDE;
const E = i ? STORE_OPTIMISTIC_OVERRIDE : STORE_OVERRIDE;
return {
base: o,
overrideKey: s,
overrideKey: E,
state: n

@@ -540,5 +565,5 @@ };

const i = isWrappable(n) ? wrap(n, e) : n;
const s = getNode(e, t, r, i, isEqual, o);
registerTransientStoreNode(s);
return s;
const E = getNode(e, t, r, i, isEqual, o);
registerTransientStoreNode(E);
return E;
}

@@ -550,29 +575,29 @@

// (whose whole purpose is immediate visibility via STORE_OPTIMISTIC_OVERRIDE).
const s = projectionWriteActive || e[STORE_OPTIMISTIC];
const E = r !== "delete";
const O = e[STORE_HAS]?.[t];
if (O) {
setSignal(O, E);
} else if (!s && r !== "invalidate" && i !== E) {
const E = projectionWriteActive || e[STORE_OPTIMISTIC];
const O = r !== "delete";
const s = e[STORE_HAS]?.[t];
if (s) {
setSignal(s, O);
} else if (!E && r !== "invalidate" && i !== O) {
const r = upsertStoreNode(e, getNodes(e, STORE_HAS), t, i);
setSignal(r, E);
setSignal(r, O);
}
const c = getNodes(e, STORE_NODE);
const S = getNodes(e, STORE_NODE);
if (r === "set") {
if (c[t]) {
setSignal(c[t], () => isWrappable(n) ? wrap(n, e) : n);
} else if (!s) {
const r = upsertStoreNode(e, c, t, o, e[STORE_SNAPSHOT_PROPS]);
if (S[t]) {
setSignal(S[t], () => isWrappable(n) ? wrap(n, e) : n);
} else if (!E) {
const r = upsertStoreNode(e, S, t, o, e[STORE_SNAPSHOT_PROPS]);
setSignal(r, () => isWrappable(n) ? wrap(n, e) : n);
}
} else if (r === "invalidate") {
if (c[t]) {
setSignal(c[t], {});
delete c[t];
if (S[t]) {
setSignal(S[t], {});
delete S[t];
}
} else {
if (c[t]) {
setSignal(c[t], undefined);
} else if (!s) {
const r = upsertStoreNode(e, c, t, o, e[STORE_SNAPSHOT_PROPS]);
if (S[t]) {
setSignal(S[t], undefined);
} else if (!E) {
const r = upsertStoreNode(e, S, t, o, e[STORE_SNAPSHOT_PROPS]);
setSignal(r, undefined);

@@ -609,20 +634,35 @@ }

}
// Hot path: an existing node on a plain target — no firewall (so neither
// selfRead nor the uninitialized guard can apply), no override layers,
// no write scope, and a raw (non-proxy) source. This is the shape of
// every effect re-read of a settled store; it pays one node read and the
// wrap check, nothing else. Any exotic bit falls through to the full
// resolution below, and dev strictRead keeps its warning path.
if (e[STORE_FIREWALL] === undefined && e[STORE_OVERRIDE] === undefined && e[STORE_OPTIMISTIC_OVERRIDE] === undefined && !writeOverride && (Writing === null || !Writing.has(r))) {
const r = e[STORE_NODE];
const n = r && r[t];
if (n !== undefined && e[STORE_VALUE][$TARGET] === undefined) {
let t = read(n);
if (t === $DELETED) t = undefined;
return isWrappable(t) ? wrap(t, e) : t;
}
}
const n = getObserver() === e[STORE_FIREWALL];
const o = getNodes(e, STORE_NODE);
const i = n ? undefined : o[t];
const s = e[STORE_VALUE];
if (!i && !e[STORE_OVERRIDE] && !e[STORE_OPTIMISTIC_OVERRIDE] && !e[STORE_CUSTOM_PROTO] && !e[STORE_OPTIMISTIC] && !e[STORE_SNAPSHOT_PROPS] && !s[$TARGET] && !(t in s) && getObserver() && !n && !writeOnly(r)) {
const E = e[STORE_VALUE];
if (!i && !e[STORE_OVERRIDE] && !e[STORE_OPTIMISTIC_OVERRIDE] && !e[STORE_CUSTOM_PROTO] && !e[STORE_OPTIMISTIC] && !e[STORE_SNAPSHOT_PROPS] && !E[$TARGET] && !(t in E) && getObserver() && !n && !writeOnly(r)) {
return read(getNode(e, o, t, undefined));
}
const E = getOverlayLayer(e, t);
const O = !!E;
const c = !!e[STORE_VALUE][$TARGET];
const S = E ?? e[STORE_VALUE];
const O = getOverlayLayer(e, t);
const s = !!O;
const S = !!e[STORE_VALUE][$TARGET];
const c = O ?? e[STORE_VALUE];
if (!i) {
const n = Object.getOwnPropertyDescriptor(S, t);
const n = Object.getOwnPropertyDescriptor(c, t);
if (n && n.get) return n.get.call(r);
if (!n && !O && e[STORE_CUSTOM_PROTO]) {
const e = unwrapStoreValue(S);
if (!n && !s && e[STORE_CUSTOM_PROTO]) {
const e = unwrapStoreValue(c);
if (hasInheritedAccessor(e, t)) {
return Reflect.get(S, t, r);
return Reflect.get(c, t, r);
}

@@ -633,3 +673,3 @@ }

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

@@ -641,10 +681,10 @@ if (!isWrappable(r)) return r;

}
let f = i ? O || !c ? read(o[t]) : (read(o[t]), S[t]) : S[t];
f === $DELETED && (f = undefined);
let T = i ? s || !S ? read(o[t]) : (read(o[t]), c[t]) : c[t];
T === $DELETED && (T = undefined);
if (!i) {
if (!O && typeof f === "function" && !Object.prototype.hasOwnProperty.call(S, t)) {
if (!s && typeof T === "function" && !Object.prototype.hasOwnProperty.call(c, t)) {
let t;
return !Array.isArray(e[STORE_VALUE]) && (t = Object.getPrototypeOf(e[STORE_VALUE])) && t !== Object.prototype ? f.bind(S) : f;
return !Array.isArray(e[STORE_VALUE]) && (t = Object.getPrototypeOf(e[STORE_VALUE])) && t !== Object.prototype ? T.bind(c) : T;
} else if (getObserver() && !n) {
return read(getNode(e, o, t, isWrappable(f) ? wrap(f, e) : f, isEqual, e[STORE_SNAPSHOT_PROPS]));
return read(getNode(e, o, t, isWrappable(T) ? wrap(T, e) : T, isEqual, e[STORE_SNAPSHOT_PROPS]));
}

@@ -655,3 +695,3 @@ }

if (!n) throwIfUninitialized(e);
return isWrappable(f) ? wrap(f, e) : f;
return isWrappable(T) ? wrap(T, e) : T;
},

@@ -683,18 +723,18 @@ has(e, t) {

untrack(() => {
const {base: o, overrideKey: i, state: s} = prepareStoreWrite(e, n, t);
const E = getOverlayLayer(e, t);
const O = E ? E[t] : o;
const c = E ? E[t] !== $DELETED : t in e[STORE_VALUE];
const S = unwrapStoreValue(r);
const {base: o, overrideKey: i, state: E} = prepareStoreWrite(e, n, t);
const O = getOverlayLayer(e, t);
const s = O ? O[t] : o;
const S = O ? O[t] !== $DELETED : t in e[STORE_VALUE];
const c = 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 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;
const T = typeof t === "string" ? Number(t) : -1;
const R = Array.isArray(E) && Number.isInteger(T) && T >= 0 && T < 4294967295 && String(T) === t;
const f = R ? T + 1 : 0;
const u = R && (getOverlayLayer(e, "length") ?? E).length;
const l = R && f > u ? f : undefined;
if (s === c && l === undefined) return true;
armOptimisticStoreWrite(e, n);
if (S !== undefined && S === o && l === undefined) {
if (c !== undefined && c === o && l === undefined) {
delete e[i]?.[t];

@@ -704,3 +744,3 @@ if (i === STORE_OPTIMISTIC_OVERRIDE) delete e[STORE_OPTIMISTIC_OWNERS]?.[t];

const r = e[i] || (e[i] = Object.create(null));
r[t] = S;
r[t] = c;
stampOptimisticOwner(e, i, t);

@@ -712,12 +752,12 @@ if (l !== undefined) {

}
notifyStoreProperty(e, t, "set", S, O, c);
notifyStoreProperty(e, t, "set", c, s, S);
// 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 S === "number" && typeof O === "number" && S < O) {
if (Array.isArray(E) && t === "length" && typeof c === "number" && typeof s === "number" && c < s) {
const t = e[i] || (e[i] = Object.create(null));
for (let r = S; r < O; r++) {
for (let r = c; r < s; r++) {
if (t[r] === $DELETED) continue;
const n = r in t ? t[r] : s[r];
if (!(r in t) && !(r in s)) continue;
const n = r in t ? t[r] : E[r];
if (!(r in t) && !(r in E)) continue;
t[r] = $DELETED;

@@ -729,3 +769,3 @@ stampOptimisticOwner(e, i, r);

// notify length change
if (Array.isArray(s) && t !== "length" && l !== undefined) {
if (Array.isArray(E) && t !== "length" && l !== undefined) {
const t = getNodes(e, STORE_NODE);

@@ -751,7 +791,7 @@ if (t.length) {

armOptimisticStoreWrite(e, n);
const s = "value" in r ? {
const E = "value" in r ? {
...r,
value: unwrapStoreValue(r.value)
} : r;
Object.defineProperty(e[i] || (e[i] = Object.create(null)), t, s);
Object.defineProperty(e[i] || (e[i] = Object.create(null)), t, E);
stampOptimisticOwner(e, i, t);

@@ -758,0 +798,0 @@ notifyStoreProperty(e, t, "invalidate");

@@ -30,2 +30,14 @@ import { pendingCheckActive } from "../core/core.js";

i = mergedOverlay(o);
// A derived store's STORE_VALUE is the inner store's live proxy
// (store-in-store: createOptimisticStore/createProjection over a store).
// Without an overlay of its own, the fast path below would map to — and
// could return — that proxy verbatim. Recurse instead so the
// inner store's own target branch unwraps it (chains of any depth).
// With an overlay, a fresh `result` is always built, so the walk over the
// inner proxy already copies plain values.
if (!i && o[STORE_VALUE][$TARGET]) {
f = snapshotImpl(o[STORE_VALUE], t, r, n);
r.set(e, f);
return f;
}
s = Array.isArray(o[STORE_VALUE]);

@@ -32,0 +44,0 @@ r.set(e, i ? c = s ? [] : Object.create(Object.getPrototypeOf(o[STORE_VALUE])) : o[STORE_VALUE]);

@@ -26,3 +26,2 @@ /**

export declare class NotReadyError extends Error {
source: any;
/**

@@ -35,2 +34,3 @@ * Tags a visibility-only notification on the affects() boundary channel:

_markVisual?: boolean;
source: any;
constructor(source: any);

@@ -37,0 +37,0 @@ }

/**
* Returns a draft-mutating function that smart-merges `value` into a store,
* preserving the identity of items whose `key` field matches between old and
* new states. Useful when applying server payloads or full-replacement data
* onto an existing store without losing fine-grained reactivity.
* preserving fine-grained reactivity: only changed leaves trigger updates.
*
* Items with the same key are updated in place (only changed properties
* trigger updates). Items added or removed update the corresponding signals.
* With a `key` (default `"id"`), array items whose key matches between old
* and new states keep their identity (updated in place, moves and removals
* update the corresponding signals) — the shape for keyed server payloads.
* Items without the key field fall back to positional matching.
*
* With `key: null`, matching is purely positional: index N of the new array
* merges into index N of the old, and object properties merge recursively —
* the classic pattern for fixed-shape data that churns in place (dashboards,
* monitors), where no keyed diff pass is needed or wanted.
*
* @param value the next state to merge in
* @param key property name (string) or extractor function for stable identity
* @param key property name (string) or extractor function for stable
* identity (default `"id"`); pass `null` for positional merging
*

@@ -19,6 +25,9 @@ * @example

* const fresh = await api.getTodos();
* setTodos(reconcile(fresh, "id")); // diff-merge by `id`
* setTodos(reconcile(fresh)); // diff-merge by `id`
* }
*
* // fixed-shape polling data — positional merge
* setStats(reconcile(nextStats, null));
* ```
*/
export declare function reconcile<T extends U, U>(value: T, key: string | ((item: NonNullable<any>) => any)): (state: U) => void;
export declare function reconcile<T extends U, U>(value: T, key?: string | ((item: NonNullable<any>) => any) | null): (state: U) => void;

@@ -26,3 +26,2 @@ /**

export declare class NotReadyError extends Error {
source: any;
/**

@@ -35,2 +34,3 @@ * Tags a visibility-only notification on the affects() boundary channel:

_markVisual?: boolean;
source: any;
constructor(source: any);

@@ -37,0 +37,0 @@ }

/**
* Returns a draft-mutating function that smart-merges `value` into a store,
* preserving the identity of items whose `key` field matches between old and
* new states. Useful when applying server payloads or full-replacement data
* onto an existing store without losing fine-grained reactivity.
* preserving fine-grained reactivity: only changed leaves trigger updates.
*
* Items with the same key are updated in place (only changed properties
* trigger updates). Items added or removed update the corresponding signals.
* With a `key` (default `"id"`), array items whose key matches between old
* and new states keep their identity (updated in place, moves and removals
* update the corresponding signals) — the shape for keyed server payloads.
* Items without the key field fall back to positional matching.
*
* With `key: null`, matching is purely positional: index N of the new array
* merges into index N of the old, and object properties merge recursively —
* the classic pattern for fixed-shape data that churns in place (dashboards,
* monitors), where no keyed diff pass is needed or wanted.
*
* @param value the next state to merge in
* @param key property name (string) or extractor function for stable identity
* @param key property name (string) or extractor function for stable
* identity (default `"id"`); pass `null` for positional merging
*

@@ -19,6 +25,9 @@ * @example

* const fresh = await api.getTodos();
* setTodos(reconcile(fresh, "id")); // diff-merge by `id`
* setTodos(reconcile(fresh)); // diff-merge by `id`
* }
*
* // fixed-shape polling data — positional merge
* setStats(reconcile(nextStats, null));
* ```
*/
export declare function reconcile<T extends U, U>(value: T, key: string | ((item: NonNullable<any>) => any)): (state: U) => void;
export declare function reconcile<T extends U, U>(value: T, key?: string | ((item: NonNullable<any>) => any) | null): (state: U) => void;
{
"name": "@solidjs/signals",
"version": "2.0.0-beta.23",
"version": "2.0.0-beta.24",
"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