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