@solidjs/signals 2.0.0-rc.17 → 2.0.0-rc.18

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 (40) hide show
  1. package/dist/dev-shared.js +296 -191
  2. package/dist/dev.attribution.js +16 -16
  3. package/dist/dev.js +527 -311
  4. package/dist/observe/core/async.js +148 -115
  5. package/dist/observe/core/constants.js +3 -1
  6. package/dist/observe/core/core.js +27 -27
  7. package/dist/observe/core/effect.js +29 -32
  8. package/dist/observe/core/graph.js +5 -5
  9. package/dist/observe/core/owner.js +7 -7
  10. package/dist/observe/core/scheduler.js +23 -12
  11. package/dist/observe/core/verdict.js +2 -2
  12. package/dist/observe/deferred.js +16 -13
  13. package/dist/observe/from.js +248 -0
  14. package/dist/observe/index.js +2 -0
  15. package/dist/observe/store/reconcile.js +137 -121
  16. package/dist/observe/store/store.js +595 -618
  17. package/dist/observe/store/target.js +1 -5
  18. package/dist/prod/core/async.js +209 -176
  19. package/dist/prod/core/constants.js +3 -1
  20. package/dist/prod/core/core.js +33 -33
  21. package/dist/prod/core/effect.js +24 -27
  22. package/dist/prod/core/graph.js +2 -2
  23. package/dist/prod/core/lanes.js +14 -14
  24. package/dist/prod/core/scheduler.js +35 -24
  25. package/dist/prod/core/truth.js +4 -4
  26. package/dist/prod/core/verdict.js +7 -7
  27. package/dist/prod/deferred.js +17 -14
  28. package/dist/prod/from.js +248 -0
  29. package/dist/prod/index.js +2 -0
  30. package/dist/prod/store/optimistic.js +1 -1
  31. package/dist/prod/store/reconcile.js +137 -121
  32. package/dist/prod/store/store.js +94 -117
  33. package/dist/prod/store/target.js +13 -17
  34. package/dist/types/core/async.d.ts +12 -2
  35. package/dist/types/core/constants.d.ts +5 -0
  36. package/dist/types/core/core.d.ts +19 -3
  37. package/dist/types/core/scheduler.d.ts +15 -3
  38. package/dist/types/from.d.ts +47 -0
  39. package/dist/types/index.d.ts +1 -0
  40. package/package.json +1 -1
@@ -228,4 +228,6 @@ const defaultContext = {};
228
228
  * @internal
229
229
  */ const $REFRESH = Symbol("refresh");
230
230
 
231
- export { $REFRESH, CAUGHT_REARMED, CONFIG_AUTHORITATIVE, CONFIG_AUTO_DISPOSE, CONFIG_CHILDREN_FORBIDDEN, CONFIG_FRESH_READ, CONFIG_GUESS, CONFIG_HAS_SNAPSHOT, CONFIG_HELD, CONFIG_HELD_FRAME, CONFIG_INPUTS_PUBLISHED, CONFIG_IN_SNAPSHOT_SCOPE, CONFIG_LAGS, CONFIG_MANUAL_WRITE, CONFIG_NO_SNAPSHOT, CONFIG_OVERRIDE, CONFIG_OWNED_WRITE, CONFIG_PROMOTED, CONFIG_REDERIVE, CONFIG_SLOT_NODE, CONFIG_STAGED, CONFIG_STAGED_ERROR, CONFIG_SYNC, CONFIG_TRANSPARENT, CONFIG_VERDICT, EFFECT_RENDER, EFFECT_TRACKED, EFFECT_USER, NOT_PENDING, NO_SNAPSHOT, REACTIVE_CHECK, REACTIVE_DIRTY, REACTIVE_DISPOSED, REACTIVE_FRAME_READ, REACTIVE_IN_HEAP, REACTIVE_IN_HEAP_HEIGHT, REACTIVE_JOINED, REACTIVE_LANE_DIRTY, REACTIVE_LANE_READ, REACTIVE_LAZY, REACTIVE_MANUAL_WRITE, REACTIVE_MISSED_WAKE, REACTIVE_NONE, REACTIVE_PROBED, REACTIVE_PROBE_UNANSWERED, REACTIVE_REASK, REACTIVE_RECOMPUTING_DEPS, REACTIVE_SCREEN_READ, REACTIVE_SNAPSHOT_STALE, REACTIVE_STAGED_READ, REACTIVE_ZOMBIE, STATUS_ERROR, STATUS_NONE, STATUS_PENDING, STATUS_UNINITIALIZED, SUPPORTS_PROXY, defaultContext };
231
+ const NewSet = Set;
232
+
233
+ export { $REFRESH, CAUGHT_REARMED, CONFIG_AUTHORITATIVE, CONFIG_AUTO_DISPOSE, CONFIG_CHILDREN_FORBIDDEN, CONFIG_FRESH_READ, CONFIG_GUESS, CONFIG_HAS_SNAPSHOT, CONFIG_HELD, CONFIG_HELD_FRAME, CONFIG_INPUTS_PUBLISHED, CONFIG_IN_SNAPSHOT_SCOPE, CONFIG_LAGS, CONFIG_MANUAL_WRITE, CONFIG_NO_SNAPSHOT, CONFIG_OVERRIDE, CONFIG_OWNED_WRITE, CONFIG_PROMOTED, CONFIG_REDERIVE, CONFIG_SLOT_NODE, CONFIG_STAGED, CONFIG_STAGED_ERROR, CONFIG_SYNC, CONFIG_TRANSPARENT, CONFIG_VERDICT, EFFECT_RENDER, EFFECT_TRACKED, EFFECT_USER, NOT_PENDING, NO_SNAPSHOT, NewSet, REACTIVE_CHECK, REACTIVE_DIRTY, REACTIVE_DISPOSED, REACTIVE_FRAME_READ, REACTIVE_IN_HEAP, REACTIVE_IN_HEAP_HEIGHT, REACTIVE_JOINED, REACTIVE_LANE_DIRTY, REACTIVE_LANE_READ, REACTIVE_LAZY, REACTIVE_MANUAL_WRITE, REACTIVE_MISSED_WAKE, REACTIVE_NONE, REACTIVE_PROBED, REACTIVE_PROBE_UNANSWERED, REACTIVE_REASK, REACTIVE_RECOMPUTING_DEPS, REACTIVE_SCREEN_READ, REACTIVE_SNAPSHOT_STALE, REACTIVE_STAGED_READ, REACTIVE_ZOMBIE, STATUS_ERROR, STATUS_NONE, STATUS_PENDING, STATUS_UNINITIALIZED, SUPPORTS_PROXY, defaultContext };
@@ -1,6 +1,6 @@
1
1
  import { notifyStatus, releaseFlightTeardown, handleAsync, clearStatus, parkLoadingWindow, stageError, settlePendingSource, releaseIfSettledUnobserved, settleErroredDependents } from "./async.js";
2
2
 
3
- import { STATUS_UNINITIALIZED, REACTIVE_LAZY, REACTIVE_NONE, defaultContext, CONFIG_TRANSPARENT, CONFIG_OWNED_WRITE, CONFIG_AUTO_DISPOSE, CONFIG_SYNC, CONFIG_NO_SNAPSHOT, CONFIG_IN_SNAPSHOT_SCOPE, NOT_PENDING, NO_SNAPSHOT, CONFIG_HAS_SNAPSHOT, CONFIG_HELD, REACTIVE_MANUAL_WRITE, STATUS_ERROR, REACTIVE_DIRTY, CONFIG_OVERRIDE, REACTIVE_DISPOSED, REACTIVE_CHECK, REACTIVE_IN_HEAP, REACTIVE_REASK, REACTIVE_SNAPSHOT_STALE, CONFIG_CHILDREN_FORBIDDEN, CONFIG_VERDICT, STATUS_PENDING, REACTIVE_LANE_DIRTY, CONFIG_GUESS, REACTIVE_RECOMPUTING_DEPS, CONFIG_LAGS, EFFECT_RENDER, CONFIG_HELD_FRAME, EFFECT_TRACKED, CONFIG_STAGED, CONFIG_STAGED_ERROR, REACTIVE_ZOMBIE, REACTIVE_MISSED_WAKE, REACTIVE_JOINED, REACTIVE_STAGED_READ, REACTIVE_PROBED, REACTIVE_FRAME_READ, REACTIVE_LANE_READ, REACTIVE_SCREEN_READ, REACTIVE_PROBE_UNANSWERED, CAUGHT_REARMED, CONFIG_PROMOTED, CONFIG_INPUTS_PUBLISHED, CONFIG_FRESH_READ, REACTIVE_IN_HEAP_HEIGHT, CONFIG_SLOT_NODE, EFFECT_USER, STATUS_NONE } from "./constants.js";
3
+ import { STATUS_UNINITIALIZED, REACTIVE_LAZY, REACTIVE_NONE, defaultContext, CONFIG_TRANSPARENT, CONFIG_OWNED_WRITE, CONFIG_AUTO_DISPOSE, CONFIG_SYNC, CONFIG_NO_SNAPSHOT, CONFIG_IN_SNAPSHOT_SCOPE, NOT_PENDING, NO_SNAPSHOT, CONFIG_HAS_SNAPSHOT, CONFIG_HELD, REACTIVE_MANUAL_WRITE, STATUS_ERROR, REACTIVE_DIRTY, CONFIG_OVERRIDE, REACTIVE_DISPOSED, REACTIVE_CHECK, REACTIVE_IN_HEAP, REACTIVE_REASK, REACTIVE_SNAPSHOT_STALE, CONFIG_CHILDREN_FORBIDDEN, CONFIG_VERDICT, STATUS_PENDING, REACTIVE_LANE_DIRTY, CONFIG_GUESS, REACTIVE_RECOMPUTING_DEPS, CONFIG_LAGS, EFFECT_RENDER, CONFIG_HELD_FRAME, EFFECT_TRACKED, CONFIG_STAGED, CONFIG_STAGED_ERROR, REACTIVE_ZOMBIE, REACTIVE_MISSED_WAKE, REACTIVE_JOINED, REACTIVE_STAGED_READ, REACTIVE_PROBED, REACTIVE_FRAME_READ, REACTIVE_LANE_READ, REACTIVE_SCREEN_READ, REACTIVE_PROBE_UNANSWERED, CAUGHT_REARMED, CONFIG_PROMOTED, CONFIG_INPUTS_PUBLISHED, CONFIG_FRESH_READ, REACTIVE_IN_HEAP_HEIGHT, CONFIG_SLOT_NODE, EFFECT_USER, NewSet, STATUS_NONE } from "./constants.js";
4
4
 
5
5
  import { NotReadyError } from "./error.js";
6
6
 
@@ -78,7 +78,7 @@ function ownerInSnapshotScope(e) {
78
78
 
79
79
  function setSnapshotCapture(e) {
80
80
  snapshotCaptureActive = e;
81
- if (e && !snapshotSources) snapshotSources = new Set;
81
+ if (e && !snapshotSources) snapshotSources = new NewSet;
82
82
  }
83
83
 
84
84
  function markSnapshotScope(e) {
@@ -199,7 +199,7 @@ function recompute(e, t = false) {
199
199
  // after the new frame's first runs, and the held pass's own
200
200
  // children die below (`disposeChildren`). The landing re-derives a
201
201
  // fresh held frame for the reveal (`_reruns`).
202
- e.se = NOT_PENDING;
202
+ e.ue = NOT_PENDING;
203
203
  if (n === EFFECT_RENDER) e.l &= ~CONFIG_HELD;
204
204
  }
205
205
  }
@@ -221,7 +221,7 @@ function recompute(e, t = false) {
221
221
  // An error a pass staged (D-41) is replaced by this pass's answer: a
222
222
  // value, a flight, or an error of its own (#4109, #4145).
223
223
  if (e.l & CONFIG_STAGED_ERROR) {
224
- e.se = NOT_PENDING;
224
+ e.ue = NOT_PENDING;
225
225
  e.l ^= CONFIG_STAGED_ERROR;
226
226
  }
227
227
  }
@@ -257,7 +257,7 @@ function recompute(e, t = false) {
257
257
  e.ie = clock;
258
258
  // The pass's previous value: its staging, else a lane node's lane value
259
259
  // when it has one (lanes.ts), else the committed one.
260
- let T = e.se !== NOT_PENDING ? e.se : e.l & CONFIG_OVERRIDE && e.T.v !== NOT_PENDING ? e.T.v : e.Ie;
260
+ let T = e.ue !== NOT_PENDING ? e.ue : e.l & CONFIG_OVERRIDE && e.T.v !== NOT_PENDING ? e.T.v : e.Ie;
261
261
  let S = e.Fe;
262
262
  let c = false;
263
263
  // L2: did this pass read the future (REACTIVE_JOINED, set by read) — or,
@@ -307,7 +307,7 @@ function recompute(e, t = false) {
307
307
  // D-41: a pass of a held node, or one that read a staging of this
308
308
  // flush, stages its error (`stageError`).
309
309
  if (n || !(I = stageError(e, t, true))) {
310
- if (n) ext(e).ue = true;
310
+ if (n) ext(e).se = true;
311
311
  notifyStatus(e, n ? STATUS_PENDING : STATUS_ERROR, t);
312
312
  // The replacement source is fully propagated now. If no new flight
313
313
  // re-owned self, retire the superseded flight and its dependent copies.
@@ -390,7 +390,7 @@ function recompute(e, t = false) {
390
390
  // publishes to — the staged `_pendingValue` when staged; a lane node's
391
391
  // lane value (a node entering the lane this pass has none yet — its
392
392
  // committed value, `laneStage` having voided a frame staging).
393
- const o = e.se !== NOT_PENDING ? e.se : e.l & CONFIG_OVERRIDE && e.T.v !== NOT_PENDING ? e.T.v : e.Ie;
393
+ const o = e.ue !== NOT_PENDING ? e.ue : e.l & CONFIG_OVERRIDE && e.T.v !== NOT_PENDING ? e.T.v : e.Ie;
394
394
  // A throwing comparator is contained inside `isSame` (A3, #2837; #4111).
395
395
  const E = !n && l || !!e.T?.de || !isSame(e, o, T);
396
396
  // Effects use `_equals: false` (no per-effect closure). The side effects that
@@ -403,7 +403,7 @@ function recompute(e, t = false) {
403
403
  // last applied owes no run (lanes.ts).
404
404
  !(e.R & REACTIVE_SCREEN_READ && !e.Ae && Object.is(e.ge, T)) &&
405
405
  // A re-staging pass owes no run: the commit replays the effect (#3802).
406
- e.se === NOT_PENDING) {
406
+ e.ue === NOT_PENDING) {
407
407
  e.Ae = !e.T?.F;
408
408
  // Reuse one bound runner per effect — runEffect no-ops on a stale
409
409
  // `_modified`, so re-enqueueing the same function is harmless.
@@ -442,8 +442,8 @@ function recompute(e, t = false) {
442
442
  // effect's value slot is private: the run the lane holds is what
443
443
  // shows it — unless the effect was born held, whose first run is the
444
444
  // commit's (A29): it re-stages, as below.
445
- if (n && e.se === NOT_PENDING) e.Ie = T; else if (n || C.ke) e.se = T; else e.T.v = T;
446
- } else if (t ? !((flushTransaction !== null || passTx !== null) && (A || creatorPass(f)?.R & REACTIVE_JOINED)) : n && e.se === NOT_PENDING) {
445
+ if (n && e.ue === NOT_PENDING) e.Ie = T; else if (n || C.ke) e.ue = T; else e.T.v = T;
446
+ } else if (t ? !((flushTransaction !== null || passTx !== null) && (A || creatorPass(f)?.R & REACTIVE_JOINED)) : n && e.ue === NOT_PENDING) {
447
447
  // A first pass publishes directly. So does an effect: its value slot
448
448
  // is private (only its own run reads it), and the staging round-trip
449
449
  // (queuePendingNode + commitPendingNodes) paid per effect on the
@@ -456,7 +456,7 @@ function recompute(e, t = false) {
456
456
  // that separation; it cannot be skipped on any path a pull can reach.
457
457
  e.Ie = T;
458
458
  } else {
459
- e.se = T;
459
+ e.ue = T;
460
460
  // Born held: in the transaction from birth, so a read of it this
461
461
  // tick is a read of its world (the mount-during-a-hold case), not
462
462
  // only after the seam parks it.
@@ -532,7 +532,7 @@ function recompute(e, t = false) {
532
532
  // kept at a park — one spurious recompute at most). A creation pass and a
533
533
  // tracked effect trim now: their frames are replaceable like a direct
534
534
  // commit, and a tracked effect's spurious run would be user-visible.
535
- if (!e.T?.F && e.se === NOT_PENDING && !(n && e.Ae)) {
535
+ if (!e.T?.F && e.ue === NOT_PENDING && !(n && e.Ae)) {
536
536
  if (t || n === EFFECT_TRACKED) trimStaleDeps(e); else heldTrim(e);
537
537
  }
538
538
  // A staged value, a parked frame (L2: the commit retires it), or status the
@@ -540,7 +540,7 @@ function recompute(e, t = false) {
540
540
  // node for this flush's commit. A first pass queues only when pending or
541
541
  // born held: otherwise its value published directly. A queued pass's
542
542
  // children are uncommitted (CONFIG_STAGED) until that commit.
543
- if (e.se !== NOT_PENDING || e.T !== null && (e.T.Ue !== null || e.T.me !== null) || (e.m & (STATUS_PENDING | STATUS_UNINITIALIZED)) !== 0 && (!t || (e.m & STATUS_PENDING) !== 0)) {
543
+ if (e.ue !== NOT_PENDING || e.T !== null && (e.T.Ue !== null || e.T.me !== null) || (e.m & (STATUS_PENDING | STATUS_UNINITIALIZED)) !== 0 && (!t || (e.m & STATUS_PENDING) !== 0)) {
544
544
  e.l |= CONFIG_STAGED;
545
545
  // Lane work is the lane's to reveal (its seam), not this flush's commit.
546
546
  if (C === null) queuePendingNode(e);
@@ -629,7 +629,7 @@ function computed(e, t) {
629
629
  // A loadingValue node is born committed: commit #0 is already in _value.
630
630
  m: o ? 0 : STATUS_UNINITIALIZED,
631
631
  ie: clock,
632
- se: NOT_PENDING,
632
+ ue: NOT_PENDING,
633
633
  Me: -1,
634
634
  ce: o,
635
635
  // Cold machinery (async slots) lives one hop away in the lazily-allocated extension — the core literal MUST
@@ -650,7 +650,7 @@ function computed(e, t) {
650
650
  Se: null,
651
651
  ae: null,
652
652
  F: undefined,
653
- ue: undefined,
653
+ se: undefined,
654
654
  fe: undefined,
655
655
  we: undefined,
656
656
  Pe: undefined,
@@ -802,7 +802,7 @@ function computed(e, t) {
802
802
  R: REACTIVE_LAZY,
803
803
  m: STATUS_UNINITIALIZED,
804
804
  ie: clock,
805
- se: NOT_PENDING,
805
+ ue: NOT_PENDING,
806
806
  Me: -1,
807
807
  ce: false,
808
808
  Ae: false,
@@ -839,7 +839,7 @@ function setEffectStatusNotify(e) {
839
839
  * 0, and only effect literals carry the field) get the shared notifier.
840
840
  * Presence doubles as the "display consumer" membership test in the status
841
841
  * walks, exactly as the per-node field did when every effect carried one. */ function statusNotifierOf(e) {
842
- return e.i ? effectStatusNotify ?? undefined : undefined;
842
+ return e.i ? effectStatusNotify : null;
843
843
  }
844
844
 
845
845
  const lazyOptions = {
@@ -874,7 +874,7 @@ function signal(e, t) {
874
874
  o: null,
875
875
  ve: null,
876
876
  ie: clock,
877
- se: NOT_PENDING,
877
+ ue: NOT_PENDING,
878
878
  // Signal-literal diet (§12e): NO _time/_fn/_statusFlags slots. Stores
879
879
  // materialize one signal per touched leaf, so signal bytes are store
880
880
  // bytes. _time is write-only on signals (every read site is computed-
@@ -914,7 +914,7 @@ function isEqual(e, t) {
914
914
  o: null,
915
915
  ve: null,
916
916
  ie: clock,
917
- se: NOT_PENDING,
917
+ ue: NOT_PENDING,
918
918
  Me: -1,
919
919
  T: null,
920
920
  O: n,
@@ -1044,7 +1044,7 @@ function setVerdict(e) {
1044
1044
  * never the graph — A32).
1045
1045
  * False means the reader derives from the staged value.
1046
1046
  */ function readerSeesCommitted(e, t) {
1047
- return !t || e.se === NOT_PENDING || t.l & CONFIG_CHILDREN_FORBIDDEN;
1047
+ return !t || e.ue === NOT_PENDING || t.l & CONFIG_CHILDREN_FORBIDDEN;
1048
1048
  }
1049
1049
 
1050
1050
  /** A28 — set when a node is staged (queuePendingNode) OUTSIDE a flush;
@@ -1065,7 +1065,7 @@ function markUnflushedStaged() {
1065
1065
  * is unflushed. Exempt: promoted writes (A28 (4): a write issued inside a
1066
1066
  * recompute is promoted at that recompute's end — boundary and loading
1067
1067
  * machinery, signals declared for in-computation writes). */ function unflushedValue(e) {
1068
- if (globalQueue.q || e.se === NOT_PENDING || e.l & CONFIG_PROMOTED) return NOT_PENDING;
1068
+ if (globalQueue.q || e.ue === NOT_PENDING || e.l & CONFIG_PROMOTED) return NOT_PENDING;
1069
1069
  // A held node (L2) carries a staged value between flushes too, but it was
1070
1070
  // flushed — and parked. Not an unflushed write — unless rewritten since:
1071
1071
  // then the flushed staging is what the world sees until the next flush.
@@ -1078,7 +1078,7 @@ function markUnflushedStaged() {
1078
1078
  * stash is stamped with the clock, which that flush advances. */ function stashFlushed(e) {
1079
1079
  const t = e.T;
1080
1080
  if (t.Be !== clock) {
1081
- t.je = e.se;
1081
+ t.je = e.ue;
1082
1082
  t.Be = clock;
1083
1083
  unflushedStaged = true;
1084
1084
  }
@@ -1137,7 +1137,7 @@ function read(e) {
1137
1137
  // The verdict windows (`latest`, `isPending`; verdict.ts): one cold path,
1138
1138
  // off this one, present only while a window is open.
1139
1139
  if (verdict !== null) return verdict(e, t);
1140
- if (!n.X && e.T?.Pe === undefined && !snapshotCaptureActive && (!unflushedStaged || e.se === NOT_PENDING) && // A28, see readNodeFast
1140
+ if (!n.X && e.T?.Pe === undefined && !snapshotCaptureActive && (!unflushedStaged || e.ue === NOT_PENDING) && // A28, see readNodeFast
1141
1141
  !(e.l & CONFIG_OVERRIDE) && // lanes: the slow path's one arm
1142
1142
  true) {
1143
1143
  if (t) {
@@ -1158,13 +1158,13 @@ function read(e) {
1158
1158
  }
1159
1159
  }
1160
1160
  // Committed visibility for untracked and children-forbidden readers.
1161
- if (!t || e.se === NOT_PENDING || t.l & CONFIG_CHILDREN_FORBIDDEN) return e.Ie;
1161
+ if (!t || e.ue === NOT_PENDING || t.l & CONFIG_CHILDREN_FORBIDDEN) return e.Ie;
1162
1162
  // (A plain signal is never uninitialized — the carve-out is `serve`'s.)
1163
1163
  // Lane work over a folded lane (D-34), once the frame has a
1164
1164
  // transaction, reads the screen too.
1165
1165
  if ((observeFlightHook && t.l & CONFIG_VERDICT || laneStageHook && passLane?.folded) && stagedScreen(t)) return e.Ie;
1166
1166
  stagedRead(t);
1167
- return e.se;
1167
+ return e.ue;
1168
1168
  }
1169
1169
  // Rule 3: this read serves the committed value to a render effect outside a
1170
1170
  // parking flush (decided after the pull below — a pull can join).
@@ -1250,7 +1250,7 @@ function read(e) {
1250
1250
  const n = e.T?.Pe;
1251
1251
  if (n !== undefined) {
1252
1252
  const o = n === NO_SNAPSHOT ? undefined : n;
1253
- const E = e.se !== NOT_PENDING ? e.se : e.Ie;
1253
+ const E = e.ue !== NOT_PENDING ? e.ue : e.Ie;
1254
1254
  if (E !== o) t.R |= REACTIVE_SNAPSHOT_STALE;
1255
1255
  return o;
1256
1256
  }
@@ -1287,7 +1287,7 @@ function read(e) {
1287
1287
  // an untracked reader has nothing to serve and holds (A19 exception 1) — a
1288
1288
  // bookkeeping read (`spectate`) likewise, and a children-forbidden reader
1289
1289
  // (A32: the frame, which has nothing here).
1290
- if (e.se !== NOT_PENDING && e.m & STATUS_UNINITIALIZED && (!t || spectating || t.l & CONFIG_CHILDREN_FORBIDDEN)) throw new NotReadyError(null);
1290
+ if (e.ue !== NOT_PENDING && e.m & STATUS_UNINITIALIZED && (!t || spectating || t.l & CONFIG_CHILDREN_FORBIDDEN)) throw new NotReadyError(null);
1291
1291
  const n = t && unflushedStaged ? unflushedValue(e) : NOT_PENDING;
1292
1292
  if (n !== NOT_PENDING) {
1293
1293
  markLateLinker(t);
@@ -1297,8 +1297,8 @@ function read(e) {
1297
1297
  if (observeFlightHook && t.l & CONFIG_VERDICT && !(e.m & STATUS_UNINITIALIZED) && stagedScreen(t)) return e.Ie;
1298
1298
  stagedRead(t, e);
1299
1299
  // D-41: the staged answer is an error (`stageError`).
1300
- if (e.l & CONFIG_STAGED_ERROR) throw e.se;
1301
- return e.se;
1300
+ if (e.l & CONFIG_STAGED_ERROR) throw e.ue;
1301
+ return e.ue;
1302
1302
  }
1303
1303
 
1304
1304
  /** A pass read a staging of this flush: it derives from what the flush may
@@ -1393,7 +1393,7 @@ function read(e) {
1393
1393
 
1394
1394
  function setSignal(e, t) {
1395
1395
  if (writeHook !== null) writeHook(e);
1396
- const n = e.se === NOT_PENDING ? e.Ie : e.se;
1396
+ const n = e.ue === NOT_PENDING ? e.Ie : e.ue;
1397
1397
  if (typeof t === "function") t = t(n);
1398
1398
  // Lanes: a write landing on a guess is its truth (A18 — an async guess's
1399
1399
  // own landing), judged against the guess; one landing on a lane's
@@ -1420,12 +1420,12 @@ function setSignal(e, t) {
1420
1420
  // pass reads a value the server never had. Store leaves written here are
1421
1421
  // plain signals and qualify the same way.
1422
1422
  if (snapshotCaptureActive) captureWriteSnapshot(e, n);
1423
- const E = e.se !== NOT_PENDING;
1423
+ const E = e.ue !== NOT_PENDING;
1424
1424
  if (!E) queuePendingNode(e);
1425
1425
  // A28: a held node rewritten outside a flush keeps its flushed staging
1426
1426
  // for every reader until the flush that carries the rewrite.
1427
1427
  else if (e.l & CONFIG_HELD && !globalQueue.q) stashFlushed(e);
1428
- e.se = t;
1428
+ e.ue = t;
1429
1429
  if (context !== null) notePromotedWrite(e);
1430
1430
  // _time is a computed-only slot (§12e): writing it on a signal would fork
1431
1431
  // the lean shape. Every read site is computed-typed.
@@ -1482,7 +1482,7 @@ function setSignal(e, t) {
1482
1482
  e.R = e.R & ~REACTIVE_CHECK | REACTIVE_DIRTY;
1483
1483
  insertIntoHeap(e, dirtyQueue);
1484
1484
  schedule();
1485
- } else if (e.se !== NOT_PENDING) e.R |= REACTIVE_MANUAL_WRITE;
1485
+ } else if (e.ue !== NOT_PENDING) e.R |= REACTIVE_MANUAL_WRITE;
1486
1486
  return t;
1487
1487
  }
1488
1488
 
@@ -15,44 +15,47 @@ import { globalQueue, schedule, heldRunHook, haltReactivity, GlobalQueue } from
15
15
  * automatically added to the queue of effects to re-execute, which will cause them to fetch their
16
16
  * sources and recompute
17
17
  */ function effect(t, e, E, u) {
18
- const f = !!u?.user;
19
- const r = createEffectNode(t, e, E, f ? EFFECT_USER : EFFECT_RENDER, u);
20
- recompute(r, true);
18
+ const r = !!u?.user;
19
+ const f = createEffectNode(t, e, E, r ? EFFECT_USER : EFFECT_RENDER, u);
20
+ recompute(f, true);
21
21
  // A first pass that read a staged value was staged itself (recompute: born
22
22
  // staged); the flush's commit replays this effect. Its first run is not this
23
23
  // creation's (A29). A lane's node likewise: its run is the lane's, queued
24
24
  // by the pass and released at the reveal (lanes.ts).
25
- !u?.defer && r.se === NOT_PENDING && !r.T?.Re?.v && (r.i === EFFECT_USER || u?.schedule ? globalQueue.enqueue(r.i, runEffect.bind(null, r)) : runEffect(r, r.i));
25
+ !u?.defer && f.ue === NOT_PENDING && !f.T?.Re?.v && (f.i === EFFECT_USER || u?.schedule ? globalQueue.enqueue(f.i, runEffect.bind(null, f)) : runEffect(f, f.i));
26
26
  }
27
27
 
28
- function notifyEffectStatus(t, e) {
29
- // Use passed values if provided, otherwise read from node
30
- const E = t !== undefined ? t : this.m;
31
- const u = e !== undefined ? e : this.T?.F;
32
- if (E & STATUS_ERROR) {
28
+ function notifyEffectStatus() {
29
+ const t = this.m;
30
+ const e = this.T?.F;
31
+ if (t & STATUS_ERROR) {
33
32
  globalQueue.notify(this, STATUS_PENDING, 0);
34
33
  if (this.i === EFFECT_USER) {
35
34
  // The error handler is the error arm of the effect phase (#2840 ruling):
36
35
  // queue it like the effect function. It runs in the same imperative,
37
36
  // writable scope and throws escalate the same way. No payload is queued — the node already carries `_statusFlags`/`_error`,
38
37
  // and the runner dispatches on them, so a recovery before the effect
39
- // phase takes the success arm instead. Blocked forwards (explicit
40
- // `status` arg without node-state writes) don't queue: the status
41
- // re-propagates unblocked at commit.
38
+ // phase takes the success arm instead. (Re-read: the boundary
39
+ // notify above can run a redraw.)
42
40
  if (this.m & STATUS_ERROR) {
43
41
  this.Ae = true;
44
42
  globalQueue.enqueue(this.i, this.W ??= runEffect.bind(null, this));
45
43
  }
46
44
  return;
47
45
  }
48
- if (!globalQueue.notify(this, STATUS_ERROR, STATUS_ERROR)) {
49
- // Dev locates the halt at the node that threw: the StatusError's
50
- // source, upstream of this effect when a computed failed.
51
- haltReactivity(unwrapStatusError(u));
52
- throw u;
53
- }
46
+ // Dev locates the halt at the node that threw: the StatusError's
47
+ // source, upstream of this effect when a computed failed.
48
+ escalate(this, unwrapStatusError(e), e, this);
54
49
  } else if (this.i === EFFECT_RENDER) {
55
- globalQueue.notify(this, STATUS_PENDING | STATUS_ERROR, E, u);
50
+ globalQueue.notify(this, STATUS_PENDING | STATUS_ERROR, t, e);
51
+ }
52
+ }
53
+
54
+ /** An effect's error no boundary takes halts the system (boundary-or-halt):
55
+ * halted with `cause`, `thrown` rethrown; dev locates it at `subject`. */ function escalate(t, e, E = e, u = t) {
56
+ if (!globalQueue.notify(t, STATUS_ERROR, STATUS_ERROR)) {
57
+ haltReactivity(e);
58
+ throw E;
56
59
  }
57
60
  }
58
61
 
@@ -88,10 +91,7 @@ function runEffect(t, e) {
88
91
  e?.();
89
92
  }) : console.error(e);
90
93
  } catch (e) {
91
- if (!globalQueue.notify(t, STATUS_ERROR, STATUS_ERROR)) {
92
- haltReactivity(e);
93
- throw e;
94
- }
94
+ escalate(t, e);
95
95
  }
96
96
  return;
97
97
  }
@@ -109,10 +109,7 @@ function runEffect(t, e) {
109
109
  } catch (e) {
110
110
  ext(t).F = new StatusError(t, e);
111
111
  t.m |= STATUS_ERROR;
112
- if (!globalQueue.notify(t, STATUS_ERROR, STATUS_ERROR)) {
113
- haltReactivity(e);
114
- throw e;
115
- }
112
+ escalate(t, e);
116
113
  } finally {
117
114
  t.ge = t.Ie;
118
115
  t.Ae = false;
@@ -1,4 +1,4 @@
1
- import { REACTIVE_RECOMPUTING_DEPS, CONFIG_AUTO_DISPOSE, STATUS_PENDING, REACTIVE_DISPOSED, REACTIVE_ZOMBIE, CONFIG_SLOT_NODE } from "./constants.js";
1
+ import { NewSet, REACTIVE_RECOMPUTING_DEPS, CONFIG_AUTO_DISPOSE, STATUS_PENDING, REACTIVE_DISPOSED, REACTIVE_ZOMBIE, CONFIG_SLOT_NODE } from "./constants.js";
2
2
 
3
3
  import { deleteFromHeap } from "./heap.js";
4
4
 
@@ -107,7 +107,7 @@ function unobserved(e) {
107
107
  * stays alive and serves its cache, uniform with observed memos) while
108
108
  * reclamation still happens within one microtask — the enqueue site arms
109
109
  * schedule(), so a flush is guaranteed even when no other work is queued.
110
- */ const dormantNodes = new Set;
110
+ */ const dormantNodes = new NewSet;
111
111
 
112
112
  function sweepDormant() {
113
113
  if (dormantNodes.size === 0) return;
@@ -119,7 +119,7 @@ function applyGuesses(e) {
119
119
  // value re-asked renews the question; nothing to notify.
120
120
  u = !t.xe || !t.xe(laneValueOf(t), l);
121
121
  if (!(t.l & CONFIG_GUESS)) {
122
- t.se = NOT_PENDING;
122
+ t.ue = NOT_PENDING;
123
123
  t.l |= CONFIG_GUESS;
124
124
  }
125
125
  t.T.v = l;
@@ -264,7 +264,7 @@ function unlink(e) {
264
264
  }
265
265
  if (!e.i) {
266
266
  if (!(e.l & CONFIG_OVERRIDE)) {
267
- e.se = NOT_PENDING;
267
+ e.ue = NOT_PENDING;
268
268
  e.l = e.l & -1048833 | CONFIG_OVERRIDE;
269
269
  ext(e);
270
270
  }
@@ -321,7 +321,7 @@ function supersede(e, n, s) {
321
321
  // A confirmation is silent (A17); only truth readers are told.
322
322
  GlobalQueue.sn?.(t);
323
323
  }
324
- e.se = n;
324
+ e.ue = n;
325
325
  joinFuture(l);
326
326
  }
327
327
 
@@ -365,7 +365,7 @@ function supersede(e, n, s) {
365
365
  // of its flight observes it (#3648, #3651; #3305's commit beneath a
366
366
  // flight).
367
367
  u.Re = null;
368
- if (!i && !r) l.se = f;
368
+ if (!i && !r) l.ue = f;
369
369
  commitPendingNode(l);
370
370
  if (!i) {
371
371
  if (l.R & (REACTIVE_IN_HEAP | REACTIVE_DIRTY | REACTIVE_CHECK) || l.m & STATUS_PENDING) l.l |= CONFIG_INPUTS_PUBLISHED;
@@ -376,7 +376,7 @@ function supersede(e, n, s) {
376
376
  // else the value it covered — is the parent's to land, and its readers
377
377
  // re-derive from it there. What the screen showed stays the screen
378
378
  // until then (A18 (c)).
379
- if (l.se === NOT_PENDING) l.se = covered(l);
379
+ if (l.ue === NOT_PENDING) l.ue = covered(l);
380
380
  if (e.ke) l.Ie = o;
381
381
  holdNode(l, n);
382
382
  insertSubs(l);
@@ -390,7 +390,7 @@ function supersede(e, n, s) {
390
390
  // it: an input's truth equal to its committed value is silent).
391
391
  u.Re = null;
392
392
  if (!i) {
393
- if (E !== NOT_PENDING && e.ke) l.se = E;
393
+ if (E !== NOT_PENDING && e.ke) l.ue = E;
394
394
  if (l.m & STATUS_PENDING) {
395
395
  u.Se = null;
396
396
  enqueueSub(l);
@@ -470,7 +470,7 @@ function supersede(e, n, s) {
470
470
  // joined its transaction) reads the answer it waited on — the lane's
471
471
  // staging, revealed at this seam.
472
472
  if (u & STATUS_PENDING && n.T.v === NOT_PENDING) return NOT_PENDING;
473
- if (n.se !== NOT_PENDING && e.l & CONFIG_HELD && flushTransaction !== null && resolveTx(flushTransaction) === txOf(e)) return n.se;
473
+ if (n.ue !== NOT_PENDING && e.l & CONFIG_HELD && flushTransaction !== null && resolveTx(flushTransaction) === txOf(e)) return n.ue;
474
474
  }
475
475
  if (t.ln || !s && u & STATUS_PENDING) {
476
476
  if (!s && !t.ke && u & STATUS_UNINITIALIZED) throw new NotReadyError(null);
@@ -529,7 +529,7 @@ function supersede(e, n, s) {
529
529
  for (let e = 0; e < t.tn.length && lanes.indexOf(t) !== -1; e++) {
530
530
  const n = t.tn[e];
531
531
  if (n.l & CONFIG_GUESS && n.T.Re === t) {
532
- const e = n.se !== NOT_PENDING ? n.se : covered(n);
532
+ const e = n.ue !== NOT_PENDING ? n.ue : covered(n);
533
533
  supersede(n, e, !n.xe || !n.xe(n.T.v, e));
534
534
  }
535
535
  }
@@ -555,9 +555,9 @@ function laneSeam(e, n) {
555
555
  }
556
556
  const t = s.T;
557
557
  if (t.Re !== e) continue;
558
- if (!(s.l & CONFIG_GUESS) && !s.i && s.se !== NOT_PENDING) {
559
- t.v = s.se;
560
- s.se = NOT_PENDING;
558
+ if (!(s.l & CONFIG_GUESS) && !s.i && s.ue !== NOT_PENDING) {
559
+ t.v = s.ue;
560
+ s.ue = NOT_PENDING;
561
561
  }
562
562
  // The frame this pass built is the screen's now; the one it replaced
563
563
  // goes (as `commitPendingNode` retires a committed pass's).
@@ -611,7 +611,7 @@ function laneSeam(e, n) {
611
611
  // folds a current one beneath it (D-34): true.
612
612
  const s = stale(e, question);
613
613
  if (GlobalQueue.an?.(e, n, s) || s) {
614
- e.se = n;
614
+ e.ue = n;
615
615
  e.l |= CONFIG_HELD;
616
616
  schedule();
617
617
  return n;
@@ -620,7 +620,7 @@ function laneSeam(e, n) {
620
620
  } else {
621
621
  const s = txOf(e);
622
622
  if (e.xe && e.xe(laneValueOf(e), n)) return n;
623
- if (s.ke) e.se = n; else e.T.v = n;
623
+ if (s.ke) e.ue = n; else e.T.v = n;
624
624
  if (e.X !== undefined) e.ie = clock;
625
625
  insertSubs(e);
626
626
  laneDirty(e, s);
@@ -643,7 +643,7 @@ function laneSeam(e, n) {
643
643
  * A24). The guess's own question answering supersedes as usual. */ function laneOutcome(e, n, s) {
644
644
  if (s) return true;
645
645
  if (stale(e, answered(e))) {
646
- e.se = n;
646
+ e.ue = n;
647
647
  e.l |= CONFIG_HELD;
648
648
  return true;
649
649
  }
@@ -612,18 +612,18 @@ class GlobalQueue {
612
612
  // stale stamp was the bridge that entangled unrelated updates
613
613
  // with a never-resolving flight). Its observers are held and
614
614
  // wait on its flight (`blocked`).
615
- if (e.se === NOT_PENDING && e.m & STATUS_UNINITIALIZED && e.i !== EFFECT_RENDER) continue;
615
+ if (e.ue === NOT_PENDING && e.m & STATUS_UNINITIALIZED && e.i !== EFFECT_RENDER) continue;
616
616
  // A34 (2): a tick whose writes net to the committed value made no
617
617
  // proposal (`setShow(false); setShow(true)` on a committed true) —
618
618
  // not staged, not held, pends nothing; a later write to it is
619
619
  // plain mainline. (A node already held stays held when written
620
620
  // back: A34 (1).)
621
- if (e.se !== NOT_PENDING && !(e.m & (STATUS_PENDING | STATUS_UNINITIALIZED)) && e.T?.Ue == null && e.T?.me == null &&
621
+ if (e.ue !== NOT_PENDING && !(e.m & (STATUS_PENDING | STATUS_UNINITIALIZED)) && e.T?.Ue == null && e.T?.me == null &&
622
622
  // The comparator last: a pass that created children (an object
623
623
  // owning a fresh async memo, #4111) must not have it read them.
624
624
  // A NotReady read there is a change (`isSame`).
625
- isSame(e, e.Ie, e.se)) {
626
- e.se = NOT_PENDING;
625
+ isSame(e, e.Ie, e.ue)) {
626
+ e.ue = NOT_PENDING;
627
627
  e.l &= ~CONFIG_STAGED;
628
628
  continue;
629
629
  }
@@ -796,28 +796,31 @@ class GlobalQueue {
796
796
  * or without the held read) owes nothing. A run this transaction stashed
797
797
  * for it is void (#3322: an effect has one value slot, and the stale pass
798
798
  * overwrote the value that run was for) — the re-derivation's run replaces
799
- * it. */ function reruns(e) {
800
- for (let n = 0; n < e.En.length; n++) {
801
- const o = e.En[n];
799
+ * it. Truthy when an effect re-derives (`land`). */ function reruns(e) {
800
+ let n;
801
+ for (let o = 0; o < e.En.length; o++) {
802
+ const t = e.En[o];
802
803
  // An errored reader's display is its error (D-39): the landing gives it
803
804
  // nothing to re-derive, and a re-run would be a read that retries the
804
805
  // errored source (#3945).
805
- if (o.R & REACTIVE_DISPOSED || o.m & STATUS_ERROR) continue;
806
- if (o.R & REACTIVE_FRAME_READ || o.l & CONFIG_VERDICT) {
807
- o.R &= ~REACTIVE_FRAME_READ;
808
- o.Ae = false;
806
+ if (t.R & REACTIVE_DISPOSED || t.m & STATUS_ERROR) continue;
807
+ if (t.R & REACTIVE_FRAME_READ || t.l & CONFIG_VERDICT) {
808
+ n ||= t.i;
809
+ t.R &= ~REACTIVE_FRAME_READ;
810
+ t.Ae = false;
809
811
  // Its frame is replaced by the re-derivation: a run one of its
810
812
  // children queued this flush (re-run by the same landing) is for the
811
813
  // frame being replaced — void too (#3404).
812
- for (let e = o.De; e !== null; e = e.pe) if (e.i) e.Ae = false;
813
- enqueueSub(o);
814
+ for (let e = t.De; e !== null; e = e.pe) if (e.i) e.Ae = false;
815
+ enqueueSub(t);
814
816
  }
815
817
  }
816
818
  e.En.length = 0;
819
+ return n;
817
820
  }
818
821
 
819
822
  /** The landing: the held frame becomes the frame. */ function land(e) {
820
- reruns(e);
823
+ const n = reruns(e);
821
824
  if (e.I !== null) releaseMarksHook(e.I);
822
825
  // Old children die in the commits (cleanups first), then the stashed
823
826
  // effects run ahead of this flush's own. A node a lane took over since
@@ -832,7 +835,15 @@ class GlobalQueue {
832
835
  // `u`, which has left `transactions`: the write is this landing's, and
833
836
  // the next flush must not park in it (#4089).
834
837
  if (flushTransaction === e) flushTransaction = null;
835
- releaseQueues(e);
838
+ // An effect re-derives next round (`reruns`): the held runs wait for it
839
+ // (D-9, the A15 reveal corollary, #4071). Its re-derivation can discover a
840
+ // flight — a stale reader whose mainline pass stopped reading it, now
841
+ // reading the landed world — and a landing that showed its runs first
842
+ // would reveal the held write beside a frame that can only show its old
843
+ // value. Queued last in this effect phase, the release hands them to the
844
+ // next round's own runs, ahead of the re-derivation's: they show with it,
845
+ // or park with it in the hold its pending opens. The commits stand.
846
+ if (n) globalQueue.un[1].push(() => releaseQueues(e)); else releaseQueues(e);
836
847
  }
837
848
 
838
849
  /** The held runs join this flush's effect phase after its own runs (the
@@ -935,16 +946,16 @@ function commitPendingNode(e) {
935
946
  // commits a value.
936
947
  if (!n.X) {
937
948
  e.l &= -385;
938
- if (e.se !== NOT_PENDING) {
939
- e.Ie = e.se;
940
- e.se = NOT_PENDING;
949
+ if (e.ue !== NOT_PENDING) {
950
+ e.Ie = e.ue;
951
+ e.ue = NOT_PENDING;
941
952
  }
942
953
  return;
943
954
  }
944
955
  // Computeds only from here (`_statusFlags` is not a signal field). A
945
956
  // commit beneath a flight publishes its inputs (A15 reveal corollary,
946
957
  // #3305): a reveal of it must observe, not show the pre-flight value.
947
- const o = e.se === NOT_PENDING && (n.m & STATUS_PENDING) !== 0;
958
+ const o = e.ue === NOT_PENDING && (n.m & STATUS_PENDING) !== 0;
948
959
  const t = e.l & CONFIG_STAGED_ERROR;
949
960
  // An answer commits: the error it replaces leaves the display (D-41).
950
961
  if (!o && n.T) n.T.de = undefined;
@@ -954,13 +965,13 @@ function commitPendingNode(e) {
954
965
  // see it in the cycle after this seam: a tracked re-read there is not a
955
966
  // retry (`read`), or a boundary re-derived by it would retry it forever.
956
967
  if (t) {
957
- const o = e.se;
958
- e.se = NOT_PENDING;
968
+ const o = e.ue;
969
+ e.ue = NOT_PENDING;
959
970
  e.ie = clock + 1;
960
971
  notifyStatus(n, STATUS_ERROR, o);
961
- } else if (e.se !== NOT_PENDING) {
962
- e.Ie = e.se;
963
- e.se = NOT_PENDING;
972
+ } else if (e.ue !== NOT_PENDING) {
973
+ e.Ie = e.ue;
974
+ e.ue = NOT_PENDING;
964
975
  // A node born staged (recompute) initializes at this commit.
965
976
  n.m &= ~STATUS_UNINITIALIZED;
966
977
  // A quiet re-ask's classification survives its landing and dies with
@@ -12,7 +12,7 @@ import { enqueueSub } from "./heap.js";
12
12
  * staging a shown lane holds for its next reveal, else its slot; the
13
13
  * committed value while it has none (its first lane pass in flight). */
14
14
  function laneValueOf(r) {
15
- if (!(r.l & CONFIG_GUESS) && r.se !== NOT_PENDING) return r.se;
15
+ if (!(r.l & CONFIG_GUESS) && r.ue !== NOT_PENDING) return r.ue;
16
16
  const t = r.T.v;
17
17
  return t !== NOT_PENDING ? t : r.Ie;
18
18
  }
@@ -25,7 +25,7 @@ function laneValueOf(r) {
25
25
  * value. */ function truthRead(r) {
26
26
  if (truth(r)) return laneValueOf(r);
27
27
  if (!(r.l & CONFIG_GUESS)) throw new NotReadyError(null);
28
- return r.se !== NOT_PENDING ? r.se : r.Ie;
28
+ return r.ue !== NOT_PENDING ? r.ue : r.Ie;
29
29
  }
30
30
 
31
31
  /** A guess confirmed: its lane's truth readers re-read — of the lane's
@@ -36,8 +36,8 @@ function laneValueOf(r) {
36
36
  /** A lane node's value is the truth when every guess beneath it is
37
37
  * confirmed — its source staged a value equal to the guess — and nothing
38
38
  * beneath it is held in the parent: same inputs, same value. */ function truth(r) {
39
- if (r.l & CONFIG_GUESS) return r.se !== NOT_PENDING && r.xe && r.xe(r.T.v, r.se);
40
- if (r.se !== NOT_PENDING) return;
39
+ if (r.l & CONFIG_GUESS) return r.ue !== NOT_PENDING && r.xe && r.xe(r.T.v, r.ue);
40
+ if (r.ue !== NOT_PENDING) return;
41
41
  if (r.l & CONFIG_OVERRIDE) for (let t = r.C; t; t = t.G) if (!truth(t.S)) return;
42
42
  return 1;
43
43
  }