@solidjs/signals 2.0.0-beta.8 → 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.
Files changed (95) hide show
  1. package/README.md +13 -9
  2. package/dist/dev.js +6078 -1440
  3. package/dist/node.cjs +7684 -3248
  4. package/dist/prod/affects.js +126 -0
  5. package/dist/prod/boundaries.js +572 -0
  6. package/dist/prod/core/action.js +139 -0
  7. package/dist/prod/core/async.js +553 -0
  8. package/dist/prod/core/constants.js +92 -0
  9. package/dist/prod/core/context.js +67 -0
  10. package/dist/prod/core/core.js +817 -0
  11. package/dist/prod/core/dev.js +3 -0
  12. package/dist/prod/core/effect.js +145 -0
  13. package/dist/prod/core/error.js +68 -0
  14. package/dist/prod/core/external.js +98 -0
  15. package/dist/prod/core/graph.js +104 -0
  16. package/dist/prod/core/heap.js +140 -0
  17. package/dist/prod/core/invariants.js +42 -0
  18. package/dist/prod/core/lanes.js +140 -0
  19. package/dist/prod/core/optimistic.js +245 -0
  20. package/dist/prod/core/owner.js +302 -0
  21. package/dist/prod/core/scheduler.js +749 -0
  22. package/dist/prod/core/verdict.js +369 -0
  23. package/dist/prod/index.js +45 -0
  24. package/dist/prod/map.js +317 -0
  25. package/dist/prod/signals.js +375 -0
  26. package/dist/prod/store/optimistic.js +217 -0
  27. package/dist/prod/store/projection.js +231 -0
  28. package/dist/prod/store/reconcile.js +707 -0
  29. package/dist/prod/store/store.js +1081 -0
  30. package/dist/prod/store/storePath.js +103 -0
  31. package/dist/prod/store/utils.js +328 -0
  32. package/dist/types/affects.d.ts +47 -0
  33. package/dist/types/boundaries.d.ts +93 -11
  34. package/dist/types/core/action.d.ts +63 -0
  35. package/dist/types/core/async.d.ts +16 -1
  36. package/dist/types/core/constants.d.ts +46 -0
  37. package/dist/types/core/context.d.ts +10 -2
  38. package/dist/types/core/core.d.ts +111 -17
  39. package/dist/types/core/dev.d.ts +17 -2
  40. package/dist/types/core/effect.d.ts +1 -2
  41. package/dist/types/core/error.d.ts +33 -0
  42. package/dist/types/core/external.d.ts +0 -11
  43. package/dist/types/core/graph.d.ts +2 -1
  44. package/dist/types/core/heap.d.ts +8 -0
  45. package/dist/types/core/index.d.ts +3 -2
  46. package/dist/types/core/invariants.d.ts +59 -0
  47. package/dist/types/core/lanes.d.ts +2 -0
  48. package/dist/types/core/optimistic.d.ts +6 -0
  49. package/dist/types/core/owner.d.ts +101 -3
  50. package/dist/types/core/scheduler.d.ts +97 -9
  51. package/dist/types/core/types.d.ts +68 -6
  52. package/dist/types/core/verdict.d.ts +2 -0
  53. package/dist/types/index.d.ts +4 -3
  54. package/dist/types/map.d.ts +50 -5
  55. package/dist/types/signals.d.ts +424 -19
  56. package/dist/types/store/index.d.ts +1 -1
  57. package/dist/types/store/optimistic.d.ts +39 -13
  58. package/dist/types/store/projection.d.ts +48 -18
  59. package/dist/types/store/reconcile.d.ts +46 -1
  60. package/dist/types/store/store.d.ts +164 -19
  61. package/dist/types/store/storePath.d.ts +28 -0
  62. package/dist/types/store/utils.d.ts +78 -7
  63. package/dist/types-cjs/affects.d.cts +47 -0
  64. package/dist/types-cjs/boundaries.d.cts +93 -11
  65. package/dist/types-cjs/core/action.d.cts +63 -0
  66. package/dist/types-cjs/core/async.d.cts +16 -1
  67. package/dist/types-cjs/core/constants.d.cts +46 -0
  68. package/dist/types-cjs/core/context.d.cts +10 -2
  69. package/dist/types-cjs/core/core.d.cts +111 -17
  70. package/dist/types-cjs/core/dev.d.cts +17 -2
  71. package/dist/types-cjs/core/effect.d.cts +1 -2
  72. package/dist/types-cjs/core/error.d.cts +33 -0
  73. package/dist/types-cjs/core/external.d.cts +0 -11
  74. package/dist/types-cjs/core/graph.d.cts +2 -1
  75. package/dist/types-cjs/core/heap.d.cts +8 -0
  76. package/dist/types-cjs/core/index.d.cts +3 -2
  77. package/dist/types-cjs/core/invariants.d.cts +59 -0
  78. package/dist/types-cjs/core/lanes.d.cts +2 -0
  79. package/dist/types-cjs/core/optimistic.d.cts +6 -0
  80. package/dist/types-cjs/core/owner.d.cts +101 -3
  81. package/dist/types-cjs/core/scheduler.d.cts +97 -9
  82. package/dist/types-cjs/core/types.d.cts +68 -6
  83. package/dist/types-cjs/core/verdict.d.cts +2 -0
  84. package/dist/types-cjs/index.d.cts +4 -3
  85. package/dist/types-cjs/map.d.cts +50 -5
  86. package/dist/types-cjs/signals.d.cts +424 -19
  87. package/dist/types-cjs/store/index.d.cts +1 -1
  88. package/dist/types-cjs/store/optimistic.d.cts +39 -13
  89. package/dist/types-cjs/store/projection.d.cts +48 -18
  90. package/dist/types-cjs/store/reconcile.d.cts +46 -1
  91. package/dist/types-cjs/store/store.d.cts +164 -19
  92. package/dist/types-cjs/store/storePath.d.cts +28 -0
  93. package/dist/types-cjs/store/utils.d.cts +78 -7
  94. package/package.json +14 -11
  95. package/dist/prod.js +0 -3577
@@ -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 };