@solidjs/signals 2.0.0-beta.26 → 2.0.0-beta.27
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 +258 -67
- package/dist/node.cjs +1117 -915
- package/dist/prod/core/action.js +11 -6
- package/dist/prod/core/async.js +42 -11
- package/dist/prod/core/core.js +83 -69
- package/dist/prod/core/external.js +2 -2
- package/dist/prod/core/graph.js +4 -4
- package/dist/prod/core/lanes.js +1 -1
- package/dist/prod/core/optimistic.js +5 -5
- package/dist/prod/core/owner.js +1 -1
- package/dist/prod/core/scheduler.js +53 -32
- package/dist/prod/core/verdict.js +26 -26
- package/dist/prod/map.js +2 -2
- package/dist/prod/store/optimistic.js +52 -33
- package/dist/prod/store/projection.js +38 -33
- package/dist/prod/store/reconcile.js +299 -243
- package/dist/prod/store/store.js +98 -47
- package/dist/types/core/async.d.ts +1 -0
- package/dist/types/core/scheduler.d.ts +1 -0
- package/dist/types/store/optimistic.d.ts +2 -2
- package/dist/types/store/projection.d.ts +7 -2
- package/dist/types/store/reconcile.d.ts +14 -0
- package/dist/types/store/store.d.ts +2 -2
- package/dist/types-cjs/core/async.d.cts +1 -0
- package/dist/types-cjs/core/scheduler.d.cts +1 -0
- package/dist/types-cjs/store/optimistic.d.cts +2 -2
- package/dist/types-cjs/store/projection.d.cts +7 -2
- package/dist/types-cjs/store/reconcile.d.cts +14 -0
- package/dist/types-cjs/store/store.d.cts +2 -2
- package/package.json +1 -1
package/dist/prod/core/action.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { globalQueue, activeTransition, currentTransition,
|
|
1
|
+
import { globalQueue, activeTransition, currentTransition, schedule, flush } from "./scheduler.js";
|
|
2
2
|
|
|
3
3
|
import { isThenable } from "./async.js";
|
|
4
4
|
|
|
@@ -79,13 +79,18 @@ function restoreTransition(e, r) {
|
|
|
79
79
|
globalQueue.initTransition();
|
|
80
80
|
let o = activeTransition;
|
|
81
81
|
o.ie.push(i);
|
|
82
|
-
const done = (e, r,
|
|
82
|
+
const done = (e, r, u = false) => {
|
|
83
83
|
o = currentTransition(o);
|
|
84
|
-
const
|
|
85
|
-
if (
|
|
86
|
-
|
|
84
|
+
const s = o.ie.indexOf(i);
|
|
85
|
+
if (s >= 0) o.ie.splice(s, 1);
|
|
86
|
+
// Re-adopt through initTransition like every other resumption site:
|
|
87
|
+
// a bare setActiveTransition leaves globalQueue._batch as a detached
|
|
88
|
+
// ambient batch, and anything registered before the scheduled flush
|
|
89
|
+
// (held writes on a merging transition, optimistic overrides,
|
|
90
|
+
// affects() marks) lands there with nothing to ever finalize it.
|
|
91
|
+
globalQueue.initTransition(o);
|
|
87
92
|
schedule();
|
|
88
|
-
|
|
93
|
+
u ? n(r) : t(e);
|
|
89
94
|
};
|
|
90
95
|
const step = (e, r) => {
|
|
91
96
|
let t;
|
package/dist/prod/core/async.js
CHANGED
|
@@ -92,6 +92,37 @@ function releaseSettledDependents(e) {
|
|
|
92
92
|
if (n) for (const e of n) releaseIfSettledUnobserved(e);
|
|
93
93
|
}
|
|
94
94
|
|
|
95
|
+
// Error-dimension twin of settlePendingSource's blocked re-enqueue (#2949):
|
|
96
|
+
// a node in STATUS_ERROR that recovers by recomputing to an UNCHANGED value
|
|
97
|
+
// fires no value notification — the recovery is completely silent. But a
|
|
98
|
+
// dependent that re-ran during the error window consumed its dirty flag and
|
|
99
|
+
// committed nothing (the fresh sibling values it read were absorbed into an
|
|
100
|
+
// errored run), so its committed value is stale. The propagated error is one
|
|
101
|
+
// object identity down the whole dependent tree, and holding it is exactly
|
|
102
|
+
// the "blocked on this error" marker — re-enqueue those holders so they
|
|
103
|
+
// re-run: fresh values commit and flow, and a dependent with another
|
|
104
|
+
// still-broken source simply re-errors. The async dimension needs no twin of
|
|
105
|
+
// its own: recovery there passes through a pending window whose re-runners
|
|
106
|
+
// set _blocked and ride settlePendingSource. Walks the full dependent graph
|
|
107
|
+
// (releaseSettledDependents shape): identity holders can sit below an
|
|
108
|
+
// intermediate whose own error state has since been scrubbed or replaced
|
|
109
|
+
// (e.g. an error boundary's tree node).
|
|
110
|
+
function settleErroredDependents(e, n) {
|
|
111
|
+
let t = false;
|
|
112
|
+
const r = new Set;
|
|
113
|
+
const visit = e => {
|
|
114
|
+
if (r.has(e)) return;
|
|
115
|
+
r.add(e);
|
|
116
|
+
if (e._ === n) {
|
|
117
|
+
enqueueSub(e);
|
|
118
|
+
t = true;
|
|
119
|
+
}
|
|
120
|
+
forEachDependent(e, visit);
|
|
121
|
+
};
|
|
122
|
+
forEachDependent(e, visit);
|
|
123
|
+
if (t) schedule();
|
|
124
|
+
}
|
|
125
|
+
|
|
95
126
|
function settlePendingSource(e) {
|
|
96
127
|
let n = false;
|
|
97
128
|
let t;
|
|
@@ -206,8 +237,8 @@ function handleAsync(e, n, t) {
|
|
|
206
237
|
// (A17 — every reader sees the override); the revert reveals whatever
|
|
207
238
|
// has committed by then, so corrections reveal atomically with their
|
|
208
239
|
// transition rather than escaping it.
|
|
209
|
-
if (e.
|
|
210
|
-
e.
|
|
240
|
+
if (e.De === NOT_PENDING) queuePendingNode(e);
|
|
241
|
+
e.De = r;
|
|
211
242
|
// The hold is a companion-visible write like any other (A13/A19): the
|
|
212
243
|
// clearStatus() above computed its verdict before the hold existed, so
|
|
213
244
|
// isPending must re-derive (the value is not final until commit — V1)
|
|
@@ -215,14 +246,14 @@ function handleAsync(e, n, t) {
|
|
|
215
246
|
// only notified when the hold is visible to them: under an active
|
|
216
247
|
// override every reader sees the override (A17), so waking subs would
|
|
217
248
|
// re-show an unchanged view — the revert is the notification point.
|
|
218
|
-
GlobalQueue.
|
|
249
|
+
GlobalQueue._e !== null && GlobalQueue._e(e, r);
|
|
219
250
|
if (!hasActiveOverride(e)) insertSubs(e);
|
|
220
251
|
e.Se = clock;
|
|
221
252
|
} else if (l) {
|
|
222
253
|
// Route through lane's effect queue for independent flushing
|
|
223
254
|
const n = e.Pe;
|
|
224
255
|
const t = e.Ue;
|
|
225
|
-
const o = e.
|
|
256
|
+
const o = e.be;
|
|
226
257
|
try {
|
|
227
258
|
if (!n && u || !o || !o(r, t)) {
|
|
228
259
|
e.Ue = r;
|
|
@@ -230,7 +261,7 @@ function handleAsync(e, n, t) {
|
|
|
230
261
|
// The latest() shadow write gives latest() effects independent lanes; the
|
|
231
262
|
// _pendingSignal update is a no-op repeat of the clearStatus() call above
|
|
232
263
|
// (computePendingState doesn't read _value).
|
|
233
|
-
GlobalQueue.
|
|
264
|
+
GlobalQueue._e !== null && GlobalQueue._e(e, r);
|
|
234
265
|
insertSubs(e, true);
|
|
235
266
|
}
|
|
236
267
|
} catch (n) {
|
|
@@ -383,13 +414,13 @@ function clearStatus(e, n = false) {
|
|
|
383
414
|
// The pending window is over; its quiet classification dies with it.
|
|
384
415
|
// (Unconditional: _reask is baked into the node literals, so this is a
|
|
385
416
|
// plain store to an existing slot — no shape change.)
|
|
386
|
-
e.
|
|
417
|
+
e.Re = false;
|
|
387
418
|
e.S = n ? 0 : e.S & STATUS_UNINITIALIZED;
|
|
388
419
|
if (e._) setPendingError(e);
|
|
389
420
|
// Update pending signal for isPending() reactivity (companions only exist
|
|
390
421
|
// once the verdict layer created them, which installs the hooks).
|
|
391
|
-
if (e.he || e.
|
|
392
|
-
if (e.u && GlobalQueue.
|
|
422
|
+
if (e.he || e.pe) GlobalQueue.ce(e);
|
|
423
|
+
if (e.u && GlobalQueue.Ge !== null) GlobalQueue.Ge(e);
|
|
393
424
|
if (e.i) e.i();
|
|
394
425
|
}
|
|
395
426
|
|
|
@@ -413,7 +444,7 @@ function notifyStatus(e, n, t, r, o) {
|
|
|
413
444
|
e._ = t;
|
|
414
445
|
}
|
|
415
446
|
GlobalQueue.ce !== null && GlobalQueue.ce(e);
|
|
416
|
-
if (e.u && GlobalQueue.
|
|
447
|
+
if (e.u && GlobalQueue.Ge !== null) GlobalQueue.Ge(e);
|
|
417
448
|
}
|
|
418
449
|
if (o && !r) {
|
|
419
450
|
assignOrMergeLane(e, o);
|
|
@@ -439,7 +470,7 @@ function notifyStatus(e, n, t, r, o) {
|
|
|
439
470
|
// not carry a real (non-NotReadyError) error — the synchronous `isPending`
|
|
440
471
|
// read swallows those, and the async path must match. Re-run the observer
|
|
441
472
|
// so `isPending` re-evaluates (to not-pending) instead of forwarding.
|
|
442
|
-
if (r.
|
|
473
|
+
if (r.Oe && n !== STATUS_PENDING && !(t instanceof NotReadyError)) {
|
|
443
474
|
enqueueSub(e);
|
|
444
475
|
schedule();
|
|
445
476
|
return;
|
|
@@ -450,4 +481,4 @@ function notifyStatus(e, n, t, r, o) {
|
|
|
450
481
|
});
|
|
451
482
|
}
|
|
452
483
|
|
|
453
|
-
export { addPendingSource, clearStatus, forEachDependent, handleAsync, isThenable, notifyStatus, releaseSettledDependents, setPendingError, settlePendingSource };
|
|
484
|
+
export { addPendingSource, clearStatus, forEachDependent, handleAsync, isThenable, notifyStatus, releaseSettledDependents, setPendingError, settleErroredDependents, settlePendingSource };
|
package/dist/prod/core/core.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { handleAsync, clearStatus, notifyStatus } from "./async.js";
|
|
1
|
+
import { handleAsync, clearStatus, notifyStatus, settleErroredDependents } from "./async.js";
|
|
2
2
|
|
|
3
|
-
import { EFFECT_TRACKED, REACTIVE_OPTIMISTIC_DIRTY, NOT_PENDING, STATUS_UNINITIALIZED, REACTIVE_REASK, REACTIVE_RECOMPUTING_DEPS, CONFIG_SYNC, STATUS_PENDING,
|
|
3
|
+
import { EFFECT_TRACKED, REACTIVE_OPTIMISTIC_DIRTY, NOT_PENDING, STATUS_UNINITIALIZED, STATUS_ERROR, REACTIVE_REASK, REACTIVE_RECOMPUTING_DEPS, CONFIG_SYNC, STATUS_PENDING, REACTIVE_NONE, REACTIVE_SNAPSHOT_STALE, unwrapOverride, OVERRIDE_UNDEFINED, REACTIVE_LAZY, REACTIVE_DISPOSED, CONFIG_AUTO_DISPOSE, 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";
|
|
4
4
|
|
|
5
5
|
import { NotReadyError } from "./error.js";
|
|
6
6
|
|
|
@@ -14,9 +14,9 @@ import "./invariants.js";
|
|
|
14
14
|
|
|
15
15
|
import { disposeChildren, markDisposal, inheritId } from "./owner.js";
|
|
16
16
|
|
|
17
|
-
GlobalQueue.
|
|
17
|
+
GlobalQueue.Ce = recompute;
|
|
18
18
|
|
|
19
|
-
GlobalQueue.
|
|
19
|
+
GlobalQueue.me = disposeChildren;
|
|
20
20
|
|
|
21
21
|
let tracking = false;
|
|
22
22
|
|
|
@@ -48,7 +48,7 @@ let snapshotSources = null;
|
|
|
48
48
|
|
|
49
49
|
function ownerInSnapshotScope(e) {
|
|
50
50
|
while (e) {
|
|
51
|
-
if (e.
|
|
51
|
+
if (e.ge) return true;
|
|
52
52
|
e = e.ve;
|
|
53
53
|
}
|
|
54
54
|
return false;
|
|
@@ -60,11 +60,11 @@ function setSnapshotCapture(e) {
|
|
|
60
60
|
}
|
|
61
61
|
|
|
62
62
|
function markSnapshotScope(e) {
|
|
63
|
-
e.
|
|
63
|
+
e.ge = true;
|
|
64
64
|
}
|
|
65
65
|
|
|
66
66
|
function releaseSnapshotScope(e) {
|
|
67
|
-
e.
|
|
67
|
+
e.ge = false;
|
|
68
68
|
releaseSubtree(e);
|
|
69
69
|
schedule();
|
|
70
70
|
}
|
|
@@ -72,7 +72,7 @@ function releaseSnapshotScope(e) {
|
|
|
72
72
|
function releaseSubtree(e) {
|
|
73
73
|
let t = e.ke;
|
|
74
74
|
while (t) {
|
|
75
|
-
if (t.
|
|
75
|
+
if (t.ge) {
|
|
76
76
|
t = t.Fe;
|
|
77
77
|
continue;
|
|
78
78
|
}
|
|
@@ -124,19 +124,23 @@ function recompute(e, t = false) {
|
|
|
124
124
|
let i = !!(e.se & REACTIVE_OPTIMISTIC_DIRTY);
|
|
125
125
|
const u = e.Ae !== undefined && e.Ae !== NOT_PENDING;
|
|
126
126
|
const l = !!(e.S & STATUS_UNINITIALIZED);
|
|
127
|
+
// Outgoing error, captured before the compute clears status: if this run
|
|
128
|
+
// recovers to an unchanged value, dependents still holding this object must
|
|
129
|
+
// be swept (settleErroredDependents, #2949).
|
|
130
|
+
const o = e.S & STATUS_ERROR ? e._ : undefined;
|
|
127
131
|
// Re-ask classification lives in the verdict module; capture the flag before
|
|
128
132
|
// the recompute wipes _flags below.
|
|
129
|
-
const
|
|
130
|
-
const
|
|
133
|
+
const s = (e.se & REACTIVE_REASK) !== 0;
|
|
134
|
+
const a = context;
|
|
131
135
|
context = e;
|
|
132
136
|
e.We = null;
|
|
133
137
|
e.Ye++;
|
|
134
138
|
e.se = REACTIVE_RECOMPUTING_DEPS;
|
|
135
139
|
e.Se = clock;
|
|
136
|
-
let
|
|
137
|
-
let
|
|
138
|
-
let
|
|
139
|
-
let
|
|
140
|
+
let r = e.De === NOT_PENDING ? e.Ue : e.De;
|
|
141
|
+
let c = e.Ve;
|
|
142
|
+
let _ = tracking;
|
|
143
|
+
let f = currentOptimisticLane;
|
|
140
144
|
tracking = true;
|
|
141
145
|
// A computed's fn establishes its OWN dependencies, so it must never run
|
|
142
146
|
// inside a latest() read window: read() short-circuits through the
|
|
@@ -144,7 +148,7 @@ function recompute(e, t = false) {
|
|
|
144
148
|
// computed) inside latest(fn) came out permanently dependency-less (#2926).
|
|
145
149
|
// latestRead() already suspends the flag for its pull-recomputes; this
|
|
146
150
|
// covers creation-time computes and flushes that run inside the window.
|
|
147
|
-
const
|
|
151
|
+
const E = latestReadActive;
|
|
148
152
|
latestReadActive = false;
|
|
149
153
|
// Lane posture lives with the engine: OPTIMISTIC_DIRTY is only ever set by
|
|
150
154
|
// engine-driven paths, and _optimisticNodes is only pushed by
|
|
@@ -162,12 +166,12 @@ function recompute(e, t = false) {
|
|
|
162
166
|
currentOptimisticLane = t;
|
|
163
167
|
}
|
|
164
168
|
}
|
|
165
|
-
const
|
|
166
|
-
const
|
|
167
|
-
if (
|
|
169
|
+
const N = n && n !== EFFECT_USER;
|
|
170
|
+
const T = stale;
|
|
171
|
+
if (N) stale = true;
|
|
168
172
|
try {
|
|
169
173
|
if (!false && e.T & CONFIG_SYNC) {
|
|
170
|
-
|
|
174
|
+
r = e.ae(r);
|
|
171
175
|
e.Te = null;
|
|
172
176
|
} else {
|
|
173
177
|
// Snapshot `_inFlight` so we can detect whether `_fn` self-registered an async
|
|
@@ -176,16 +180,16 @@ function recompute(e, t = false) {
|
|
|
176
180
|
// clobber the fresh subscription, so we skip it and let the internally-registered
|
|
177
181
|
// iteration drive updates.
|
|
178
182
|
const t = e.Te;
|
|
179
|
-
const n = e.ae(
|
|
183
|
+
const n = e.ae(r);
|
|
180
184
|
const i = typeof n === "object" && n !== null;
|
|
181
185
|
const u = e.Te !== t;
|
|
182
|
-
|
|
186
|
+
r = u || !i ? n : handleAsync(e, n);
|
|
183
187
|
if (!u && !i) e.Te = null;
|
|
184
188
|
}
|
|
185
189
|
// On a status-free node clearStatus is a guaranteed no-op: every branch
|
|
186
190
|
// in its body is gated on one of these fields, and with _statusFlags === 0
|
|
187
191
|
// the flags write (create or not) stores the 0 already there.
|
|
188
|
-
if (e.S !== 0 || e.i !== undefined || e._ || e.
|
|
192
|
+
if (e.S !== 0 || e.i !== undefined || e._ || e.Re || e.de || e.oe !== undefined || e.he !== undefined || e.pe !== undefined || e.u !== null) clearStatus(e, t);
|
|
189
193
|
// _optimisticLane is only ever assigned by engine paths.
|
|
190
194
|
if (e.Me) GlobalQueue.Ke(e);
|
|
191
195
|
} catch (t) {
|
|
@@ -195,23 +199,23 @@ function recompute(e, t = false) {
|
|
|
195
199
|
let n = false;
|
|
196
200
|
if (t instanceof NotReadyError) {
|
|
197
201
|
e.de = true;
|
|
198
|
-
if (GlobalQueue.$e !== null) n = GlobalQueue.$e(e,
|
|
202
|
+
if (GlobalQueue.$e !== null) n = GlobalQueue.$e(e, s);
|
|
199
203
|
}
|
|
200
204
|
notifyStatus(e, t instanceof NotReadyError ? STATUS_PENDING : STATUS_ERROR, t, undefined, t instanceof NotReadyError ? e.Me : undefined);
|
|
201
205
|
if (n) GlobalQueue.k(e);
|
|
202
206
|
} finally {
|
|
203
|
-
tracking =
|
|
204
|
-
latestReadActive =
|
|
205
|
-
if (
|
|
207
|
+
tracking = _;
|
|
208
|
+
latestReadActive = E;
|
|
209
|
+
if (N) stale = T;
|
|
206
210
|
e.se = REACTIVE_NONE | (t ? e.se & REACTIVE_SNAPSHOT_STALE : 0);
|
|
207
|
-
context =
|
|
211
|
+
context = a;
|
|
208
212
|
}
|
|
209
213
|
if (!e._) {
|
|
210
214
|
trimStaleDeps(e);
|
|
211
|
-
const
|
|
212
|
-
let
|
|
215
|
+
const s = u ? unwrapOverride(e.Ae) : e.De === NOT_PENDING ? e.Ue : e.De;
|
|
216
|
+
let a = false;
|
|
213
217
|
try {
|
|
214
|
-
|
|
218
|
+
a = !n && l || !e.be || !e.be(s, r);
|
|
215
219
|
} catch (t) {
|
|
216
220
|
// A throwing user comparator is an error of this node's computation.
|
|
217
221
|
// Route it through the same status path as a compute-phase throw so
|
|
@@ -224,13 +228,13 @@ function recompute(e, t = false) {
|
|
|
224
228
|
// its runner — happen here instead. `!create` matches the previous `initialized`
|
|
225
229
|
// gate: the explicit recompute(node, true) inside effect() does not enqueue, so
|
|
226
230
|
// effect() can call its runner synchronously for the first run.
|
|
227
|
-
if (n &&
|
|
231
|
+
if (n && a) {
|
|
228
232
|
e.Be = !e._;
|
|
229
233
|
// Reuse one bound runner per effect — runEffect no-ops on a stale
|
|
230
234
|
// `_modified`, so re-enqueueing the same function is harmless.
|
|
231
235
|
if (!t) e.C.enqueue(n, e.Je ??= GlobalQueue.Xe.bind(null, e));
|
|
232
236
|
}
|
|
233
|
-
if (e._) ; else if (
|
|
237
|
+
if (e._) ; else if (a) {
|
|
234
238
|
const l = u ? e.Ae : undefined;
|
|
235
239
|
if (t ||
|
|
236
240
|
// Plain sync flush (no transition on either side) commits effect
|
|
@@ -238,23 +242,23 @@ function recompute(e, t = false) {
|
|
|
238
242
|
// commitPendingNodes) exists to sequence transition reveals, and
|
|
239
243
|
// paying it per effect on the plain path is pure overhead.
|
|
240
244
|
n && (activeTransition !== e.Ie || activeTransition === null) || i) {
|
|
241
|
-
e.Ue =
|
|
245
|
+
e.Ue = r;
|
|
242
246
|
// Lane-propagated correction: upstream data is fresh, correct the
|
|
243
247
|
// override unconditionally. The direct _value commit is the lane's
|
|
244
248
|
// own reveal schedule; drop any superseded older hold so its queued
|
|
245
249
|
// commit can't clobber the fresh value.
|
|
246
250
|
if (u && i) {
|
|
247
|
-
e.Ae =
|
|
248
|
-
e.
|
|
251
|
+
e.Ae = r === undefined ? OVERRIDE_UNDEFINED : r;
|
|
252
|
+
e.De = NOT_PENDING;
|
|
249
253
|
}
|
|
250
254
|
} else {
|
|
251
|
-
e.
|
|
255
|
+
e.De = r;
|
|
252
256
|
// Transition-held sync recompute is a write path like setSignal/asyncWrite,
|
|
253
257
|
// so sync derivations of held sources stay visible to isPending()/latest()
|
|
254
258
|
// (#2831). Both companion writes are transition-scoped (optimistic) and
|
|
255
259
|
// auto-revert/re-derive at commit. Skipped for plain flushes where the
|
|
256
260
|
// pending value commits before effects run.
|
|
257
|
-
if ((activeTransition || e.Ie) && GlobalQueue.
|
|
261
|
+
if ((activeTransition || e.Ie) && GlobalQueue._e !== null) GlobalQueue._e(e, r);
|
|
258
262
|
}
|
|
259
263
|
// insertSubs only walks _subs (no scheduling of its own), so a
|
|
260
264
|
// subscriber-less node has nothing to notify.
|
|
@@ -263,22 +267,28 @@ function recompute(e, t = false) {
|
|
|
263
267
|
// Unchanged value (equals the override) recomputed while the override
|
|
264
268
|
// is active: _value may still be stale, so hold the authoritative value
|
|
265
269
|
// for commit on its own transition's schedule — invisibly (A17/A18).
|
|
266
|
-
if (e.
|
|
267
|
-
e.
|
|
268
|
-
} else if (e.Ve !=
|
|
270
|
+
if (e.De === NOT_PENDING) queuePendingNode(e);
|
|
271
|
+
e.De = r;
|
|
272
|
+
} else if (e.Ve != c) {
|
|
269
273
|
for (let t = e.o; t !== null; t = t.ue) {
|
|
270
274
|
insertIntoHeapHeight(t.le, queueFor(t.le));
|
|
271
275
|
}
|
|
272
276
|
}
|
|
277
|
+
// Silent recovery: errored → unchanged value fires no notification, but
|
|
278
|
+
// dependents still holding the propagated error consumed their dirty flag
|
|
279
|
+
// in an errored run and may sit on stale commits (#2949). Changed-value
|
|
280
|
+
// recoveries ride insertSubs above; a comparator throw re-errored the node
|
|
281
|
+
// (el._error re-set), so this only runs on a genuinely clean recovery.
|
|
282
|
+
if (o !== undefined && !a && !e._) settleErroredDependents(e, o);
|
|
273
283
|
}
|
|
274
|
-
currentOptimisticLane =
|
|
275
|
-
const
|
|
284
|
+
currentOptimisticLane = f;
|
|
285
|
+
const d = e.De !== NOT_PENDING || e.Qe !== null || e.ye !== null || (e.S & (STATUS_PENDING | STATUS_UNINITIALIZED)) !== 0;
|
|
276
286
|
// Override-covered holds (hasOverride) always queue: their commit belongs
|
|
277
287
|
// to their own transition's schedule (A18 re-rule) and is unobservable
|
|
278
288
|
// under the override (A17). Revert no longer commits anything, so an
|
|
279
289
|
// unqueued covered hold would leak (INV-7) once the revert clears
|
|
280
290
|
// _transition.
|
|
281
|
-
|
|
291
|
+
d && (!t || e.S & STATUS_PENDING) && (!e.Ie || u) && queuePendingNode(e);
|
|
282
292
|
e.Ie && n && activeTransition !== e.Ie && runInTransition(e.Ie, () => recompute(e));
|
|
283
293
|
}
|
|
284
294
|
|
|
@@ -306,7 +316,7 @@ function computed(e, t) {
|
|
|
306
316
|
const i = {
|
|
307
317
|
id: inheritId(t, n, context),
|
|
308
318
|
T: (n ? CONFIG_TRANSPARENT : 0) | (t?.ownedWrite ? CONFIG_OWNED_WRITE : 0) | (!context || t?.lazy ? CONFIG_AUTO_DISPOSE : 0) | (t?.sync ? CONFIG_SYNC : 0) | (t?.V ? CONFIG_NO_SNAPSHOT : 0) | (snapshotCaptureActive && ownerInSnapshotScope(context) ? CONFIG_IN_SNAPSHOT_SCOPE : 0),
|
|
309
|
-
|
|
319
|
+
be: t?.equals != null ? t.equals : isEqual,
|
|
310
320
|
ut: t?.unobserved,
|
|
311
321
|
Le: null,
|
|
312
322
|
C: context?.C ?? globalQueue,
|
|
@@ -330,12 +340,12 @@ function computed(e, t) {
|
|
|
330
340
|
se: t?.lazy ? REACTIVE_LAZY : REACTIVE_NONE,
|
|
331
341
|
S: STATUS_UNINITIALIZED,
|
|
332
342
|
Se: clock,
|
|
333
|
-
|
|
343
|
+
De: NOT_PENDING,
|
|
334
344
|
ye: null,
|
|
335
345
|
Qe: null,
|
|
336
346
|
Te: null,
|
|
337
347
|
Ie: null,
|
|
338
|
-
|
|
348
|
+
Re: false
|
|
339
349
|
};
|
|
340
350
|
setupComputedNode(i, t);
|
|
341
351
|
return i;
|
|
@@ -351,7 +361,7 @@ function computed(e, t) {
|
|
|
351
361
|
const s = {
|
|
352
362
|
id: inheritId(l, o, context),
|
|
353
363
|
T: (o ? CONFIG_TRANSPARENT : 0) | (l?.ownedWrite ? CONFIG_OWNED_WRITE : 0) | (l?.sync ? CONFIG_SYNC : 0) | (snapshotCaptureActive && ownerInSnapshotScope(context) ? CONFIG_IN_SNAPSHOT_SCOPE : 0),
|
|
354
|
-
|
|
364
|
+
be: false,
|
|
355
365
|
ut: l?.unobserved,
|
|
356
366
|
Le: null,
|
|
357
367
|
C: context?.C ?? globalQueue,
|
|
@@ -375,12 +385,12 @@ function computed(e, t) {
|
|
|
375
385
|
se: REACTIVE_LAZY,
|
|
376
386
|
S: STATUS_UNINITIALIZED,
|
|
377
387
|
Se: clock,
|
|
378
|
-
|
|
388
|
+
De: NOT_PENDING,
|
|
379
389
|
ye: null,
|
|
380
390
|
Qe: null,
|
|
381
391
|
Te: null,
|
|
382
392
|
Ie: null,
|
|
383
|
-
|
|
393
|
+
Re: false,
|
|
384
394
|
Be: false,
|
|
385
395
|
ct: undefined,
|
|
386
396
|
_t: t,
|
|
@@ -411,7 +421,7 @@ function setupComputedNode(e, t) {
|
|
|
411
421
|
}
|
|
412
422
|
}
|
|
413
423
|
if (n) e.Ve = n.Ve + 1;
|
|
414
|
-
if (GlobalQueue.
|
|
424
|
+
if (GlobalQueue.dt !== null) GlobalQueue.dt(e);
|
|
415
425
|
!t?.lazy && recompute(e, true);
|
|
416
426
|
if (snapshotCaptureActive && !t?.lazy) {
|
|
417
427
|
if (!(e.S & STATUS_PENDING) && !(e.T & CONFIG_NO_SNAPSHOT)) {
|
|
@@ -423,7 +433,7 @@ function setupComputedNode(e, t) {
|
|
|
423
433
|
|
|
424
434
|
function signal(e, t, n = null) {
|
|
425
435
|
const i = {
|
|
426
|
-
|
|
436
|
+
be: t?.equals != null ? t.equals : isEqual,
|
|
427
437
|
T: (t?.ownedWrite ? CONFIG_OWNED_WRITE : 0) | (t?.V ? CONFIG_NO_SNAPSHOT : 0),
|
|
428
438
|
ut: t?.unobserved,
|
|
429
439
|
Ue: e,
|
|
@@ -432,7 +442,7 @@ function signal(e, t, n = null) {
|
|
|
432
442
|
Se: clock,
|
|
433
443
|
it: n,
|
|
434
444
|
fe: n?.u || null,
|
|
435
|
-
|
|
445
|
+
De: NOT_PENDING
|
|
436
446
|
};
|
|
437
447
|
n && (n.u = i);
|
|
438
448
|
if (snapshotCaptureActive && !(i.T & CONFIG_NO_SNAPSHOT) && !((n?.S ?? 0) & STATUS_PENDING)) {
|
|
@@ -480,11 +490,11 @@ function isEqual(e, t) {
|
|
|
480
490
|
* );
|
|
481
491
|
* ```
|
|
482
492
|
*/ function untrack(e, t) {
|
|
483
|
-
if (GlobalQueue.
|
|
493
|
+
if (GlobalQueue.It === null && !tracking && true) return e();
|
|
484
494
|
const n = tracking;
|
|
485
495
|
tracking = false;
|
|
486
496
|
try {
|
|
487
|
-
if (GlobalQueue.
|
|
497
|
+
if (GlobalQueue.It !== null) return GlobalQueue.It(e);
|
|
488
498
|
return e();
|
|
489
499
|
} finally {
|
|
490
500
|
tracking = n;
|
|
@@ -524,7 +534,7 @@ function isEqual(e, t) {
|
|
|
524
534
|
let t = context;
|
|
525
535
|
if (t?.Nt) t = t.Tt;
|
|
526
536
|
if (t && tracking) link(e, t);
|
|
527
|
-
return !t || e.
|
|
537
|
+
return !t || e.De === NOT_PENDING ? e.Ue : e.De;
|
|
528
538
|
}
|
|
529
539
|
|
|
530
540
|
function read(e) {
|
|
@@ -548,7 +558,7 @@ function read(e) {
|
|
|
548
558
|
}
|
|
549
559
|
if (!n.ae && u === e && e.Ae === undefined && e.xe === undefined && activeTransition === null && currentOptimisticLane === null && !snapshotCaptureActive && true) {
|
|
550
560
|
if (t && tracking) link(e, t);
|
|
551
|
-
return !t || e.
|
|
561
|
+
return !t || e.De === NOT_PENDING ? e.Ue : e.De;
|
|
552
562
|
}
|
|
553
563
|
if (t && tracking) {
|
|
554
564
|
link(e, t, pendingCheckActive);
|
|
@@ -582,21 +592,25 @@ function read(e) {
|
|
|
582
592
|
throw u._;
|
|
583
593
|
}
|
|
584
594
|
}
|
|
585
|
-
|
|
595
|
+
// `owner` is the computed itself, or the firewall behind a store node —
|
|
596
|
+
// firewall-backed reads follow the same rules (memo parity, #2897 ruling):
|
|
597
|
+
// an errored derive throws for every late reader instead of silently
|
|
598
|
+
// serving node values (the seed, or last-good data after a failed refetch).
|
|
599
|
+
if (u.ae && u.S & STATUS_ERROR) {
|
|
586
600
|
// Only a genuine reactive re-read may retry an errored async source:
|
|
587
601
|
// - tracking: owned/tracked scope only (never events / `untrack` / effect side-effect phase)
|
|
588
602
|
// - !pendingCheckActive: an `isPending` probe observes the error, never refetches
|
|
589
|
-
// -
|
|
590
|
-
if (tracking && !pendingCheckActive &&
|
|
591
|
-
recompute(
|
|
603
|
+
// - owner._time < clock: only on a later cycle than the one the error was found
|
|
604
|
+
if (tracking && !pendingCheckActive && u.Se < clock) {
|
|
605
|
+
recompute(u);
|
|
592
606
|
return read(e);
|
|
593
|
-
} else throw
|
|
607
|
+
} else throw u._;
|
|
594
608
|
}
|
|
595
609
|
if (snapshotCaptureActive && t && t.T & CONFIG_IN_SNAPSHOT_SCOPE) {
|
|
596
610
|
const n = e.xe;
|
|
597
611
|
if (n !== undefined) {
|
|
598
612
|
const i = n === NO_SNAPSHOT ? undefined : n;
|
|
599
|
-
const u = e.
|
|
613
|
+
const u = e.De !== NOT_PENDING ? e.De : e.Ue;
|
|
600
614
|
if (u !== i) t.se |= REACTIVE_SNAPSHOT_STALE;
|
|
601
615
|
return i;
|
|
602
616
|
}
|
|
@@ -619,7 +633,7 @@ function read(e) {
|
|
|
619
633
|
// and for regular signals in stale mode (render effects). Non-stale readers (user
|
|
620
634
|
// effects) see _pendingValue so that latest() and direct reads stay consistent.
|
|
621
635
|
// (The lane-context clause lives with the engine.)
|
|
622
|
-
const l = !t || currentOptimisticLane !== null && GlobalQueue.Rt(e, u, t) || e.
|
|
636
|
+
const l = !t || currentOptimisticLane !== null && GlobalQueue.Rt(e, u, t) || e.De === NOT_PENDING || stale && e.Ie && activeTransition !== e.Ie ? e.Ue : e.De;
|
|
623
637
|
// Record that this isPending() probe observed the fresh pending value, so
|
|
624
638
|
// the probe doesn't pair "pending" with the new value (#2831).
|
|
625
639
|
if (pendingCheckActive) GlobalQueue.Pt(e, l);
|
|
@@ -636,18 +650,18 @@ function setSignal(e, t) {
|
|
|
636
650
|
// _overrideValue slot, and every module that creates one installs the
|
|
637
651
|
// engine first.
|
|
638
652
|
if (e.Ae !== undefined && !projectionWriteActive) return GlobalQueue.ht(e, t);
|
|
639
|
-
const n = e.
|
|
653
|
+
const n = e.De === NOT_PENDING ? e.Ue : e.De;
|
|
640
654
|
if (typeof t === "function") t = t(n);
|
|
641
655
|
// Uninitialized check first: the first commit has no previous value, so the
|
|
642
656
|
// user comparator must not run against `undefined` (matches recompute).
|
|
643
|
-
const i = !!(e.S & STATUS_UNINITIALIZED) || !e.
|
|
657
|
+
const i = !!(e.S & STATUS_UNINITIALIZED) || !e.be || !e.be(n, t);
|
|
644
658
|
if (!i) return t;
|
|
645
|
-
if (e.
|
|
646
|
-
e.
|
|
659
|
+
if (e.De === NOT_PENDING) queuePendingNode(e);
|
|
660
|
+
e.De = t;
|
|
647
661
|
// syncCompanions only pokes _pendingSignal/_latestValueComputed — with
|
|
648
662
|
// neither companion present the call is a guaranteed no-op (companions are
|
|
649
663
|
// only ever created, never removed, and creating one installs the hook).
|
|
650
|
-
(e.he !== undefined || e.
|
|
664
|
+
(e.he !== undefined || e.pe !== undefined) && GlobalQueue._e !== null && GlobalQueue._e(e, t);
|
|
651
665
|
e.Se = clock;
|
|
652
666
|
insertSubs(e);
|
|
653
667
|
schedule();
|
|
@@ -662,7 +676,7 @@ function setSignal(e, t) {
|
|
|
662
676
|
* cleanup point.
|
|
663
677
|
*/ function suppressComputedRecompute(e) {
|
|
664
678
|
deleteFromHeap(e, queueFor(e));
|
|
665
|
-
if (!(e.se & REACTIVE_MANUAL_WRITE) && e.
|
|
679
|
+
if (!(e.se & REACTIVE_MANUAL_WRITE) && e.De === NOT_PENDING) {
|
|
666
680
|
queuePendingNode(e);
|
|
667
681
|
schedule();
|
|
668
682
|
}
|
|
@@ -64,8 +64,8 @@ function externalUntrack(e) {
|
|
|
64
64
|
// removed on reset) so core's null checks stay equivalent to the old
|
|
65
65
|
// `externalSourceConfig` truthiness checks.
|
|
66
66
|
function syncExternalHooks() {
|
|
67
|
-
GlobalQueue.
|
|
68
|
-
GlobalQueue.
|
|
67
|
+
GlobalQueue.dt = externalSourceConfig ? wireExternalSource : null;
|
|
68
|
+
GlobalQueue.It = externalSourceConfig ? externalUntrack : null;
|
|
69
69
|
}
|
|
70
70
|
|
|
71
71
|
function enableExternalSource(e) {
|
package/dist/prod/core/graph.js
CHANGED
|
@@ -61,7 +61,7 @@ function link(l, n, e = false) {
|
|
|
61
61
|
// read order within the computation.
|
|
62
62
|
const u = n.We;
|
|
63
63
|
if (u !== null && u.nt === l) {
|
|
64
|
-
u.
|
|
64
|
+
u.Oe &&= e;
|
|
65
65
|
return;
|
|
66
66
|
}
|
|
67
67
|
let s = null;
|
|
@@ -72,7 +72,7 @@ function link(l, n, e = false) {
|
|
|
72
72
|
s.nl = n.Ye;
|
|
73
73
|
n.We = s;
|
|
74
74
|
// First touch of this pass: the previous pass's label is stale.
|
|
75
|
-
s.
|
|
75
|
+
s.Oe = e;
|
|
76
76
|
return;
|
|
77
77
|
}
|
|
78
78
|
}
|
|
@@ -85,7 +85,7 @@ function link(l, n, e = false) {
|
|
|
85
85
|
if (i !== null && i.le === n && (!t || i.nl === n.Ye)) {
|
|
86
86
|
// Gen-matched during a recompute = repeat touch this pass (AND); outside
|
|
87
87
|
// a recompute there is no pass boundary, so the latest read labels it.
|
|
88
|
-
if (t) i.
|
|
88
|
+
if (t) i.Oe &&= e; else i.Oe = e;
|
|
89
89
|
return;
|
|
90
90
|
}
|
|
91
91
|
const o = n.We = l.st = {
|
|
@@ -95,7 +95,7 @@ function link(l, n, e = false) {
|
|
|
95
95
|
ll: i,
|
|
96
96
|
ue: null,
|
|
97
97
|
nl: n.Ye,
|
|
98
|
-
|
|
98
|
+
Oe: e
|
|
99
99
|
};
|
|
100
100
|
if (u !== null) u.tt = o; else n.et = o;
|
|
101
101
|
if (i !== null) i.ue = o; else l.o = o;
|
package/dist/prod/core/lanes.js
CHANGED
|
@@ -37,7 +37,7 @@ const activeLanes = new Set;
|
|
|
37
37
|
// the owner's write merges the companion's subscribers into this lane and
|
|
38
38
|
// their effects wait on its async instead of flushing immediately.
|
|
39
39
|
adoptCompanionLane(n.he, e);
|
|
40
|
-
adoptCompanionLane(n.
|
|
40
|
+
adoptCompanionLane(n.pe, e);
|
|
41
41
|
return e;
|
|
42
42
|
}
|
|
43
43
|
|
|
@@ -27,7 +27,7 @@ import { GlobalQueue, activeTransition, globalQueue, clock, insertSubs, schedule
|
|
|
27
27
|
const t = e.Ae !== NOT_PENDING;
|
|
28
28
|
const i = t ? unwrapOverride(e.Ae) : e.Ue;
|
|
29
29
|
if (typeof n === "function") n = n(i);
|
|
30
|
-
const u = !!(e.S & STATUS_UNINITIALIZED) || !e.
|
|
30
|
+
const u = !!(e.S & STATUS_UNINITIALIZED) || !e.be || !e.be(i, n);
|
|
31
31
|
if (!u) {
|
|
32
32
|
// Same-value write with an active override still entangles the current
|
|
33
33
|
// action's transition — the hold must outlast all overlapping actions.
|
|
@@ -54,7 +54,7 @@ import { GlobalQueue, activeTransition, globalQueue, clock, insertSubs, schedule
|
|
|
54
54
|
e.Ae = n === undefined ? OVERRIDE_UNDEFINED : n;
|
|
55
55
|
// syncCompanions only pokes _pendingSignal/_latestValueComputed — with
|
|
56
56
|
// neither companion present the call is a guaranteed no-op.
|
|
57
|
-
(e.he !== undefined || e.
|
|
57
|
+
(e.he !== undefined || e.pe !== undefined) && GlobalQueue._e !== null && GlobalQueue._e(e, n);
|
|
58
58
|
e.Se = clock;
|
|
59
59
|
insertSubs(e, true);
|
|
60
60
|
schedule();
|
|
@@ -98,9 +98,9 @@ function resolveOptimisticNodes(e) {
|
|
|
98
98
|
// transition that produced them (A19 — pending is a property of the data).
|
|
99
99
|
for (let t = 0; t < n; t++) {
|
|
100
100
|
const n = e[t];
|
|
101
|
-
if (n.he || n.
|
|
101
|
+
if (n.he || n.pe) GlobalQueue.un(n);
|
|
102
102
|
const i = n.nn;
|
|
103
|
-
if (i && (i.he === n || i.
|
|
103
|
+
if (i && (i.he === n || i.pe === n)) GlobalQueue.un(i);
|
|
104
104
|
}
|
|
105
105
|
e.splice(0, n);
|
|
106
106
|
}
|
|
@@ -153,7 +153,7 @@ function cleanupCompletedLanes(e) {
|
|
|
153
153
|
* that reads a pending mid-transition write sees the committed value; the sub
|
|
154
154
|
* is recorded for replay at commit.
|
|
155
155
|
*/ function gatedRead(e, n, t) {
|
|
156
|
-
if (latestReadActive || e.
|
|
156
|
+
if (latestReadActive || e.De === NOT_PENDING || e.ae || n !== e && !(n.se & REACTIVE_MANUAL_WRITE)) {
|
|
157
157
|
return false;
|
|
158
158
|
}
|
|
159
159
|
activeTransition.ln.add(t);
|