@solidjs/signals 2.0.0-rc.6 → 2.0.0-rc.8

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 (131) hide show
  1. package/dist/dev-shared.js +5243 -0
  2. package/dist/dev.attribution.js +2333 -0
  3. package/dist/dev.js +1758 -6621
  4. package/dist/observe/affects.js +127 -0
  5. package/dist/observe/attribution.js +1 -0
  6. package/dist/observe/boundaries.js +586 -0
  7. package/dist/observe/core/action.js +167 -0
  8. package/dist/observe/core/async.js +736 -0
  9. package/dist/observe/core/attribution-hooks.js +60 -0
  10. package/dist/observe/core/attribution.js +2318 -0
  11. package/dist/{types-cjs/core/constants.d.cts → observe/core/constants.js} +135 -71
  12. package/dist/{types-cjs/core/context.d.cts → observe/core/context.js} +37 -12
  13. package/dist/observe/core/core.js +1289 -0
  14. package/dist/observe/core/dev.js +232 -0
  15. package/dist/observe/core/effect.js +179 -0
  16. package/dist/observe/core/error.js +80 -0
  17. package/dist/observe/core/external.js +98 -0
  18. package/dist/observe/core/graph.js +155 -0
  19. package/dist/observe/core/heap.js +147 -0
  20. package/dist/observe/core/invariants.js +43 -0
  21. package/dist/observe/core/lanes.js +168 -0
  22. package/dist/observe/core/optimistic.js +438 -0
  23. package/dist/observe/core/owner.js +308 -0
  24. package/dist/observe/core/scheduler.js +1190 -0
  25. package/dist/observe/core/verdict.js +551 -0
  26. package/dist/observe/index.js +55 -0
  27. package/dist/observe/map.js +482 -0
  28. package/dist/{types-cjs/signals.d.cts → observe/signals.js} +305 -317
  29. package/dist/observe/store/index.js +38 -0
  30. package/dist/observe/store/next/optimistic.js +651 -0
  31. package/dist/observe/store/next/projection.js +242 -0
  32. package/dist/observe/store/next/reconcile.js +373 -0
  33. package/dist/observe/store/next/store.js +2213 -0
  34. package/dist/observe/store/next/target.js +51 -0
  35. package/dist/observe/store/store.js +323 -0
  36. package/dist/observe/store/storePath.js +103 -0
  37. package/dist/observe/store/utils.js +210 -0
  38. package/dist/prod/attribution.js +32 -0
  39. package/dist/prod/boundaries.js +3 -1
  40. package/dist/prod/core/action.js +52 -32
  41. package/dist/prod/core/async.js +177 -128
  42. package/dist/prod/core/attribution-hooks.js +3 -0
  43. package/dist/prod/core/constants.js +32 -1
  44. package/dist/prod/core/context.js +10 -16
  45. package/dist/prod/core/core.js +505 -286
  46. package/dist/prod/core/dev.js +2 -0
  47. package/dist/prod/core/effect.js +72 -43
  48. package/dist/prod/core/external.js +2 -2
  49. package/dist/prod/core/graph.js +40 -36
  50. package/dist/prod/core/heap.js +50 -51
  51. package/dist/prod/core/lanes.js +43 -22
  52. package/dist/prod/core/optimistic.js +224 -80
  53. package/dist/prod/core/owner.js +43 -39
  54. package/dist/prod/core/scheduler.js +347 -232
  55. package/dist/prod/core/verdict.js +71 -74
  56. package/dist/prod/index.js +9 -5
  57. package/dist/prod/map.js +311 -150
  58. package/dist/prod/signals.js +52 -38
  59. package/dist/prod/store/next/optimistic.js +127 -131
  60. package/dist/prod/store/next/projection.js +3 -3
  61. package/dist/prod/store/next/reconcile.js +149 -294
  62. package/dist/prod/store/next/store.js +694 -380
  63. package/dist/prod/store/next/target.js +32 -10
  64. package/dist/prod/store/store.js +12 -14
  65. package/dist/prod/store/utils.js +36 -27
  66. package/dist/types/attribution.d.ts +15 -0
  67. package/dist/types/attribution.prod.d.ts +13 -0
  68. package/dist/types/core/async.d.ts +1 -1
  69. package/dist/types/core/attribution-hooks.d.ts +183 -6
  70. package/dist/types/core/attribution.d.ts +435 -24
  71. package/dist/types/core/constants.d.ts +31 -0
  72. package/dist/types/core/core.d.ts +19 -3
  73. package/dist/types/core/dev.d.ts +165 -26
  74. package/dist/types/core/effect.d.ts +8 -1
  75. package/dist/types/core/heap.d.ts +5 -3
  76. package/dist/types/core/index.d.ts +2 -1
  77. package/dist/types/core/invariants.d.ts +1 -1
  78. package/dist/types/core/lanes.d.ts +17 -0
  79. package/dist/types/core/scheduler.d.ts +69 -4
  80. package/dist/types/core/types.d.ts +29 -9
  81. package/dist/types/index.d.ts +9 -2
  82. package/dist/types/map.d.ts +2 -0
  83. package/dist/types/signals.d.ts +10 -0
  84. package/dist/types/store/index.d.ts +4 -6
  85. package/dist/types/store/next/optimistic.d.ts +4 -2
  86. package/dist/types/store/next/reconcile.d.ts +5 -12
  87. package/dist/types/store/next/store.d.ts +8 -9
  88. package/dist/types/store/next/target.d.ts +56 -55
  89. package/dist/types/store/store.d.ts +14 -9
  90. package/dist/types/store/utils.d.ts +7 -0
  91. package/package.json +21 -16
  92. package/dist/node.cjs +0 -11121
  93. package/dist/prod/store/next/patch-hooks.js +0 -13
  94. package/dist/prod/store/next/patch.js +0 -614
  95. package/dist/types/store/next/patch-hooks.d.ts +0 -41
  96. package/dist/types/store/next/patch.d.ts +0 -91
  97. package/dist/types-cjs/affects.d.cts +0 -47
  98. package/dist/types-cjs/boundaries.d.cts +0 -175
  99. package/dist/types-cjs/core/action.d.cts +0 -64
  100. package/dist/types-cjs/core/async.d.cts +0 -23
  101. package/dist/types-cjs/core/attribution-hooks.d.cts +0 -63
  102. package/dist/types-cjs/core/attribution.d.cts +0 -239
  103. package/dist/types-cjs/core/core.d.cts +0 -176
  104. package/dist/types-cjs/core/dev.d.cts +0 -95
  105. package/dist/types-cjs/core/effect.d.cts +0 -30
  106. package/dist/types-cjs/core/error.d.cts +0 -56
  107. package/dist/types-cjs/core/external.d.cts +0 -15
  108. package/dist/types-cjs/core/graph.d.cts +0 -28
  109. package/dist/types-cjs/core/heap.d.cts +0 -22
  110. package/dist/types-cjs/core/index.d.cts +0 -13
  111. package/dist/types-cjs/core/invariants.d.cts +0 -59
  112. package/dist/types-cjs/core/lanes.d.cts +0 -44
  113. package/dist/types-cjs/core/optimistic.d.cts +0 -6
  114. package/dist/types-cjs/core/owner.d.cts +0 -124
  115. package/dist/types-cjs/core/scheduler.d.cts +0 -231
  116. package/dist/types-cjs/core/types.d.cts +0 -204
  117. package/dist/types-cjs/core/verdict.d.cts +0 -2
  118. package/dist/types-cjs/index.d.cts +0 -10
  119. package/dist/types-cjs/map.d.cts +0 -69
  120. package/dist/types-cjs/package.json +0 -3
  121. package/dist/types-cjs/store/index.d.cts +0 -21
  122. package/dist/types-cjs/store/next/optimistic.d.cts +0 -23
  123. package/dist/types-cjs/store/next/patch-hooks.d.cts +0 -41
  124. package/dist/types-cjs/store/next/patch.d.cts +0 -91
  125. package/dist/types-cjs/store/next/projection.d.cts +0 -8
  126. package/dist/types-cjs/store/next/reconcile.d.cts +0 -17
  127. package/dist/types-cjs/store/next/store.d.cts +0 -127
  128. package/dist/types-cjs/store/next/target.d.cts +0 -192
  129. package/dist/types-cjs/store/store.d.cts +0 -138
  130. package/dist/types-cjs/store/storePath.d.cts +0 -58
  131. package/dist/types-cjs/store/utils.d.cts +0 -74
@@ -0,0 +1,551 @@
1
+ import { NOT_PENDING, REACTIVE_DISPOSED, REACTIVE_DIRTY, REACTIVE_CHECK, unwrapOverride, REACTIVE_ZOMBIE, STATUS_UNINITIALIZED, STATUS_PENDING, REACTIVE_OPTIMISTIC_DIRTY, REACTIVE_MANUAL_WRITE, CONFIG_HAS_COMPANIONS, CONFIG_CHILD_COMPANIONS, STATUS_ERROR, REACTIVE_RECOMPUTING_DEPS } from "./constants.js";
2
+
3
+ import { setSignal, pendingCheckActive, setPendingCheckActive, prepareComputed, read, context, stale, currentOptimisticLane, tracking, ext, setLatestReadActive, setContextInternal, optimisticComputed, latestReadActive, optimisticSignal } from "./core.js";
4
+
5
+ import { NotReadyError } from "./error.js";
6
+
7
+ import { link } from "./graph.js";
8
+
9
+ import { insertIntoHeap, queueFor, markHeap, enqueueSub } from "./heap.js";
10
+
11
+ import "./invariants.js";
12
+
13
+ import { findLane, laneHeld, hasActiveOverride, assignOrMergeLane } from "./lanes.js";
14
+
15
+ import { installOptimisticEngine } from "./optimistic.js";
16
+
17
+ import { GlobalQueue, insertSubs, schedule, activeTransition, currentTransition, activeAffectsMarks } from "./scheduler.js";
18
+
19
+ /**
20
+ * The isPending()/latest() verdict layer, moved out of core.ts. Importing this
21
+ * module installs the companion-maintenance hooks on GlobalQueue; apps that
22
+ * never import isPending/latest never pay for any of it.
23
+ */
24
+ // Companions (pending signals / latest shadows) are optimistic nodes: their
25
+ // writes go through the optimistic write path and their reversion rides the
26
+ // same lanes, so the verdict layer brings the engine with it.
27
+ installOptimisticEngine();
28
+
29
+ let pendingProbe = null;
30
+
31
+ /**
32
+ * Probes whose verdict was suppressed by the fresh-read pairing rule while
33
+ * the held write's fate was still undecided (see recordFreshRead /
34
+ * wakeSuppressedProbes): held node → the wrapper computeds that probed it.
35
+ * Entries die with the hold — the commit/revert snap clears them.
36
+ */ const suppressedProbes = new Map;
37
+
38
+ /**
39
+ * Get or create the pending signal for a node (lazy).
40
+ * Used by isPending() to track pending state reactively.
41
+ */
42
+ /** #3038: register a companion-carrying firewall child on its firewall's
43
+ * companion set and arm the post-recompute snap (CONFIG_CHILD_COMPANIONS is
44
+ * the one-load gate at the call sites). The snap then iterates exactly the
45
+ * children someone asked verdicts of — O(companions) — never the full
46
+ * `_child` chain, which carries one node per materialized leaf (the
47
+ * O(all-leaves-ever-read)-per-update pathology). Entries are permanent like
48
+ * the companions themselves; a store with no leaf-level isPending()/latest()
49
+ * reads never allocates the set or pays the walk. */ function markFirewallChildCompanions(e) {
50
+ const t = e.re;
51
+ if (!t) return;
52
+ t.T |= CONFIG_CHILD_COMPANIONS;
53
+ (ext(t).jt ??= new Set).add(e);
54
+ }
55
+
56
+ function getPendingSignal(e) {
57
+ let t = e.o?.be;
58
+ if (!t) {
59
+ // Start false, write true if pending - ensures reversion returns to false
60
+ t = optimisticSignal(false, {
61
+ ownedWrite: true
62
+ });
63
+ ext(e).be = t;
64
+ e.T |= CONFIG_HAS_COMPANIONS;
65
+ markFirewallChildCompanions(e);
66
+ ext(t).Qe = e;
67
+ if (computePendingState(e)) setSignal(t, true);
68
+ }
69
+ return t;
70
+ }
71
+
72
+ function collectPendingSources(e) {
73
+ if (!pendingProbe) return;
74
+ pendingProbe.sources.add(e);
75
+ const t = e.re || e;
76
+ if (t !== e) pendingProbe.sources.add(t);
77
+ }
78
+
79
+ /**
80
+ * Adds a node to the active isPending() probe without reading it. The store's
81
+ * untracked-probe fallback (`witnessAffectsMark`) reaches this through
82
+ * `GlobalQueue._witnessAffects` — its callers guard on `pendingCheckActive`,
83
+ * which only flips inside `isPending()`, so the hook is always installed by
84
+ * the time it can fire.
85
+ */ function witnessAffects(e) {
86
+ pendingProbe?.sources.add(e);
87
+ }
88
+
89
+ /**
90
+ * The affects() coverage walk — the read half of the dedicated mark channel.
91
+ * A node is covered by a live mark iff it carries one (`_affectsCount`) or
92
+ * derives, through its CURRENT deps (hopping store firewalls), from a node
93
+ * that does. Pull-based coverage means graph rewires, mid-window recomputes,
94
+ * and probe-triggered recomputes can never strand or strip a mark — there is
95
+ * nothing stored downstream to corrupt. Probe-created links
96
+ * (`_pendingObserver`) are skipped so an `isPending` wrapper memo never
97
+ * inherits the coverage it reports on.
98
+ */ function markWalk(e, t) {
99
+ if (e.o?.t) return true;
100
+ // A real error outranks an inherited mark (A16/A24c): an errored node
101
+ // answers probes with its error, not a coverage verdict, and coverage does
102
+ // not flow through it — matching the rails' behavior, where propagation
103
+ // stopped at errored nodes. A DIRECT mark on an errored node still reads
104
+ // pending (the count check above), also matching.
105
+ if (e.S & STATUS_ERROR) return false;
106
+ if (t.has(e)) return false;
107
+ t.add(e);
108
+ const n = e.re;
109
+ if (n && markWalk(n, t)) return true;
110
+ // Mid-recompute (the clearStatus companion poke runs before
111
+ // trimStaleDeps), only the validated prefix [_deps.._depsTail] is this
112
+ // pass's dependency set — walking past it would read dropped deps and
113
+ // latch a stale verdict on the companion.
114
+ const i = e;
115
+ const r = i.it & REACTIVE_RECOMPUTING_DEPS ? i.Dt : undefined;
116
+ if (r !== null) {
117
+ for (let e = i.te ?? null; e !== null; e = e.ne) {
118
+ if (!e.ye && markWalk(e.oe, t)) return true;
119
+ if (e === r) break;
120
+ }
121
+ }
122
+ return false;
123
+ }
124
+
125
+ function quietPending(e) {
126
+ if (e.o?.ee) {
127
+ for (const t of e.o.ee) if (!t.o?.Ne) return false;
128
+ return true;
129
+ }
130
+ return e.o?.Ne ?? false;
131
+ }
132
+
133
+ // NOTE: a loadingValue node's open loading window (_loading) is verdict-quiet
134
+ // on purpose: commit #0 answers the question by declaration, so the window
135
+ // reads NOT pending — first-load affordances live in the value channel
136
+ // (null / skeleton provenance the author encoded), and isPending stays what
137
+ // it always was: refetch truth for an answered question. This keeps the
138
+ // verdict fully correlated with transition-class machinery and keeps server
139
+ // (always false) and client hydration trivially consistent.
140
+ function newQuestionInFlight(e) {
141
+ return !!(e.S & STATUS_PENDING) && !(e.S & STATUS_UNINITIALIZED) && !quietPending(e);
142
+ }
143
+
144
+ function computePendingState(e) {
145
+ const t = e;
146
+ if (t.it & REACTIVE_DISPOSED) return false;
147
+ // Mark coverage is transitive by dep-graph reachability: a latest() shadow
148
+ // reaches its owner (and a store leaf its firewall) through its own deps,
149
+ // so the one walk covers direct marks, derivation, and companion chains.
150
+ // Gated: apps with no live mark pay one integer compare.
151
+ if (activeAffectsMarks !== 0 && markWalk(e, new Set)) return true;
152
+ const n = e.re;
153
+ if (e.o?.Qe) {
154
+ const t = e.o?.Qe;
155
+ const n = t.re || t;
156
+ return newQuestionInFlight(n);
157
+ }
158
+ if (n && e.Pe !== NOT_PENDING && !hasActiveOverride(e)) {
159
+ return !!(n.it & REACTIVE_MANUAL_WRITE) || !n.o?.Se && !(n.S & STATUS_PENDING) || !!(n.S & STATUS_PENDING) && quietPending(n);
160
+ }
161
+ // `!comp._loading`: a hold created while the loading window is still open is
162
+ // the window's own landing in flight to its commit — verdict-quiet like the
163
+ // rest of the window (the UNINITIALIZED check suppresses exactly this frame
164
+ // for windowless first loads; born-committed nodes need their own gate, #2990).
165
+ if (e.Pe !== NOT_PENDING && !(t.S & STATUS_UNINITIALIZED) && !t.de) {
166
+ if (hasActiveOverride(e)) return !e.Ue || !e.Ue(e.Pe, unwrapOverride(e.o?.De));
167
+ // A quiet re-ask's held landing still answers the same question: the
168
+ // classification survives the landing (asyncWrite) and dies with the
169
+ // commit (commitPendingNode) — verdict-quiet through the reveal, like
170
+ // the loading window above (#3178).
171
+ if (!t.o?.Ne) return true;
172
+ }
173
+ return newQuestionInFlight(t);
174
+ }
175
+
176
+ function syncCompanions(e, t) {
177
+ if (e.o?.be) updatePendingSignal(e);
178
+ if (e.o?.pe) setSignal(e.o?.pe, t);
179
+ }
180
+
181
+ function updatePendingSignal(e) {
182
+ if (e.o?.be) {
183
+ setSignal(e.o?.be, computePendingState(e));
184
+ }
185
+ if (e.o?.pe) updatePendingSignal(e.o?.pe);
186
+ }
187
+
188
+ function updateChildCompanions(e) {
189
+ const t = e.o?.jt;
190
+ if (t === undefined) return;
191
+ for (const e of t) updatePendingSignal(e);
192
+ }
193
+
194
+ /**
195
+ * Re-derive every verdict companion downstream of `el` (subs + firewall
196
+ * children, dedup'd). The affects() channel's poke walk: registration and
197
+ * re-ask flips use the live write path (companion setSignal — its own lane
198
+ * lets the wake escape an incomplete transition's effect stash, #2887);
199
+ * mark release passes `snap` because it runs inside queue finalization,
200
+ * where companion writes must land committed (a setSignal there would open
201
+ * a fresh override window that nothing settles).
202
+ */ function repollDownstreamVerdicts(e, t = false) {
203
+ const n = t ? snapCompanionsToState : updatePendingSignal;
204
+ const i = new Set;
205
+ const visit = e => {
206
+ if (i.has(e)) return;
207
+ i.add(e);
208
+ if (e.o?.be || e.o?.pe) n(e);
209
+ for (let t = e.u; t !== null; t = t.se) visit(t.le);
210
+ for (let t = e.o?.i ?? null; t !== null; t = t.ue) {
211
+ visit(t);
212
+ }
213
+ };
214
+ visit(e);
215
+ }
216
+
217
+ /**
218
+ * The correction half of the provisional fresh-read suppression (see
219
+ * collectPending): fired from the sanctioned async-registration site
220
+ * (GlobalQueue.notify) when a transaction gains an in-flight async blocker.
221
+ * Every probe that returned "not pending" purely because it read a held
222
+ * value belonging to that transaction re-runs — its re-probe now sees the
223
+ * live blocker through heldAwaitingAsync and lands the true verdict. The
224
+ * wake mirrors a companion write's own notification (optimistic-dirty on the
225
+ * companion's lane) so the corrected verdict commits and flushes immediately
226
+ * instead of being held with the transaction it reports on.
227
+ */ function wakeSuppressedProbes(e) {
228
+ if (suppressedProbes.size === 0) return;
229
+ let t = false;
230
+ for (const [n, i] of suppressedProbes) {
231
+ const r = n.Ee;
232
+ const s = r ? currentTransition(r) : null;
233
+ if (!s) {
234
+ suppressedProbes.delete(n);
235
+ continue;
236
+ }
237
+ if (s !== e) continue;
238
+ suppressedProbes.delete(n);
239
+ const o = n.o?.be?.o?.Te;
240
+ for (const e of i) {
241
+ if (e.it & REACTIVE_DISPOSED) continue;
242
+ e.it |= REACTIVE_OPTIMISTIC_DIRTY;
243
+ if (o) assignOrMergeLane(e, o); else if (e.o !== null) e.o.Te = undefined;
244
+ enqueueSub(e);
245
+ t = true;
246
+ }
247
+ }
248
+ if (t) schedule();
249
+ }
250
+
251
+ function snapCompanionsToState(e) {
252
+ suppressedProbes.size !== 0 && suppressedProbes.delete(e);
253
+ const t = e.o?.be;
254
+ if (t && (t.o?.De === undefined || t.o?.De === NOT_PENDING)) {
255
+ const n = computePendingState(e);
256
+ if (t.ge !== n || t.Pe !== NOT_PENDING) {
257
+ t.ge = n;
258
+ t.Pe = NOT_PENDING;
259
+ insertSubs(t);
260
+ schedule();
261
+ }
262
+ }
263
+ const n = e.o?.pe;
264
+ if (n && !(n.it & REACTIVE_DISPOSED)) {
265
+ if ((n.o?.De === undefined || n.o?.De === NOT_PENDING) && n.Pe === NOT_PENDING && !Object.is(n.ge, e.ge) && !(n.it & (REACTIVE_DIRTY | REACTIVE_CHECK))) {
266
+ n.it |= REACTIVE_DIRTY;
267
+ insertIntoHeap(n, queueFor(n));
268
+ insertSubs(n);
269
+ schedule();
270
+ }
271
+ snapCompanionsToState(n);
272
+ }
273
+ }
274
+
275
+ function getLatestValueComputed(e) {
276
+ let t = e.o?.pe;
277
+ // A shadow disposed while unobserved (its gated reader unmounted at a
278
+ // landing) is a corpse: sync writes into it equality-swallow against its
279
+ // frozen _value, and a later read revives it via recompute — clearing
280
+ // DISPOSED and re-deriving from the committed view, so the banner showed
281
+ // the previous transition's target (#3041 follow-up). Treat it as absent;
282
+ // recreation backfills from the in-flight write below.
283
+ if (t && t.it & REACTIVE_DISPOSED) t = undefined;
284
+ if (!t) {
285
+ const n = latestReadActive;
286
+ setLatestReadActive(false);
287
+ const i = pendingCheckActive;
288
+ setPendingCheckActive(false);
289
+ const r = context;
290
+ setContextInternal(null);
291
+ // Detach from owner so it isn't disposed with effects
292
+ t = optimisticComputed(() => read(e));
293
+ ext(e).pe = t;
294
+ e.T |= CONFIG_HAS_COMPANIONS;
295
+ markFirewallChildCompanions(e);
296
+ ext(t).Qe = e;
297
+ // Parent-child lane relationship
298
+ // Backfill an in-flight write (mirrors getPendingSignal): the companion is
299
+ // created lazily, possibly after the write was processed — syncCompanions
300
+ // only pushes into companions that already exist, so the first latest()
301
+ // read inside a held transition showed the committed value (#3041).
302
+ if (e.Pe !== NOT_PENDING && !hasActiveOverride(e)) setSignal(t, e.Pe);
303
+ setContextInternal(r);
304
+ setPendingCheckActive(i);
305
+ setLatestReadActive(n);
306
+ }
307
+ return t;
308
+ }
309
+
310
+ /** The latest()-mode read path, installed as GlobalQueue._latestRead. */ function latestRead(e) {
311
+ const t = getLatestValueComputed(e);
312
+ const n = latestReadActive;
313
+ setLatestReadActive(false);
314
+ const i = e.o?.De !== undefined && e.o?.De !== NOT_PENDING ? unwrapOverride(e.o?.De) : e.ge;
315
+ let r;
316
+ try {
317
+ // An untracked latest() read has no reading context, so read() never
318
+ // performs its mid-tick pull — a plain write queued between two latest()
319
+ // calls left a still-subscribed shadow at its previous speculative value
320
+ // until the flush (#2922). Mirror the tracked-read pull here: mark the
321
+ // queued staleness through the graph, then bring the shadow up to date.
322
+ const e = queueFor(t);
323
+ if (t.At >= e.St && !(t.it & (REACTIVE_DISPOSED | REACTIVE_ZOMBIE))) {
324
+ markHeap(e);
325
+ // Suspend probe collection during the pull (mirrors pendingCheckRead's
326
+ // prepare): a probe through latest() answers for the SHADOW — the
327
+ // read() dispatch collects it deliberately, so the verdict reflects
328
+ // async still in flight for the latest view, not the parent's held
329
+ // write. A stale shadow recomputing HERE ran its `read(parent)` with
330
+ // the probe still live and collected the parent too, so the verdict
331
+ // depended on whether anything had pulled the shadow current earlier
332
+ // in the tick (#3104: reading latest(m) flipped a later
333
+ // latest(() => isPending(x)) from true to false).
334
+ const n = pendingCheckActive;
335
+ setPendingCheckActive(false);
336
+ try {
337
+ prepareComputed(t, true);
338
+ } finally {
339
+ setPendingCheckActive(n);
340
+ }
341
+ }
342
+ r = read(t);
343
+ } catch (t) {
344
+ if (t instanceof NotReadyError && (!context || !(e.S & STATUS_UNINITIALIZED))) return i;
345
+ throw t;
346
+ } finally {
347
+ setLatestReadActive(n);
348
+ }
349
+ if (t.S & STATUS_PENDING) return i;
350
+ if (stale && currentOptimisticLane && t.o?.Te) {
351
+ const e = findLane(t.o?.Te);
352
+ const n = findLane(currentOptimisticLane);
353
+ if (e !== n && laneHeld(e)) {
354
+ return i;
355
+ }
356
+ }
357
+ // A shadow recomputed by the pull above (not at creation) holds its fresh
358
+ // speculative value in _pendingValue; a contextless read() only surfaces
359
+ // _value. Overrides stay authoritative (A17), and stale readers keep the
360
+ // other transition's committed view, matching read()'s own selection.
361
+ if (t.Pe !== NOT_PENDING && !hasActiveOverride(t) && !(stale && t.Ee && activeTransition !== t.Ee)) return t.Pe;
362
+ return r;
363
+ }
364
+
365
+ /**
366
+ * A latest() shadow that is uninitialized only because it was CREATED during
367
+ * an active flight — its parent source already has a committed value, so
368
+ * latest() serves that as the visible value and a tracked reader has
369
+ * something to pair a verdict with (#3166). Same parent resolution as
370
+ * computePendingState. The pending signal companion also carries
371
+ * `_parentSource` but is a plain signal (no `_fn`) that never goes pending,
372
+ * so the `_fn` check is belt-and-braces for this call site.
373
+ */ function latestShadowWithInitializedParent(e) {
374
+ if (typeof e.ot !== "function") return false;
375
+ const t = e.o?.Qe;
376
+ if (t === undefined) return false;
377
+ const n = t.re || t;
378
+ return !(n.S & STATUS_UNINITIALIZED);
379
+ }
380
+
381
+ /** The isPending()-probe read path, installed as GlobalQueue._pendingCheck. */ function pendingCheckRead(e, t, n, i) {
382
+ setPendingCheckActive(false);
383
+ if (typeof e.ot === "function") prepareComputed(e, true);
384
+ const r = n.S;
385
+ if (t && r & STATUS_PENDING && r & STATUS_UNINITIALIZED &&
386
+ // The suspend-throw is for a genuinely-first-load source: the tracked
387
+ // reader has nothing to pair a verdict with, so it parks on the source.
388
+ // A latest() SHADOW created lazily mid-flight is born uninitialized even
389
+ // though its parent has a committed value latest() will serve — throwing
390
+ // here (swallowed by latestRead's fallback) dropped the shadow from the
391
+ // probe, so a tracked latest(isPending()) probe created during a
392
+ // new-question flight cached `false` for that whole flight (#3166).
393
+ // Defer to the PARENT's initialization state and fall through to normal
394
+ // collection; the plain pending throw downstream still links the reader.
395
+ !latestShadowWithInitializedParent(n)) {
396
+ if (tracking && e !== t) link(e, t);
397
+ setPendingCheckActive(true);
398
+ throw n.o?._;
399
+ }
400
+ collectPendingSources(e);
401
+ if (i) collectPendingSources(i);
402
+ setPendingCheckActive(true);
403
+ }
404
+
405
+ /**
406
+ * A held node whose transaction still has an async question in flight. The
407
+ * probe's fresh-read pairing rule (#2831 — "a reader that sees the fresh
408
+ * value must not also be told it is pending") only applies to LANDED answers
409
+ * awaiting reveal; while the answer is still computing, the fresh value the
410
+ * reader saw is an input, and pending remains the truth for every reader
411
+ * (#3028).
412
+ */ function heldAwaitingAsync(e) {
413
+ const t = e.Ee;
414
+ const n = t ? currentTransition(t) : activeTransition;
415
+ if (!n || n.Ut) return false;
416
+ // A plain staged write (a signal/store leaf — no _fn) held while an action
417
+ // is still running is an INPUT to a computation still in flight (#3078):
418
+ // the pairing rule must not suppress the verdict, or a memo recomputing
419
+ // mid-action reads the staged value, gets told "not pending", and
420
+ // disagrees with a direct isPending() probe for the whole action window.
421
+ // A computed's staged value is the opposite case — a LANDED answer
422
+ // awaiting reveal — where the pairing rule stands even inside an open
423
+ // action (#2831: a reader that saw the new value must not also see
424
+ // pending); still-computing answers are covered by the reporter scan.
425
+ if (n.ut.length && !e.ot) return true;
426
+ // A node not yet stamped with a transition only qualifies through the
427
+ // action check above; the reporter scan below is for transition-held
428
+ // writes whose source async is still computing.
429
+ if (!t) return false;
430
+ for (const [e, t] of n.Ie) {
431
+ if (t.size && e.S & STATUS_PENDING && e.o?._?.source === e) return true;
432
+ }
433
+ return false;
434
+ }
435
+
436
+ function recordFreshRead(e, t) {
437
+ if (pendingProbe !== null && e.Pe !== NOT_PENDING && t === e.Pe) {
438
+ if (heldAwaitingAsync(e)) return;
439
+ pendingProbe.freshReads.add(e);
440
+ }
441
+ }
442
+
443
+ function applyReask(e, t) {
444
+ const n = !!(e.S & STATUS_PENDING);
445
+ const i = t && !(n && !e.o?.Ne);
446
+ const r = n && (e.o?.Ne ?? false) !== i;
447
+ // Allocation-free for the quiet case: false is the extension default.
448
+ if (i) ext(e).Ne = true; else if (e.o !== null) e.o.Ne = false;
449
+ return r;
450
+ }
451
+
452
+ function latest(e) {
453
+ const t = latestReadActive;
454
+ setLatestReadActive(true);
455
+ try {
456
+ return e();
457
+ } finally {
458
+ setLatestReadActive(t);
459
+ }
460
+ }
461
+
462
+ function isPending(e) {
463
+ const t = pendingCheckActive;
464
+ const n = pendingProbe;
465
+ setPendingCheckActive(true);
466
+ const i = pendingProbe = {
467
+ found: false,
468
+ sources: new Set,
469
+ freshReads: new Set,
470
+ suppressed: []
471
+ };
472
+ const collectPending = () => {
473
+ setPendingCheckActive(false);
474
+ // Companion reads are mode-neutral plumbing: under an outer latest()
475
+ // (isPending inside a latest window — #3104's memo shape) leaving latest
476
+ // mode active dispatched these reads through latestRead, which built a
477
+ // SHADOW OF THE PENDING SIGNAL itself. The next updatePendingSignal then
478
+ // wrote that companion-on-companion from inside a recompute
479
+ // (syncCompanions → setSignal on a shadow created without ownedWrite)
480
+ // and halted dev with the owned-scope write guard. The creation paths
481
+ // (getLatestValueComputed / getPendingSignal) already suspend both
482
+ // modes; this read site must too.
483
+ const e = latestReadActive;
484
+ setLatestReadActive(false);
485
+ try {
486
+ i.sources.forEach(e => {
487
+ if (read(getPendingSignal(e))) {
488
+ if (!i.freshReads.has(e)) i.found = true; else i.suppressed.push(e);
489
+ }
490
+ });
491
+ } finally {
492
+ setLatestReadActive(e);
493
+ setPendingCheckActive(true);
494
+ }
495
+ // A "not pending" verdict that exists only because this reader saw the
496
+ // fresh held value is provisional: if the write turns out NOT to commit
497
+ // this flush (a downstream async pends and holds it), the suppression was
498
+ // wrong and the wrapper must re-ask (#3028). Remember who to wake — the
499
+ // async registration (GlobalQueue.notify) triggers wakeSuppressedProbes.
500
+ if (!i.found && i.suppressed.length && context && typeof context.ot === "function") {
501
+ for (const e of i.suppressed) {
502
+ let t = suppressedProbes.get(e);
503
+ if (!t) suppressedProbes.set(e, t = new Set);
504
+ t.add(context);
505
+ }
506
+ }
507
+ };
508
+ try {
509
+ e();
510
+ collectPending();
511
+ return i.found;
512
+ } catch (e) {
513
+ collectPending();
514
+ if (e instanceof NotReadyError) {
515
+ const t = !!(e.source?.S & STATUS_UNINITIALIZED);
516
+ if (i.found && !t) return true;
517
+ if (context && t) throw e;
518
+ }
519
+ return i.found;
520
+ } finally {
521
+ setPendingCheckActive(t);
522
+ pendingProbe = n;
523
+ }
524
+ }
525
+
526
+ // Hook installation (same late-binding pattern as GlobalQueue._update /
527
+ // _propagateAffects): core call sites fire these behind the same guards the
528
+ // direct calls used, so behavior is identical once this module loads.
529
+ GlobalQueue.Ae = syncCompanions;
530
+
531
+ GlobalQueue.fe = updatePendingSignal;
532
+
533
+ GlobalQueue.Ge = updateChildCompanions;
534
+
535
+ GlobalQueue.Dn = snapCompanionsToState;
536
+
537
+ GlobalQueue.In = latestRead;
538
+
539
+ GlobalQueue.Nn = pendingCheckRead;
540
+
541
+ GlobalQueue.Cn = recordFreshRead;
542
+
543
+ GlobalQueue.gt = applyReask;
544
+
545
+ GlobalQueue.k = repollDownstreamVerdicts;
546
+
547
+ GlobalQueue.oi = witnessAffects;
548
+
549
+ GlobalQueue.si = wakeSuppressedProbes;
550
+
551
+ export { isPending, latest };
@@ -0,0 +1,55 @@
1
+ export { ContextNotFoundError, NoOwnerError, NotReadyError, TimeoutError } from "./core/error.js";
2
+
3
+ export { clearSnapshots, isEqual, markSnapshotScope, releaseSnapshotScope, runWithOwner, setSnapshotCapture, untrack } from "./core/core.js";
4
+
5
+ export { enableExternalSource } from "./core/external.js";
6
+
7
+ export { createOwner, createRoot, getNextChildId, getObserver, getOwner, isDisposed, peekNextChildId } from "./core/owner.js";
8
+
9
+ export { createContext, getContext, setContext } from "./core/context.js";
10
+
11
+ export { $REFRESH, SUPPORTS_PROXY } from "./core/constants.js";
12
+
13
+ import "./core/invariants.js";
14
+
15
+ import { OBSERVE as OBSERVE$1 } from "./core/dev.js";
16
+
17
+ export { enforceLoadingBoundary, flush, resetErrorHalt } from "./core/scheduler.js";
18
+
19
+ export { isPending, latest } from "./core/verdict.js";
20
+
21
+ import "./core/effect.js";
22
+
23
+ export { action } from "./core/action.js";
24
+
25
+ export { createEffect, createMemo, createOptimistic, createReaction, createRenderEffect, createSignal, createTrackedEffect, onCleanup, onSettled, refresh, resolve, until } from "./signals.js";
26
+
27
+ export { affects } from "./affects.js";
28
+
29
+ export { mapArray, repeat } from "./map.js";
30
+
31
+ export { createStore, deep, reconcile, snapshot } from "./store/index.js";
32
+
33
+ export { createErrorBoundary, createLoadingBoundary, createRevealOrder, flatten } from "./boundaries.js";
34
+
35
+ export { $PROXY, $TARGET, $TRACK, isWrappable } from "./store/store.js";
36
+
37
+ export { createOptimisticStoreNext as createOptimisticStore } from "./store/next/optimistic.js";
38
+
39
+ export { createProjectionNext as createProjection } from "./store/next/projection.js";
40
+
41
+ export { merge, mergeSources, omit } from "./store/utils.js";
42
+
43
+ export { storeHasFamily, storeHasOptimisticFamily, storeIsShallow } from "./store/next/store.js";
44
+
45
+ export { storePath } from "./store/storePath.js";
46
+
47
+ /**
48
+ * Observe tier (diagnostics channel, attribution hook slot + interaction
49
+ * frame): dev and observe builds. The attribution engine itself is the
50
+ * `@solidjs/signals/attribution` entry.
51
+ */ const OBSERVE = OBSERVE$1;
52
+
53
+ /** Dev tier (devtools hooks, graph traversal, console reporting): dev builds only. */ const DEV = undefined;
54
+
55
+ export { DEV, OBSERVE };