@solidjs/signals 2.0.0-rc.5 → 2.0.0-rc.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/dev.js +475 -72
- package/dist/node.cjs +851 -662
- package/dist/prod/core/async.js +41 -19
- package/dist/prod/core/core.js +58 -62
- package/dist/prod/core/effect.js +6 -6
- package/dist/prod/core/heap.js +1 -1
- package/dist/prod/core/lanes.js +4 -4
- package/dist/prod/core/optimistic.js +16 -16
- package/dist/prod/core/owner.js +4 -4
- package/dist/prod/core/scheduler.js +45 -32
- package/dist/prod/core/verdict.js +23 -19
- package/dist/prod/signals.js +68 -1
- package/dist/prod/store/next/optimistic.js +137 -64
- package/dist/prod/store/next/projection.js +2 -2
- package/dist/prod/store/next/store.js +122 -106
- package/dist/types/core/attribution-hooks.d.ts +11 -0
- package/dist/types/core/attribution.d.ts +48 -0
- package/dist/types/core/dev.d.ts +8 -2
- package/dist/types/core/scheduler.d.ts +5 -0
- package/dist/types/signals.d.ts +0 -11
- package/dist/types/store/next/target.d.ts +10 -0
- package/dist/types-cjs/core/attribution-hooks.d.cts +11 -0
- package/dist/types-cjs/core/attribution.d.cts +48 -0
- package/dist/types-cjs/core/dev.d.cts +8 -2
- package/dist/types-cjs/core/scheduler.d.cts +5 -0
- package/dist/types-cjs/signals.d.cts +0 -11
- package/dist/types-cjs/store/next/target.d.cts +10 -0
- package/package.json +1 -1
|
@@ -24,8 +24,8 @@ import { GlobalQueue, activeTransition, globalQueue, clock, insertSubs, schedule
|
|
|
24
24
|
* once the gate holds — the same late-binding contract as verdict.ts.
|
|
25
25
|
*/
|
|
26
26
|
/** The optimistic half of setSignal, fired when `_overrideValue !== undefined`. */ function optimisticWrite(e, n) {
|
|
27
|
-
const t = e.o?.
|
|
28
|
-
const i = t ? unwrapOverride(e.o?.
|
|
27
|
+
const t = e.o?.Pe !== NOT_PENDING;
|
|
28
|
+
const i = t ? unwrapOverride(e.o?.Pe) : e.be;
|
|
29
29
|
if (typeof n === "function") n = n(i);
|
|
30
30
|
const u = !!(e.S & STATUS_UNINITIALIZED) ||
|
|
31
31
|
// A dirty node's _value is stale (its queued recompute hasn't run — e.g.
|
|
@@ -34,7 +34,7 @@ import { GlobalQueue, activeTransition, globalQueue, clock, insertSubs, schedule
|
|
|
34
34
|
// push returning the shadow to that stale value was dropped, the snap
|
|
35
35
|
// recompute then committed the parent's old value, and the banner showed
|
|
36
36
|
// the previous transition's target (#3041 follow-up).
|
|
37
|
-
!!((e.ie ?? 0) & (REACTIVE_DIRTY | REACTIVE_CHECK)) || !e.
|
|
37
|
+
!!((e.ie ?? 0) & (REACTIVE_DIRTY | REACTIVE_CHECK)) || !e.pe || !e.pe(i, n);
|
|
38
38
|
if (!u) {
|
|
39
39
|
// Same-value write with an active override still entangles the current
|
|
40
40
|
// action's transition — the hold must outlast all overlapping actions.
|
|
@@ -59,10 +59,10 @@ import { GlobalQueue, activeTransition, globalQueue, clock, insertSubs, schedule
|
|
|
59
59
|
// Literal undefined must not land raw: the slot doubles as the optimistic
|
|
60
60
|
// brand, and erasing it makes the write invisible and routes follow-up
|
|
61
61
|
// writes off the optimistic path into permanent commits (#2898).
|
|
62
|
-
ext(e).
|
|
62
|
+
ext(e).Pe = n === undefined ? OVERRIDE_UNDEFINED : n;
|
|
63
63
|
// syncCompanions only pokes _pendingSignal/_latestValueComputed — with
|
|
64
64
|
// neither companion present the call is a guaranteed no-op.
|
|
65
|
-
(e.o?.ye !== undefined || e.o?.Ce !== undefined) && GlobalQueue.
|
|
65
|
+
(e.o?.ye !== undefined || e.o?.Ce !== undefined) && GlobalQueue.Ue !== null && GlobalQueue.Ue(e, n);
|
|
66
66
|
if (e.oe !== undefined) e.Te = clock;
|
|
67
67
|
// §12e: computed-only slot
|
|
68
68
|
insertSubs(e, true);
|
|
@@ -96,8 +96,8 @@ function resolveOptimisticNodes(e) {
|
|
|
96
96
|
// override-covered authoritative values hold in _pendingValue and
|
|
97
97
|
// elevate on their OWN transition's schedule (A18 as re-ruled 2026-07-07).
|
|
98
98
|
if (!(n.S & STATUS_PENDING)) n.S &= ~STATUS_UNINITIALIZED;
|
|
99
|
-
const i = n.o?.
|
|
100
|
-
ext(n).
|
|
99
|
+
const i = n.o?.Pe;
|
|
100
|
+
ext(n).Pe = NOT_PENDING;
|
|
101
101
|
if (i !== NOT_PENDING && n.be !== unwrapOverride(i)) insertSubs(n, true);
|
|
102
102
|
n.Ae = null;
|
|
103
103
|
if (n.o !== null) n.o.Nt = null;
|
|
@@ -122,7 +122,7 @@ function runQueue(e, n) {
|
|
|
122
122
|
* Run effects from all lanes that are ready (no pending async).
|
|
123
123
|
*/ function runLaneEffects(e) {
|
|
124
124
|
for (const n of activeLanes) {
|
|
125
|
-
if (n.rn || n.
|
|
125
|
+
if (n.rn || n.Oe.size > 0) continue;
|
|
126
126
|
const t = n.tn[e - 1];
|
|
127
127
|
if (t.length) {
|
|
128
128
|
n.tn[e - 1] = [];
|
|
@@ -143,7 +143,7 @@ function cleanupCompletedLanes(e) {
|
|
|
143
143
|
if (n.tn[1].length) runQueue(n.tn[1], EFFECT_USER);
|
|
144
144
|
}
|
|
145
145
|
if (n.nn.o?.Je === n) if (n.nn.o !== null) n.nn.o.Je = undefined;
|
|
146
|
-
n.
|
|
146
|
+
n.Oe.clear();
|
|
147
147
|
n.tn[0].length = 0;
|
|
148
148
|
n.tn[1].length = 0;
|
|
149
149
|
activeLanes.delete(n);
|
|
@@ -165,7 +165,7 @@ function cleanupCompletedLanes(e) {
|
|
|
165
165
|
* that reads a pending mid-transition write sees the committed value; the sub
|
|
166
166
|
* is recorded for replay at commit.
|
|
167
167
|
*/ function gatedRead(e, n, t) {
|
|
168
|
-
if (latestReadActive || e.
|
|
168
|
+
if (latestReadActive || e.Re === NOT_PENDING || e.oe || n !== e && !(n.ie & REACTIVE_MANUAL_WRITE)) {
|
|
169
169
|
return false;
|
|
170
170
|
}
|
|
171
171
|
activeTransition.cn.add(t);
|
|
@@ -176,7 +176,7 @@ function cleanupCompletedLanes(e) {
|
|
|
176
176
|
* read()'s value selection under a lane: return the committed `_value` for
|
|
177
177
|
* optimistic/lane-assigned signals, stale-mode reads, and pending owners.
|
|
178
178
|
*/ function laneReadsCommitted(e, n, t) {
|
|
179
|
-
if (e.o?.
|
|
179
|
+
if (e.o?.Pe !== undefined || !!e.o?.Je || !!(n.S & STATUS_PENDING)) {
|
|
180
180
|
// The committed view hides a staged in-flight value that will promote
|
|
181
181
|
// silently (commitPendingNode never re-notifies). gatedRead records plain
|
|
182
182
|
// signals for replay at commit; async memos are excluded from it by the
|
|
@@ -185,7 +185,7 @@ function cleanupCompletedLanes(e) {
|
|
|
185
185
|
// committed value to a reader that never re-ran after the landing, so a
|
|
186
186
|
// pending-gated branch stayed one value behind permanently (#3041
|
|
187
187
|
// follow-up). Record the reader under the same replay contract.
|
|
188
|
-
if (e.
|
|
188
|
+
if (e.Re !== NOT_PENDING) (activeTransition ?? globalQueue.m).cn.add(t);
|
|
189
189
|
return true;
|
|
190
190
|
}
|
|
191
191
|
if (n === e && stale && t.o?.Tt !== e) {
|
|
@@ -200,7 +200,7 @@ function cleanupCompletedLanes(e) {
|
|
|
200
200
|
// into the transaction (#3041 follow-up: a pending-gated branch that
|
|
201
201
|
// first read its async source during the landing flush stayed one value
|
|
202
202
|
// behind permanently); with none, into the ambient batch.
|
|
203
|
-
if (e.
|
|
203
|
+
if (e.Re !== NOT_PENDING) (activeTransition ?? globalQueue.m).cn.add(t);
|
|
204
204
|
return true;
|
|
205
205
|
}
|
|
206
206
|
return false;
|
|
@@ -223,7 +223,7 @@ function cleanupCompletedLanes(e) {
|
|
|
223
223
|
// recompute() runs plain: the value stages and commits with the flush.
|
|
224
224
|
// (el's own override slot excludes companions themselves; a lane merged
|
|
225
225
|
// into a real optimistic lane resolves to a non-companion source.)
|
|
226
|
-
if (!globalQueue.fn && !activeTransition && !n.Ae && n.nn.o?.Tt !== undefined && e.o?.
|
|
226
|
+
if (!globalQueue.fn && !activeTransition && !n.Ae && n.nn.o?.Tt !== undefined && e.o?.Pe === undefined) {
|
|
227
227
|
if (e.o !== null) e.o.Je = undefined;
|
|
228
228
|
return false;
|
|
229
229
|
}
|
|
@@ -246,7 +246,7 @@ function cleanupCompletedLanes(e) {
|
|
|
246
246
|
/** recompute()'s catch path: track pending async in the current lane. */ function laneAsyncPending(e) {
|
|
247
247
|
const n = findLane(currentOptimisticLane);
|
|
248
248
|
if (n.nn !== e) {
|
|
249
|
-
n.
|
|
249
|
+
n.Oe.add(e);
|
|
250
250
|
ext(e).Je = n;
|
|
251
251
|
e.T |= CONFIG_HAS_LANE;
|
|
252
252
|
GlobalQueue.de !== null && GlobalQueue.de(n.nn);
|
|
@@ -256,7 +256,7 @@ function cleanupCompletedLanes(e) {
|
|
|
256
256
|
/** recompute()'s success path: the node's async settled, clear it from its lane. */ function laneAsyncSettled(e) {
|
|
257
257
|
const n = resolveLane(e);
|
|
258
258
|
if (n) {
|
|
259
|
-
n.
|
|
259
|
+
n.Oe.delete(e);
|
|
260
260
|
GlobalQueue.de !== null && GlobalQueue.de(n.nn);
|
|
261
261
|
}
|
|
262
262
|
}
|
package/dist/prod/core/owner.js
CHANGED
|
@@ -107,7 +107,7 @@ function disposeChildren(e, t = false, n) {
|
|
|
107
107
|
}
|
|
108
108
|
|
|
109
109
|
function runDisposal(e, t) {
|
|
110
|
-
let n = t ? e.o?.qe : e.
|
|
110
|
+
let n = t ? e.o?.qe : e.Ge;
|
|
111
111
|
if (!n) return;
|
|
112
112
|
if (Array.isArray(n)) {
|
|
113
113
|
for (let e = 0; e < n.length; e++) {
|
|
@@ -119,7 +119,7 @@ function runDisposal(e, t) {
|
|
|
119
119
|
}
|
|
120
120
|
if (t) {
|
|
121
121
|
if (e.o !== null) e.o.qe = null;
|
|
122
|
-
} else e.
|
|
122
|
+
} else e.Ge = null;
|
|
123
123
|
}
|
|
124
124
|
|
|
125
125
|
function childId(e, t) {
|
|
@@ -206,7 +206,7 @@ function formatId(e, t) {
|
|
|
206
206
|
* checks. `cleanup()` is the unchecked primitive used by internals.
|
|
207
207
|
*/ function cleanup(e) {
|
|
208
208
|
if (!context) return e;
|
|
209
|
-
if (!context.
|
|
209
|
+
if (!context.Ge) context.Ge = e; else if (Array.isArray(context.Ge)) context.Ge.push(e); else context.Ge = [ context.Ge, e ];
|
|
210
210
|
return e;
|
|
211
211
|
}
|
|
212
212
|
|
|
@@ -251,7 +251,7 @@ function disposeRootSelf(e = true) {
|
|
|
251
251
|
xe: null,
|
|
252
252
|
Le: null,
|
|
253
253
|
ft: null,
|
|
254
|
-
|
|
254
|
+
Ge: null,
|
|
255
255
|
C: t?.C ?? globalQueue,
|
|
256
256
|
we: t?.we || defaultContext,
|
|
257
257
|
Ze: 0,
|
|
@@ -73,8 +73,8 @@ function sweepTransientStoreNodes() {
|
|
|
73
73
|
transientStoreNodes.delete(e);
|
|
74
74
|
continue;
|
|
75
75
|
}
|
|
76
|
-
if (e.
|
|
77
|
-
if (e.o?.
|
|
76
|
+
if (e.Re !== NOT_PENDING) continue;
|
|
77
|
+
if (e.o?.Pe !== undefined && e.o?.Pe !== NOT_PENDING) continue;
|
|
78
78
|
// A live affects() mark keeps the node addressable: sweeping it would
|
|
79
79
|
// detach the refcount from the slot (a fresh probe would upsert a new,
|
|
80
80
|
// unmarked node for the same property).
|
|
@@ -202,9 +202,9 @@ function mergeTransitionState(e, t) {
|
|
|
202
202
|
// reporter deadlocked exactly that way).
|
|
203
203
|
let i = false;
|
|
204
204
|
for (let n = e.ut; n !== null; n = n.lt) {
|
|
205
|
-
const
|
|
206
|
-
if (
|
|
207
|
-
const e =
|
|
205
|
+
const r = n.ot;
|
|
206
|
+
if (r.Re !== NOT_PENDING) {
|
|
207
|
+
const e = r.Ae;
|
|
208
208
|
const n = e != null ? currentTransition(e) : null;
|
|
209
209
|
// Skip the awaiting transaction's own cargo (t === target: a
|
|
210
210
|
// fold-staged landing or a write the action itself issued — hold and
|
|
@@ -212,8 +212,8 @@ function mergeTransitionState(e, t) {
|
|
|
212
212
|
// (t === null) must leave the batch NOW — it commits at this flush's
|
|
213
213
|
// end, which would reveal the confirmation under the live optimism
|
|
214
214
|
// it just confirmed.
|
|
215
|
-
const
|
|
216
|
-
if (
|
|
215
|
+
const s = n === null ? currentBatch.Lt : n !== t && n.sn !== true ? n.Lt : null;
|
|
216
|
+
if (s !== null) i = stealEntangledCargo(s, t) || i;
|
|
217
217
|
}
|
|
218
218
|
if (n === e.je) break;
|
|
219
219
|
}
|
|
@@ -257,7 +257,7 @@ function mergeTransitionState(e, t) {
|
|
|
257
257
|
n.T |= CONFIG_HELD_TRUTH;
|
|
258
258
|
for (let e = n.u; e !== null; e = e.ae) {
|
|
259
259
|
const t = e.ce;
|
|
260
|
-
if (t.
|
|
260
|
+
if (t.ge && !(t.T & CONFIG_AUTHORITATIVE_READ)) enqueueSub(t);
|
|
261
261
|
}
|
|
262
262
|
}
|
|
263
263
|
}
|
|
@@ -343,11 +343,11 @@ class Queue {
|
|
|
343
343
|
const t = this.Qt;
|
|
344
344
|
const i = ++queueRunToken;
|
|
345
345
|
for (let n = 0; n < t.length; ) {
|
|
346
|
-
const
|
|
347
|
-
if (
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
if (t[n] !==
|
|
346
|
+
const r = t[n];
|
|
347
|
+
if (r.jt !== i) {
|
|
348
|
+
r.jt = i;
|
|
349
|
+
r.run?.(e);
|
|
350
|
+
if (t[n] !== r) {
|
|
351
351
|
n = 0;
|
|
352
352
|
continue;
|
|
353
353
|
}
|
|
@@ -425,7 +425,7 @@ class GlobalQueue extends Queue {
|
|
|
425
425
|
// there: `_pendingSignal`/`_latestValueComputed` are only ever assigned by
|
|
426
426
|
// verdict.ts, and `pendingCheckActive`/`latestReadActive` only flip inside
|
|
427
427
|
// isPending()/latest()).
|
|
428
|
-
static
|
|
428
|
+
static Ue=null;
|
|
429
429
|
static de=null;
|
|
430
430
|
static me=null;
|
|
431
431
|
static un=null;
|
|
@@ -462,7 +462,7 @@ class GlobalQueue extends Queue {
|
|
|
462
462
|
/** Authoritative-view reader wakeup (until()): installed at first until() call.
|
|
463
463
|
* Call sites are gated by CONFIG_AUTHORITATIVE_OBSERVED, which only until()'s
|
|
464
464
|
* carve-out read can set, so `!` invocations are safe once the gate holds. */
|
|
465
|
-
static
|
|
465
|
+
static he=null;
|
|
466
466
|
static Xe=null;
|
|
467
467
|
static _n=null;
|
|
468
468
|
flush() {
|
|
@@ -591,9 +591,9 @@ class GlobalQueue extends Queue {
|
|
|
591
591
|
const i = t.source;
|
|
592
592
|
let n = activeTransition._e.get(i);
|
|
593
593
|
if (!n) activeTransition._e.set(i, n = new Set);
|
|
594
|
-
const
|
|
594
|
+
const r = n.size;
|
|
595
595
|
n.add(e);
|
|
596
|
-
if (n.size !==
|
|
596
|
+
if (n.size !== r) {
|
|
597
597
|
schedule();
|
|
598
598
|
GlobalQueue.zt?.(activeTransition);
|
|
599
599
|
}
|
|
@@ -704,13 +704,13 @@ function insertSubs(e, t = false) {
|
|
|
704
704
|
// stays authoritative when a bit is set.
|
|
705
705
|
const i = e.T;
|
|
706
706
|
const n = (i & CONFIG_HAS_LANE ? e.o?.Je : undefined) || currentOptimisticLane;
|
|
707
|
-
const
|
|
708
|
-
const
|
|
707
|
+
const r = (i & CONFIG_HAS_SNAPSHOT) !== 0 && e.o?.We !== undefined;
|
|
708
|
+
const s = reaskArmed;
|
|
709
709
|
for (let i = e.u; i !== null; i = i.ae) {
|
|
710
710
|
const e = i.ce;
|
|
711
711
|
// A value-change notification is a new question for the subscriber: any
|
|
712
712
|
// pending re-ask mark (refresh) it carried is superseded.
|
|
713
|
-
if (
|
|
713
|
+
if (s) e.ie &= ~REACTIVE_REASK;
|
|
714
714
|
// Missed-wake latch (#3037): this write is landing while the subscriber
|
|
715
715
|
// is mid-recompute (a nested pull committing beneath its reads), and the
|
|
716
716
|
// heap refuses RECOMPUTING nodes. A gen-current link means the pass
|
|
@@ -720,7 +720,7 @@ function insertSubs(e, t = false) {
|
|
|
720
720
|
// tail link: it is the read IN FLIGHT — read() links before it pulls, so
|
|
721
721
|
// this very commit is what that read returns.
|
|
722
722
|
if (e.ie & REACTIVE_RECOMPUTING_DEPS && i.Ft === e.Ke && i !== e.je) e.ie |= REACTIVE_MISSED_WAKE;
|
|
723
|
-
if (
|
|
723
|
+
if (r && e.T & CONFIG_IN_SNAPSHOT_SCOPE) {
|
|
724
724
|
e.ie |= REACTIVE_SNAPSHOT_STALE;
|
|
725
725
|
continue;
|
|
726
726
|
}
|
|
@@ -739,18 +739,24 @@ function insertSubs(e, t = false) {
|
|
|
739
739
|
function commitPendingNode(e) {
|
|
740
740
|
const t = e;
|
|
741
741
|
if (!t.oe) {
|
|
742
|
-
if (e.
|
|
743
|
-
e.be = e.
|
|
744
|
-
e.
|
|
742
|
+
if (e.Re !== NOT_PENDING) {
|
|
743
|
+
e.be = e.Re;
|
|
744
|
+
e.Re = NOT_PENDING;
|
|
745
745
|
}
|
|
746
746
|
if (e.T & CONFIG_HAS_COMPANIONS) GlobalQueue.un(e);
|
|
747
747
|
return;
|
|
748
748
|
}
|
|
749
|
-
if (e.
|
|
750
|
-
e.be = e.
|
|
751
|
-
e.
|
|
749
|
+
if (e.Re !== NOT_PENDING) {
|
|
750
|
+
e.be = e.Re;
|
|
751
|
+
e.Re = NOT_PENDING;
|
|
752
752
|
// Set _modified for effects, but not for tracked effects (they handle their own scheduling)
|
|
753
|
-
if (e.
|
|
753
|
+
if (e.ge && e.ge !== EFFECT_TRACKED) e.tt = true;
|
|
754
|
+
// A quiet re-ask classification preserved through a held landing dies
|
|
755
|
+
// with the value commit — the commit IS the reveal (#3178). Gated on the
|
|
756
|
+
// staged value: status propagation queues pending nodes whose windows
|
|
757
|
+
// are still OPEN (no staged value), and their live classification must
|
|
758
|
+
// survive this sweep.
|
|
759
|
+
if (e.o) e.o.De = false;
|
|
754
760
|
}
|
|
755
761
|
// The committed hold is the first observable answer for a loading-window
|
|
756
762
|
// node — the window closes here, not at compute time (#2990). Unconditional
|
|
@@ -971,15 +977,15 @@ function transitionComplete(e) {
|
|
|
971
977
|
if (e.ue.length) return false;
|
|
972
978
|
let t = true;
|
|
973
979
|
for (const [i, n] of e._e) {
|
|
974
|
-
let
|
|
980
|
+
let r = false;
|
|
975
981
|
for (const e of n) {
|
|
976
982
|
if (reporterBlocksSource(e, i)) {
|
|
977
|
-
|
|
983
|
+
r = true;
|
|
978
984
|
break;
|
|
979
985
|
}
|
|
980
986
|
n.delete(e);
|
|
981
987
|
}
|
|
982
|
-
if (!
|
|
988
|
+
if (!r) e._e.delete(i); else if (i.S & STATUS_PENDING && i.o?._?.source === i) {
|
|
983
989
|
t = false;
|
|
984
990
|
break;
|
|
985
991
|
}
|
|
@@ -992,6 +998,13 @@ function transitionComplete(e) {
|
|
|
992
998
|
return t;
|
|
993
999
|
}
|
|
994
1000
|
|
|
1001
|
+
/** A fresh, unentered transaction (#3146): the optimistic store's truth
|
|
1002
|
+
* flight DECLARES an owned transaction instead of relying on whatever the
|
|
1003
|
+
* ambient adoption machinery stamped on its firewall. Activate it with
|
|
1004
|
+
* initTransition; it is a plain batch until then. */ function createTransition() {
|
|
1005
|
+
return createBatch();
|
|
1006
|
+
}
|
|
1007
|
+
|
|
995
1008
|
function currentTransition(e) {
|
|
996
1009
|
while (e.sn && typeof e.sn === "object") e = e.sn;
|
|
997
1010
|
return e;
|
|
@@ -1030,4 +1043,4 @@ function runInTransition(e, t) {
|
|
|
1030
1043
|
}
|
|
1031
1044
|
}
|
|
1032
1045
|
|
|
1033
|
-
export { GlobalQueue, Queue, activeAffectsMarks, activeLanes, activeTransition, armReaskClear, assignOrMergeLane, bumpNotifyEpoch, clock, currentTransition, dirtyQueue, enforceLoadingBoundary, entangleConfirmingTransitions, finalizePureQueue, findLane, flush, globalQueue, haltReactivity, hasActiveOverride, insertSubs, notifyEpoch, patchCommitHook, projectionWriteActive, queuePendingNode, reaskArmed, resetErrorHalt, runAsTransitionBatch, runInTransition, schedule, setPatchCommitHook, setProjectionWriteActive, setStoreCommitHook, shiftAffectsMarks, storeCommitHook, zombieQueue };
|
|
1046
|
+
export { GlobalQueue, Queue, activeAffectsMarks, activeLanes, activeTransition, armReaskClear, assignOrMergeLane, bumpNotifyEpoch, clock, createTransition, currentTransition, dirtyQueue, enforceLoadingBoundary, entangleConfirmingTransitions, finalizePureQueue, findLane, flush, globalQueue, haltReactivity, hasActiveOverride, insertSubs, notifyEpoch, patchCommitHook, projectionWriteActive, queuePendingNode, reaskArmed, resetErrorHalt, runAsTransitionBatch, runInTransition, schedule, setPatchCommitHook, setProjectionWriteActive, setStoreCommitHook, shiftAffectsMarks, storeCommitHook, zombieQueue };
|
|
@@ -128,10 +128,10 @@ function collectPendingSources(e) {
|
|
|
128
128
|
|
|
129
129
|
function quietPending(e) {
|
|
130
130
|
if (e.o?.le) {
|
|
131
|
-
for (const t of e.o.le) if (!t.o?.
|
|
131
|
+
for (const t of e.o.le) if (!t.o?.De) return false;
|
|
132
132
|
return true;
|
|
133
133
|
}
|
|
134
|
-
return e.o?.
|
|
134
|
+
return e.o?.De ?? false;
|
|
135
135
|
}
|
|
136
136
|
|
|
137
137
|
// NOTE: a loadingValue node's open loading window (_loading) is verdict-quiet
|
|
@@ -158,16 +158,20 @@ function computePendingState(e) {
|
|
|
158
158
|
const n = t.st || t;
|
|
159
159
|
return newQuestionInFlight(n);
|
|
160
160
|
}
|
|
161
|
-
if (n && e.
|
|
161
|
+
if (n && e.Re !== NOT_PENDING && !hasActiveOverride(e)) {
|
|
162
162
|
return !!(n.ie & REACTIVE_MANUAL_WRITE) || !n.o?.Ie && !(n.S & STATUS_PENDING) || !!(n.S & STATUS_PENDING) && quietPending(n);
|
|
163
163
|
}
|
|
164
164
|
// `!comp._loading`: a hold created while the loading window is still open is
|
|
165
165
|
// the window's own landing in flight to its commit — verdict-quiet like the
|
|
166
166
|
// rest of the window (the UNINITIALIZED check suppresses exactly this frame
|
|
167
167
|
// for windowless first loads; born-committed nodes need their own gate, #2990).
|
|
168
|
-
if (e.
|
|
169
|
-
if (hasActiveOverride(e)) return !e.
|
|
170
|
-
|
|
168
|
+
if (e.Re !== NOT_PENDING && !(t.S & STATUS_UNINITIALIZED) && !t.Ne) {
|
|
169
|
+
if (hasActiveOverride(e)) return !e.pe || !e.pe(e.Re, unwrapOverride(e.o?.Pe));
|
|
170
|
+
// A quiet re-ask's held landing still answers the same question: the
|
|
171
|
+
// classification survives the landing (asyncWrite) and dies with the
|
|
172
|
+
// commit (commitPendingNode) — verdict-quiet through the reveal, like
|
|
173
|
+
// the loading window above (#3178).
|
|
174
|
+
if (!t.o?.De) return true;
|
|
171
175
|
}
|
|
172
176
|
return newQuestionInFlight(t);
|
|
173
177
|
}
|
|
@@ -250,18 +254,18 @@ function updateChildCompanions(e) {
|
|
|
250
254
|
function snapCompanionsToState(e) {
|
|
251
255
|
suppressedProbes.size !== 0 && suppressedProbes.delete(e);
|
|
252
256
|
const t = e.o?.ye;
|
|
253
|
-
if (t && (t.o?.
|
|
257
|
+
if (t && (t.o?.Pe === undefined || t.o?.Pe === NOT_PENDING)) {
|
|
254
258
|
const n = computePendingState(e);
|
|
255
|
-
if (t.be !== n || t.
|
|
259
|
+
if (t.be !== n || t.Re !== NOT_PENDING) {
|
|
256
260
|
t.be = n;
|
|
257
|
-
t.
|
|
261
|
+
t.Re = NOT_PENDING;
|
|
258
262
|
insertSubs(t);
|
|
259
263
|
schedule();
|
|
260
264
|
}
|
|
261
265
|
}
|
|
262
266
|
const n = e.o?.Ce;
|
|
263
267
|
if (n && !(n.ie & REACTIVE_DISPOSED)) {
|
|
264
|
-
if ((n.o?.
|
|
268
|
+
if ((n.o?.Pe === undefined || n.o?.Pe === NOT_PENDING) && n.Re === NOT_PENDING && !Object.is(n.be, e.be) && !(n.ie & (REACTIVE_DIRTY | REACTIVE_CHECK))) {
|
|
265
269
|
n.ie |= REACTIVE_DIRTY;
|
|
266
270
|
insertIntoHeap(n, queueFor(n));
|
|
267
271
|
insertSubs(n);
|
|
@@ -298,7 +302,7 @@ function getLatestValueComputed(e) {
|
|
|
298
302
|
// created lazily, possibly after the write was processed — syncCompanions
|
|
299
303
|
// only pushes into companions that already exist, so the first latest()
|
|
300
304
|
// read inside a held transition showed the committed value (#3041).
|
|
301
|
-
if (e.
|
|
305
|
+
if (e.Re !== NOT_PENDING && !hasActiveOverride(e)) setSignal(t, e.Re);
|
|
302
306
|
setContextInternal(r);
|
|
303
307
|
setPendingCheckActive(i);
|
|
304
308
|
setLatestReadActive(n);
|
|
@@ -310,7 +314,7 @@ function getLatestValueComputed(e) {
|
|
|
310
314
|
const t = getLatestValueComputed(e);
|
|
311
315
|
const n = latestReadActive;
|
|
312
316
|
setLatestReadActive(false);
|
|
313
|
-
const i = e.o?.
|
|
317
|
+
const i = e.o?.Pe !== undefined && e.o?.Pe !== NOT_PENDING ? unwrapOverride(e.o?.Pe) : e.be;
|
|
314
318
|
let r;
|
|
315
319
|
try {
|
|
316
320
|
// An untracked latest() read has no reading context, so read() never
|
|
@@ -349,7 +353,7 @@ function getLatestValueComputed(e) {
|
|
|
349
353
|
if (stale && currentOptimisticLane && t.o?.Je) {
|
|
350
354
|
const e = findLane(t.o?.Je);
|
|
351
355
|
const n = findLane(currentOptimisticLane);
|
|
352
|
-
if (e !== n && e.
|
|
356
|
+
if (e !== n && e.Oe.size > 0) {
|
|
353
357
|
return i;
|
|
354
358
|
}
|
|
355
359
|
}
|
|
@@ -357,7 +361,7 @@ function getLatestValueComputed(e) {
|
|
|
357
361
|
// speculative value in _pendingValue; a contextless read() only surfaces
|
|
358
362
|
// _value. Overrides stay authoritative (A17), and stale readers keep the
|
|
359
363
|
// other transition's committed view, matching read()'s own selection.
|
|
360
|
-
if (t.
|
|
364
|
+
if (t.Re !== NOT_PENDING && !hasActiveOverride(t) && !(stale && t.Ae && activeTransition !== t.Ae)) return t.Re;
|
|
361
365
|
return r;
|
|
362
366
|
}
|
|
363
367
|
|
|
@@ -433,7 +437,7 @@ function getLatestValueComputed(e) {
|
|
|
433
437
|
}
|
|
434
438
|
|
|
435
439
|
function recordFreshRead(e, t) {
|
|
436
|
-
if (pendingProbe !== null && e.
|
|
440
|
+
if (pendingProbe !== null && e.Re !== NOT_PENDING && t === e.Re) {
|
|
437
441
|
if (heldAwaitingAsync(e)) return;
|
|
438
442
|
pendingProbe.freshReads.add(e);
|
|
439
443
|
}
|
|
@@ -441,10 +445,10 @@ function recordFreshRead(e, t) {
|
|
|
441
445
|
|
|
442
446
|
function applyReask(e, t) {
|
|
443
447
|
const n = !!(e.S & STATUS_PENDING);
|
|
444
|
-
const i = t && !(n && !e.o?.
|
|
445
|
-
const r = n && (e.o?.
|
|
448
|
+
const i = t && !(n && !e.o?.De);
|
|
449
|
+
const r = n && (e.o?.De ?? false) !== i;
|
|
446
450
|
// Allocation-free for the quiet case: false is the extension default.
|
|
447
|
-
if (i) ext(e).
|
|
451
|
+
if (i) ext(e).De = true; else if (e.o !== null) e.o.De = false;
|
|
448
452
|
return r;
|
|
449
453
|
}
|
|
450
454
|
|
|
@@ -525,7 +529,7 @@ function isPending(e) {
|
|
|
525
529
|
// Hook installation (same late-binding pattern as GlobalQueue._update /
|
|
526
530
|
// _propagateAffects): core call sites fire these behind the same guards the
|
|
527
531
|
// direct calls used, so behavior is identical once this module loads.
|
|
528
|
-
GlobalQueue.
|
|
532
|
+
GlobalQueue.Ue = syncCompanions;
|
|
529
533
|
|
|
530
534
|
GlobalQueue.de = updatePendingSignal;
|
|
531
535
|
|
package/dist/prod/signals.js
CHANGED
|
@@ -78,7 +78,74 @@ function createMemo(e, t) {
|
|
|
78
78
|
return accessor(computed(e, t));
|
|
79
79
|
}
|
|
80
80
|
|
|
81
|
-
|
|
81
|
+
/**
|
|
82
|
+
* Creates a reactive effect with **separate compute and effect phases**.
|
|
83
|
+
*
|
|
84
|
+
* - `compute(prev)` runs reactively — *put all reactive reads here*. The
|
|
85
|
+
* returned value is passed to `effect` and is also the new "previous" value
|
|
86
|
+
* for the next run.
|
|
87
|
+
* - `effect(next, prev?)` runs imperatively (untracked) after the queue
|
|
88
|
+
* flushes. *Put DOM writes / fetch / logging / subscriptions here.* It may
|
|
89
|
+
* return a cleanup function which runs before the next effect or on
|
|
90
|
+
* disposal.
|
|
91
|
+
*
|
|
92
|
+
* Reactive reads inside `effect` will *not* re-trigger this effect — that's
|
|
93
|
+
* intentional. If you need a single-phase tracked effect, use
|
|
94
|
+
* `createTrackedEffect` (with the tradeoffs noted there).
|
|
95
|
+
*
|
|
96
|
+
* Pass an `EffectBundle` (`{ effect, error }`) instead of a plain function to
|
|
97
|
+
* intercept **compute-phase** errors — errors thrown by `compute` or arriving
|
|
98
|
+
* from upstream reactive sources (including async rejections), which your own
|
|
99
|
+
* code has no frame to `try/catch`. The `error` handler is the error arm of
|
|
100
|
+
* the effect phase: it runs on the same schedule and in the same imperative,
|
|
101
|
+
* writable scope as `effect` (setting error state via signals is fine), and
|
|
102
|
+
* only for *settled* errors — a transient error that recovers before the
|
|
103
|
+
* effect phase runs `effect` with the recovered value instead, and a held
|
|
104
|
+
* transition defers it exactly as it defers `effect`. Without an `error`
|
|
105
|
+
* handler a compute-phase error is logged and the effect simply skips that
|
|
106
|
+
* run — a non-render effect's reactivity failing does not crash the app.
|
|
107
|
+
* Rethrowing from `error` escalates it to the nearest error boundary
|
|
108
|
+
* (halting the system if none exists).
|
|
109
|
+
*
|
|
110
|
+
* The **effect phase is different**: it is your own imperative code, so handle
|
|
111
|
+
* failures with `try/catch` where they occur. An uncaught effect-phase throw
|
|
112
|
+
* is treated as an unhandled application error — caught by the nearest
|
|
113
|
+
* `createErrorBoundary`/`<Errored>`, and permanently halting the reactive
|
|
114
|
+
* system if there is none. It is *not* routed to the bundle's `error` handler.
|
|
115
|
+
*
|
|
116
|
+
* ```typescript
|
|
117
|
+
* createEffect<T>(compute, effectFn | { effect, error }, options?: EffectOptions);
|
|
118
|
+
* ```
|
|
119
|
+
* @param compute a function that receives its previous value and returns a new value used to react on a computation
|
|
120
|
+
* @param effectFn a function that receives the new value and is used to perform side effects (return a cleanup function), or an `EffectBundle` with `effect` and `error` handlers
|
|
121
|
+
* @param options `EffectOptions` -- name, defer, schedule, transparent
|
|
122
|
+
*
|
|
123
|
+
* @example
|
|
124
|
+
* ```ts
|
|
125
|
+
* const [count, setCount] = createSignal(0);
|
|
126
|
+
*
|
|
127
|
+
* createEffect(
|
|
128
|
+
* () => count(), // compute: tracks `count`
|
|
129
|
+
* value => console.log(value) // effect: side effect
|
|
130
|
+
* );
|
|
131
|
+
*
|
|
132
|
+
* setCount(1); // logs 1 after the next flush
|
|
133
|
+
* ```
|
|
134
|
+
*
|
|
135
|
+
* @example
|
|
136
|
+
* ```ts
|
|
137
|
+
* createEffect(
|
|
138
|
+
* () => userId(),
|
|
139
|
+
* id => {
|
|
140
|
+
* const ctrl = new AbortController();
|
|
141
|
+
* fetch(`/users/${id}`, { signal: ctrl.signal });
|
|
142
|
+
* return () => ctrl.abort(); // cleanup before next run / disposal
|
|
143
|
+
* }
|
|
144
|
+
* );
|
|
145
|
+
* ```
|
|
146
|
+
*
|
|
147
|
+
* @description https://docs.solidjs.com/reference/basic-reactivity/create-effect
|
|
148
|
+
*/ function createEffect(e, t, n) {
|
|
82
149
|
effect(e, t.effect || t, t.error, {
|
|
83
150
|
user: true,
|
|
84
151
|
...n
|