@solidjs/signals 2.0.0-beta.9 → 2.0.0-rc.0
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/README.md +13 -9
- package/dist/dev.js +5971 -1426
- package/dist/node.cjs +7681 -3293
- package/dist/prod/affects.js +126 -0
- package/dist/prod/boundaries.js +572 -0
- package/dist/prod/core/action.js +139 -0
- package/dist/prod/core/async.js +553 -0
- package/dist/prod/core/constants.js +92 -0
- package/dist/prod/core/context.js +67 -0
- package/dist/prod/core/core.js +817 -0
- package/dist/prod/core/dev.js +3 -0
- package/dist/prod/core/effect.js +145 -0
- package/dist/prod/core/error.js +68 -0
- package/dist/prod/core/external.js +98 -0
- package/dist/prod/core/graph.js +104 -0
- package/dist/prod/core/heap.js +140 -0
- package/dist/prod/core/invariants.js +42 -0
- package/dist/prod/core/lanes.js +140 -0
- package/dist/prod/core/optimistic.js +245 -0
- package/dist/prod/core/owner.js +302 -0
- package/dist/prod/core/scheduler.js +749 -0
- package/dist/prod/core/verdict.js +369 -0
- package/dist/prod/index.js +45 -0
- package/dist/prod/map.js +317 -0
- package/dist/prod/signals.js +375 -0
- package/dist/prod/store/optimistic.js +217 -0
- package/dist/prod/store/projection.js +231 -0
- package/dist/prod/store/reconcile.js +707 -0
- package/dist/prod/store/store.js +1081 -0
- package/dist/prod/store/storePath.js +103 -0
- package/dist/prod/store/utils.js +328 -0
- package/dist/types/affects.d.ts +47 -0
- package/dist/types/boundaries.d.ts +60 -13
- package/dist/types/core/action.d.ts +35 -6
- package/dist/types/core/async.d.ts +16 -1
- package/dist/types/core/constants.d.ts +35 -6
- package/dist/types/core/context.d.ts +10 -2
- package/dist/types/core/core.d.ts +58 -63
- package/dist/types/core/dev.d.ts +17 -2
- package/dist/types/core/effect.d.ts +1 -2
- package/dist/types/core/error.d.ts +33 -0
- package/dist/types/core/external.d.ts +0 -11
- package/dist/types/core/graph.d.ts +2 -1
- package/dist/types/core/heap.d.ts +8 -0
- package/dist/types/core/index.d.ts +3 -2
- package/dist/types/core/invariants.d.ts +59 -0
- package/dist/types/core/lanes.d.ts +2 -0
- package/dist/types/core/optimistic.d.ts +6 -0
- package/dist/types/core/owner.d.ts +50 -3
- package/dist/types/core/scheduler.d.ts +82 -10
- package/dist/types/core/types.d.ts +66 -1
- package/dist/types/core/verdict.d.ts +2 -0
- package/dist/types/index.d.ts +3 -2
- package/dist/types/map.d.ts +21 -5
- package/dist/types/signals.d.ts +164 -12
- package/dist/types/store/index.d.ts +1 -1
- package/dist/types/store/optimistic.d.ts +3 -3
- package/dist/types/store/projection.d.ts +10 -6
- package/dist/types/store/reconcile.d.ts +31 -8
- package/dist/types/store/store.d.ts +101 -6
- package/dist/types-cjs/affects.d.cts +47 -0
- package/dist/types-cjs/boundaries.d.cts +60 -13
- package/dist/types-cjs/core/action.d.cts +35 -6
- package/dist/types-cjs/core/async.d.cts +16 -1
- package/dist/types-cjs/core/constants.d.cts +35 -6
- package/dist/types-cjs/core/context.d.cts +10 -2
- package/dist/types-cjs/core/core.d.cts +58 -63
- package/dist/types-cjs/core/dev.d.cts +17 -2
- package/dist/types-cjs/core/effect.d.cts +1 -2
- package/dist/types-cjs/core/error.d.cts +33 -0
- package/dist/types-cjs/core/external.d.cts +0 -11
- package/dist/types-cjs/core/graph.d.cts +2 -1
- package/dist/types-cjs/core/heap.d.cts +8 -0
- package/dist/types-cjs/core/index.d.cts +3 -2
- package/dist/types-cjs/core/invariants.d.cts +59 -0
- package/dist/types-cjs/core/lanes.d.cts +2 -0
- package/dist/types-cjs/core/optimistic.d.cts +6 -0
- package/dist/types-cjs/core/owner.d.cts +50 -3
- package/dist/types-cjs/core/scheduler.d.cts +82 -10
- package/dist/types-cjs/core/types.d.cts +66 -1
- package/dist/types-cjs/core/verdict.d.cts +2 -0
- package/dist/types-cjs/index.d.cts +3 -2
- package/dist/types-cjs/map.d.cts +21 -5
- package/dist/types-cjs/signals.d.cts +164 -12
- package/dist/types-cjs/store/index.d.cts +1 -1
- package/dist/types-cjs/store/optimistic.d.cts +3 -3
- package/dist/types-cjs/store/projection.d.cts +10 -6
- package/dist/types-cjs/store/reconcile.d.cts +31 -8
- package/dist/types-cjs/store/store.d.cts +101 -6
- package/package.json +11 -9
- package/dist/prod.js +0 -3627
|
@@ -0,0 +1,817 @@
|
|
|
1
|
+
import { handleAsync, clearStatus, parkLoadingWindow, notifyStatus, settleErroredDependents } from "./async.js";
|
|
2
|
+
|
|
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
|
+
|
|
5
|
+
import { NotReadyError } from "./error.js";
|
|
6
|
+
|
|
7
|
+
import { trimStaleDeps, link, unobserved } from "./graph.js";
|
|
8
|
+
|
|
9
|
+
import { deleteFromHeap, queueFor, insertIntoHeapHeight, markNode, markHeap, insertIntoHeap } from "./heap.js";
|
|
10
|
+
|
|
11
|
+
import { GlobalQueue, activeTransition, globalQueue, clock, insertSubs, queuePendingNode, runInTransition, schedule, dirtyQueue, projectionWriteActive, armReaskClear } from "./scheduler.js";
|
|
12
|
+
|
|
13
|
+
import "./invariants.js";
|
|
14
|
+
|
|
15
|
+
import { disposeChildren, markDisposal, inheritId } from "./owner.js";
|
|
16
|
+
|
|
17
|
+
GlobalQueue.Ce = recompute;
|
|
18
|
+
|
|
19
|
+
GlobalQueue.me = disposeChildren;
|
|
20
|
+
|
|
21
|
+
let tracking = false;
|
|
22
|
+
|
|
23
|
+
/** @internal verdict-module glue */ function setPendingCheckActive(e) {
|
|
24
|
+
pendingCheckActive = e;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** @internal verdict-module glue */ function setLatestReadActive(e) {
|
|
28
|
+
latestReadActive = e;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** @internal verdict-module glue */ function setContextInternal(e) {
|
|
32
|
+
context = e;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
let stale = false;
|
|
36
|
+
|
|
37
|
+
let pendingCheckActive = false;
|
|
38
|
+
|
|
39
|
+
let latestReadActive = false;
|
|
40
|
+
|
|
41
|
+
let context = null;
|
|
42
|
+
|
|
43
|
+
let currentOptimisticLane = null;
|
|
44
|
+
|
|
45
|
+
let snapshotCaptureActive = false;
|
|
46
|
+
|
|
47
|
+
let snapshotSources = null;
|
|
48
|
+
|
|
49
|
+
function ownerInSnapshotScope(e) {
|
|
50
|
+
while (e) {
|
|
51
|
+
if (e.ve) return true;
|
|
52
|
+
e = e.ke;
|
|
53
|
+
}
|
|
54
|
+
return false;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function setSnapshotCapture(e) {
|
|
58
|
+
snapshotCaptureActive = e;
|
|
59
|
+
if (e && !snapshotSources) snapshotSources = new Set;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function markSnapshotScope(e) {
|
|
63
|
+
e.ve = true;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function releaseSnapshotScope(e) {
|
|
67
|
+
e.ve = false;
|
|
68
|
+
releaseSubtree(e);
|
|
69
|
+
schedule();
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function releaseSubtree(e) {
|
|
73
|
+
let t = e.Fe;
|
|
74
|
+
while (t) {
|
|
75
|
+
if (t.ve) {
|
|
76
|
+
t = t.He;
|
|
77
|
+
continue;
|
|
78
|
+
}
|
|
79
|
+
if (t.ce) {
|
|
80
|
+
const e = t;
|
|
81
|
+
e.T &= ~CONFIG_IN_SNAPSHOT_SCOPE;
|
|
82
|
+
if (e.se & REACTIVE_SNAPSHOT_STALE) {
|
|
83
|
+
e.se &= ~REACTIVE_SNAPSHOT_STALE;
|
|
84
|
+
e.se |= REACTIVE_DIRTY;
|
|
85
|
+
if (dirtyQueue.Ve > e.xe) dirtyQueue.Ve = e.xe;
|
|
86
|
+
insertIntoHeap(e, dirtyQueue);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
releaseSubtree(t);
|
|
90
|
+
t = t.He;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function clearSnapshots() {
|
|
95
|
+
if (snapshotSources) {
|
|
96
|
+
for (const e of snapshotSources) {
|
|
97
|
+
delete e.Le;
|
|
98
|
+
// StoreNode targets share one pre-initialized hidden class (see
|
|
99
|
+
// createStoreProxy) — assign undefined instead of deleting, and only
|
|
100
|
+
// when present so signal-node sources don't grow the field.
|
|
101
|
+
if (e[STORE_SNAPSHOT_PROPS] !== undefined) e[STORE_SNAPSHOT_PROPS] = undefined;
|
|
102
|
+
}
|
|
103
|
+
snapshotSources = null;
|
|
104
|
+
}
|
|
105
|
+
snapshotCaptureActive = false;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function recompute(e, t = false) {
|
|
109
|
+
const n = e.Re;
|
|
110
|
+
if (!t) {
|
|
111
|
+
if (e.Ne && (!n || activeTransition) && activeTransition !== e.Ne) globalQueue.initTransition(e.Ne);
|
|
112
|
+
deleteFromHeap(e, queueFor(e));
|
|
113
|
+
e.Te = null;
|
|
114
|
+
// Tracked effects run after finalizePureQueue, so dispose immediately instead of deferring
|
|
115
|
+
if (e.Ne || n === EFFECT_TRACKED) disposeChildren(e); else if (e.Fe !== null || e.ye !== null) {
|
|
116
|
+
markDisposal(e);
|
|
117
|
+
e.Qe = e.ye;
|
|
118
|
+
e.We = e.Fe;
|
|
119
|
+
e.ye = null;
|
|
120
|
+
e.Fe = null;
|
|
121
|
+
e.qe = 0;
|
|
122
|
+
} else ;
|
|
123
|
+
}
|
|
124
|
+
let i = !!(e.se & REACTIVE_OPTIMISTIC_DIRTY);
|
|
125
|
+
const l = e._e !== undefined && e._e !== NOT_PENDING;
|
|
126
|
+
const u = !!(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;
|
|
131
|
+
// Re-ask classification lives in the verdict module; capture the flag before
|
|
132
|
+
// the recompute wipes _flags below.
|
|
133
|
+
const s = (e.se & REACTIVE_REASK) !== 0;
|
|
134
|
+
// Captured before the compute clears it on a sync landing: if that landing
|
|
135
|
+
// is transition-held below, the window must stay open until the hold
|
|
136
|
+
// commits (commitPendingNode) — a closed window plus a held value reads as
|
|
137
|
+
// a pending frame to live observers of the verdict (#2990).
|
|
138
|
+
const a = e.Ee;
|
|
139
|
+
const r = context;
|
|
140
|
+
context = e;
|
|
141
|
+
e.Ye = null;
|
|
142
|
+
e.Ze++;
|
|
143
|
+
e.se = REACTIVE_RECOMPUTING_DEPS;
|
|
144
|
+
e.de = clock;
|
|
145
|
+
let c = e.De === NOT_PENDING ? e.Ue : e.De;
|
|
146
|
+
let _ = e.xe;
|
|
147
|
+
let f = tracking;
|
|
148
|
+
let E = currentOptimisticLane;
|
|
149
|
+
tracking = true;
|
|
150
|
+
// A computed's fn establishes its OWN dependencies, so it must never run
|
|
151
|
+
// inside a latest() read window: read() short-circuits through the
|
|
152
|
+
// companion path before dependency linking, so a memo created (eagerly
|
|
153
|
+
// computed) inside latest(fn) came out permanently dependency-less (#2926).
|
|
154
|
+
// latestRead() already suspends the flag for its pull-recomputes; this
|
|
155
|
+
// covers creation-time computes and flushes that run inside the window.
|
|
156
|
+
const T = latestReadActive;
|
|
157
|
+
latestReadActive = false;
|
|
158
|
+
// Lane posture lives with the engine: OPTIMISTIC_DIRTY is only ever set by
|
|
159
|
+
// engine-driven paths, and _optimisticNodes is only pushed by
|
|
160
|
+
// _optimisticWrite, so the hook is installed whenever either gate holds.
|
|
161
|
+
if (i) {
|
|
162
|
+
const t = GlobalQueue.je(e, true);
|
|
163
|
+
if (t) currentOptimisticLane = t;
|
|
164
|
+
} else if (activeTransition && !t && activeTransition.Me.length) {
|
|
165
|
+
// Lane adoption: parent-deeper-than-owned-child can run before its OPT-dirty
|
|
166
|
+
// child propagates. Walk deps once and inherit the OPT lane so this node
|
|
167
|
+
// recomputes under the right posture and propagates correctly.
|
|
168
|
+
const t = GlobalQueue.je(e, false);
|
|
169
|
+
if (t) {
|
|
170
|
+
i = true;
|
|
171
|
+
currentOptimisticLane = t;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
const N = n && n !== EFFECT_USER;
|
|
175
|
+
const d = stale;
|
|
176
|
+
if (N) stale = true;
|
|
177
|
+
try {
|
|
178
|
+
if (!false && e.T & CONFIG_SYNC) {
|
|
179
|
+
c = e.ce(c);
|
|
180
|
+
e.Te = null;
|
|
181
|
+
e.Ee = false;
|
|
182
|
+
} else {
|
|
183
|
+
// Snapshot `_inFlight` so we can detect whether `_fn` self-registered an async
|
|
184
|
+
// subscription (e.g. `createProjection` calls `handleAsync` from inside its body
|
|
185
|
+
// with a setter callback). In that case, the outer `handleAsync` call below would
|
|
186
|
+
// clobber the fresh subscription, so we skip it and let the internally-registered
|
|
187
|
+
// iteration drive updates.
|
|
188
|
+
const t = e.Te;
|
|
189
|
+
const n = e.ce(c);
|
|
190
|
+
const i = typeof n === "object" && n !== null;
|
|
191
|
+
const l = e.Te !== t;
|
|
192
|
+
c = l || !i ? n : handleAsync(e, n);
|
|
193
|
+
if (!l && !i) {
|
|
194
|
+
e.Te = null;
|
|
195
|
+
// A sync (non-object) return is the first real answer; async-shaped
|
|
196
|
+
// results clear inside handleAsync at their own landing points, and a
|
|
197
|
+
// self-registered flight (inFlightChanged — projections) clears when
|
|
198
|
+
// its internal handleAsync lands.
|
|
199
|
+
e.Ee = false;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
// On a status-free node clearStatus is a guaranteed no-op: every branch
|
|
203
|
+
// in its body is gated on one of these fields, and with _statusFlags === 0
|
|
204
|
+
// the flags write (create or not) stores the 0 already there.
|
|
205
|
+
if (e.S !== 0 || e.i !== undefined || e._ || e.he || e.ue || e.oe !== undefined || e.ge !== undefined || e.pe !== undefined || e.u !== null) clearStatus(e, t);
|
|
206
|
+
// _optimisticLane is only ever assigned by engine paths.
|
|
207
|
+
if (e.Ke) GlobalQueue.ze(e);
|
|
208
|
+
} catch (t) {
|
|
209
|
+
const n = t instanceof NotReadyError;
|
|
210
|
+
if (n && e.Ee) {
|
|
211
|
+
// Loading window with an unready sync dependency: register for the
|
|
212
|
+
// source's settle (the settlePendingSource walk runs off
|
|
213
|
+
// _pendingSources + _blocked alone) but take NO read-visible pending
|
|
214
|
+
// status, no downstream propagation, no transition, no lane
|
|
215
|
+
// registration — the committed loading value keeps serving. If the
|
|
216
|
+
// node is currently errored the error stays the answer until this
|
|
217
|
+
// retry can actually run.
|
|
218
|
+
parkLoadingWindow(e, t);
|
|
219
|
+
} else {
|
|
220
|
+
// Track pending async in the lane (not the lane's source — it creates the lane
|
|
221
|
+
// but doesn't belong to it). Set lane BEFORE notifyStatus for downstream propagation.
|
|
222
|
+
if (n && currentOptimisticLane) GlobalQueue.$e(e);
|
|
223
|
+
let i = false;
|
|
224
|
+
if (n) {
|
|
225
|
+
e.ue = true;
|
|
226
|
+
if (GlobalQueue.Be !== null) i = GlobalQueue.Be(e, s);
|
|
227
|
+
}
|
|
228
|
+
notifyStatus(e, n ? STATUS_PENDING : STATUS_ERROR, t, undefined, n ? e.Ke : undefined);
|
|
229
|
+
if (i) GlobalQueue.k(e);
|
|
230
|
+
}
|
|
231
|
+
} finally {
|
|
232
|
+
tracking = f;
|
|
233
|
+
latestReadActive = T;
|
|
234
|
+
if (N) stale = d;
|
|
235
|
+
e.se = REACTIVE_NONE | (t ? e.se & REACTIVE_SNAPSHOT_STALE : 0);
|
|
236
|
+
context = r;
|
|
237
|
+
}
|
|
238
|
+
if (!e._) {
|
|
239
|
+
trimStaleDeps(e);
|
|
240
|
+
const s = l ? unwrapOverride(e._e) : e.De === NOT_PENDING ? e.Ue : e.De;
|
|
241
|
+
let r = false;
|
|
242
|
+
try {
|
|
243
|
+
r = !n && u || !e.be || !e.be(s, c);
|
|
244
|
+
} catch (t) {
|
|
245
|
+
// A throwing user comparator is an error of this node's computation.
|
|
246
|
+
// Route it through the same status path as a compute-phase throw so
|
|
247
|
+
// error boundaries contain it; otherwise it unwinds the scheduler
|
|
248
|
+
// flush, bypassing every boundary and wedging the queue (#2837).
|
|
249
|
+
notifyStatus(e, STATUS_ERROR, t);
|
|
250
|
+
}
|
|
251
|
+
// Effects use `_equals: false` (no per-effect closure). The side effects that
|
|
252
|
+
// the equals closure used to perform — flagging the effect dirty and enqueueing
|
|
253
|
+
// its runner — happen here instead. `!create` matches the previous `initialized`
|
|
254
|
+
// gate: the explicit recompute(node, true) inside effect() does not enqueue, so
|
|
255
|
+
// effect() can call its runner synchronously for the first run.
|
|
256
|
+
if (n && r) {
|
|
257
|
+
e.Je = !e._;
|
|
258
|
+
// Reuse one bound runner per effect — runEffect no-ops on a stale
|
|
259
|
+
// `_modified`, so re-enqueueing the same function is harmless.
|
|
260
|
+
if (!t) e.C.enqueue(n, e.Xe ??= GlobalQueue.et.bind(null, e));
|
|
261
|
+
}
|
|
262
|
+
if (e._) ; else if (r) {
|
|
263
|
+
const u = l ? e._e : undefined;
|
|
264
|
+
if (t ||
|
|
265
|
+
// Plain sync flush (no transition on either side) commits effect
|
|
266
|
+
// values directly — the pending round-trip (queuePendingNode +
|
|
267
|
+
// commitPendingNodes) exists to sequence transition reveals, and
|
|
268
|
+
// paying it per effect on the plain path is pure overhead.
|
|
269
|
+
n && (activeTransition !== e.Ne || activeTransition === null) || i) {
|
|
270
|
+
e.Ue = c;
|
|
271
|
+
// Lane-propagated correction: upstream data is fresh, correct the
|
|
272
|
+
// override unconditionally. The direct _value commit is the lane's
|
|
273
|
+
// own reveal schedule; drop any superseded older hold so its queued
|
|
274
|
+
// commit can't clobber the fresh value.
|
|
275
|
+
if (l && i) {
|
|
276
|
+
e._e = c === undefined ? OVERRIDE_UNDEFINED : c;
|
|
277
|
+
e.De = NOT_PENDING;
|
|
278
|
+
}
|
|
279
|
+
} else {
|
|
280
|
+
e.De = c;
|
|
281
|
+
// A window landing that gets held re-opens the window until the hold
|
|
282
|
+
// commits — the verdict's held-value branch is window-gated (#2990).
|
|
283
|
+
if (a) e.Ee = true;
|
|
284
|
+
// Transition-held sync recompute is a write path like setSignal/asyncWrite,
|
|
285
|
+
// so sync derivations of held sources stay visible to isPending()/latest()
|
|
286
|
+
// (#2831). Both companion writes are transition-scoped (optimistic) and
|
|
287
|
+
// auto-revert/re-derive at commit. Skipped for plain flushes where the
|
|
288
|
+
// pending value commits before effects run.
|
|
289
|
+
if ((activeTransition || e.Ne) && GlobalQueue.Pe !== null) GlobalQueue.Pe(e, c);
|
|
290
|
+
}
|
|
291
|
+
// insertSubs only walks _subs (no scheduling of its own), so a
|
|
292
|
+
// subscriber-less node has nothing to notify.
|
|
293
|
+
if (e.o !== null && (!l || i || e._e !== u)) insertSubs(e, i || l);
|
|
294
|
+
} else if (l) {
|
|
295
|
+
// Unchanged value (equals the override) recomputed while the override
|
|
296
|
+
// is active: _value may still be stale, so hold the authoritative value
|
|
297
|
+
// for commit on its own transition's schedule — invisibly (A17/A18).
|
|
298
|
+
if (e.De === NOT_PENDING) queuePendingNode(e);
|
|
299
|
+
e.De = c;
|
|
300
|
+
if (a) e.Ee = true;
|
|
301
|
+
// see the held branch above (#2990)
|
|
302
|
+
} else if (e.xe != _) {
|
|
303
|
+
for (let t = e.o; t !== null; t = t.le) {
|
|
304
|
+
insertIntoHeapHeight(t.fe, queueFor(t.fe));
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
// Silent recovery: errored → unchanged value fires no notification, but
|
|
308
|
+
// dependents still holding the propagated error consumed their dirty flag
|
|
309
|
+
// in an errored run and may sit on stale commits (#2949). Changed-value
|
|
310
|
+
// recoveries ride insertSubs above; a comparator throw re-errored the node
|
|
311
|
+
// (el._error re-set), so this only runs on a genuinely clean recovery.
|
|
312
|
+
if (o !== undefined && !r && !e._) settleErroredDependents(e, o);
|
|
313
|
+
}
|
|
314
|
+
currentOptimisticLane = E;
|
|
315
|
+
const I = e.De !== NOT_PENDING || e.We !== null || e.Qe !== null || (e.S & (STATUS_PENDING | STATUS_UNINITIALIZED)) !== 0;
|
|
316
|
+
// Override-covered holds (hasOverride) always queue: their commit belongs
|
|
317
|
+
// to their own transition's schedule (A18 re-rule) and is unobservable
|
|
318
|
+
// under the override (A17). Revert no longer commits anything, so an
|
|
319
|
+
// unqueued covered hold would leak (INV-7) once the revert clears
|
|
320
|
+
// _transition.
|
|
321
|
+
I && (!t || e.S & STATUS_PENDING) && (!e.Ne || l) && queuePendingNode(e);
|
|
322
|
+
e.Ne && n && activeTransition !== e.Ne && runInTransition(e.Ne, () => recompute(e));
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
function updateIfNecessary(e) {
|
|
326
|
+
if (e.se & REACTIVE_CHECK) {
|
|
327
|
+
for (let t = e.tt; t; t = t.nt) {
|
|
328
|
+
const n = t.it;
|
|
329
|
+
const i = n.lt || n;
|
|
330
|
+
if (i.ce) {
|
|
331
|
+
updateIfNecessary(i);
|
|
332
|
+
}
|
|
333
|
+
if (e.se & REACTIVE_DIRTY) {
|
|
334
|
+
break;
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
if (e.se & (REACTIVE_DIRTY | REACTIVE_OPTIMISTIC_DIRTY) || e._ && e.de < clock && !e.Te) {
|
|
339
|
+
recompute(e);
|
|
340
|
+
}
|
|
341
|
+
e.se = e.se & (REACTIVE_SNAPSHOT_STALE | REACTIVE_IN_HEAP | REACTIVE_IN_HEAP_HEIGHT);
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
function computed(e, t) {
|
|
345
|
+
const n = t?.transparent ?? false;
|
|
346
|
+
// `in` (not `!== undefined`): an explicit `loadingValue: undefined` on a
|
|
347
|
+
// `T | undefined` node is a real commit #0. The typeof guard tolerates
|
|
348
|
+
// non-object option values that older call shapes force through `as any`.
|
|
349
|
+
const i = t !== null && typeof t === "object" && "loadingValue" in t;
|
|
350
|
+
const l = {
|
|
351
|
+
id: inheritId(t, n, context),
|
|
352
|
+
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),
|
|
353
|
+
be: t?.equals != null ? t.equals : isEqual,
|
|
354
|
+
ut: t?.unobserved,
|
|
355
|
+
ye: null,
|
|
356
|
+
C: context?.C ?? globalQueue,
|
|
357
|
+
we: context?.we ?? defaultContext,
|
|
358
|
+
qe: 0,
|
|
359
|
+
ce: e,
|
|
360
|
+
Ue: i ? t.loadingValue : undefined,
|
|
361
|
+
xe: 0,
|
|
362
|
+
u: null,
|
|
363
|
+
ot: undefined,
|
|
364
|
+
st: null,
|
|
365
|
+
tt: null,
|
|
366
|
+
Ye: null,
|
|
367
|
+
Ze: 0,
|
|
368
|
+
o: null,
|
|
369
|
+
rt: null,
|
|
370
|
+
ke: context,
|
|
371
|
+
He: null,
|
|
372
|
+
ct: null,
|
|
373
|
+
Fe: null,
|
|
374
|
+
se: t?.lazy ? REACTIVE_LAZY : REACTIVE_NONE,
|
|
375
|
+
// A loadingValue node is born committed: commit #0 is already in _value.
|
|
376
|
+
S: i ? 0 : STATUS_UNINITIALIZED,
|
|
377
|
+
de: clock,
|
|
378
|
+
De: NOT_PENDING,
|
|
379
|
+
Qe: null,
|
|
380
|
+
We: null,
|
|
381
|
+
Te: null,
|
|
382
|
+
Ne: null,
|
|
383
|
+
he: false,
|
|
384
|
+
Ee: i
|
|
385
|
+
};
|
|
386
|
+
setupComputedNode(l, t);
|
|
387
|
+
return l;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
/**
|
|
391
|
+
* Build an Effect node with all effect-specific fields baked into a single object literal,
|
|
392
|
+
* so V8 sees the full hidden class shape at construction time. Effects always run in lazy
|
|
393
|
+
* mode (recompute is called explicitly by `effect()`), so we hardcode the lazy bits and skip
|
|
394
|
+
* the auto-dispose CONFIG bit (effect() previously cleared it post-construction).
|
|
395
|
+
*/ function createEffectNode(e, t, n, i, l, u) {
|
|
396
|
+
const o = u?.transparent ?? false;
|
|
397
|
+
const s = {
|
|
398
|
+
id: inheritId(u, o, context),
|
|
399
|
+
T: (o ? CONFIG_TRANSPARENT : 0) | (u?.ownedWrite ? CONFIG_OWNED_WRITE : 0) | (u?.sync ? CONFIG_SYNC : 0) | (snapshotCaptureActive && ownerInSnapshotScope(context) ? CONFIG_IN_SNAPSHOT_SCOPE : 0),
|
|
400
|
+
be: false,
|
|
401
|
+
ut: u?.unobserved,
|
|
402
|
+
ye: null,
|
|
403
|
+
C: context?.C ?? globalQueue,
|
|
404
|
+
we: context?.we ?? defaultContext,
|
|
405
|
+
qe: 0,
|
|
406
|
+
ce: e,
|
|
407
|
+
Ue: undefined,
|
|
408
|
+
xe: 0,
|
|
409
|
+
u: null,
|
|
410
|
+
ot: undefined,
|
|
411
|
+
st: null,
|
|
412
|
+
tt: null,
|
|
413
|
+
Ye: null,
|
|
414
|
+
Ze: 0,
|
|
415
|
+
o: null,
|
|
416
|
+
rt: null,
|
|
417
|
+
ke: context,
|
|
418
|
+
He: null,
|
|
419
|
+
ct: null,
|
|
420
|
+
Fe: null,
|
|
421
|
+
se: REACTIVE_LAZY,
|
|
422
|
+
S: STATUS_UNINITIALIZED,
|
|
423
|
+
de: clock,
|
|
424
|
+
De: NOT_PENDING,
|
|
425
|
+
Qe: null,
|
|
426
|
+
We: null,
|
|
427
|
+
Te: null,
|
|
428
|
+
Ne: null,
|
|
429
|
+
he: false,
|
|
430
|
+
Ee: false,
|
|
431
|
+
Je: false,
|
|
432
|
+
_t: undefined,
|
|
433
|
+
ft: t,
|
|
434
|
+
Et: n,
|
|
435
|
+
Tt: undefined,
|
|
436
|
+
Re: i,
|
|
437
|
+
i: l
|
|
438
|
+
};
|
|
439
|
+
setupComputedNode(s, lazyOptions);
|
|
440
|
+
return s;
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
const lazyOptions = {
|
|
444
|
+
lazy: true
|
|
445
|
+
};
|
|
446
|
+
|
|
447
|
+
function setupComputedNode(e, t) {
|
|
448
|
+
e.st = e;
|
|
449
|
+
const n = context?.Nt ? context.dt : context;
|
|
450
|
+
if (context) {
|
|
451
|
+
const t = context.Fe;
|
|
452
|
+
if (t === null) {
|
|
453
|
+
context.Fe = e;
|
|
454
|
+
} else {
|
|
455
|
+
e.He = t;
|
|
456
|
+
t.ct = e;
|
|
457
|
+
context.Fe = e;
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
if (n) e.xe = n.xe + 1;
|
|
461
|
+
if (GlobalQueue.It !== null) GlobalQueue.It(e);
|
|
462
|
+
!t?.lazy && recompute(e, true);
|
|
463
|
+
if (snapshotCaptureActive && !t?.lazy) {
|
|
464
|
+
if (!(e.S & STATUS_PENDING) && !(e.T & CONFIG_NO_SNAPSHOT)) {
|
|
465
|
+
e.Le = e.Ue === undefined ? NO_SNAPSHOT : e.Ue;
|
|
466
|
+
snapshotSources.add(e);
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
function signal(e, t, n = null) {
|
|
472
|
+
const i = {
|
|
473
|
+
be: t?.equals != null ? t.equals : isEqual,
|
|
474
|
+
T: (t?.ownedWrite ? CONFIG_OWNED_WRITE : 0) | (t?.V ? CONFIG_NO_SNAPSHOT : 0),
|
|
475
|
+
ut: t?.unobserved,
|
|
476
|
+
Ue: e,
|
|
477
|
+
o: null,
|
|
478
|
+
rt: null,
|
|
479
|
+
de: clock,
|
|
480
|
+
lt: n,
|
|
481
|
+
ae: n?.u || null,
|
|
482
|
+
De: NOT_PENDING
|
|
483
|
+
};
|
|
484
|
+
n && (n.u = i);
|
|
485
|
+
if (snapshotCaptureActive && !(i.T & CONFIG_NO_SNAPSHOT) && !((n?.S ?? 0) & STATUS_PENDING)) {
|
|
486
|
+
i.Le = e === undefined ? NO_SNAPSHOT : e;
|
|
487
|
+
snapshotSources.add(i);
|
|
488
|
+
}
|
|
489
|
+
return i;
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
function optimisticSignal(e, t) {
|
|
493
|
+
const n = signal(e, t);
|
|
494
|
+
n._e = NOT_PENDING;
|
|
495
|
+
return n;
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
function optimisticComputed(e, t) {
|
|
499
|
+
const n = computed(e, t);
|
|
500
|
+
n._e = NOT_PENDING;
|
|
501
|
+
return n;
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
function isEqual(e, t) {
|
|
505
|
+
return e === t;
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
/**
|
|
509
|
+
* Runs `fn` outside of any reactive tracking — reads inside `fn` will not
|
|
510
|
+
* subscribe the current scope. Returns whatever `fn` returns.
|
|
511
|
+
*
|
|
512
|
+
* Use `untrack` inside a memo or effect when you need to read a signal once
|
|
513
|
+
* without making the surrounding computation depend on its future changes.
|
|
514
|
+
*
|
|
515
|
+
* Pass a `strictReadLabel` string to enable a dev-mode warning: any reactive
|
|
516
|
+
* read inside `fn` that isn't inside a nested tracking scope will log a
|
|
517
|
+
* warning naming the label.
|
|
518
|
+
*
|
|
519
|
+
* @example
|
|
520
|
+
* ```ts
|
|
521
|
+
* createEffect(
|
|
522
|
+
* () => trigger(), // tracks `trigger` only
|
|
523
|
+
* () => {
|
|
524
|
+
* const snapshot = untrack(() => state); // read once, untracked
|
|
525
|
+
* log(snapshot);
|
|
526
|
+
* }
|
|
527
|
+
* );
|
|
528
|
+
* ```
|
|
529
|
+
*/ function untrack(e, t) {
|
|
530
|
+
if (GlobalQueue.St === null && !tracking && true) return e();
|
|
531
|
+
const n = tracking;
|
|
532
|
+
tracking = false;
|
|
533
|
+
try {
|
|
534
|
+
if (GlobalQueue.St !== null) return GlobalQueue.St(e);
|
|
535
|
+
return e();
|
|
536
|
+
} finally {
|
|
537
|
+
tracking = n;
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
/**
|
|
542
|
+
* Bring a computed to a readable state: lazy/disposed nodes are (re)computed;
|
|
543
|
+
* an isPending() probe (`refresh`) additionally pulls the node fully up to
|
|
544
|
+
* date so its status flags reflect the current graph.
|
|
545
|
+
*/ function prepareComputed(e, t) {
|
|
546
|
+
if (e.se & REACTIVE_LAZY) {
|
|
547
|
+
e.se &= ~REACTIVE_LAZY;
|
|
548
|
+
recompute(e, true);
|
|
549
|
+
} else if (e.se & REACTIVE_DISPOSED) {
|
|
550
|
+
recompute(e, true);
|
|
551
|
+
} else if (t) {
|
|
552
|
+
updateIfNecessary(e);
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
/**
|
|
557
|
+
* Sentinel returned by readNodeFast when the plain-signal fast path does not
|
|
558
|
+
* apply and the caller must fall back to the full read().
|
|
559
|
+
*/ const READ_SLOW = Symbol("read-slow");
|
|
560
|
+
|
|
561
|
+
/**
|
|
562
|
+
* read()'s plain-signal fast path as a standalone entry for hot callers
|
|
563
|
+
* (store traps). Safe to substitute for read() only because the bail
|
|
564
|
+
* conditions mirror read()'s prelude and fast-path guard exactly: the
|
|
565
|
+
* latestRead and pendingCheck windows run side-effectful hooks before the
|
|
566
|
+
* fast path, `_fn` nodes need prepareComputed, and firewall / override /
|
|
567
|
+
* snapshot / transition / lane / dev-strictRead state all take the full
|
|
568
|
+
* resolution. Anything slow returns READ_SLOW; the caller then calls read().
|
|
569
|
+
*/ function readNodeFast(e) {
|
|
570
|
+
if (latestReadActive || pendingCheckActive || e.ce || e.lt || e._e !== undefined || e.Le !== undefined || activeTransition !== null || currentOptimisticLane !== null || snapshotCaptureActive || false) return READ_SLOW;
|
|
571
|
+
let t = context;
|
|
572
|
+
if (t?.Nt) t = t.dt;
|
|
573
|
+
if (t && tracking) link(e, t);
|
|
574
|
+
return !t || e.De === NOT_PENDING ? e.Ue : e.De;
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
function read(e) {
|
|
578
|
+
// Handle latest() mode: read from _latestValueComputed
|
|
579
|
+
// Checked before isPending so that isPending(() => latest(x)) checks
|
|
580
|
+
// the _pendingSignal of _latestValueComputed (async in flight) rather
|
|
581
|
+
// than the original node (which stays "pending" while held in a transition).
|
|
582
|
+
if (latestReadActive) return GlobalQueue.At(e);
|
|
583
|
+
let t = context;
|
|
584
|
+
if (t?.Nt) t = t.dt;
|
|
585
|
+
const n = e;
|
|
586
|
+
const i = e.lt;
|
|
587
|
+
const l = i || e;
|
|
588
|
+
// Handle isPending() mode: collect pending state while preserving normal read semantics.
|
|
589
|
+
// Probe mode is suspended while preparing the node so nested reads during a
|
|
590
|
+
// recompute don't collect into the probe.
|
|
591
|
+
if (pendingCheckActive) {
|
|
592
|
+
GlobalQueue.Ct(e, t, l, i);
|
|
593
|
+
} else if (typeof n.ce === "function") {
|
|
594
|
+
prepareComputed(e, false);
|
|
595
|
+
}
|
|
596
|
+
if (!n.ce && l === e && e._e === undefined && e.Le === undefined && activeTransition === null && currentOptimisticLane === null && !snapshotCaptureActive && true) {
|
|
597
|
+
if (t && tracking) link(e, t);
|
|
598
|
+
return !t || e.De === NOT_PENDING ? e.Ue : e.De;
|
|
599
|
+
}
|
|
600
|
+
if (t && tracking) {
|
|
601
|
+
link(e, t, pendingCheckActive);
|
|
602
|
+
if (l.ce) {
|
|
603
|
+
const n = queueFor(e);
|
|
604
|
+
if (l.xe >= n.Ve) {
|
|
605
|
+
markNode(t);
|
|
606
|
+
markHeap(n);
|
|
607
|
+
updateIfNecessary(l);
|
|
608
|
+
}
|
|
609
|
+
const i = l.xe;
|
|
610
|
+
// parent check is shallow, might need to be recursive
|
|
611
|
+
if (i >= t.xe && e.ke !== t) {
|
|
612
|
+
t.xe = i + 1;
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
if (l.S & STATUS_PENDING) {
|
|
617
|
+
if (t && !(stale && l.Ne && activeTransition !== l.Ne)) {
|
|
618
|
+
// Per-lane suspension lives with the engine (a non-null lane implies it
|
|
619
|
+
// is installed): under a lane, only same-lane pending async without an
|
|
620
|
+
// active override throws.
|
|
621
|
+
if (currentOptimisticLane === null || GlobalQueue.Ot(l)) {
|
|
622
|
+
if (!tracking && e !== t) link(e, t);
|
|
623
|
+
throw l._;
|
|
624
|
+
}
|
|
625
|
+
} else if (t && l !== e && l.S & STATUS_UNINITIALIZED) {
|
|
626
|
+
if (!tracking && e !== t) link(e, t);
|
|
627
|
+
throw l._;
|
|
628
|
+
} else if (!t && l.S & STATUS_UNINITIALIZED) {
|
|
629
|
+
throw l._;
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
// `owner` is the computed itself, or the firewall behind a store node —
|
|
633
|
+
// firewall-backed reads follow the same rules (memo parity, #2897 ruling):
|
|
634
|
+
// an errored derive throws for every late reader instead of silently
|
|
635
|
+
// serving node values (the seed, or last-good data after a failed refetch).
|
|
636
|
+
if (l.ce && l.S & STATUS_ERROR) {
|
|
637
|
+
// Only a genuine reactive re-read may retry an errored async source:
|
|
638
|
+
// - tracking: owned/tracked scope only (never events / `untrack` / effect side-effect phase)
|
|
639
|
+
// - !pendingCheckActive: an `isPending` probe observes the error, never refetches
|
|
640
|
+
// - owner._time < clock: only on a later cycle than the one the error was found
|
|
641
|
+
if (tracking && !pendingCheckActive && l.de < clock) {
|
|
642
|
+
recompute(l);
|
|
643
|
+
return read(e);
|
|
644
|
+
} else throw l._;
|
|
645
|
+
}
|
|
646
|
+
if (snapshotCaptureActive && t && t.T & CONFIG_IN_SNAPSHOT_SCOPE) {
|
|
647
|
+
const n = e.Le;
|
|
648
|
+
if (n !== undefined) {
|
|
649
|
+
const i = n === NO_SNAPSHOT ? undefined : n;
|
|
650
|
+
const l = e.De !== NOT_PENDING ? e.De : e.Ue;
|
|
651
|
+
if (l !== i) t.se |= REACTIVE_SNAPSHOT_STALE;
|
|
652
|
+
return i;
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
if (e._e !== undefined && e._e !== NOT_PENDING) {
|
|
656
|
+
// A17: the override IS the value for every reader.
|
|
657
|
+
return unwrapOverride(e._e);
|
|
658
|
+
}
|
|
659
|
+
// Entanglement gate: a reader recomputing under an optimistic lane that reads
|
|
660
|
+
// a pending mid-transition write sees the committed value. Projection-store
|
|
661
|
+
// manual writes use the firewall's manual-write flag to opt into this path.
|
|
662
|
+
// Async drivers are not under an optimistic lane and so bypass this, reading
|
|
663
|
+
// _pendingValue for correct fetching. The sub is recorded for replay at commit
|
|
664
|
+
// so it re-runs with the new committed view. (Gate details live with the
|
|
665
|
+
// engine — a non-null lane implies it is installed.)
|
|
666
|
+
if (currentOptimisticLane !== null && activeTransition !== null && t !== null && GlobalQueue.Rt(e, l, t)) {
|
|
667
|
+
return e.Ue;
|
|
668
|
+
}
|
|
669
|
+
// In optimistic lane context, return _value for optimistic/lane-assigned signals
|
|
670
|
+
// and for regular signals in stale mode (render effects). Non-stale readers (user
|
|
671
|
+
// effects) see _pendingValue so that latest() and direct reads stay consistent.
|
|
672
|
+
// (The lane-context clause lives with the engine.)
|
|
673
|
+
const u = !t || currentOptimisticLane !== null && GlobalQueue.Pt(e, l, t) || e.De === NOT_PENDING || stale && e.Ne && activeTransition !== e.Ne ? e.Ue : e.De;
|
|
674
|
+
// Record that this isPending() probe observed the fresh pending value, so
|
|
675
|
+
// the probe doesn't pair "pending" with the new value (#2831).
|
|
676
|
+
if (pendingCheckActive) GlobalQueue.ht(e, u);
|
|
677
|
+
if (!t && l === e && typeof n.ce === "function" && e.T & CONFIG_AUTO_DISPOSE && !(l.S & STATUS_PENDING) && !e.o) {
|
|
678
|
+
unobserved(e);
|
|
679
|
+
}
|
|
680
|
+
return u;
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
function setSignal(e, t) {
|
|
684
|
+
if (e.Ne && activeTransition !== e.Ne) globalQueue.initTransition(e.Ne);
|
|
685
|
+
// The optimistic write path lives with the engine: only optimisticSignal /
|
|
686
|
+
// optimisticComputed callers and optimistic store nodes carry an
|
|
687
|
+
// _overrideValue slot, and every module that creates one installs the
|
|
688
|
+
// engine first.
|
|
689
|
+
if (e._e !== undefined && !projectionWriteActive) return GlobalQueue.Gt(e, t);
|
|
690
|
+
const n = e.De === NOT_PENDING ? e.Ue : e.De;
|
|
691
|
+
if (typeof t === "function") t = t(n);
|
|
692
|
+
// Uninitialized check first: the first commit has no previous value, so the
|
|
693
|
+
// user comparator must not run against `undefined` (matches recompute).
|
|
694
|
+
const i = !!(e.S & STATUS_UNINITIALIZED) || !e.be || !e.be(n, t);
|
|
695
|
+
if (!i) return t;
|
|
696
|
+
if (e.De === NOT_PENDING) queuePendingNode(e);
|
|
697
|
+
e.De = t;
|
|
698
|
+
// syncCompanions only pokes _pendingSignal/_latestValueComputed — with
|
|
699
|
+
// neither companion present the call is a guaranteed no-op (companions are
|
|
700
|
+
// only ever created, never removed, and creating one installs the hook).
|
|
701
|
+
(e.ge !== undefined || e.pe !== undefined) && GlobalQueue.Pe !== null && GlobalQueue.Pe(e, t);
|
|
702
|
+
e.de = clock;
|
|
703
|
+
insertSubs(e);
|
|
704
|
+
schedule();
|
|
705
|
+
return t;
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
/**
|
|
709
|
+
* Suppresses automatic recomputation of `el` until the scheduler drains. Used
|
|
710
|
+
* when a manual write should win over dependency changes queued in the same
|
|
711
|
+
* tick. The MANUAL_WRITE flag is cleared by the pending-node drain; projection
|
|
712
|
+
* computeds don't commit values, but they still need the same end-of-tick
|
|
713
|
+
* cleanup point.
|
|
714
|
+
*/ function suppressComputedRecompute(e) {
|
|
715
|
+
deleteFromHeap(e, queueFor(e));
|
|
716
|
+
if (!(e.se & REACTIVE_MANUAL_WRITE) && e.De === NOT_PENDING) {
|
|
717
|
+
queuePendingNode(e);
|
|
718
|
+
schedule();
|
|
719
|
+
}
|
|
720
|
+
e.se = e.se & -4 | REACTIVE_MANUAL_WRITE;
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
/**
|
|
724
|
+
* User-facing setter for the memo form of `createSignal(fn)`. Behaves like
|
|
725
|
+
* `setSignal`, but also cancels any pending recompute of the memo so the
|
|
726
|
+
* manual value wins over a value that would otherwise be produced by an
|
|
727
|
+
* upstream change in the same tick.
|
|
728
|
+
*/ function setMemo(e, t) {
|
|
729
|
+
const n = setSignal(e, t);
|
|
730
|
+
suppressComputedRecompute(e);
|
|
731
|
+
return n;
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
/**
|
|
735
|
+
* Executes `fn` with the given `owner` set as the current owner. Any reactive
|
|
736
|
+
* primitives (`createSignal`, `createMemo`, `createEffect`, `onCleanup`,
|
|
737
|
+
* `cleanup`, etc.) created inside `fn` are attached to that owner, so they
|
|
738
|
+
* are disposed when the owner is disposed.
|
|
739
|
+
*
|
|
740
|
+
* The classic pattern: capture the current owner with `getOwner()` inside a
|
|
741
|
+
* component, then re-enter it from a callback (event handler, async resolve,
|
|
742
|
+
* setTimeout) so disposables created in the callback get cleaned up with the
|
|
743
|
+
* component.
|
|
744
|
+
*
|
|
745
|
+
* @example
|
|
746
|
+
* ```ts
|
|
747
|
+
* function delayed<T>(ms: number, fn: () => T) {
|
|
748
|
+
* const owner = getOwner();
|
|
749
|
+
* setTimeout(() => runWithOwner(owner, fn), ms);
|
|
750
|
+
* }
|
|
751
|
+
* ```
|
|
752
|
+
*/ function runWithOwner(e, t) {
|
|
753
|
+
const n = context;
|
|
754
|
+
const i = tracking;
|
|
755
|
+
context = e;
|
|
756
|
+
tracking = false;
|
|
757
|
+
try {
|
|
758
|
+
return t();
|
|
759
|
+
} finally {
|
|
760
|
+
context = n;
|
|
761
|
+
tracking = i;
|
|
762
|
+
}
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
function staleValues(e, t = true) {
|
|
766
|
+
const n = stale;
|
|
767
|
+
stale = t;
|
|
768
|
+
try {
|
|
769
|
+
return e();
|
|
770
|
+
} finally {
|
|
771
|
+
stale = n;
|
|
772
|
+
}
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
/**
|
|
776
|
+
* Invalidates one reactive source, forcing it to re-execute even if its inputs
|
|
777
|
+
* haven't changed.
|
|
778
|
+
*
|
|
779
|
+
* Pass either a Solid-created accessor or a projected store created from
|
|
780
|
+
* `createStore(fn, ...)` / `createProjection(...)`. `refresh()` is a
|
|
781
|
+
* write-like invalidation operation: it does not read the target's value, and
|
|
782
|
+
* refreshing a plain signal accessor is a no-op.
|
|
783
|
+
*
|
|
784
|
+
* Use it to invalidate cached async values (e.g. force a re-fetch) without
|
|
785
|
+
* tearing the consumer down.
|
|
786
|
+
*
|
|
787
|
+
* @example
|
|
788
|
+
* ```ts
|
|
789
|
+
* const user = createMemo(async () => fetch(`/users/${id()}`).then(r => r.json()));
|
|
790
|
+
*
|
|
791
|
+
* // Re-fetch on demand
|
|
792
|
+
* <button onClick={() => refresh(user)}>Reload</button>
|
|
793
|
+
* ```
|
|
794
|
+
*/ function refresh(e) {
|
|
795
|
+
const t = e?.[$REFRESH];
|
|
796
|
+
if (!t) {
|
|
797
|
+
return;
|
|
798
|
+
}
|
|
799
|
+
if (typeof t.ce === "function" && !(t.se & (REACTIVE_DISPOSED | REACTIVE_MANUAL_WRITE))) {
|
|
800
|
+
// A refresh with no value-change dirt already queued is a re-ask of the
|
|
801
|
+
// same question: mark it so the recompute classifies any resulting
|
|
802
|
+
// pending window as quiet (not pending). If the node is already dirty
|
|
803
|
+
// from a real input change, the question changed — don't mark.
|
|
804
|
+
// REACTIVE_IN_HEAP counts as dirt: insertSubs schedules subscribers by
|
|
805
|
+
// heap insertion alone (no DIRTY/CHECK flag), so a same-batch value
|
|
806
|
+
// change followed by refresh() must not be laundered into a quiet re-ask.
|
|
807
|
+
if (!(t.se & (REACTIVE_DIRTY | REACTIVE_CHECK | REACTIVE_IN_HEAP))) {
|
|
808
|
+
t.se |= REACTIVE_REASK;
|
|
809
|
+
armReaskClear();
|
|
810
|
+
}
|
|
811
|
+
t.se = t.se & ~REACTIVE_CHECK | REACTIVE_DIRTY;
|
|
812
|
+
insertIntoHeap(t, queueFor(t));
|
|
813
|
+
schedule();
|
|
814
|
+
}
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
export { READ_SLOW, clearSnapshots, computed, context, createEffectNode, currentOptimisticLane, isEqual, latestReadActive, markSnapshotScope, optimisticComputed, optimisticSignal, pendingCheckActive, prepareComputed, read, readNodeFast, recompute, refresh, releaseSnapshotScope, runWithOwner, setContextInternal, setLatestReadActive, setMemo, setPendingCheckActive, setSignal, setSnapshotCapture, signal, snapshotCaptureActive, snapshotSources, stale, staleValues, suppressComputedRecompute, tracking, untrack };
|