@solidjs/signals 0.13.2 → 0.13.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/dev.js CHANGED
@@ -153,12 +153,16 @@ let scheduled = false;
153
153
  let projectionWriteActive = false;
154
154
  let _enforceLoadingBoundary = false;
155
155
  let _hitUnhandledAsync = false;
156
+ let stashedOptimisticReads = null;
156
157
  function resetUnhandledAsync() {
157
158
  _hitUnhandledAsync = false;
158
159
  }
159
160
  function enforceLoadingBoundary(enabled) {
160
161
  _enforceLoadingBoundary = enabled;
161
162
  }
163
+ function shouldReadStashedOptimisticValue(node) {
164
+ return !!stashedOptimisticReads?.has(node);
165
+ }
162
166
  function runLaneEffects(type) {
163
167
  for (const lane of activeLanes) {
164
168
  if (lane._mergedInto || lane._pendingAsync.size > 0) continue;
@@ -169,14 +173,89 @@ function runLaneEffects(type) {
169
173
  }
170
174
  }
171
175
  }
176
+ function queueStashedOptimisticEffects(node) {
177
+ for (let s = node._subs; s !== null; s = s._nextSub) {
178
+ const sub = s._sub;
179
+ if (!sub._type) continue;
180
+ if (sub._type === EFFECT_TRACKED) {
181
+ if (!sub._modified) {
182
+ sub._modified = true;
183
+ sub._queue.enqueue(EFFECT_USER, sub._run);
184
+ }
185
+ continue;
186
+ }
187
+ const queue = sub._flags & REACTIVE_ZOMBIE ? zombieQueue : dirtyQueue;
188
+ if (queue._min > sub._height) queue._min = sub._height;
189
+ insertIntoHeap(sub, queue);
190
+ }
191
+ }
172
192
  function setProjectionWriteActive(value) {
173
193
  projectionWriteActive = value;
174
194
  }
195
+ function mergeTransitionState(target, outgoing) {
196
+ outgoing._done = target;
197
+ target._actions.push(...outgoing._actions);
198
+ for (const lane of activeLanes) {
199
+ if (lane._transition === outgoing) lane._transition = target;
200
+ }
201
+ target._optimisticNodes.push(...outgoing._optimisticNodes);
202
+ for (const store of outgoing._optimisticStores) target._optimisticStores.add(store);
203
+ for (const node of outgoing._asyncNodes) {
204
+ if (!target._asyncNodes.includes(node)) target._asyncNodes.push(node);
205
+ }
206
+ }
207
+ function resolveOptimisticNodes(nodes) {
208
+ for (let i = 0; i < nodes.length; i++) {
209
+ const node = nodes[i];
210
+ node._optimisticLane = undefined;
211
+ if (node._pendingValue !== NOT_PENDING) {
212
+ node._value = node._pendingValue;
213
+ node._pendingValue = NOT_PENDING;
214
+ }
215
+ const prevOverride = node._overrideValue;
216
+ node._overrideValue = NOT_PENDING;
217
+ if (prevOverride !== NOT_PENDING && node._value !== prevOverride) insertSubs(node, true);
218
+ node._transition = null;
219
+ }
220
+ nodes.length = 0;
221
+ }
222
+ function cleanupCompletedLanes(completingTransition) {
223
+ for (const lane of activeLanes) {
224
+ const owned = completingTransition
225
+ ? lane._transition === completingTransition
226
+ : !lane._transition;
227
+ if (!owned) continue;
228
+ if (!lane._mergedInto) {
229
+ if (lane._effectQueues[0].length) runQueue(lane._effectQueues[0], EFFECT_RENDER);
230
+ if (lane._effectQueues[1].length) runQueue(lane._effectQueues[1], EFFECT_USER);
231
+ }
232
+ if (lane._source._optimisticLane === lane) lane._source._optimisticLane = undefined;
233
+ lane._pendingAsync.clear();
234
+ lane._effectQueues[0].length = 0;
235
+ lane._effectQueues[1].length = 0;
236
+ activeLanes.delete(lane);
237
+ signalLanes.delete(lane._source);
238
+ }
239
+ }
175
240
  function schedule() {
176
241
  if (scheduled) return;
177
242
  scheduled = true;
178
243
  if (!globalQueue._running && !projectionWriteActive) queueMicrotask(flush);
179
244
  }
245
+ function addTransitionBlocker(node) {
246
+ if (activeTransition && !activeTransition._asyncNodes.includes(node)) {
247
+ activeTransition._asyncNodes.push(node);
248
+ }
249
+ }
250
+ function removeTransitionBlocker(node) {
251
+ const remove = list => {
252
+ if (!list) return;
253
+ const index = list.indexOf(node);
254
+ if (index >= 0) list.splice(index, 1);
255
+ };
256
+ remove(node._transition?._asyncNodes);
257
+ remove(activeTransition?._asyncNodes);
258
+ }
180
259
  class Queue {
181
260
  _parent = null;
182
261
  _queues = [[], []];
@@ -256,19 +335,32 @@ class GlobalQueue extends Queue {
256
335
  if (activeTransition) {
257
336
  const isComplete = transitionComplete(activeTransition);
258
337
  if (!isComplete) {
259
- let t = activeTransition;
338
+ const stashedTransition = activeTransition;
260
339
  runHeap(zombieQueue, GlobalQueue._update);
261
340
  this._pendingNodes = [];
262
341
  this._optimisticNodes = [];
263
342
  this._optimisticStores = new Set();
264
343
  runLaneEffects(EFFECT_RENDER);
265
344
  runLaneEffects(EFFECT_USER);
266
- this.stashQueues(activeTransition._queueStash);
345
+ this.stashQueues(stashedTransition._queueStash);
267
346
  clock++;
268
347
  scheduled = dirtyQueue._max >= dirtyQueue._min;
269
- reassignPendingTransition(activeTransition._pendingNodes);
348
+ reassignPendingTransition(stashedTransition._pendingNodes);
270
349
  activeTransition = null;
271
- finalizePureQueue(null, true);
350
+ if (!stashedTransition._actions.length && stashedTransition._optimisticNodes.length) {
351
+ stashedOptimisticReads = new Set();
352
+ for (let i = 0; i < stashedTransition._optimisticNodes.length; i++) {
353
+ const node = stashedTransition._optimisticNodes[i];
354
+ if (node._fn || node._pureWrite) continue;
355
+ stashedOptimisticReads.add(node);
356
+ queueStashedOptimisticEffects(node);
357
+ }
358
+ }
359
+ try {
360
+ finalizePureQueue(null, true);
361
+ } finally {
362
+ stashedOptimisticReads = null;
363
+ }
272
364
  return;
273
365
  }
274
366
  this._pendingNodes !== activeTransition._pendingNodes &&
@@ -297,13 +389,12 @@ class GlobalQueue extends Queue {
297
389
  if (mask & STATUS_PENDING) {
298
390
  if (flags & STATUS_PENDING) {
299
391
  const actualError = error !== undefined ? error : node._error;
300
- if (
301
- activeTransition &&
302
- actualError &&
303
- !activeTransition._asyncNodes.includes(actualError.source)
304
- ) {
305
- activeTransition._asyncNodes.push(actualError.source);
306
- schedule();
392
+ if (activeTransition && actualError) {
393
+ const source = actualError.source;
394
+ if (!activeTransition._asyncNodes.includes(source)) {
395
+ activeTransition._asyncNodes.push(source);
396
+ schedule();
397
+ }
307
398
  }
308
399
  if (_enforceLoadingBoundary) _hitUnhandledAsync = true;
309
400
  }
@@ -328,37 +419,35 @@ class GlobalQueue extends Queue {
328
419
  };
329
420
  } else if (transition) {
330
421
  const outgoing = activeTransition;
331
- outgoing._done = transition;
332
- transition._actions.push(...outgoing._actions);
333
- for (const lane of activeLanes) {
334
- if (lane._transition === outgoing) lane._transition = transition;
335
- }
336
- transition._optimisticNodes.push(...outgoing._optimisticNodes);
337
- for (const store of outgoing._optimisticStores) {
338
- transition._optimisticStores.add(store);
339
- }
422
+ mergeTransitionState(transition, outgoing);
340
423
  transitions.delete(outgoing);
341
424
  activeTransition = transition;
342
425
  }
343
426
  transitions.add(activeTransition);
344
427
  activeTransition._time = clock;
345
- for (let i = 0; i < this._pendingNodes.length; i++) {
346
- const n = this._pendingNodes[i];
347
- n._transition = activeTransition;
348
- activeTransition._pendingNodes.push(n);
349
- }
350
- this._pendingNodes = activeTransition._pendingNodes;
351
- for (let i = 0; i < this._optimisticNodes.length; i++) {
352
- const node = this._optimisticNodes[i];
353
- node._transition = activeTransition;
354
- activeTransition._optimisticNodes.push(node);
355
- }
356
- this._optimisticNodes = activeTransition._optimisticNodes;
428
+ if (this._pendingNodes !== activeTransition._pendingNodes) {
429
+ for (let i = 0; i < this._pendingNodes.length; i++) {
430
+ const node = this._pendingNodes[i];
431
+ node._transition = activeTransition;
432
+ activeTransition._pendingNodes.push(node);
433
+ }
434
+ this._pendingNodes = activeTransition._pendingNodes;
435
+ }
436
+ if (this._optimisticNodes !== activeTransition._optimisticNodes) {
437
+ for (let i = 0; i < this._optimisticNodes.length; i++) {
438
+ const node = this._optimisticNodes[i];
439
+ node._transition = activeTransition;
440
+ activeTransition._optimisticNodes.push(node);
441
+ }
442
+ this._optimisticNodes = activeTransition._optimisticNodes;
443
+ }
357
444
  for (const lane of activeLanes) {
358
445
  if (!lane._transition) lane._transition = activeTransition;
359
446
  }
360
- for (const store of this._optimisticStores) activeTransition._optimisticStores.add(store);
361
- this._optimisticStores = activeTransition._optimisticStores;
447
+ if (this._optimisticStores !== activeTransition._optimisticStores) {
448
+ for (const store of this._optimisticStores) activeTransition._optimisticStores.add(store);
449
+ this._optimisticStores = activeTransition._optimisticStores;
450
+ }
362
451
  }
363
452
  }
364
453
  function insertSubs(node, optimistic = false) {
@@ -398,40 +487,21 @@ function commitPendingNodes() {
398
487
  n._pendingValue = NOT_PENDING;
399
488
  if (n._type && n._type !== EFFECT_TRACKED) n._modified = true;
400
489
  }
401
- if (n._statusFlags & STATUS_PENDING) {
402
- const _src = n._error?.source;
403
- if (_src && !(_src._statusFlags & STATUS_PENDING)) {
404
- n._statusFlags &= -6;
405
- n._error = null;
406
- }
407
- } else n._statusFlags &= ~STATUS_UNINITIALIZED;
490
+ if (!(n._statusFlags & STATUS_PENDING)) n._statusFlags &= ~STATUS_UNINITIALIZED;
408
491
  if (n._fn) GlobalQueue._dispose(n, false, true);
409
492
  }
410
493
  pendingNodes.length = 0;
411
494
  }
412
495
  function finalizePureQueue(completingTransition = null, incomplete = false) {
413
- let resolvePending = !incomplete;
496
+ const resolvePending = !incomplete;
414
497
  if (resolvePending) commitPendingNodes();
415
498
  if (!incomplete) checkBoundaryChildren(globalQueue);
416
499
  if (dirtyQueue._max >= dirtyQueue._min) runHeap(dirtyQueue, GlobalQueue._update);
417
500
  if (resolvePending) {
418
501
  commitPendingNodes();
419
- const optimisticNodes = completingTransition
420
- ? completingTransition._optimisticNodes
421
- : globalQueue._optimisticNodes;
422
- for (let i = 0; i < optimisticNodes.length; i++) {
423
- const n = optimisticNodes[i];
424
- n._optimisticLane = undefined;
425
- if (n._pendingValue !== NOT_PENDING) {
426
- n._value = n._pendingValue;
427
- n._pendingValue = NOT_PENDING;
428
- }
429
- const prevOverride = n._overrideValue;
430
- n._overrideValue = NOT_PENDING;
431
- if (prevOverride !== NOT_PENDING && n._value !== prevOverride) insertSubs(n, true);
432
- n._transition = null;
433
- }
434
- optimisticNodes.length = 0;
502
+ resolveOptimisticNodes(
503
+ completingTransition ? completingTransition._optimisticNodes : globalQueue._optimisticNodes
504
+ );
435
505
  const optimisticStores = completingTransition
436
506
  ? completingTransition._optimisticStores
437
507
  : globalQueue._optimisticStores;
@@ -442,22 +512,7 @@ function finalizePureQueue(completingTransition = null, incomplete = false) {
442
512
  optimisticStores.clear();
443
513
  schedule();
444
514
  }
445
- for (const lane of activeLanes) {
446
- const owned = completingTransition
447
- ? lane._transition === completingTransition
448
- : !lane._transition;
449
- if (!owned) continue;
450
- if (!lane._mergedInto) {
451
- if (lane._effectQueues[0].length) runQueue(lane._effectQueues[0], EFFECT_RENDER);
452
- if (lane._effectQueues[1].length) runQueue(lane._effectQueues[1], EFFECT_USER);
453
- }
454
- if (lane._source._optimisticLane === lane) lane._source._optimisticLane = undefined;
455
- lane._pendingAsync.clear();
456
- lane._effectQueues[0].length = 0;
457
- lane._effectQueues[1].length = 0;
458
- activeLanes.delete(lane);
459
- signalLanes.delete(lane._source);
460
- }
515
+ cleanupCompletedLanes(completingTransition);
461
516
  }
462
517
  }
463
518
  function checkBoundaryChildren(queue) {
@@ -478,7 +533,7 @@ function reassignPendingTransition(pendingNodes) {
478
533
  const globalQueue = new GlobalQueue();
479
534
  function flush() {
480
535
  let count = 0;
481
- while (scheduled) {
536
+ while (scheduled || activeTransition) {
482
537
  if (++count === 1e5) throw new Error("Potential Infinite Loop Detected.");
483
538
  globalQueue.flush();
484
539
  }
@@ -560,6 +615,9 @@ function resolveLane(el) {
560
615
  el._optimisticLane = undefined;
561
616
  return undefined;
562
617
  }
618
+ function resolveTransition(el) {
619
+ return resolveLane(el)?._transition ?? el._transition;
620
+ }
563
621
  function hasActiveOverride(el) {
564
622
  return !!(el._overrideValue !== undefined && el._overrideValue !== NOT_PENDING);
565
623
  }
@@ -584,6 +642,96 @@ function assignOrMergeLane(el, sourceLane) {
584
642
  }
585
643
  el._optimisticLane = sourceLane;
586
644
  }
645
+ function addPendingSource(el, source) {
646
+ if (el._pendingSource === source || el._pendingSources?.has(source)) return false;
647
+ if (!el._pendingSource) {
648
+ el._pendingSource = source;
649
+ return true;
650
+ }
651
+ if (!el._pendingSources) {
652
+ el._pendingSources = new Set([el._pendingSource, source]);
653
+ } else {
654
+ el._pendingSources.add(source);
655
+ }
656
+ el._pendingSource = undefined;
657
+ return true;
658
+ }
659
+ function removePendingSource(el, source) {
660
+ if (el._pendingSource) {
661
+ if (el._pendingSource !== source) return false;
662
+ el._pendingSource = undefined;
663
+ return true;
664
+ }
665
+ if (!el._pendingSources?.delete(source)) return false;
666
+ if (el._pendingSources.size === 1) {
667
+ el._pendingSource = el._pendingSources.values().next().value;
668
+ el._pendingSources = undefined;
669
+ } else if (el._pendingSources.size === 0) {
670
+ el._pendingSources = undefined;
671
+ }
672
+ return true;
673
+ }
674
+ function clearPendingSources(el) {
675
+ el._pendingSource = undefined;
676
+ el._pendingSources?.clear();
677
+ el._pendingSources = undefined;
678
+ }
679
+ function setPendingError(el, source, error) {
680
+ if (!source) {
681
+ el._error = null;
682
+ return;
683
+ }
684
+ if (error instanceof NotReadyError && error.source === source) {
685
+ el._error = error;
686
+ return;
687
+ }
688
+ const current = el._error;
689
+ if (!(current instanceof NotReadyError) || current.source !== source) {
690
+ el._error = new NotReadyError(source);
691
+ }
692
+ }
693
+ function forEachDependent(el, fn) {
694
+ for (let s = el._subs; s !== null; s = s._nextSub) fn(s._sub);
695
+ for (let child = el._child; child !== null; child = child._nextChild) {
696
+ for (let s = child._subs; s !== null; s = s._nextSub) fn(s._sub);
697
+ }
698
+ }
699
+ function settlePendingSource(el) {
700
+ let scheduled = false;
701
+ const visited = new Set();
702
+ const settle = node => {
703
+ if (visited.has(node) || !removePendingSource(node, el)) return;
704
+ visited.add(node);
705
+ node._time = clock;
706
+ const source = node._pendingSource ?? node._pendingSources?.values().next().value;
707
+ if (source) {
708
+ setPendingError(node, source);
709
+ updatePendingSignal(node);
710
+ } else {
711
+ node._statusFlags &= ~STATUS_PENDING;
712
+ setPendingError(node);
713
+ updatePendingSignal(node);
714
+ if (node._blocked) {
715
+ if (node._type === EFFECT_TRACKED) {
716
+ const tracked = node;
717
+ if (!tracked._modified) {
718
+ tracked._modified = true;
719
+ tracked._queue.enqueue(EFFECT_USER, tracked._run);
720
+ }
721
+ } else {
722
+ const queue = node._flags & REACTIVE_ZOMBIE ? zombieQueue : dirtyQueue;
723
+ if (queue._min > node._height) queue._min = node._height;
724
+ insertIntoHeap(node, queue);
725
+ }
726
+ scheduled = true;
727
+ }
728
+ node._blocked = false;
729
+ }
730
+ forEachDependent(node, settle);
731
+ };
732
+ forEachDependent(el, settle);
733
+ if (scheduled) schedule();
734
+ }
587
735
  function handleAsync(el, result, setter) {
588
736
  const isObject = typeof result === "object" && result !== null;
589
737
  const iterator = isObject && untrack(() => result[Symbol.asyncIterator]);
@@ -596,14 +744,14 @@ function handleAsync(el, result, setter) {
596
744
  let syncValue;
597
745
  const handleError = error => {
598
746
  if (el._inFlight !== result) return;
599
- globalQueue.initTransition(el._transition);
747
+ globalQueue.initTransition(resolveTransition(el));
600
748
  notifyStatus(el, error instanceof NotReadyError ? STATUS_PENDING : STATUS_ERROR, error);
601
749
  el._time = clock;
602
750
  };
603
751
  const asyncWrite = (value, then) => {
604
752
  if (el._inFlight !== result) return;
605
753
  if (el._flags & (REACTIVE_DIRTY | REACTIVE_OPTIMISTIC_DIRTY)) return;
606
- globalQueue.initTransition(el._transition);
754
+ globalQueue.initTransition(resolveTransition(el));
607
755
  clearStatus(el);
608
756
  const lane = resolveLane(el);
609
757
  if (lane) lane._pendingAsync.delete(el);
@@ -630,6 +778,7 @@ function handleAsync(el, result, setter) {
630
778
  } else {
631
779
  setSignal(el, () => value);
632
780
  }
781
+ settlePendingSource(el);
633
782
  schedule();
634
783
  flush();
635
784
  then?.();
@@ -650,7 +799,7 @@ function handleAsync(el, result, setter) {
650
799
  );
651
800
  isSync = false;
652
801
  if (!resolved) {
653
- globalQueue.initTransition(el._transition);
802
+ globalQueue.initTransition(resolveTransition(el));
654
803
  throw new NotReadyError(context);
655
804
  }
656
805
  }
@@ -686,15 +835,18 @@ function handleAsync(el, result, setter) {
686
835
  };
687
836
  const immediatelyDone = iterate();
688
837
  if (!hadSyncValue && !immediatelyDone) {
689
- globalQueue.initTransition(el._transition);
838
+ globalQueue.initTransition(resolveTransition(el));
690
839
  throw new NotReadyError(context);
691
840
  }
692
841
  }
693
842
  return syncValue;
694
843
  }
695
- function clearStatus(el) {
696
- el._statusFlags = el._statusFlags & STATUS_UNINITIALIZED;
697
- el._error = null;
844
+ function clearStatus(el, clearUninitialized = false) {
845
+ clearPendingSources(el);
846
+ removeTransitionBlocker(el);
847
+ el._blocked = false;
848
+ el._statusFlags = clearUninitialized ? 0 : el._statusFlags & STATUS_UNINITIALIZED;
849
+ setPendingError(el);
698
850
  updatePendingSignal(el);
699
851
  el._notifyStatus?.();
700
852
  }
@@ -705,25 +857,30 @@ function notifyStatus(el, status, error, blockStatus, lane) {
705
857
  !(error instanceof NotReadyError)
706
858
  )
707
859
  error = new StatusError(el, error);
708
- const isSource = error instanceof NotReadyError && error.source === el;
860
+ const pendingSource =
861
+ status === STATUS_PENDING && error instanceof NotReadyError ? error.source : undefined;
862
+ const isSource = pendingSource === el;
709
863
  const isOptimisticBoundary =
710
864
  status === STATUS_PENDING && el._overrideValue !== undefined && !isSource;
711
865
  const startsBlocking = isOptimisticBoundary && hasActiveOverride(el);
712
866
  if (!blockStatus) {
713
- el._statusFlags =
714
- status | (status !== STATUS_ERROR ? el._statusFlags & STATUS_UNINITIALIZED : 0);
715
- el._error = error;
867
+ if (status === STATUS_PENDING && pendingSource) {
868
+ addPendingSource(el, pendingSource);
869
+ el._statusFlags = STATUS_PENDING | (el._statusFlags & STATUS_UNINITIALIZED);
870
+ setPendingError(el, el._pendingSource ?? el._pendingSources?.values().next().value, error);
871
+ if (pendingSource === el) addTransitionBlocker(el);
872
+ } else {
873
+ clearPendingSources(el);
874
+ removeTransitionBlocker(el);
875
+ el._statusFlags =
876
+ status | (status !== STATUS_ERROR ? el._statusFlags & STATUS_UNINITIALIZED : 0);
877
+ el._error = error;
878
+ }
716
879
  updatePendingSignal(el);
717
880
  }
718
881
  if (lane && !blockStatus) {
719
882
  assignOrMergeLane(el, lane);
720
883
  }
721
- if (startsBlocking && activeTransition && error instanceof NotReadyError) {
722
- const source = error.source;
723
- if (!activeTransition._asyncNodes.includes(source)) {
724
- activeTransition._asyncNodes.push(source);
725
- }
726
- }
727
884
  const downstreamBlockStatus = blockStatus || startsBlocking;
728
885
  const downstreamLane = blockStatus || isOptimisticBoundary ? undefined : lane;
729
886
  if (el._notifyStatus) {
@@ -734,84 +891,43 @@ function notifyStatus(el, status, error, blockStatus, lane) {
734
891
  }
735
892
  return;
736
893
  }
737
- for (let s = el._subs; s !== null; s = s._nextSub) {
738
- s._sub._time = clock;
739
- if (s._sub._error !== error) {
740
- !s._sub._transition && globalQueue._pendingNodes.push(s._sub);
741
- notifyStatus(s._sub, status, error, downstreamBlockStatus, downstreamLane);
742
- }
743
- }
744
- for (let child = el._child; child !== null; child = child._nextChild) {
745
- for (let s = child._subs; s !== null; s = s._nextSub) {
746
- s._sub._time = clock;
747
- if (s._sub._error !== error) {
748
- !s._sub._transition && globalQueue._pendingNodes.push(s._sub);
749
- notifyStatus(s._sub, status, error, downstreamBlockStatus, downstreamLane);
750
- }
751
- }
752
- }
753
- }
754
- function unlinkSubs(link) {
755
- const dep = link._dep;
756
- const nextDep = link._nextDep;
757
- const nextSub = link._nextSub;
758
- const prevSub = link._prevSub;
759
- if (nextSub !== null) nextSub._prevSub = prevSub;
760
- else dep._subsTail = prevSub;
761
- if (prevSub !== null) prevSub._nextSub = nextSub;
762
- else {
763
- dep._subs = nextSub;
764
- if (nextSub === null) {
765
- dep._unobserved?.();
766
- dep._fn && !dep._preventAutoDisposal && !(dep._flags & REACTIVE_ZOMBIE) && unobserved(dep);
767
- }
768
- }
769
- return nextDep;
770
- }
771
- function unobserved(el) {
772
- deleteFromHeap(el, el._flags & REACTIVE_ZOMBIE ? zombieQueue : dirtyQueue);
773
- let dep = el._deps;
774
- while (dep !== null) {
775
- dep = unlinkSubs(dep);
776
- }
777
- el._deps = null;
778
- disposeChildren(el, true);
779
- }
780
- function link(dep, sub) {
781
- const prevDep = sub._depsTail;
782
- if (prevDep !== null && prevDep._dep === dep) return;
783
- let nextDep = null;
784
- const isRecomputing = sub._flags & REACTIVE_RECOMPUTING_DEPS;
785
- if (isRecomputing) {
786
- nextDep = prevDep !== null ? prevDep._nextDep : sub._deps;
787
- if (nextDep !== null && nextDep._dep === dep) {
788
- sub._depsTail = nextDep;
789
- return;
894
+ forEachDependent(el, sub => {
895
+ sub._time = clock;
896
+ if (
897
+ (status === STATUS_PENDING &&
898
+ pendingSource &&
899
+ sub._pendingSource !== pendingSource &&
900
+ !sub._pendingSources?.has(pendingSource)) ||
901
+ (status !== STATUS_PENDING &&
902
+ (sub._error !== error || sub._pendingSource || sub._pendingSources))
903
+ ) {
904
+ !sub._transition && globalQueue._pendingNodes.push(sub);
905
+ notifyStatus(sub, status, error, downstreamBlockStatus, downstreamLane);
790
906
  }
791
- }
792
- const prevSub = dep._subsTail;
793
- if (prevSub !== null && prevSub._sub === sub && (!isRecomputing || isValidLink(prevSub, sub)))
794
- return;
795
- const newLink =
796
- (sub._depsTail =
797
- dep._subsTail =
798
- { _dep: dep, _sub: sub, _nextDep: nextDep, _prevSub: prevSub, _nextSub: null });
799
- if (prevDep !== null) prevDep._nextDep = newLink;
800
- else sub._deps = newLink;
801
- if (prevSub !== null) prevSub._nextSub = newLink;
802
- else dep._subs = newLink;
907
+ });
803
908
  }
804
- function isValidLink(checkLink, sub) {
805
- const depsTail = sub._depsTail;
806
- if (depsTail !== null) {
807
- let link = sub._deps;
808
- do {
809
- if (link === checkLink) return true;
810
- if (link === depsTail) break;
811
- link = link._nextDep;
812
- } while (link !== null);
909
+ let externalSourceConfig = null;
910
+ function enableExternalSource(config) {
911
+ const { factory: factory, untrack: untrackFn = fn => fn() } = config;
912
+ if (externalSourceConfig) {
913
+ const { factory: oldFactory, untrack: oldUntrack } = externalSourceConfig;
914
+ externalSourceConfig = {
915
+ factory: (fn, trigger) => {
916
+ const oldSource = oldFactory(fn, trigger);
917
+ const source = factory(x => oldSource.track(x), trigger);
918
+ return {
919
+ track: x => source.track(x),
920
+ dispose() {
921
+ source.dispose();
922
+ oldSource.dispose();
923
+ }
924
+ };
925
+ },
926
+ untrack: fn => oldUntrack(() => untrackFn(fn))
927
+ };
928
+ } else {
929
+ externalSourceConfig = { factory: factory, untrack: untrackFn };
813
930
  }
814
- return false;
815
931
  }
816
932
  const PENDING_OWNER = {};
817
933
  function markDisposal(el) {
@@ -901,7 +1017,15 @@ function getOwner() {
901
1017
  return context;
902
1018
  }
903
1019
  function onCleanup(fn) {
904
- if (!context) return fn;
1020
+ if (!context) {
1021
+ console.warn("onCleanup called outside a reactive context will never be run");
1022
+ return fn;
1023
+ }
1024
+ if (context._childrenForbidden) {
1025
+ throw new Error(
1026
+ "Cannot use onCleanup inside createTrackedEffect or onSettled; return a cleanup function instead"
1027
+ );
1028
+ }
905
1029
  if (!context._disposal) context._disposal = fn;
906
1030
  else if (Array.isArray(context._disposal)) context._disposal.push(fn);
907
1031
  else context._disposal = [context._disposal, fn];
@@ -933,8 +1057,10 @@ function createOwner(options) {
933
1057
  disposeChildren(owner, self);
934
1058
  }
935
1059
  };
936
- if (leafEffectActive && parent) {
937
- throw new Error("Cannot create reactive primitives inside createTrackedEffect");
1060
+ if (parent?._childrenForbidden) {
1061
+ throw new Error(
1062
+ "Cannot create reactive primitives inside createTrackedEffect or owner-backed onSettled"
1063
+ );
938
1064
  }
939
1065
  if (parent) {
940
1066
  const lastChild = parent._firstChild;
@@ -951,136 +1077,67 @@ function createRoot(init, options) {
951
1077
  const owner = createOwner(options);
952
1078
  return runWithOwner(owner, () => init(owner.dispose));
953
1079
  }
954
- let leafEffectActive = false;
955
- function effect(compute, effect, error, initialValue, options) {
956
- let initialized = false;
957
- const node = computed(
958
- options?.render ? p => staleValues(() => compute(p)) : compute,
959
- initialValue,
960
- {
961
- ...options,
962
- equals: () => {
963
- node._modified = !node._error;
964
- if (initialized) node._queue.enqueue(node._type, runEffect.bind(node));
965
- return false;
966
- },
967
- lazy: true
968
- }
969
- );
970
- node._prevValue = initialValue;
971
- node._effectFn = effect;
972
- node._errorFn = error;
973
- node._cleanup = undefined;
974
- node._type = options?.render ? EFFECT_RENDER : EFFECT_USER;
975
- node._notifyStatus = (status, error) => {
976
- const actualStatus = status !== undefined ? status : node._statusFlags;
977
- const actualError = error !== undefined ? error : node._error;
978
- if (actualStatus & STATUS_ERROR) {
979
- let err = actualError;
980
- node._queue.notify(node, STATUS_PENDING, 0);
981
- if (node._type === EFFECT_USER) {
982
- try {
983
- return node._errorFn
984
- ? node._errorFn(err, () => {
985
- node._cleanup?.();
986
- node._cleanup = undefined;
987
- })
988
- : console.error(err);
989
- } catch (e) {
990
- err = e;
991
- }
992
- }
993
- if (!node._queue.notify(node, STATUS_ERROR, STATUS_ERROR)) throw err;
994
- } else if (node._type === EFFECT_RENDER) {
995
- node._queue.notify(node, STATUS_PENDING | STATUS_ERROR, actualStatus, actualError);
996
- if (_hitUnhandledAsync) {
997
- resetUnhandledAsync();
998
- const err = new Error("An async value must be rendered inside a Loading boundary.");
999
- if (!node._queue.notify(node, STATUS_ERROR, STATUS_ERROR)) throw err;
1000
- }
1080
+ function unlinkSubs(link) {
1081
+ const dep = link._dep;
1082
+ const nextDep = link._nextDep;
1083
+ const nextSub = link._nextSub;
1084
+ const prevSub = link._prevSub;
1085
+ if (nextSub !== null) nextSub._prevSub = prevSub;
1086
+ else dep._subsTail = prevSub;
1087
+ if (prevSub !== null) prevSub._nextSub = nextSub;
1088
+ else {
1089
+ dep._subs = nextSub;
1090
+ if (nextSub === null) {
1091
+ dep._unobserved?.();
1092
+ dep._fn && !dep._preventAutoDisposal && !(dep._flags & REACTIVE_ZOMBIE) && unobserved(dep);
1001
1093
  }
1002
- };
1003
- recompute(node, true);
1004
- !options?.defer &&
1005
- (node._type === EFFECT_USER
1006
- ? node._queue.enqueue(node._type, runEffect.bind(node))
1007
- : runEffect.call(node));
1008
- initialized = true;
1009
- onCleanup(() => node._cleanup?.());
1010
- if (!node._parent)
1011
- console.warn("Effects created outside a reactive context will never be disposed");
1012
- }
1013
- function runEffect() {
1014
- if (!this._modified || this._flags & REACTIVE_DISPOSED) return;
1015
- let prevStrictRead = false;
1016
- {
1017
- prevStrictRead = setStrictRead("an effect callback");
1018
1094
  }
1019
- this._cleanup?.();
1020
- this._cleanup = undefined;
1021
- try {
1022
- this._cleanup = this._effectFn(this._value, this._prevValue);
1023
- } catch (error) {
1024
- this._error = new StatusError(this, error);
1025
- this._statusFlags |= STATUS_ERROR;
1026
- if (!this._queue.notify(this, STATUS_ERROR, STATUS_ERROR)) throw error;
1027
- } finally {
1028
- setStrictRead(prevStrictRead);
1029
- this._prevValue = this._value;
1030
- this._modified = false;
1095
+ return nextDep;
1096
+ }
1097
+ function unobserved(el) {
1098
+ deleteFromHeap(el, el._flags & REACTIVE_ZOMBIE ? zombieQueue : dirtyQueue);
1099
+ let dep = el._deps;
1100
+ while (dep !== null) {
1101
+ dep = unlinkSubs(dep);
1031
1102
  }
1103
+ el._deps = null;
1104
+ disposeChildren(el, true);
1032
1105
  }
1033
- function trackedEffect(fn, options) {
1034
- const run = () => {
1035
- if (!node._modified || node._flags & REACTIVE_DISPOSED) return;
1036
- node._modified = false;
1037
- recompute(node);
1038
- };
1039
- const node = computed(
1040
- () => {
1041
- leafEffectActive = true;
1042
- try {
1043
- node._cleanup?.();
1044
- node._cleanup = undefined;
1045
- node._cleanup = staleValues(fn) || undefined;
1046
- } finally {
1047
- leafEffectActive = false;
1048
- }
1049
- },
1050
- undefined,
1051
- { ...options, lazy: true }
1052
- );
1053
- node._cleanup = undefined;
1054
- node._modified = true;
1055
- node._type = EFFECT_TRACKED;
1056
- node._run = run;
1057
- node._queue.enqueue(EFFECT_USER, run);
1058
- onCleanup(() => node._cleanup?.());
1059
- if (!node._parent)
1060
- console.warn("Effects created outside a reactive context will never be disposed");
1106
+ function link(dep, sub) {
1107
+ const prevDep = sub._depsTail;
1108
+ if (prevDep !== null && prevDep._dep === dep) return;
1109
+ let nextDep = null;
1110
+ const isRecomputing = sub._flags & REACTIVE_RECOMPUTING_DEPS;
1111
+ if (isRecomputing) {
1112
+ nextDep = prevDep !== null ? prevDep._nextDep : sub._deps;
1113
+ if (nextDep !== null && nextDep._dep === dep) {
1114
+ sub._depsTail = nextDep;
1115
+ return;
1116
+ }
1117
+ }
1118
+ const prevSub = dep._subsTail;
1119
+ if (prevSub !== null && prevSub._sub === sub && (!isRecomputing || isValidLink(prevSub, sub)))
1120
+ return;
1121
+ const newLink =
1122
+ (sub._depsTail =
1123
+ dep._subsTail =
1124
+ { _dep: dep, _sub: sub, _nextDep: nextDep, _prevSub: prevSub, _nextSub: null });
1125
+ if (prevDep !== null) prevDep._nextDep = newLink;
1126
+ else sub._deps = newLink;
1127
+ if (prevSub !== null) prevSub._nextSub = newLink;
1128
+ else dep._subs = newLink;
1061
1129
  }
1062
- let externalSourceConfig = null;
1063
- function enableExternalSource(config) {
1064
- const { factory: factory, untrack: untrackFn = fn => fn() } = config;
1065
- if (externalSourceConfig) {
1066
- const { factory: oldFactory, untrack: oldUntrack } = externalSourceConfig;
1067
- externalSourceConfig = {
1068
- factory: (fn, trigger) => {
1069
- const oldSource = oldFactory(fn, trigger);
1070
- const source = factory(x => oldSource.track(x), trigger);
1071
- return {
1072
- track: x => source.track(x),
1073
- dispose() {
1074
- source.dispose();
1075
- oldSource.dispose();
1076
- }
1077
- };
1078
- },
1079
- untrack: fn => oldUntrack(() => untrackFn(fn))
1080
- };
1081
- } else {
1082
- externalSourceConfig = { factory: factory, untrack: untrackFn };
1130
+ function isValidLink(checkLink, sub) {
1131
+ const depsTail = sub._depsTail;
1132
+ if (depsTail !== null) {
1133
+ let link = sub._deps;
1134
+ do {
1135
+ if (link === checkLink) return true;
1136
+ if (link === depsTail) break;
1137
+ link = link._nextDep;
1138
+ } while (link !== null);
1083
1139
  }
1140
+ return false;
1084
1141
  }
1085
1142
  GlobalQueue._update = recompute;
1086
1143
  GlobalQueue._dispose = disposeChildren;
@@ -1184,7 +1241,7 @@ function recompute(el, create = false) {
1184
1241
  }
1185
1242
  try {
1186
1243
  value = handleAsync(el, el._fn(value));
1187
- clearStatus(el);
1244
+ clearStatus(el, create);
1188
1245
  const resolvedLane = resolveLane(el);
1189
1246
  if (resolvedLane) {
1190
1247
  resolvedLane._pendingAsync.delete(el);
@@ -1199,6 +1256,7 @@ function recompute(el, create = false) {
1199
1256
  updatePendingSignal(lane._source);
1200
1257
  }
1201
1258
  }
1259
+ if (e instanceof NotReadyError) el._blocked = true;
1202
1260
  notifyStatus(
1203
1261
  el,
1204
1262
  e instanceof NotReadyError ? STATUS_PENDING : STATUS_ERROR,
@@ -1319,8 +1377,10 @@ function computed(fn, initialValue, options) {
1319
1377
  self._name = options?.name ?? "computed";
1320
1378
  self._prevHeap = self;
1321
1379
  const parent = context?._root ? context._parentComputed : context;
1322
- if (leafEffectActive && context) {
1323
- throw new Error("Cannot create reactive primitives inside createTrackedEffect");
1380
+ if (context?._childrenForbidden) {
1381
+ throw new Error(
1382
+ "Cannot create reactive primitives inside createTrackedEffect or owner-backed onSettled"
1383
+ );
1324
1384
  }
1325
1385
  if (context) {
1326
1386
  const lastChild = context._firstChild;
@@ -1357,6 +1417,7 @@ function signal(v, options, firewall = null) {
1357
1417
  const s = {
1358
1418
  _equals: options?.equals != null ? options.equals : isEqual,
1359
1419
  _pureWrite: !!options?.pureWrite,
1420
+ _noSnapshot: !!options?._noSnapshot,
1360
1421
  _unobserved: options?.unobserved,
1361
1422
  _value: v,
1362
1423
  _subs: null,
@@ -1368,7 +1429,11 @@ function signal(v, options, firewall = null) {
1368
1429
  };
1369
1430
  s._name = options?.name ?? "signal";
1370
1431
  firewall && (firewall._child = s);
1371
- if (snapshotCaptureActive && !s._pureWrite && !((firewall?._statusFlags ?? 0) & STATUS_PENDING)) {
1432
+ if (
1433
+ snapshotCaptureActive &&
1434
+ !s._noSnapshot &&
1435
+ !((firewall?._statusFlags ?? 0) & STATUS_PENDING)
1436
+ ) {
1372
1437
  s._snapshotValue = v === undefined ? NO_SNAPSHOT : v;
1373
1438
  snapshotSources.add(s);
1374
1439
  }
@@ -1436,13 +1501,11 @@ function read(el) {
1436
1501
  return value;
1437
1502
  }
1438
1503
  if (pendingCheckActive) {
1439
- const target = el._firewall || el;
1440
- const pendingSig = getPendingSignal(target);
1504
+ const firewall = el._firewall;
1441
1505
  const prevCheck = pendingCheckActive;
1442
1506
  pendingCheckActive = false;
1443
- if (read(pendingSig)) {
1444
- foundPending = true;
1445
- }
1507
+ if (read(getPendingSignal(el))) foundPending = true;
1508
+ if (firewall && read(getPendingSignal(firewall))) foundPending = true;
1446
1509
  pendingCheckActive = prevCheck;
1447
1510
  return el._value;
1448
1511
  }
@@ -1477,9 +1540,13 @@ function read(el) {
1477
1540
  }
1478
1541
  }
1479
1542
  if (owner._statusFlags & STATUS_PENDING) {
1480
- const _errSource = owner._error?.source;
1481
- if (_errSource && !(_errSource._statusFlags & STATUS_PENDING)) clearStatus(owner);
1482
- else if (c && !(stale && owner._transition && activeTransition !== owner._transition)) {
1543
+ if (c && !(stale && owner._transition && activeTransition !== owner._transition)) {
1544
+ if (c?._childrenForbidden) {
1545
+ console.warn(
1546
+ "Reading a pending async value inside createTrackedEffect or onSettled will throw. " +
1547
+ "Use createEffect instead which supports async-aware reactivity."
1548
+ );
1549
+ }
1483
1550
  if (currentOptimisticLane) {
1484
1551
  const pendingLane = owner._optimisticLane;
1485
1552
  const lane = findLane(currentOptimisticLane);
@@ -1515,8 +1582,10 @@ function read(el) {
1515
1582
  `Reactive value read directly in ${strictRead} will not update. ` +
1516
1583
  `Move it into a tracking scope (JSX, a memo, or an effect's compute function).`
1517
1584
  );
1518
- if (el._overrideValue !== undefined && el._overrideValue !== NOT_PENDING)
1585
+ if (el._overrideValue !== undefined && el._overrideValue !== NOT_PENDING) {
1586
+ if (c && stale && shouldReadStashedOptimisticValue(el)) return el._value;
1519
1587
  return el._overrideValue;
1588
+ }
1520
1589
  return !c ||
1521
1590
  (currentOptimisticLane !== null &&
1522
1591
  (el._overrideValue !== undefined ||
@@ -1529,7 +1598,7 @@ function read(el) {
1529
1598
  : el._pendingValue;
1530
1599
  }
1531
1600
  function setSignal(el, v) {
1532
- if (!el._pureWrite && !leafEffectActive && context && el._firewall !== context)
1601
+ if (!el._pureWrite && !context?._childrenForbidden && context && el._firewall !== context)
1533
1602
  console.warn("A Signal was written to in an owned scope.");
1534
1603
  if (el._transition && activeTransition !== el._transition)
1535
1604
  globalQueue.initTransition(el._transition);
@@ -1554,7 +1623,7 @@ function setSignal(el, v) {
1554
1623
  }
1555
1624
  if (isOptimistic) {
1556
1625
  const firstOverride = el._overrideValue === NOT_PENDING;
1557
- if (!firstOverride && el._transition) globalQueue.initTransition(el._transition);
1626
+ if (!firstOverride) globalQueue.initTransition(resolveTransition(el));
1558
1627
  if (firstOverride) {
1559
1628
  el._pendingValue = el._value;
1560
1629
  globalQueue._optimisticNodes.push(el);
@@ -1577,6 +1646,10 @@ function setSignal(el, v) {
1577
1646
  return v;
1578
1647
  }
1579
1648
  function runWithOwner(owner, fn) {
1649
+ if (owner && owner._flags & REACTIVE_DISPOSED)
1650
+ console.warn(
1651
+ "runWithOwner called with a disposed owner. Children created inside will never be disposed."
1652
+ );
1580
1653
  const oldContext = context;
1581
1654
  const prevTracking = tracking;
1582
1655
  context = owner;
@@ -1600,6 +1673,10 @@ function getPendingSignal(el) {
1600
1673
  }
1601
1674
  function computePendingState(el) {
1602
1675
  const comp = el;
1676
+ const firewall = el._firewall;
1677
+ if (firewall && el._pendingValue !== NOT_PENDING) {
1678
+ return !firewall._inFlight && !(firewall._statusFlags & STATUS_PENDING);
1679
+ }
1603
1680
  if (el._overrideValue !== undefined && el._overrideValue !== NOT_PENDING) {
1604
1681
  if (comp._statusFlags & STATUS_PENDING && !(comp._statusFlags & STATUS_UNINITIALIZED))
1605
1682
  return true;
@@ -1726,6 +1803,117 @@ function hasContext(context, owner) {
1726
1803
  function isUndefined(value) {
1727
1804
  return typeof value === "undefined";
1728
1805
  }
1806
+ function effect(compute, effect, error, initialValue, options) {
1807
+ let initialized = false;
1808
+ const node = computed(
1809
+ options?.render ? p => staleValues(() => compute(p)) : compute,
1810
+ initialValue,
1811
+ {
1812
+ ...options,
1813
+ equals: () => {
1814
+ node._modified = !node._error;
1815
+ if (initialized) node._queue.enqueue(node._type, runEffect.bind(node));
1816
+ return false;
1817
+ },
1818
+ lazy: true
1819
+ }
1820
+ );
1821
+ node._prevValue = initialValue;
1822
+ node._effectFn = effect;
1823
+ node._errorFn = error;
1824
+ node._cleanup = undefined;
1825
+ node._type = options?.render ? EFFECT_RENDER : EFFECT_USER;
1826
+ node._notifyStatus = (status, error) => {
1827
+ const actualStatus = status !== undefined ? status : node._statusFlags;
1828
+ const actualError = error !== undefined ? error : node._error;
1829
+ if (actualStatus & STATUS_ERROR) {
1830
+ let err = actualError;
1831
+ node._queue.notify(node, STATUS_PENDING, 0);
1832
+ if (node._type === EFFECT_USER) {
1833
+ try {
1834
+ return node._errorFn
1835
+ ? node._errorFn(err, () => {
1836
+ node._cleanup?.();
1837
+ node._cleanup = undefined;
1838
+ })
1839
+ : console.error(err);
1840
+ } catch (e) {
1841
+ err = e;
1842
+ }
1843
+ }
1844
+ if (!node._queue.notify(node, STATUS_ERROR, STATUS_ERROR)) throw err;
1845
+ } else if (node._type === EFFECT_RENDER) {
1846
+ node._queue.notify(node, STATUS_PENDING | STATUS_ERROR, actualStatus, actualError);
1847
+ if (_hitUnhandledAsync) {
1848
+ resetUnhandledAsync();
1849
+ const err = new Error("An async value must be rendered inside a Loading boundary.");
1850
+ if (!node._queue.notify(node, STATUS_ERROR, STATUS_ERROR)) throw err;
1851
+ }
1852
+ }
1853
+ };
1854
+ recompute(node, true);
1855
+ !options?.defer &&
1856
+ (node._type === EFFECT_USER
1857
+ ? node._queue.enqueue(node._type, runEffect.bind(node))
1858
+ : runEffect.call(node));
1859
+ initialized = true;
1860
+ onCleanup(() => node._cleanup?.());
1861
+ if (!node._parent)
1862
+ console.warn("Effects created outside a reactive context will never be disposed");
1863
+ }
1864
+ function runEffect() {
1865
+ if (!this._modified || this._flags & REACTIVE_DISPOSED) return;
1866
+ let prevStrictRead = false;
1867
+ {
1868
+ prevStrictRead = setStrictRead("an effect callback");
1869
+ }
1870
+ this._cleanup?.();
1871
+ this._cleanup = undefined;
1872
+ try {
1873
+ this._cleanup = this._effectFn(this._value, this._prevValue);
1874
+ } catch (error) {
1875
+ this._error = new StatusError(this, error);
1876
+ this._statusFlags |= STATUS_ERROR;
1877
+ if (!this._queue.notify(this, STATUS_ERROR, STATUS_ERROR)) throw error;
1878
+ } finally {
1879
+ setStrictRead(prevStrictRead);
1880
+ this._prevValue = this._value;
1881
+ this._modified = false;
1882
+ }
1883
+ }
1884
+ function trackedEffect(fn, options) {
1885
+ const run = () => {
1886
+ if (!node._modified || node._flags & REACTIVE_DISPOSED) return;
1887
+ node._modified = false;
1888
+ recompute(node);
1889
+ };
1890
+ const node = computed(
1891
+ () => {
1892
+ node._cleanup?.();
1893
+ node._cleanup = undefined;
1894
+ node._cleanup = staleValues(fn) || undefined;
1895
+ },
1896
+ undefined,
1897
+ { ...options, lazy: true }
1898
+ );
1899
+ node._cleanup = undefined;
1900
+ node._childrenForbidden = true;
1901
+ node._modified = true;
1902
+ node._type = EFFECT_TRACKED;
1903
+ node._notifyStatus = (status, error) => {
1904
+ const actualStatus = status !== undefined ? status : node._statusFlags;
1905
+ if (actualStatus & STATUS_ERROR) {
1906
+ node._queue.notify(node, STATUS_PENDING, 0);
1907
+ const err = error !== undefined ? error : node._error;
1908
+ if (!node._queue.notify(node, STATUS_ERROR, STATUS_ERROR)) throw err;
1909
+ }
1910
+ };
1911
+ node._run = run;
1912
+ node._queue.enqueue(EFFECT_USER, run);
1913
+ onCleanup(() => node._cleanup?.());
1914
+ if (!node._parent)
1915
+ console.warn("Effects created outside a reactive context will never be disposed");
1916
+ }
1729
1917
  function restoreTransition(transition, fn) {
1730
1918
  globalQueue.initTransition(transition);
1731
1919
  const result = fn();
@@ -1823,6 +2011,11 @@ function createReaction(effectFn, options) {
1823
2011
  };
1824
2012
  }
1825
2013
  function resolve(fn) {
2014
+ if (getObserver()) {
2015
+ throw new Error(
2016
+ "Cannot call resolve inside a reactive scope; it only resolves the current value and does not track updates."
2017
+ );
2018
+ }
1826
2019
  return new Promise((res, rej) => {
1827
2020
  createRoot(dispose => {
1828
2021
  computed(() => {
@@ -1845,7 +2038,8 @@ function createOptimistic(first, second, third) {
1845
2038
  return [accessor(node), setSignal.bind(null, node)];
1846
2039
  }
1847
2040
  function onSettled(callback) {
1848
- getOwner()
2041
+ const owner = getOwner();
2042
+ owner && !owner._childrenForbidden
1849
2043
  ? createTrackedEffect(() => untrack(callback))
1850
2044
  : globalQueue.enqueue(EFFECT_USER, () => {
1851
2045
  const cleanup = callback();
@@ -3018,7 +3212,7 @@ const ON_INIT = Symbol();
3018
3212
  class CollectionQueue extends Queue {
3019
3213
  _collectionType;
3020
3214
  _sources = new Set();
3021
- _disabled = signal(false, { pureWrite: true });
3215
+ _disabled = signal(false, { pureWrite: true, _noSnapshot: true });
3022
3216
  _initialized = false;
3023
3217
  _onFn;
3024
3218
  _prevOn = ON_INIT;
@@ -3048,6 +3242,9 @@ class CollectionQueue extends Queue {
3048
3242
  }
3049
3243
  if (this._collectionType & STATUS_PENDING && this._initialized)
3050
3244
  return super.notify(node, type, flags, error);
3245
+ if (this._collectionType & STATUS_PENDING && flags & STATUS_ERROR) {
3246
+ return super.notify(node, STATUS_ERROR, flags, error);
3247
+ }
3051
3248
  if (flags & this._collectionType) {
3052
3249
  const source = error?.source || node._error?.source;
3053
3250
  if (source) {
@@ -3061,7 +3258,11 @@ class CollectionQueue extends Queue {
3061
3258
  }
3062
3259
  checkSources() {
3063
3260
  for (const source of this._sources) {
3064
- if (!(source._statusFlags & this._collectionType)) this._sources.delete(source);
3261
+ if (
3262
+ !(source._statusFlags & this._collectionType) &&
3263
+ !(this._collectionType & STATUS_ERROR && source._statusFlags & STATUS_PENDING)
3264
+ )
3265
+ this._sources.delete(source);
3065
3266
  }
3066
3267
  if (!this._sources.size) {
3067
3268
  setSignal(this._disabled, false);
@@ -3074,6 +3275,8 @@ class CollectionQueue extends Queue {
3074
3275
  }
3075
3276
  }
3076
3277
  function createCollectionBoundary(type, fn, fallback, onFn) {
3278
+ if (!getOwner())
3279
+ console.warn("Boundaries created outside a reactive context will never be disposed.");
3077
3280
  const owner = createOwner();
3078
3281
  const queue = new CollectionQueue(type);
3079
3282
  if (onFn) queue._onFn = onFn;