@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
@@ -306,6 +306,8 @@ const defaultContext = {};
306
306
  */
307
307
  const $REFRESH = Symbol("refresh");
308
308
 
309
+ const NewSet = Set;
310
+
309
311
  let attrHooks = null;
310
312
  /**
311
313
  * The installed engine, registered on `globalThis` as well (the records
@@ -2032,8 +2034,18 @@ function nextQuestion() {
2032
2034
  }
2033
2035
  const MAINLINE_QUESTION = 0x7fffffff;
2034
2036
  let inEffectCallback = false;
2035
- function setEffectCallback(value) {
2036
- inEffectCallback = value;
2037
+ /** Dev-only: matches no event, so a window or phase holding it applies
2038
+ * whatever event is current (see `strictReadEvent`, core.ts). */
2039
+ const ANY_EVENT = {};
2040
+ /** Dev-only: the DOM event current when the effect phase was entered. A
2041
+ * handler dispatched synchronously from that phase (`el.focus()`) runs under
2042
+ * a different event and is not in it (#4235). */
2043
+ let effectCallbackEvent = ANY_EVENT;
2044
+ function setEffectCallback(value, event = ANY_EVENT) {
2045
+ {
2046
+ inEffectCallback = value;
2047
+ effectCallbackEvent = event;
2048
+ }
2037
2049
  }
2038
2050
  function schedule() {
2039
2051
  if (halted) {
@@ -2421,8 +2433,9 @@ class GlobalQueue {
2421
2433
  * or without the held read) owes nothing. A run this transaction stashed
2422
2434
  * for it is void (#3322: an effect has one value slot, and the stale pass
2423
2435
  * overwrote the value that run was for) — the re-derivation's run replaces
2424
- * it. */
2436
+ * it. Truthy when an effect re-derives (`land`). */
2425
2437
  function reruns(u) {
2438
+ let frame;
2426
2439
  for (let i = 0; i < u._reruns.length; i++) {
2427
2440
  const r = u._reruns[i];
2428
2441
  // An errored reader's display is its error (D-39): the landing gives it
@@ -2430,6 +2443,7 @@ function reruns(u) {
2430
2443
  // errored source (#3945).
2431
2444
  if (r._flags & REACTIVE_DISPOSED || r._statusFlags & STATUS_ERROR) continue;
2432
2445
  if (r._flags & REACTIVE_FRAME_READ || r._config & CONFIG_VERDICT) {
2446
+ frame ||= r._type;
2433
2447
  r._flags &= ~REACTIVE_FRAME_READ;
2434
2448
  r._modified = false;
2435
2449
  // Its frame is replaced by the re-derivation: a run one of its
@@ -2440,13 +2454,14 @@ function reruns(u) {
2440
2454
  }
2441
2455
  }
2442
2456
  u._reruns.length = 0;
2457
+ return frame;
2443
2458
  }
2444
2459
  /** The landing: the held frame becomes the frame. */
2445
2460
  function land(u) {
2446
2461
  // Attribution hook: judged complete — its held writes commit next
2447
2462
  // (`_nodes` still lists them).
2448
2463
  if (attrHooks !== null) attrHooks.transitionSettled(u);
2449
- reruns(u);
2464
+ const frame = reruns(u);
2450
2465
  if (u._marks !== null) releaseMarksHook(u._marks);
2451
2466
  // Old children die in the commits (cleanups first), then the stashed
2452
2467
  // effects run ahead of this flush's own. A node a lane took over since
@@ -2461,7 +2476,16 @@ function land(u) {
2461
2476
  // `u`, which has left `transactions`: the write is this landing's, and
2462
2477
  // the next flush must not park in it (#4089).
2463
2478
  if (flushTransaction === u) flushTransaction = null;
2464
- releaseQueues(u);
2479
+ // An effect re-derives next round (`reruns`): the held runs wait for it
2480
+ // (D-9, the A15 reveal corollary, #4071). Its re-derivation can discover a
2481
+ // flight — a stale reader whose mainline pass stopped reading it, now
2482
+ // reading the landed world — and a landing that showed its runs first
2483
+ // would reveal the held write beside a frame that can only show its old
2484
+ // value. Queued last in this effect phase, the release hands them to the
2485
+ // next round's own runs, ahead of the re-derivation's: they show with it,
2486
+ // or park with it in the hold its pending opens. The commits stand.
2487
+ if (frame) globalQueue._queues[1].push(() => releaseQueues(u));
2488
+ else releaseQueues(u);
2465
2489
  }
2466
2490
  /** The held runs join this flush's effect phase after its own runs (the
2467
2491
  * landing's re-passes, in height order): the frame's effects run as one
@@ -2720,8 +2744,22 @@ function flush(fn) {
2720
2744
  // (owner.ts).
2721
2745
  rethrowCleanupError();
2722
2746
  }
2747
+ /** Dev-only: the DOM event current when the running effect queue started,
2748
+ * or `ANY_EVENT` outside one. Read once per queue rather than once per
2749
+ * effect (the getter is costly under jsdom); no event starts or ends between
2750
+ * two effects of one synchronous queue (#4235). */
2751
+ let queueEvent = ANY_EVENT;
2723
2752
  function runQueue(queue, type) {
2724
- for (let i = 0; i < queue.length; i++) queue[i](type);
2753
+ {
2754
+ const prev = queueEvent;
2755
+ queueEvent = dispatchingEvent();
2756
+ try {
2757
+ for (let i = 0; i < queue.length; i++) queue[i](type);
2758
+ } finally {
2759
+ queueEvent = prev;
2760
+ }
2761
+ return;
2762
+ }
2725
2763
  }
2726
2764
 
2727
2765
  /**
@@ -2943,7 +2981,7 @@ function unobserved(el) {
2943
2981
  * reclamation still happens within one microtask — the enqueue site arms
2944
2982
  * schedule(), so a flush is guaranteed even when no other work is queued.
2945
2983
  */
2946
- const dormantNodes = new Set();
2984
+ const dormantNodes = new NewSet();
2947
2985
  function sweepDormant() {
2948
2986
  if (dormantNodes.size === 0) return;
2949
2987
  for (const el of dormantNodes) {
@@ -3011,7 +3049,7 @@ function link(dep, sub) {
3011
3049
  // to clear it, stranding the Set members' pending forever (#2893).
3012
3050
  function addPendingSource(el, source) {
3013
3051
  if (el._x?._pendingSources?.has(source)) return false;
3014
- (ext(el)._pendingSources ??= new Set()).add(source);
3052
+ (ext(el)._pendingSources ??= new NewSet()).add(source);
3015
3053
  return true;
3016
3054
  }
3017
3055
  function removePendingSource(el, source) {
@@ -3094,7 +3132,7 @@ function releaseIfSettledUnobserved(node) {
3094
3132
  // Collect-then-release so unobserved() never unlinks under the walk.
3095
3133
  function releaseSettledDependents(el) {
3096
3134
  let candidates;
3097
- const visited = new Set();
3135
+ const visited = new NewSet();
3098
3136
  const visit = node => {
3099
3137
  if (visited.has(node)) return;
3100
3138
  visited.add(node);
@@ -3160,7 +3198,7 @@ function stageError(el, error, inPass) {
3160
3198
  }
3161
3199
  function settleErroredDependents(el, error) {
3162
3200
  let scheduled = false;
3163
- const visited = new Set();
3201
+ const visited = new NewSet();
3164
3202
  const visit = node => {
3165
3203
  if (visited.has(node)) return;
3166
3204
  visited.add(node);
@@ -3237,7 +3275,7 @@ function settlePendingSource(el, source = el) {
3237
3275
  removePendingSource(el, source);
3238
3276
  let scheduled = false;
3239
3277
  let released;
3240
- const visited = new Set();
3278
+ const visited = new NewSet();
3241
3279
  const settle = node => {
3242
3280
  if (
3243
3281
  visited.has(node) ||
@@ -3304,6 +3342,37 @@ function releaseFlightTeardown(el) {
3304
3342
  teardown();
3305
3343
  }
3306
3344
  }
3345
+ /**
3346
+ * Internal (not a package export): run `fn` when the current pass's flight
3347
+ * is superseded, at the same instant as the iterator close above, instead of
3348
+ * when the replacing pass commits (owner cleanups wait for that). Chains onto
3349
+ * the node's teardown slot; fires once with it. A no-op unless the ambient
3350
+ * owner is a computed in its own pass. `consumeIterator` fills the slot
3351
+ * before its first pull, so a source opened in an async generator's body
3352
+ * (`for await`) composes after the generator's close.
3353
+ */
3354
+ function onSupersede(fn) {
3355
+ const el = context;
3356
+ if (el === null || el._fn === undefined || !(el._flags & REACTIVE_RECOMPUTING_DEPS)) return;
3357
+ const fresh = el._x === null;
3358
+ const x = ext(el);
3359
+ // Allocating the extension mid-run turns `_x?._inFlight` from undefined into
3360
+ // null, which recompute would read as a flight this body registered itself.
3361
+ // Restore undefined here, so the fix costs nothing for code that never
3362
+ // calls onSupersede.
3363
+ if (fresh) x._inFlight = undefined;
3364
+ const prev = x._flightTeardown;
3365
+ x._flightTeardown =
3366
+ prev === null
3367
+ ? fn
3368
+ : () => {
3369
+ try {
3370
+ prev();
3371
+ } finally {
3372
+ fn();
3373
+ }
3374
+ };
3375
+ }
3307
3376
  function handleAsync(
3308
3377
  el,
3309
3378
  result,
@@ -3407,8 +3476,14 @@ function handleAsync(
3407
3476
  // D-41: a held node's error is staged (`stageError`). A derived store's
3408
3477
  // readers learn its error through the family, not the derive's
3409
3478
  // dependents: it publishes now.
3410
- if (stillPending || setter || !stageError(el, error))
3479
+ if (stillPending || setter || !stageError(el, error)) {
3411
3480
  notifyStatus(el, stillPending ? STATUS_PENDING : STATUS_ERROR, error);
3481
+ // The error is this pass's answer, published now: it replaces the shown
3482
+ // frame as a value's commit would (`commitPendingNode`), so the frame
3483
+ // the pass parked goes instead of waiting for a later success. A no-op
3484
+ // when nothing is parked.
3485
+ if (!stillPending) disposeChildren(el, false, true);
3486
+ }
3412
3487
  // A NotReady rejection is a landing into another pending source. The
3413
3488
  // rejected flight will never settle its self entry, so transfer ownership
3414
3489
  // after notifyStatus has propagated the replacement source.
@@ -3523,7 +3598,11 @@ function handleAsync(
3523
3598
  cleanup(close, owner);
3524
3599
  // Flight-identity cancellation (#3122): the registration above is the
3525
3600
  // owner-death backstop; the teardown slot fires at the _inFlight release
3526
- // sites so supersede stops this stream immediately.
3601
+ // sites so supersede stops this stream immediately. Assigned, not
3602
+ // chained (chaining here costs every app bytes): it replaces any
3603
+ // `onSupersede` teardown this run registered earlier, which then falls
3604
+ // back to the owner-cleanup channel. A returned `from()` is unaffected:
3605
+ // `close` calls its `return()`, which runs its own teardown.
3527
3606
  ext(el)._flightTeardown = close;
3528
3607
  // Release check before each next pull: an unobserved lazy node must tear
3529
3608
  // down (its close above runs via disposal, closing the iterator) instead
@@ -3686,12 +3765,12 @@ function handleAsync(
3686
3765
  handleError(syncError);
3687
3766
  throw syncError;
3688
3767
  } else if (!resolved) {
3689
- // Loading window: serve commit #0 instead of suspending — first-flight
3690
- // work on a loadingValue node is loading-class (invisible to
3691
- // boundaries); the flight itself is already registered in _inFlight and
3692
- // lands through asyncWrite.
3693
- if (el._loading) return el._value;
3694
- throw new NotReadyError(context);
3768
+ // Not landed: the shared tail below suspends, or — loading window —
3769
+ // serves commit #0 (first-flight work on a loadingValue node is
3770
+ // loading-class, invisible to boundaries; the flight itself is already
3771
+ // registered in _inFlight and lands through asyncWrite). A promise is
3772
+ // never an iterator, so nothing between here and there runs.
3773
+ liveLanded = false;
3695
3774
  } else if (!flattenIfIterable(syncValue)) {
3696
3775
  // Synchronously-resolved promise: the first real answer landed.
3697
3776
  el._loading = false;
@@ -3703,7 +3782,7 @@ function handleAsync(
3703
3782
  if (iterator) flattenIfIterable(result);
3704
3783
  if (liveLanded !== null) {
3705
3784
  if (!liveLanded) {
3706
- // Loading window: serve commit #0 (see the promise branch above).
3785
+ // Loading window: serve commit #0 instead of suspending.
3707
3786
  if (el._loading) return el._value;
3708
3787
  throw new NotReadyError(context);
3709
3788
  }
@@ -3715,7 +3794,7 @@ function handleAsync(
3715
3794
  }
3716
3795
  function clearStatus(el, clearUninitialized = false) {
3717
3796
  if (el._x?._pendingSources) clearPendingSources(el);
3718
- if (el._x?._blocked) if (el._x !== null) el._x._blocked = false;
3797
+ if (el._x?._blocked) el._x._blocked = false;
3719
3798
  el._statusFlags = clearUninitialized ? 0 : el._statusFlags & STATUS_UNINITIALIZED;
3720
3799
  if (el._x?._error) setPendingError(el);
3721
3800
  const notify = statusNotifierOf(el);
@@ -3755,7 +3834,7 @@ function landStatus(el, clearUninitialized = false) {
3755
3834
  setPendingError(el, sources.values().next().value);
3756
3835
  } else clearStatus(el, clearUninitialized);
3757
3836
  }
3758
- function notifyStatus(el, status, error, blockStatus) {
3837
+ function notifyStatus(el, status, error) {
3759
3838
  // Wrap regular errors to track source node
3760
3839
  if (
3761
3840
  status === STATUS_ERROR &&
@@ -3766,21 +3845,19 @@ function notifyStatus(el, status, error, blockStatus) {
3766
3845
  const pendingSource =
3767
3846
  status === STATUS_PENDING && error instanceof NotReadyError ? error.source : undefined;
3768
3847
  // (CARVE 2: the lane assignment went with the engine.)
3769
- {
3770
- if (status === STATUS_PENDING && pendingSource) {
3771
- // D-41: a re-ask of a shown error keeps it the display (`_shownError`).
3772
- if (el._statusFlags & STATUS_ERROR) el._x._shownError = el._x._error;
3773
- addPendingSource(el, pendingSource);
3774
- el._statusFlags = STATUS_PENDING | (el._statusFlags & STATUS_UNINITIALIZED);
3775
- // Preserve the current source on this propagation so readers park on
3776
- // the distinct pending source.
3777
- setPendingError(el, pendingSource, error);
3778
- } else {
3779
- clearPendingSources(el);
3780
- el._statusFlags =
3781
- status | (status !== STATUS_ERROR ? el._statusFlags & STATUS_UNINITIALIZED : 0);
3782
- ext(el)._error = error;
3783
- }
3848
+ if (status === STATUS_PENDING && pendingSource) {
3849
+ // D-41: a re-ask of a shown error keeps it the display (`_shownError`).
3850
+ if (el._statusFlags & STATUS_ERROR) el._x._shownError = el._x._error;
3851
+ addPendingSource(el, pendingSource);
3852
+ el._statusFlags = STATUS_PENDING | (el._statusFlags & STATUS_UNINITIALIZED);
3853
+ // Preserve the current source on this propagation so readers park on
3854
+ // the distinct pending source.
3855
+ setPendingError(el, pendingSource, error);
3856
+ } else {
3857
+ clearPendingSources(el);
3858
+ el._statusFlags =
3859
+ status | (status !== STATUS_ERROR ? el._statusFlags & STATUS_UNINITIALIZED : 0);
3860
+ ext(el)._error = error;
3784
3861
  }
3785
3862
  // Lanes: a written guess is an optimistic boundary (A17 — "the optimistic
3786
3863
  // future value until we know otherwise"). Its own source in flight is its
@@ -3794,9 +3871,7 @@ function notifyStatus(el, status, error, blockStatus) {
3794
3871
  }
3795
3872
  const elNotify = statusNotifierOf(el);
3796
3873
  if (elNotify) {
3797
- {
3798
- elNotify.call(el);
3799
- }
3874
+ elNotify.call(el);
3800
3875
  return;
3801
3876
  }
3802
3877
  propagateStatus(el, status, error);
@@ -3806,7 +3881,7 @@ function notifyStatus(el, status, error, blockStatus) {
3806
3881
  * family's leaves, which are not the derive's dependents — the family
3807
3882
  * propagates the derive's status from each leaf to the leaf's readers
3808
3883
  * (store/projection.ts `wakeFamily`), exactly as a memo's would reach its. */
3809
- function propagateStatus(el, status, error, downstreamBlockStatus) {
3884
+ function propagateStatus(el, status, error) {
3810
3885
  const pendingSource =
3811
3886
  status === STATUS_PENDING && error instanceof NotReadyError ? error.source : undefined;
3812
3887
  forEachDependent(el, (sub, link) => {
@@ -3863,23 +3938,21 @@ function propagateStatus(el, status, error, downstreamBlockStatus) {
3863
3938
  // bookkeeping (loading window, uninitialized clear) runs with the flush
3864
3939
  // — with the lane, when the pass propagating is the lane's work and
3865
3940
  // the dependent's pending is the lane's own flight (`laneStage`).
3866
- {
3867
- // (No answer: a pending propagation leaves a written guess a guess.)
3868
- if (laneStageHook && passLane !== null) laneStageHook(sub, passLane, true, el);
3869
- else {
3870
- queuePendingNode(sub);
3871
- // A15 (#3443): pending propagates onto a held memo without
3872
- // recomputing it, and the propagation itself enters the memo's
3873
- // transaction — the flight flows into a memo that transaction
3874
- // holds, so the write that started it is held with it. A render
3875
- // effect's membership is its pass's (`notify`), never sticky.
3876
- if (
3877
- status === STATUS_PENDING &&
3878
- (sub._config & (CONFIG_HELD | CONFIG_OVERRIDE)) === CONFIG_HELD &&
3879
- !sub._type
3880
- )
3881
- joinFuture(txOf(sub));
3882
- }
3941
+ // (No answer: a pending propagation leaves a written guess a guess.)
3942
+ if (laneStageHook && passLane !== null) laneStageHook(sub, passLane, true, el);
3943
+ else {
3944
+ queuePendingNode(sub);
3945
+ // A15 (#3443): pending propagates onto a held memo without
3946
+ // recomputing it, and the propagation itself enters the memo's
3947
+ // transaction — the flight flows into a memo that transaction
3948
+ // holds, so the write that started it is held with it. A render
3949
+ // effect's membership is its pass's (`notify`), never sticky.
3950
+ if (
3951
+ status === STATUS_PENDING &&
3952
+ (sub._config & (CONFIG_HELD | CONFIG_OVERRIDE)) === CONFIG_HELD &&
3953
+ !sub._type
3954
+ )
3955
+ joinFuture(txOf(sub));
3883
3956
  }
3884
3957
  notifyStatus(sub, status, error);
3885
3958
  }
@@ -3945,7 +4018,11 @@ const PRIMITIVE_IN_EFFECT_CALLBACK_MESSAGE =
3945
4018
  * created there is inert and harmless. Tracked effects and onSettled are
3946
4019
  * children-forbidden and refuse through PRIMITIVE_IN_FORBIDDEN_SCOPE. */
3947
4020
  function assertNotInEffectCallback() {
3948
- if (inEffectCallback && context === null) {
4021
+ if (
4022
+ inEffectCallback &&
4023
+ context === null &&
4024
+ (effectCallbackEvent === ANY_EVENT || dispatchingEvent() === effectCallbackEvent)
4025
+ ) {
3949
4026
  emitDiagnostic({
3950
4027
  code: "PRIMITIVE_IN_EFFECT_CALLBACK",
3951
4028
  kind: "lifecycle",
@@ -3978,7 +4055,7 @@ function ownerInSnapshotScope(owner) {
3978
4055
  }
3979
4056
  function setSnapshotCapture(active) {
3980
4057
  snapshotCaptureActive = active;
3981
- if (active && !snapshotSources) snapshotSources = new Set();
4058
+ if (active && !snapshotSources) snapshotSources = new NewSet();
3982
4059
  }
3983
4060
  function markSnapshotScope(owner) {
3984
4061
  owner._snapshotScope = true;
@@ -4907,7 +4984,7 @@ function setEffectStatusNotify(fn) {
4907
4984
  * Presence doubles as the "display consumer" membership test in the status
4908
4985
  * walks, exactly as the per-node field did when every effect carried one. */
4909
4986
  function statusNotifierOf(el) {
4910
- return el._type ? (effectStatusNotify ?? undefined) : undefined;
4987
+ return el._type ? effectStatusNotify : null;
4911
4988
  }
4912
4989
  const lazyOptions = { lazy: true };
4913
4990
  function setupComputedNode(self, options) {
@@ -5056,11 +5133,31 @@ function exitDisposal() {
5056
5133
  function resetDisposalDepth() {
5057
5134
  disposalDepth = 0;
5058
5135
  }
5059
- function setStrictRead(v) {
5136
+ /**
5137
+ * Dev-only: the DOM event being dispatched when the open strict-read window
5138
+ * opened (`globalThis.event`; null between dispatches and off the main
5139
+ * thread's Window), or `ANY_EVENT` when the window did not record one. A DOM
5140
+ * call made in an effect callback that dispatches events synchronously
5141
+ * (`el.focus()`, `el.click()`, `dialog.showModal()`) runs that event's
5142
+ * handlers inside the window; a handler is a one-shot snapshot whoever
5143
+ * dispatched the event, so a read made while a different event is current is
5144
+ * outside the window (#4235). Only queued effect runs record the event, read
5145
+ * once per queue (the getter is costly under jsdom); `strictReadActive` is
5146
+ * the check.
5147
+ */
5148
+ let strictReadEvent;
5149
+ // `?? null`: no current event must not read as a missing argument below.
5150
+ const dispatchingEvent = () => globalThis.event ?? null;
5151
+ function setStrictRead(v, event = ANY_EVENT) {
5060
5152
  const prev = strictRead;
5061
5153
  strictRead = v;
5154
+ strictReadEvent = event;
5062
5155
  return prev;
5063
5156
  }
5157
+ /** Dev-only: is the strict-read window open for a read made now? */
5158
+ function strictReadActive() {
5159
+ return !!strictRead && (strictReadEvent === ANY_EVENT || dispatchingEvent() === strictReadEvent);
5160
+ }
5064
5161
  /**
5065
5162
  * Runs `fn` outside of any reactive tracking — reads inside `fn` will not
5066
5163
  * subscribe the current scope. Returns whatever `fn` returns.
@@ -5099,9 +5196,11 @@ function setStrictRead(v) {
5099
5196
  function untrack(fn, strictReadLabel) {
5100
5197
  const prevTracking = tracking;
5101
5198
  const prevStrictRead = strictRead;
5199
+ const prevStrictReadEvent = strictReadEvent;
5102
5200
  tracking = false;
5103
5201
  {
5104
5202
  strictRead = strictReadLabel || false;
5203
+ strictReadEvent = ANY_EVENT;
5105
5204
  untrackDepth++;
5106
5205
  }
5107
5206
  try {
@@ -5111,6 +5210,7 @@ function untrack(fn, strictReadLabel) {
5111
5210
  tracking = prevTracking;
5112
5211
  {
5113
5212
  strictRead = prevStrictRead;
5213
+ strictReadEvent = prevStrictReadEvent;
5114
5214
  untrackDepth--;
5115
5215
  }
5116
5216
  }
@@ -5317,7 +5417,7 @@ function read(el) {
5317
5417
  stagedRead(c);
5318
5418
  return el._pendingValue;
5319
5419
  }
5320
- if (strictRead && owner._statusFlags & STATUS_PENDING)
5420
+ if (strictRead && owner._statusFlags & STATUS_PENDING && strictReadActive())
5321
5421
  throwPendingUntrackedRead(strictRead, {
5322
5422
  ownerId: c?.id,
5323
5423
  ownerName: c?._name,
@@ -5438,7 +5538,7 @@ function read(el) {
5438
5538
  // Ahead of the snapshot serve below: a component body's direct read is
5439
5539
  // wrong in the same way whether the pass is hydrating or not, and the
5440
5540
  // hydration pass is the console nobody is watching (#3675).
5441
- if (strictRead)
5541
+ if (strictRead && strictReadActive())
5442
5542
  warnStrictReadUntracked(strictRead, {
5443
5543
  ownerId: c?.id,
5444
5544
  ownerName: c?._name,
@@ -5865,7 +5965,7 @@ export {
5865
5965
  CONFIG_STAGED as Z,
5866
5966
  merge as _,
5867
5967
  NotReadyError as a,
5868
- setMemo as a$,
5968
+ resetUnhandledAsync as a$,
5869
5969
  REACTIVE_JOINED as a0,
5870
5970
  REACTIVE_STAGED_READ as a1,
5871
5971
  stagedRead as a2,
@@ -5876,135 +5976,140 @@ export {
5876
5976
  verdict as a7,
5877
5977
  link as a8,
5878
5978
  pullComputed as a9,
5879
- cleanup as aA,
5880
- read as aB,
5881
- DEV as aC,
5882
- CONFIG_TRANSPARENT as aD,
5883
- NoOwnerError as aE,
5884
- ContextNotFoundError as aF,
5885
- createEffectNode as aG,
5886
- recompute as aH,
5887
- EFFECT_USER as aI,
5888
- reportDiagnostic as aJ,
5889
- computed as aK,
5890
- staleValues as aL,
5891
- CONFIG_AUTO_DISPOSE as aM,
5892
- EFFECT_TRACKED as aN,
5893
- heldRunHook as aO,
5894
- unwrapStatusError as aP,
5895
- haltReactivityAt as aQ,
5896
- setStrictRead as aR,
5897
- StatusError as aS,
5898
- trimStaleDeps as aT,
5899
- setTrackedQueueCallback as aU,
5900
- setEffectStatusNotify as aV,
5901
- _hitUnhandledAsync as aW,
5902
- resetUnhandledAsync as aX,
5903
- ROOT_ERROR_HOOK as aY,
5904
- registerGraph as aZ,
5905
- runWithOwner as a_,
5979
+ signal as aA,
5980
+ setSignal as aB,
5981
+ cleanup as aC,
5982
+ read as aD,
5983
+ DEV as aE,
5984
+ CONFIG_TRANSPARENT as aF,
5985
+ NoOwnerError as aG,
5986
+ ContextNotFoundError as aH,
5987
+ createEffectNode as aI,
5988
+ recompute as aJ,
5989
+ EFFECT_USER as aK,
5990
+ reportDiagnostic as aL,
5991
+ computed as aM,
5992
+ staleValues as aN,
5993
+ CONFIG_AUTO_DISPOSE as aO,
5994
+ EFFECT_TRACKED as aP,
5995
+ heldRunHook as aQ,
5996
+ unwrapStatusError as aR,
5997
+ setStrictRead as aS,
5998
+ queueEvent as aT,
5999
+ StatusError as aU,
6000
+ trimStaleDeps as aV,
6001
+ haltReactivityAt as aW,
6002
+ strictReadEvent as aX,
6003
+ setTrackedQueueCallback as aY,
6004
+ setEffectStatusNotify as aZ,
6005
+ _hitUnhandledAsync as a_,
5906
6006
  strictRead as aa,
5907
- warnStrictReadUntracked as ab,
5908
- staleReader as ac,
5909
- globalQueue as ad,
5910
- joinPass as ae,
5911
- markLateLinker as af,
5912
- spectating as ag,
5913
- unflushedValue as ah,
5914
- CONFIG_STAGED_ERROR as ai,
5915
- stagedReaders as aj,
5916
- bornInto as ak,
5917
- enterAction as al,
5918
- setEffectCallback as am,
5919
- exitAction as an,
5920
- isThenable as ao,
5921
- actionDepth as ap,
5922
- flush as aq,
5923
- setQuestion as ar,
5924
- emitDiagnostic as as,
5925
- enterCallback as at,
5926
- exitCallback as au,
5927
- getOwner as av,
5928
- inEffectCallback as aw,
5929
- setExternalHooks as ax,
5930
- signal as ay,
5931
- setSignal as az,
6007
+ strictReadActive as ab,
6008
+ warnStrictReadUntracked as ac,
6009
+ staleReader as ad,
6010
+ globalQueue as ae,
6011
+ joinPass as af,
6012
+ markLateLinker as ag,
6013
+ spectating as ah,
6014
+ unflushedValue as ai,
6015
+ CONFIG_STAGED_ERROR as aj,
6016
+ stagedReaders as ak,
6017
+ bornInto as al,
6018
+ enterAction as am,
6019
+ setEffectCallback as an,
6020
+ exitAction as ao,
6021
+ isThenable as ap,
6022
+ actionDepth as aq,
6023
+ flush as ar,
6024
+ setQuestion as as,
6025
+ emitDiagnostic as at,
6026
+ enterCallback as au,
6027
+ exitCallback as av,
6028
+ getOwner as aw,
6029
+ inEffectCallback as ax,
6030
+ effectCallbackEvent as ay,
6031
+ setExternalHooks as az,
5932
6032
  CONFIG_AUTHORITATIVE as b,
5933
- liveRootOwners as b$,
5934
- $REFRESH as b0,
5935
- markRefresh as b1,
5936
- createRoot as b2,
5937
- getObserver as b3,
5938
- dispose as b4,
5939
- TimeoutError as b5,
5940
- untrack as b6,
5941
- dirtyQueue as b7,
5942
- CONFIG_REDERIVE as b8,
5943
- passTx as b9,
5944
- settlePendingSource as bA,
5945
- joinPassAt as bB,
5946
- notifyEpoch as bC,
5947
- SUPPORTS_PROXY as bD,
5948
- releaseMarksHook as bE,
5949
- installMarkHooks as bF,
5950
- CONFIG_LAGS as bG,
5951
- REACTIVE_REASK as bH,
5952
- haltReactivity as bI,
5953
- heldUnder as bJ,
5954
- unhold as bK,
5955
- laneStageHook as bL,
5956
- REACTIVE_ZOMBIE as bM,
5957
- installBoundaryHooks as bN,
5958
- CAUGHT_REARMED as bO,
5959
- OBSERVE as bP,
5960
- clearSnapshots as bQ,
5961
- enforceLoadingBoundary as bR,
5962
- getNextChildId as bS,
5963
- markSnapshotScope as bT,
5964
- peekNextChildId as bU,
5965
- releaseSnapshotScope as bV,
5966
- resetErrorHalt as bW,
5967
- setConsoleFooter as bX,
5968
- setSnapshotCapture as bY,
5969
- setWriteHook as bZ,
5970
- setAttributionHooks as b_,
5971
- CONFIG_FRESH_READ as ba,
5972
- createOwner as bb,
5973
- writeHook as bc,
5974
- REACTIVE_WRITE_IN_OWNED_SCOPE_SIGNAL_MESSAGE as bd,
5975
- ownedScopeWriteMessage as be,
5976
- isEqual as bf,
5977
- CONFIG_MANUAL_WRITE as bg,
5978
- snapshotCaptureActive as bh,
5979
- captureWriteSnapshot as bi,
5980
- notePromotedWrite as bj,
5981
- slotSignal as bk,
5982
- stagedScreen as bl,
5983
- CONFIG_PROMOTED as bm,
5984
- setSlotUnobserved as bn,
5985
- installStoreHooks as bo,
5986
- joinPassTx as bp,
5987
- asyncTailFlights as bq,
5988
- untrackDepth as br,
5989
- checkPostAwaitRead as bs,
5990
- disposalDepth as bt,
5991
- insertIntoHeap as bu,
5992
- isDisposed as bv,
5993
- handleAsync as bw,
5994
- addPendingSource as bx,
5995
- propagateStatus as by,
5996
- forEachDependent as bz,
6033
+ resetErrorHalt as b$,
6034
+ ROOT_ERROR_HOOK as b0,
6035
+ registerGraph as b1,
6036
+ runWithOwner as b2,
6037
+ setMemo as b3,
6038
+ $REFRESH as b4,
6039
+ markRefresh as b5,
6040
+ createRoot as b6,
6041
+ getObserver as b7,
6042
+ dispose as b8,
6043
+ TimeoutError as b9,
6044
+ handleAsync as bA,
6045
+ addPendingSource as bB,
6046
+ propagateStatus as bC,
6047
+ forEachDependent as bD,
6048
+ settlePendingSource as bE,
6049
+ joinPassAt as bF,
6050
+ notifyEpoch as bG,
6051
+ SUPPORTS_PROXY as bH,
6052
+ releaseMarksHook as bI,
6053
+ installMarkHooks as bJ,
6054
+ CONFIG_LAGS as bK,
6055
+ REACTIVE_REASK as bL,
6056
+ onSupersede as bM,
6057
+ haltReactivity as bN,
6058
+ heldUnder as bO,
6059
+ unhold as bP,
6060
+ laneStageHook as bQ,
6061
+ REACTIVE_ZOMBIE as bR,
6062
+ installBoundaryHooks as bS,
6063
+ CAUGHT_REARMED as bT,
6064
+ OBSERVE as bU,
6065
+ clearSnapshots as bV,
6066
+ enforceLoadingBoundary as bW,
6067
+ getNextChildId as bX,
6068
+ markSnapshotScope as bY,
6069
+ peekNextChildId as bZ,
6070
+ releaseSnapshotScope as b_,
6071
+ untrack as ba,
6072
+ dirtyQueue as bb,
6073
+ CONFIG_REDERIVE as bc,
6074
+ passTx as bd,
6075
+ CONFIG_FRESH_READ as be,
6076
+ createOwner as bf,
6077
+ writeHook as bg,
6078
+ REACTIVE_WRITE_IN_OWNED_SCOPE_SIGNAL_MESSAGE as bh,
6079
+ ownedScopeWriteMessage as bi,
6080
+ isEqual as bj,
6081
+ CONFIG_MANUAL_WRITE as bk,
6082
+ snapshotCaptureActive as bl,
6083
+ captureWriteSnapshot as bm,
6084
+ notePromotedWrite as bn,
6085
+ slotSignal as bo,
6086
+ stagedScreen as bp,
6087
+ CONFIG_PROMOTED as bq,
6088
+ setSlotUnobserved as br,
6089
+ installStoreHooks as bs,
6090
+ joinPassTx as bt,
6091
+ asyncTailFlights as bu,
6092
+ untrackDepth as bv,
6093
+ checkPostAwaitRead as bw,
6094
+ disposalDepth as bx,
6095
+ insertIntoHeap as by,
6096
+ isDisposed as bz,
5997
6097
  CONFIG_OVERRIDE as c,
5998
- ownerPath as c0,
5999
- isSuppressed as c1,
6000
- blockersOf as c2,
6001
- CONFIG_PLUMBING as c3,
6002
- anyExcluded as c4,
6003
- isExcluded as c5,
6004
- CONFIG_WIDE as c6,
6005
- GRAPH_SIZE_WARN_AT as c7,
6006
- noteFanOut as c8,
6007
- CONFIG_SUPERSEDES as c9,
6098
+ setConsoleFooter as c0,
6099
+ setSnapshotCapture as c1,
6100
+ setWriteHook as c2,
6101
+ setAttributionHooks as c3,
6102
+ liveRootOwners as c4,
6103
+ ownerPath as c5,
6104
+ isSuppressed as c6,
6105
+ blockersOf as c7,
6106
+ CONFIG_PLUMBING as c8,
6107
+ anyExcluded as c9,
6108
+ isExcluded as ca,
6109
+ CONFIG_WIDE as cb,
6110
+ GRAPH_SIZE_WARN_AT as cc,
6111
+ noteFanOut as cd,
6112
+ CONFIG_SUPERSEDES as ce,
6008
6113
  newTransaction as d,
6009
6114
  enqueueSub as e,
6010
6115
  CONFIG_VERDICT as f,